shoulda-matchers 2.3.0 → 2.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +12 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +6 -11
  5. data/NEWS.md +6 -0
  6. data/features/rails_integration.feature +2 -3
  7. data/features/step_definitions/rails_steps.rb +40 -8
  8. data/gemfiles/3.0.gemfile +2 -1
  9. data/gemfiles/3.0.gemfile.lock +6 -6
  10. data/gemfiles/3.1.gemfile +2 -1
  11. data/gemfiles/3.1.gemfile.lock +6 -6
  12. data/gemfiles/3.2.gemfile +2 -1
  13. data/gemfiles/3.2.gemfile.lock +6 -6
  14. data/gemfiles/4.0.gemfile +17 -0
  15. data/gemfiles/4.0.gemfile.lock +152 -0
  16. data/lib/shoulda/matchers.rb +1 -0
  17. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  18. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +24 -10
  19. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +13 -7
  20. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +2 -2
  21. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +18 -6
  22. data/lib/shoulda/matchers/active_model/validation_matcher.rb +4 -2
  23. data/lib/shoulda/matchers/active_record.rb +1 -0
  24. data/lib/shoulda/matchers/active_record/association_matcher.rb +18 -37
  25. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +8 -3
  26. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +8 -3
  27. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +66 -4
  28. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +93 -0
  29. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +8 -3
  30. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -2
  31. data/lib/shoulda/matchers/integrations/test_unit.rb +10 -22
  32. data/lib/shoulda/matchers/rails_shim.rb +41 -0
  33. data/lib/shoulda/matchers/version.rb +1 -1
  34. data/shoulda-matchers.gemspec +1 -2
  35. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +7 -2
  36. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +2 -2
  37. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +1 -1
  38. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +24 -0
  39. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +24 -0
  40. data/spec/shoulda/matchers/active_model/helpers_spec.rb +5 -7
  41. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +46 -12
  42. data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +1 -1
  43. data/spec/support/active_model_versions.rb +6 -6
  44. metadata +26 -55
@@ -0,0 +1,93 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveRecord # :nodoc:
4
+ module AssociationMatchers
5
+ class OptionVerifier
6
+ delegate :reflection, to: :reflector
7
+
8
+ attr_reader :reflector
9
+
10
+ RELATION_OPTIONS = [:conditions, :order]
11
+
12
+ def initialize(reflector)
13
+ @reflector = reflector
14
+ end
15
+
16
+ def correct_for_string?(name, expected_value)
17
+ correct_for?(:string, name, expected_value)
18
+ end
19
+
20
+ def correct_for_boolean?(name, expected_value)
21
+ correct_for?(:boolean, name, expected_value)
22
+ end
23
+
24
+ def correct_for_hash?(name, expected_value)
25
+ correct_for?(:hash, name, expected_value)
26
+ end
27
+
28
+ def correct_for_relation_clause?(name, expected_value)
29
+ correct_for?(:relation_clause, name, expected_value)
30
+ end
31
+
32
+ def actual_value_for(name)
33
+ if RELATION_OPTIONS.include?(name)
34
+ actual_value_for_relation_clause(name)
35
+ else
36
+ method_name = "actual_value_for_#{name}"
37
+ if respond_to?(method_name, true)
38
+ __send__(method_name)
39
+ else
40
+ reflection.options[name]
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :reflector
48
+
49
+ def correct_for?(*args)
50
+ expected_value, name, type = args.reverse
51
+ if expected_value.nil?
52
+ true
53
+ else
54
+ expected_value = type_cast(type, expected_value_for(name, expected_value))
55
+ actual_value = type_cast(type, actual_value_for(name))
56
+ expected_value == actual_value
57
+ end
58
+ end
59
+
60
+ def type_cast(type, value)
61
+ case type
62
+ when :string, :relation_clause then value.to_s
63
+ when :boolean then !!value
64
+ when :hash then Hash(value).stringify_keys
65
+ else value
66
+ end
67
+ end
68
+
69
+ def expected_value_for(name, value)
70
+ if RELATION_OPTIONS.include?(name)
71
+ expected_value_for_relation_clause(name, value)
72
+ else
73
+ value
74
+ end
75
+ end
76
+
77
+ def expected_value_for_relation_clause(name, value)
78
+ relation = reflector.build_relation_with_clause(name, value)
79
+ reflector.extract_relation_clause_from(relation, name)
80
+ end
81
+
82
+ def actual_value_for_relation_clause(name)
83
+ reflector.extract_relation_clause_from(reflector.association_relation, name)
84
+ end
85
+
86
+ def actual_value_for_class_name
87
+ reflector.associated_class
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -16,9 +16,9 @@ module Shoulda # :nodoc:
16
16
  end
