Rails源代码分析(6):ActionController::Flash

来源:互联网 发布:mac虚拟机占内存吗 编辑:程序博客网 时间:2024/04/29 01:01
Flash的作用:
The flash provides a way to pass temporary objects between actions. 
Anything you place in the flash will be exposed to the very next action and then cleared out.
 This is a great way of doing notices and alerts。

实际上它是利用session机制来传递对象。

看看代码:
  1.   module Flash
  2.     def self.included(base)
  3.       base.class_eval do
  4.         include InstanceMethods
  5.         alias_method_chain :assign_shortcuts:flash
  6.         alias_method_chain :reset_session,    :flash
  7.       end
  8.     end
  9.     
  10.     
  11.     class FlashNow #:nodoc:
  12.       def initialize(flash)
  13.         @flash = flash
  14.       end
  15.       
  16.       def []=(k, v)
  17.         @flash[k] = v
  18.         @flash.discard(k)
  19.         v
  20.       end
  21.       
  22.       def [](k)
  23.         @flash[k]
  24.       end
  25.     end
  26.     
  27.     class FlashHash < Hash
  28.       def initialize #:nodoc:
  29.         super
  30.         @used = {}
  31.       end
  32.       
  33.       def []=(k, v) #:nodoc:
  34.         keep(k)
  35.         super
  36.       end
  37.       
  38.       def update(h) #:nodoc:
  39.         h.keys.each { |k| keep(k) }
  40.         super
  41.       end
  42.       
  43.       alias :merge:update
  44.       
  45.       def replace(h) #:nodoc:
  46.         @used = {}
  47.         super
  48.       end
  49.     
  50.       # Sets a flash that will not be available to the next action, only to the current.
  51.       #
  52.       #     flash.now[:message] = "Hello current action"
  53.       
  54.       # This method enables you to use the flash as a central messaging system in your app.
  55.       # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
  56.       # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
  57.       # vanish when the current action is done.
  58.       #
  59.       # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
  60.       def now
  61.         FlashNow.new(self)
  62.       end
  63.     
  64.       # Keeps either the entire current flash or a specific flash entry available for the next action:
  65.       #
  66.       #    flash.keep            # keeps the entire flash
  67.       #    flash.keep(:notice)   # keeps only the "notice" entry, the rest of the flash is discarded
  68.       def keep(k = nil)
  69.         use(k, false)
  70.       end
  71.     
  72.       # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
  73.       #
  74.       #     flash.discard              # discard the entire flash at the end of the current action
  75.       #     flash.discard(:warning)    # discard only the "warning" entry at the end of the current action
  76.       def discard(k = nil)
  77.         use(k)
  78.       end
  79.     
  80.       # Mark for removal entries that were kept, and delete unkept ones.
  81.       #
  82.       # This method is called automatically by filters, so you generally don't need to care about it.
  83.       def sweep #:nodoc:
  84.         keys.each do |k| 
  85.           unless @used[k]
  86.             use(k)
  87.           else
  88.             delete(k)
  89.             @used.delete(k)
  90.           end
  91.         end
  92.         # clean up after keys that could have been left over by calling reject! or shift on the flash
  93.         (@used.keys - keys).each{ |k| @used.delete(k) }
  94.       end
  95.     
  96.       private
  97.         # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
  98.         #     use()               # marks the entire flash as used
  99.         #     use('msg')          # marks the "msg" entry as used
  100.         #     use(nil, false)     # marks the entire flash as unused (keeps it around for one more action)
  101.         #     use('msg', false)   # marks the "msg" entry as unused (keeps it around for one more action)
  102.         def use(k=nil, v=true)
  103.           unless k.nil?
  104.             @used[k] = v
  105.           else
  106.             keys.each{ |key| use(key, v) }
  107.           end
  108.         end
  109.     end
  110.     module InstanceMethods #:nodoc:
  111.       protected
  112.         def reset_session_with_flash
  113.           reset_session_without_flash
  114.           remove_instance_variable(:@_flash)
  115.           flash(:refresh)
  116.         end
  117.       
  118.         # Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or 
  119.         # <tt>flash["notice"] = "hello"</tt> to put a new one.
  120.         # Note that if sessions are disabled only flash.now will work.
  121.         def flash(refresh = false#:doc:
  122.           if !defined?(@_flash) || refresh
  123.             @_flash =
  124.               if session.is_a?(Hash)
  125.                 # don't put flash in session if disabled
  126.                 FlashHash.new
  127.               else
  128.                 # otherwise, session is a CGI::Session or a TestSession
  129.                 # so make sure it gets retrieved from/saved to session storage after request processing
  130.                 session["flash"] ||= FlashHash.new
  131.               end
  132.           end
  133.           @_flash
  134.         end
  135.       private
  136.         def assign_shortcuts_with_flash(request, response) #:nodoc:
  137.           assign_shortcuts_without_flash(request, response)
  138.           flash(:refresh)
  139.           flash.sweep if @_session && !component_request?
  140.         end
  141.     end
  142.   end
可以看到分为两个类:
FlashNow
FlashHash

FlashNow是FlashHash的包装类,就是为了实现一个机制:
Sets a flash that will not be available to the next action, only to the current.
FlashHash提供了工厂方法生成:
      def now
        FlashNow.new(self)
      end

FlashHash是继承Hash来实现,里面实现的一个关键点就是它用了一个@used类变量,利用use方法来设置
        def use(k=nil, v=true)
          unless k.nil?
            @used[k] = v
          else
            keys.each{ |key| use(key, v) }
          end
        end

实现跨方法调用的关键就是:
FlashHash类
      def []=(k, v)
        keep(k)
        super
      end
      
      keep将设置为没有使用
      def keep(k = nil)
        use(k, false)
      end

      如果还没有used的设置为use,如果used的删除这个值
      效果等于把用过的值删掉,没有用过的标记为用过的
      def sweep #:nodoc:
        keys.each do |k| 
          unless @used[k]
            use(k)
          else
            delete(k)
            @used.delete(k)
          end
        end

         将used的keys和这个hash的keys同步
        (@used.keys - keys).each{ |k| @used.delete(k) }
      end


       alias_method_chain :assign_shortcuts, :flash
       alias_method_chain :reset_session,    :flash
       这个实际上先重命名
       原assign_shortcuts => assign_shortcuts_without_flash
       原reset_session => reset_session_without_flash

      然后原方法调用就变为调用:
        def reset_session_with_flash
          reset_session_without_flash
          remove_instance_variable(:@_flash)
          flash(:refresh)
        end
      
        def assign_shortcuts_with_flash(request, response) #:nodoc:
          assign_shortcuts_without_flash(request, response)
          flash(:refresh)
          flash.sweep if @_session && !component_request?
        end

       flash.sweep就是清除了 
原创粉丝点击