Skip to content

Commit

Permalink
Consolidate set_session and set_flash APIs
Browse files Browse the repository at this point in the history
* Refactor so they both use SetSessionOrFlashMatcher internally
* Remove `set_session['key']` in favor of `set_session('key')`
* `set_flash['key'].to(nil)` no longer works if set_flash has never been
  set
  • Loading branch information
mcmire committed Feb 9, 2015
1 parent 801f2c7 commit 535fe05
Show file tree
Hide file tree
Showing 10 changed files with 580 additions and 651 deletions.
8 changes: 7 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
* `ensure_inclusion_of`, `ensure_exclusion_of`, and `ensure_length_of` have been
removed in favor of their `validate_*` counterparts.

* `set_the_flash` has been removed in favor of `set_flash`.
* `set_the_flash` and `set_session` have been changed to more closely align with
each other:
* `set_the_flash` has been removed in favor of `set_flash`.
* `set_session('foo')` is no longer valid syntax, please use
`set_session['foo']` instead.
* `set_session['key'].to(nil)` will no longer pass when the key in question
has not been set yet.

# 2.8.0

Expand Down
2 changes: 2 additions & 0 deletions lib/shoulda/matchers/action_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
require 'shoulda/matchers/action_controller/callback_matcher'
require 'shoulda/matchers/action_controller/strong_parameters_matcher'
require 'shoulda/matchers/action_controller/set_session_or_flash_matcher'
require 'shoulda/matchers/action_controller/flash_store'
require 'shoulda/matchers/action_controller/session_store'

module Shoulda
module Matchers
Expand Down
95 changes: 95 additions & 0 deletions lib/shoulda/matchers/action_controller/flash_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
module Shoulda
module Matchers
module ActionController
# @private
class FlashStore
def self.future
new
end

def self.now
new.use_now!
end

attr_accessor :controller

def initialize
@use_now = false
end

def name
if @use_now
'flash.now'
else
'flash'
end
end

def has_key?(key)
values_to_check.include?(key)
end

def has_value?(expected_value)
values_to_check.values.any? do |actual_value|
expected_value === actual_value
end
end

def empty?
flash.empty?
end

def use_now!
@use_now = true
self
end

private

def flash
@_flash ||= copy_of_flash_from_controller
end

def copy_of_flash_from_controller
controller.flash.dup.tap do |flash|
copy_flashes(controller.flash, flash)
copy_discard_if_necessary(controller.flash, flash)
# sweep_flash_if_necessary(flash)
end
end

def copy_flashes(original_flash, new_flash)
flashes = original_flash.instance_variable_get('@flashes').dup
new_flash.instance_variable_set('@flashes', flashes)
end

def copy_discard_if_necessary(original_flash, new_flash)
discard = original_flash.instance_variable_get('@discard').dup
new_flash.instance_variable_set('@discard', discard)
end

def sweep_flash_if_necessary(flash)
unless @use_now
flash.sweep
end
end

def set_values
flash.instance_variable_get('@flashes')
end

def keys_to_discard
flash.instance_variable_get('@discard')
end

def values_to_check
if @use_now
set_values.slice(*keys_to_discard.to_a)
else
set_values.except(*keys_to_discard.to_a)
end
end
end
end
end
end
34 changes: 34 additions & 0 deletions lib/shoulda/matchers/action_controller/session_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module Shoulda
module Matchers
module ActionController
# @private
class SessionStore
attr_accessor :controller

def name
'session'
end

def has_key?(key)
session.key?(key)
end

def has_value?(expected_value)
session.values.any? do |actual_value|
expected_value === actual_value
end
end

def empty?
session.empty?
end

private

def session
controller.session
end
end
end
end
end
155 changes: 30 additions & 125 deletions lib/shoulda/matchers/action_controller/set_flash_matcher.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'forwardable'

module Shoulda
module Matchers
module ActionController
Expand Down Expand Up @@ -146,151 +148,54 @@ module ActionController
# @return [SetFlashMatcher]
#
def set_flash
SetFlashMatcher.new
SetFlashMatcher.new.in_context(self)
end

# @private
class SetFlashMatcher
extend Forwardable

def_delegators :underlying_matcher,
:description,
:matches?,
:failure_message,
:failure_message_when_negated
alias_method \
:failure_message_for_should,
:failure_message
alias_method \
:failure_message_for_should_not,
:failure_message_when_negated

def initialize
@options = {}
@value = nil
store = FlashStore.future
@underlying_matcher = SetSessionOrFlashMatcher.new(store)
end

def to(value)
if !value.is_a?(String) && !value.is_a?(Regexp)
raise "cannot match against #{value.inspect}"
end
@value = value
def now
store = FlashStore.now
@underlying_matcher = SetSessionOrFlashMatcher.new(store)
self
end

def now
@options[:now] = true
def in_context(context)
underlying_matcher.in_context(context)
self
end

def [](key)
@options[:key] = key
underlying_matcher[key]
self
end

def matches?(controller)
@controller = controller
sets_the_flash? && string_value_matches? && regexp_value_matches?
end

def description
description = "set the #{expected_flash_invocation}"
description << " to #{@value.inspect}" unless @value.nil?
description
end

def failure_message
"Expected #{expectation}"
end
alias failure_message_for_should failure_message

def failure_message_when_negated
"Did not expect #{expectation}"
end
alias failure_message_for_should_not failure_message_when_negated

private

def sets_the_flash?
flash_values.any?
end

def string_value_matches?
if @value.is_a?(String)
flash_values.any? {|value| value == @value }
else
true
end
end

def regexp_value_matches?
if @value.is_a?(Regexp)
flash_values.any? {|value| value =~ @value }
else
true
end
end

def flash_values
if @options.key?(:key)
flash_hash = HashWithIndifferentAccess.new(flash.to_hash)
[flash_hash[@options[:key]]]
else
flash.to_hash.values
end
end

def flash
@flash ||= copy_of_flash_from_controller
end

def copy_of_flash_from_controller
@controller.flash.dup.tap do |flash|
copy_flashes(@controller.flash, flash)
copy_discard_if_necessary(@controller.flash, flash)
sweep_flash_if_necessary(flash)
end
end

def copy_flashes(original_flash, new_flash)
flashes = original_flash.instance_variable_get('@flashes').dup
new_flash.instance_variable_set('@flashes', flashes)
end

def copy_discard_if_necessary(original_flash, new_flash)
discard_ivar = :@discard
if original_flash.instance_variable_defined?(discard_ivar)
discard = original_flash.instance_variable_get(discard_ivar).dup
new_flash.instance_variable_set(discard_ivar, discard)
end
end

def sweep_flash_if_necessary(flash)
unless @options[:now]
flash.sweep
end
end

def expectation
expectation = "the #{expected_flash_invocation} to be set"
expectation << " to #{@value.inspect}" unless @value.nil?
expectation << ", but #{flash_description}"
expectation
end

def flash_description
if flash.blank?
'no flash was set'
else
"was #{flash.inspect}"
end
end

def expected_flash_invocation
"flash#{pretty_now}#{pretty_key}"
def to(expected_value = nil, &block)
underlying_matcher.to(expected_value, &block)
self
end

def pretty_now
if @options[:now]
'.now'
else
''
end
end
protected

def pretty_key
if @options[:key]
"[:#{@options[:key]}]"
else
''
end
end
attr_reader :underlying_matcher
end
end
end
Expand Down

0 comments on commit 535fe05

Please sign in to comment.