0148fb5c04285fcb2bca32b8cbd1fc472881e9ee
[depot.git] / flash.rb
1 module ActionController #:nodoc:
2 # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
3 # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
4 # action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
5 # then expose the flash to its template. Actually, that exposure is automatically done. Example:
6 #
7 # class WeblogController < ActionController::Base
8 # def create
9 # # save post
10 # flash[:notice] = "Successfully created post"
11 # redirect_to :action => "display", :params => { :id => post.id }
12 # end
13 #
14 # def display
15 # # doesn't need to assign the flash notice to the template, that's done automatically
16 # end
17 # end
18 #
19 # display.erb
20 # <% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
21 #
22 # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
23 # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
24 #
25 # See docs on the FlashHash class for more details about the flash.
26 module Flash
27 def self.included(base)
28 base.class_eval do
29 include InstanceMethods
30 alias_method_chain :assign_shortcuts, :flash
31 alias_method_chain :reset_session, :flash
32 end
33 end
34
35
36 class FlashNow #:nodoc:
37 def initialize(flash)
38 @flash = flash
39 end
40
41 def []=(k, v)
42 @flash[k] = v
43 @flash.discard(k)
44 v
45 end
46
47 def [](k)
48 @flash[k]
49 end
50 end
51
52 class FlashHash < Hash
53 def initialize #:nodoc:
54 super
55 @used = {}
56 end
57
58 def []=(k, v) #:nodoc:
59 keep(k)
60 super
61 end
62
63 def update(h) #:nodoc:
64 h.keys.each { |k| keep(k) }
65 super
66 end
67
68 alias :merge! :update
69
70 def replace(h) #:nodoc:
71 @used = {}
72 super
73 end
74
75 # Sets a flash that will not be available to the next action, only to the current.
76 #
77 # flash.now[:message] = "Hello current action"
78 #
79 # This method enables you to use the flash as a central messaging system in your app.
80 # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
81 # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
82 # vanish when the current action is done.
83 #
84 # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
85 def now
86 FlashNow.new(self)
87 end
88
89 # Keeps either the entire current flash or a specific flash entry available for the next action:
90 #
91 # flash.keep # keeps the entire flash
92 # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
93 def keep(k = nil)
94 use(k, false)
95 end
96
97 # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
98 #
99 # flash.discard # discard the entire flash at the end of the current action
100 # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
101 def discard(k = nil)
102 use(k)
103 end
104
105 # Mark for removal entries that were kept, and delete unkept ones.
106 #
107 # This method is called automatically by filters, so you generally don't need to care about it.
108 def sweep #:nodoc:
109 keys.each do |k|
110 unless @used[k]
111 use(k)
112 else
113 delete(k)
114 @used.delete(k)
115 end
116 end
117
118 # clean up after keys that could have been left over by calling reject! or shift on the flash
119 (@used.keys - keys).each{ |k| @used.delete(k) }
120 end
121
122 private
123 # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
124 # use() # marks the entire flash as used
125 # use('msg') # marks the "msg" entry as used
126 # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
127 # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
128 def use(k=nil, v=true)
129 unless k.nil?
130 @used[k] = v
131 else
132 keys.each{ |key| use(key, v) }
133 end
134 end
135 end
136
137 module InstanceMethods #:nodoc:
138 protected
139 def reset_session_with_flash
140 reset_session_without_flash
141 remove_instance_variable(:@_flash)
142 flash(:refresh)
143 end
144
145 # Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or
146 # <tt>flash["notice"] = "hello"</tt> to put a new one.
147 # Note that if sessions are disabled only flash.now will work.
148 def flash(refresh = false) #:doc:
149 if !defined?(@_flash) || refresh
150 @_flash =
151 if session.is_a?(Hash)
152 # don't put flash in session if disabled
153 FlashHash.new
154 else
155 # otherwise, session is a CGI::Session or a TestSession
156 # so make sure it gets retrieved from/saved to session storage after request processing
157 session["flash"] ||= FlashHash.new
158 end
159 end
160
161 @_flash
162 end
163
164 private
165 def assign_shortcuts_with_flash(request, response) #:nodoc:
166 assign_shortcuts_without_flash(request, response)
167 flash(:refresh)
168 flash.sweep if @_session && !component_request?
169 end
170 end
171 end
172 end