aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG17
-rw-r--r--actionpack/lib/action_controller/flash.rb154
-rw-r--r--actionpack/lib/action_controller/test_process.rb4
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb3
-rw-r--r--actionpack/test/controller/flash_test.rb35
5 files changed, 175 insertions, 38 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index dcaa5cecd4..96895a9529 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,22 @@
*SVN*
+* Added a much improved Flash module that allows for finer-grained control on expiration and allows you to flash the current action #839 [Caio Chassot]. Example of flash.now:
+
+ class SomethingController < ApplicationController
+ def save
+ ...
+ if @something.save
+ # will redirect, use flash
+ flash['message'] = 'Save successful'
+ redirect_to :action => 'list'
+ else
+ # no redirect, message is for current action, use flash.now
+ flash.now['message'] = 'Save failed, review'
+ render_action 'edit'
+ end
+ end
+ end
+
* Added to_param call for parameters when composing an url using url_for from something else than strings #812 [Sam Stephenson]. Example:
class Page
diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/flash.rb
index b4e8d75bf6..e3a8a8c8c4 100644
--- a/actionpack/lib/action_controller/flash.rb
+++ b/actionpack/lib/action_controller/flash.rb
@@ -21,45 +21,141 @@ module ActionController #:nodoc:
#
# 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.
+ #
+ # See docs on the FlashHash class for more details about the flash.
module Flash
+
def self.append_features(base) #:nodoc:
super
base.before_filter(:fire_flash)
- base.after_filter(:clear_flash)
+ base.after_filter(:sweep_flash)
end
- protected
- # 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:
- if @session["flash"].nil?
- @session["flash"] = {}
- @session["flashes"] ||= 0
+ class FlashNow #:nodoc:
+ def initialize flash
+ @flash = flash
+ end
+
+ def []=(k, v)
+ @flash[k] = v
+ @flash.discard(k)
+ v
+ end
+ end
+
+ class FlashHash < Hash
+
+ def initialize #:nodoc:
+ super
+ @used = {}
+ end
+
+ def []=(k, v) #:nodoc:
+ keep(k)
+ super
+ end
+
+ def update h #:nodoc:
+ h.keys.each{|k| discard k }
+ super
+ end
+
+ alias merge! update
+
+ def replace h #:nodoc:
+ @used = {}
+ super
+ end
+
+ # Sets a flash that will not be available to the next action, only to the current.
+ #
+ # flash.now["message"] = "Hello current action"
+ #
+ # 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
+ FlashNow.new self
+ end
+
+ # Keeps either the entire current flash or a specific flash entry available for the next action:
+ #
+ # flash.keep # keeps the entire flash
+ # flash.keep("notice") # keeps only the "notice" entry, the rest of the flash is discarded
+ def keep(k=nil)
+ use(k, false)
+ end
+
+ # Marks the entire flash or a single flash entry to be discarded by the end of the current action
+ #
+ # flash.keep # keep entire flash available for the next action
+ # flash.discard('warning') # discard the "warning" entry (it'll still be available for the current action)
+ def discard(k=nil)
+ use(k)
+ end
+
+ # 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:
+ keys.each do |k|
+ unless @used[k]
+ use(k)
+ else
+ delete(k)
+ @used.delete(k)
+ end
end
- @session["flash"]
- end
-
- # Can be called by any action that would like to keep the current content of the flash around for one more action.
- def keep_flash #:doc:
- @session["flashes"] = 0
- end
-
- private
- # Records that the contents of @session["flash"] was flashed to the action
- def fire_flash
- if @session["flash"]
- @session["flashes"] += 1 unless @session["flash"].empty?
- @assigns["flash"] = @session["flash"]
+ (@used.keys - keys).each{|k| @used.delete k } # clean up after keys that could have been left over by calling reject! or shift on the flash
+ end
+
+ private
+
+ # 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
- @assigns["flash"] = {}
+ keys.each{|key| use key, v }
end
end
- def clear_flash
- if @session["flash"] && (@session["flashes"].nil? || @session["flashes"] >= 1)
- @session["flash"] = {}
- @session["flashes"] = 0
- end
- end
+ end
+
+
+ protected
+
+ # 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:
+ @session['flash'] ||= FlashHash.new
+ end
+
+ # deprecated. use <tt>flash.keep</tt> instead
+ def keep_flash #:doc:
+ flash.keep
+ end
+
+
+ private
+
+ # marks flash entries as used and expose the flash to the view
+ def fire_flash
+ flash.discard
+ @assigns["flash"] = flash
+ end
+
+ # deletes the flash entries that were not marked for keeping
+ def sweep_flash
+ flash.sweep
+ end
+
end
end
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index e3ab045a12..3d9a335eff 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -168,7 +168,7 @@ module ActionController #:nodoc:
# do we have a flash?
def has_flash?
- !session['flash'].nil?
+ !session['flash'].empty?
end
# do we have a flash that has contents?
@@ -283,4 +283,4 @@ module Test
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 48d7ed2f3f..4eadbf4e34 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -179,7 +179,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
# test the flash-based assertions with no flash at all
def test_flash_assertions_negative
process :nothing
- assert_flash_not_exists
assert_flash_empty
assert_flash_has_no 'hello'
assert_flash_has_no 'qwerty'
@@ -230,7 +229,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
# check the empty flashing
def test_flash_me_naked
process :flash_me_naked
- assert @response.has_flash?
+ assert !@response.has_flash?
assert !@response.has_flash_with_contents?
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 033477fe39..3a6e828bc8 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -7,12 +7,26 @@ class FlashTest < Test::Unit::TestCase
render_text "hello"
end
+ def set_flash_now
+ flash.now["that"] = "hello"
+ @flash_copy = {}.update flash
+ render_text "hello"
+ end
+
+ def attempt_to_use_flash_now
+ @flash_copy = {}.update flash
+ @flashy = flash["that"]
+ render_text "hello"
+ end
+
def use_flash
+ @flash_copy = {}.update flash
@flashy = flash["that"]
render_text "hello"
end
def use_flash_and_keep_it
+ @flash_copy = {}.update flash
@flashy = flash["that"]
keep_flash
render_text "hello"
@@ -33,11 +47,11 @@ class FlashTest < Test::Unit::TestCase
@request.action = "use_flash"
first_response = process_request
- assert_equal "hello", first_response.template.assigns["flash"]["that"]
+ assert_equal "hello", first_response.template.assigns["flash_copy"]["that"]
assert_equal "hello", first_response.template.assigns["flashy"]
second_response = process_request
- assert_nil second_response.template.assigns["flash"]["that"], "On second flash"
+ assert_nil second_response.template.assigns["flash_copy"]["that"], "On second flash"
end
def test_keep_flash
@@ -46,17 +60,28 @@ class FlashTest < Test::Unit::TestCase
@request.action = "use_flash_and_keep_it"
first_response = process_request
- assert_equal "hello", first_response.template.assigns["flash"]["that"]
+ assert_equal "hello", first_response.template.assigns["flash_copy"]["that"]
assert_equal "hello", first_response.template.assigns["flashy"]
@request.action = "use_flash"
second_response = process_request
- assert_equal "hello", second_response.template.assigns["flash"]["that"], "On second flash"
+ assert_equal "hello", second_response.template.assigns["flash_copy"]["that"], "On second flash"
third_response = process_request
- assert_nil third_response.template.assigns["flash"]["that"], "On third flash"
+ assert_nil third_response.template.assigns["flash_copy"]["that"], "On third flash"
end
+ def test_flash_now
+ @request.action = "set_flash_now"
+ response = process_request
+ assert_equal "hello", response.template.assigns["flash_copy"]["that"]
+
+ @request.action = "attempt_to_use_flash_now"
+ first_response = process_request
+ assert_nil first_response.template.assigns["flash_copy"]["that"]
+ assert_nil first_response.template.assigns["flashy"]
+ end
+
private
def initialize_request_and_response
@request = ActionController::TestRequest.new