flash.rb 5.2 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2
module ActionController #:nodoc:
  # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
3 4 5
  # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
  # action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
  # then expose the flash to its template. Actually, that exposure is automatically done. Example:
D
Initial  
David Heinemeier Hansson 已提交
6
  #
P
Pratik Naik 已提交
7
  #   class PostsController < ActionController::Base
D
Initial  
David Heinemeier Hansson 已提交
8 9
  #     def create
  #       # save post
10
  #       flash[:notice] = "Successfully created post"
P
Pratik Naik 已提交
11
  #       redirect_to posts_path(@post)
D
Initial  
David Heinemeier Hansson 已提交
12 13
  #     end
  #
P
Pratik Naik 已提交
14
  #     def show
D
Initial  
David Heinemeier Hansson 已提交
15 16 17 18
  #       # doesn't need to assign the flash notice to the template, that's done automatically
  #     end
  #   end
  #
P
Pratik Naik 已提交
19 20 21 22
  #   show.html.erb
  #     <% if flash[:notice] %>
  #       <div class="notice"><%= flash[:notice] %></div>
  #     <% end %>
D
Initial  
David Heinemeier Hansson 已提交
23
  #
24 25
  # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
  # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
26 27
  #
  # See docs on the FlashHash class for more details about the flash.
D
Initial  
David Heinemeier Hansson 已提交
28
  module Flash
29 30
    def self.included(base)
      base.class_eval do
31
        include InstanceMethods
J
Joshua Peek 已提交
32 33
        alias_method_chain :perform_action, :flash
        alias_method_chain :reset_session,  :flash
34 35
      end
    end
36

37
    class FlashNow #:nodoc:
38
      def initialize(flash)
39 40
        @flash = flash
      end
41

42 43 44 45 46
      def []=(k, v)
        @flash[k] = v
        @flash.discard(k)
        v
      end
47

48 49 50
      def [](k)
        @flash[k]
      end
51
    end
52

53 54 55 56 57
    class FlashHash < Hash
      def initialize #:nodoc:
        super
        @used = {}
      end
58

59 60 61 62
      def []=(k, v) #:nodoc:
        keep(k)
        super
      end
63

64
      def update(h) #:nodoc:
65
        h.keys.each { |k| keep(k) }
66 67
        super
      end
68

69
      alias :merge! :update
70

71
      def replace(h) #:nodoc:
72 73 74
        @used = {}
        super
      end
75

76 77
      # Sets a flash that will not be available to the next action, only to the current.
      #
78
      #     flash.now[:message] = "Hello current action"
79
      #
80 81 82 83 84 85 86
      # This method enables you to use the flash as a central messaging system in your app.
      # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
      # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
      # vanish when the current action is done.
      #
      # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
      def now
87
        FlashNow.new(self)
88
      end
89

90 91 92
      # Keeps either the entire current flash or a specific flash entry available for the next action:
      #
      #    flash.keep            # keeps the entire flash
93
      #    flash.keep(:notice)   # keeps only the "notice" entry, the rest of the flash is discarded
94
      def keep(k = nil)
95 96
        use(k, false)
      end
97

98
      # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
99
      #
100 101
      #     flash.discard              # discard the entire flash at the end of the current action
      #     flash.discard(:warning)    # discard only the "warning" entry at the end of the current action
102
      def discard(k = nil)
103 104
        use(k)
      end
105

106 107 108 109
      # Mark for removal entries that were kept, and delete unkept ones.
      #
      # This method is called automatically by filters, so you generally don't need to care about it.
      def sweep #:nodoc:
110
        keys.each do |k|
111 112 113 114 115 116
          unless @used[k]
            use(k)
          else
            delete(k)
            @used.delete(k)
          end
D
Initial  
David Heinemeier Hansson 已提交
117
        end
118

119 120
        # clean up after keys that could have been left over by calling reject! or shift on the flash
        (@used.keys - keys).each{ |k| @used.delete(k) }
121
      end
122

123
      private
124 125 126 127 128 129 130 131 132
        # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
        #     use()               # marks the entire flash as used
        #     use('msg')          # marks the "msg" entry as used
        #     use(nil, false)     # marks the entire flash as unused (keeps it around for one more action)
        #     use('msg', false)   # marks the "msg" entry as unused (keeps it around for one more action)
        def use(k=nil, v=true)
          unless k.nil?
            @used[k] = v
          else
133
            keys.each{ |key| use(key, v) }
134
          end
D
Initial  
David Heinemeier Hansson 已提交
135
        end
136
    end
137

D
David Heinemeier Hansson 已提交
138
    module InstanceMethods #:nodoc:
139
      protected
J
Joshua Peek 已提交
140 141 142 143 144
        def perform_action_with_flash
          perform_action_without_flash
          remove_instance_variable(:@_flash) if defined? @_flash
        end

145 146
        def reset_session_with_flash
          reset_session_without_flash
J
Joshua Peek 已提交
147
          remove_instance_variable(:@_flash) if defined? @_flash
148
        end
149

150 151 152 153
        # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
        # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
        # to put a new one.
        def flash #:doc:
J
Joshua Peek 已提交
154 155 156 157
          unless defined? @_flash
            @_flash = session["flash"] ||= FlashHash.new
            @_flash.sweep
          end
158

J
Joshua Peek 已提交
159
          @_flash
160
        end
161
    end
D
Initial  
David Heinemeier Hansson 已提交
162
  end
163
end