17
17
 
18
18
  def matches?(subject)
19
- subject = ModelReflector.new(subject, name)
19
+ self.subject = ModelReflector.new(subject, name)
20
20
 
21
- if subject.option_set_properly?(order, :order)
21
+ if option_verifier.correct_for_relation_clause?(:order, order)
22
22
  true
23
23
  else
24
24
  self.missing_option = "#{name} should be ordered by #{order}"
@@ -27,7 +27,12 @@ module Shoulda # :nodoc:
27
27
  end
28
28
 
29
29
  private
30
- attr_accessor :order, :name
30
+
31
+ attr_accessor :subject, :order, :name
32
+
33
+ def option_verifier
34
+ @option_verifier ||= OptionVerifier.new(subject)
35
+ end
31
36
  end
32
37
  end
33
38
  end
@@ -38,18 +38,23 @@ module Shoulda # :nodoc:
38
38
  end
39
39
 
40
40
  def through_association_correct?
41
- if subject.option_set_properly?(through, :through)
41
+ if option_verifier.correct_for_string?(:through, through)
42
42
  true
43
43
  else
44
44
  self.missing_option =
45
45
  "Expected #{name} to have #{name} through #{through}, " +
46
- "but got it through #{subject.option_string(:through)}"
46
+ "but got it through #{option_verifier.actual_value_for(:through)}"
47
47
  false
48
48
  end
49
49
  end
50
50
 
51
51
  private
52
+
52
53
  attr_accessor :through, :name, :subject
54
+
55
+ def option_verifier
56
+ @option_verifier ||= OptionVerifier.new(subject)
57
+ end
53
58
  end
54
59
  end
55
60
  end
@@ -1,10 +1,7 @@
1
- # :enddoc:
2
- require 'test/unit/testcase'
3
-
4
1
  if defined?(ActionController)
5
2
  require 'shoulda/matchers/action_controller'
6
3
 
7
- class ActionController::TestCase
4
+ ActionController::TestCase.class_eval do
8
5
  include Shoulda::Matchers::ActionController
9
6
  extend Shoulda::Matchers::ActionController
10
7
 
@@ -16,27 +13,18 @@ end
16
13
 
17
14
  if defined?(ActiveRecord)
18
15
  require 'shoulda/matchers/active_record'
19
- require 'shoulda/matchers/active_model'
20
16
 
21
- module Test
22
- module Unit
23
- class TestCase
24
- include Shoulda::Matchers::ActiveRecord
25
- extend Shoulda::Matchers::ActiveRecord
26
- include Shoulda::Matchers::ActiveModel
27
- extend Shoulda::Matchers::ActiveModel
28
- end
29
- end
17
+ ActiveSupport::TestCase.class_eval do
18
+ include Shoulda::Matchers::ActiveRecord
19
+ extend Shoulda::Matchers::ActiveRecord
30
20
  end
31
- elsif defined?(ActiveModel)
21
+ end
22
+
23
+ if defined?(ActiveModel)
32
24
  require 'shoulda/matchers/active_model'
33
25
 
34
- module Test
35
- module Unit
36
- class TestCase
37
- include Shoulda::Matchers::ActiveModel
38
- extend Shoulda::Matchers::ActiveModel
39
- end
40
- end
26
+ ActiveSupport::TestCase.class_eval do
27
+ include Shoulda::Matchers::ActiveModel
28
+ extend Shoulda::Matchers::ActiveModel
41
29
  end
42
30
  end
@@ -0,0 +1,41 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ class RailsShim # :nodoc:
4
+ def self.layouts_ivar
5
+ if rails_major_version >= 4
6
+ '@_layouts'
7
+ else
8
+ '@layouts'
9
+ end
10
+ end
11
+
12
+ def self.flashes_ivar
13
+ if rails_major_version >= 4
14
+ :@flashes
15
+ else
16
+ :@used
17
+ end
18
+ end
19
+
20
+ def self.clean_scope(klass)
21
+ if rails_major_version == 4
22
+ klass.all
23
+ else
24
+ klass.scoped
25
+ end
26
+ end
27
+
28
+ def self.validates_confirmation_of_error_attribute(matcher)
29
+ if rails_major_version == 4
30
+ matcher.confirmation_attribute
31
+ else
32
+ matcher.attribute
33
+ end
34
+ end
35
+
36
+ def self.rails_major_version
37
+ Rails::VERSION::MAJOR
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  module Shoulda
2
2
  module Matchers
3
- VERSION = '2.3.0'.freeze
3
+ VERSION = '2.4.0.rc1'.freeze
4
4
  end
5
5
  end
@@ -28,6 +28,5 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency('cucumber', '~> 1.1')
29
29
  s.add_development_dependency('rails', '~> 3.0')
30
30
  s.add_development_dependency('rake', '>= 0.9.2')
31
- s.add_development_dependency('rspec-rails', '~> 2.13')
32
- s.add_development_dependency('strong_parameters')
31
+ s.add_development_dependency('rspec-rails', '>= 2.13.1', '< 3')
33
32
  end
@@ -47,8 +47,13 @@ describe Shoulda::Matchers::ActionController::RenderWithLayoutMatcher do
47
47
  end
48
48
 
49
49
  def set_in_context_layout(layout)
50
- @layouts = Hash.new(0)
51
- @layouts[layout] = 1
50
+ layouts = Hash.new(0)
51
+ layouts[layout] = 1
52
+ self.instance_variable_set(layouts_ivar, layouts)
53
+ end
54
+
55
+ def layouts_ivar
56
+ Shoulda::Matchers::RailsShim.layouts_ivar
52
57
  end
53
58
 
54
59
  def controller_without_layout
@@ -1,12 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActionController::RouteMatcher do
3
+ describe Shoulda::Matchers::ActionController::RouteMatcher, type: :controller do
4
4
  context 'given a controller with a defined glob url' do
5
5
  it 'accepts glob route' do
6
6
  controller = define_controller('Examples').new
7
7
 
8
8
  define_routes do
9
- match 'examples/*id', :to => 'examples#example'
9
+ get 'examples/*id', :to => 'examples#example'
10
10
  end
11
11
 
12
12
  controller.should route(:get, '/examples/foo/bar').
@@ -59,7 +59,7 @@ describe Shoulda::Matchers::ActiveModel::AllowMassAssignmentOfMatcher do
59
59
  end
60
60
  end
61
61
 
62
- unless active_model_3_2?
62
+ unless active_model_3_2? || active_model_4_0?
63
63
  context 'an attribute on a class with no protected attributes' do
64
64
  it 'accepts being mass-assignable' do
65
65
  no_protected_attributes.should allow_mass_assignment_of(:attr)
@@ -56,6 +56,30 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
56
56
  end
57
57
  end
58
58
 
59
+ context 'an attribute where the message occurs on another attribute' do
60
+ it 'allows a good value' do
61
+ record_with_custom_validation.should \
62
+ allow_value('good value').for(:attr).with_message(/some message/, :against => :attr2)
63
+ end
64
+
65
+ it 'rejects a bad value' do
66
+ record_with_custom_validation.should_not \
67
+ allow_value('bad value').for(:attr).with_message(/some message/, :against => :attr2)
68
+ end
69
+
70
+ def record_with_custom_validation
71
+ define_model :example, :attr => :string, :attr2 => :string do
72
+ validate :custom_validation
73
+
74
+ def custom_validation
75
+ if self[:attr] != 'good value'
76
+ self.errors[:attr2] << 'some message'
77
+ end
78
+ end
79
+ end.new
80
+ end
81
+ end
82
+
59
83
  context "an attribute with a context-dependent validation" do
60
84
  context "without the validation context" do
61
85
  it "allows a bad value" do
@@ -55,6 +55,30 @@ describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher do
55
55
  end
56
56
  end
57
57
 
58
+ context 'an attribute where the message occurs on another attribute' do
59
+ it 'matches if the message is correct but the value is not' do
60
+ record_with_custom_validation.should \
61
+ matcher('bad value').for(:attr).with_message(/some message/, :against => :attr2)
62
+ end
63
+
64
+ it 'does not match if the value and message are both correct' do
65
+ record_with_custom_validation.should_not \
66
+ matcher('good value').for(:attr).with_message(/some message/, :against => :attr2)
67
+ end
68
+
69
+ def record_with_custom_validation
70
+ define_model :example, :attr => :string, :attr2 => :string do
71
+ validate :custom_validation
72
+
73
+ def custom_validation
74
+ if self[:attr] != 'good value'
75
+ self.errors[:attr2] << 'some message'
76
+ end
77
+ end
78
+ end.new
79
+ end
80
+ end
81
+
58
82
  def matcher(value)
59
83
  described_class.new(value)
60
84
  end
@@ -78,13 +78,11 @@ describe Shoulda::Matchers::ActiveModel::Helpers do
78
78
  end
79
79
  end
80
80
 
81
- if active_model_3_0?
82
- context 'if ActiveModel::Errors#generate_message behavior has changed' do
83
- it 'provides the right error message for validate_presence_of' do
84
- stub_active_model_message_generation(:type => :blank,
85
- :message => 'Behavior has diverged.')
86
- assert_presence_validation_has_correct_message
87
- end
81
+ context 'if ActiveModel::Errors#generate_message behavior has changed' do
82
+ it 'provides the right error message for validate_presence_of' do
83
+ stub_active_model_message_generation(:type => :blank,
84
+ :message => 'Behavior has diverged.')
85
+ assert_presence_validation_has_correct_message
88
86
  end
89
87
  end
90
88
  end
@@ -67,8 +67,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
67
67
 
68
68
  it 'accepts an association with a valid :conditions option' do
69
69
  define_model :parent, :adopter => :boolean
70
- define_model :child, :parent_id => :integer do
71
- belongs_to :parent, :conditions => { :adopter => true }
70
+ define_model(:child, :parent_id => :integer).tap do |model|
71
+ define_association_with_conditions(model, :belongs_to, :parent, :adopter => true)
72
72
  end
73
73
 
74
74
  Child.new.should belong_to(:parent).conditions(:adopter => true)
@@ -297,8 +297,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
297
297
 
298
298
  it 'accepts an association with a valid :conditions option' do
299
299
  define_model :child, :parent_id => :integer, :adopted => :boolean
300
- define_model :parent do
301
- has_many :children, :conditions => { :adopted => true }
300
+ define_model(:parent).tap do |model|
301
+ define_association_with_conditions(model, :has_many, :children, :adopted => true)
302
302
  end
303
303
 
304
304
  Parent.new.should have_many(:children).conditions(:adopted => true)
@@ -374,8 +374,13 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
374
374
 
375
375
  def having_many_children(options = {})
376
376
  define_model :child, :parent_id => :integer
377
- define_model :parent do
378
- has_many :children, options
377
+ define_model(:parent).tap do |model|
378
+ if options.key?(:order)
379
+ order = options.delete(:order)
380
+ define_association_with_order(model, :has_many, :children, order, options)
381
+ else
382
+ model.has_many :children, options
383
+ end
379
384
  end.new
380
385
  end
381
386
  end
@@ -449,8 +454,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
449
454
 
450
455
  it 'accepts an association with a valid :conditions option' do
451
456
  define_model :detail, :person_id => :integer, :disabled => :boolean
452
- define_model :person do
453
- has_one :detail, :conditions => { :disabled => true}
457
+ define_model(:person).tap do |model|
458
+ define_association_with_conditions(model, :has_one, :detail, :disabled => true)
454
459
  end
455
460
 
456
461
  Person.new.should have_one(:detail).conditions(:disabled => true)
@@ -524,8 +529,13 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
524
529
 
525
530
  def having_one_detail(options = {})
526
531
  define_model :detail, :person_id => :integer
527
- define_model :person do
528
- has_one :detail, options
532
+ define_model(:person).tap do |model|
533
+ if options.key?(:order)
534
+ order = options.delete(:order)
535
+ define_association_with_order(model, :has_one, :detail, order, options)
536
+ else
537
+ model.has_one :detail, options
538
+ end
529
539
  end.new
530
540
  end
531
541
  end
@@ -565,8 +575,8 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
565
575
 
566
576
  it 'accepts an association with a valid :conditions option' do
567
577
  define_model :relative, :adopted => :boolean
568
- define_model :person do
569
- has_and_belongs_to_many :relatives, :conditions => { :adopted => true }
578
+ define_model(:person).tap do |model|
579
+ define_association_with_conditions(model, :has_and_belongs_to_many, :relatives, :adopted => true)
570
580
  end
571
581
  define_model :people_relative, :id => false, :person_id => :integer,
572
582
  :relative_id => :integer
@@ -638,4 +648,28 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
638
648
  end.new
639
649
  end
640
650
  end
651
+
652
+ def define_association_with_conditions(model, macro, name, conditions, other_options={})
653
+ args = []
654
+ options = {}
655
+ if Shoulda::Matchers::RailsShim.rails_major_version == 4
656
+ args << lambda { where(conditions) }
657
+ else
658
+ options[:conditions] = conditions
659
+ end
660
+ args << options
661
+ model.__send__(macro, name, *args)
662
+ end
663
+
664
+ def define_association_with_order(model, macro, name, order, other_options={})
665
+ args = []
666
+ options = {}
667
+ if Shoulda::Matchers::RailsShim.rails_major_version == 4
668
+ args << lambda { order(order) }
669
+ else
670
+ options[:order] = order
671
+ end
672
+ args << options
673
+ model.__send__(macro, name, *args)
674
+ end
641
675
  end