shoulda-matchers 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -4
  3. data/Appraisals +19 -7
  4. data/Gemfile.lock +1 -1
  5. data/NEWS.md +35 -0
  6. data/README.md +1204 -46
  7. data/features/step_definitions/rails_steps.rb +1 -1
  8. data/gemfiles/3.0.gemfile.lock +1 -1
  9. data/gemfiles/3.1.gemfile.lock +1 -1
  10. data/gemfiles/3.2.gemfile.lock +1 -1
  11. data/gemfiles/{4.0.gemfile → 4.0.0.gemfile} +4 -4
  12. data/gemfiles/{4.0.gemfile.lock → 4.0.0.gemfile.lock} +24 -24
  13. data/gemfiles/4.0.1.gemfile +19 -0
  14. data/gemfiles/4.0.1.gemfile.lock +161 -0
  15. data/lib/shoulda/matchers/action_controller.rb +1 -0
  16. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +4 -2
  17. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +6 -3
  18. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +6 -3
  19. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +4 -2
  20. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +4 -2
  21. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +4 -2
  22. data/lib/shoulda/matchers/action_controller/route_matcher.rb +13 -19
  23. data/lib/shoulda/matchers/action_controller/route_params.rb +47 -0
  24. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +4 -2
  25. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +4 -2
  26. data/lib/shoulda/matchers/active_model.rb +4 -3
  27. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +9 -6
  28. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +4 -2
  29. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -4
  30. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +4 -1
  31. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +8 -1
  32. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +4 -2
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +59 -0
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_even_number_matcher.rb +51 -0
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +41 -0
  36. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +77 -0
  37. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +14 -12
  38. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +6 -6
  39. data/lib/shoulda/matchers/active_model/validation_matcher.rb +10 -7
  40. data/lib/shoulda/matchers/active_record.rb +2 -0
  41. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -2
  42. data/lib/shoulda/matchers/active_record/association_matcher.rb +11 -3
  43. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +80 -0
  44. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +20 -55
  45. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +40 -0
  46. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +4 -2
  47. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +4 -2
  48. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +7 -4
  49. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +4 -2
  50. data/lib/shoulda/matchers/integrations/test_unit.rb +3 -3
  51. data/lib/shoulda/matchers/rails_shim.rb +14 -6
  52. data/lib/shoulda/matchers/version.rb +1 -1
  53. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +1 -1
  54. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +2 -2
  55. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +5 -0
  56. data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
  57. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +1 -1
  58. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +1 -1
  59. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +2 -2
  60. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +10 -0
  61. data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +7 -0
  62. data/spec/shoulda/matchers/active_model/{comparison_matcher_spec.rb → numericality_matchers/comparison_matcher_spec.rb} +2 -2
  63. data/spec/shoulda/matchers/active_model/{odd_even_number_matcher_spec.rb → numericality_matchers/odd_even_number_matcher_spec.rb} +4 -4
  64. data/spec/shoulda/matchers/active_model/{only_integer_matcher_spec.rb → numericality_matchers/only_integer_matcher_spec.rb} +3 -3
  65. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
  66. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +7 -7
  67. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +48 -38
  68. data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +6 -6
  69. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +21 -8
  70. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
  71. data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +1 -1
  72. data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +3 -3
  73. data/spec/spec_helper.rb +9 -15
  74. data/spec/support/active_resource_builder.rb +2 -0
  75. data/spec/support/controller_builder.rb +4 -10
  76. data/spec/support/model_builder.rb +6 -2
  77. data/spec/support/rails_versions.rb +18 -0
  78. data/spec/support/shared_examples/numerical_submatcher_spec.rb +4 -4
  79. data/spec/support/test_application.rb +97 -0
  80. metadata +30 -14
  81. data/lib/shoulda/matchers/active_model/comparison_matcher.rb +0 -57
  82. data/lib/shoulda/matchers/active_model/odd_even_number_matcher.rb +0 -47
  83. data/lib/shoulda/matchers/active_model/only_integer_matcher.rb +0 -37
@@ -3,6 +3,9 @@ module Shoulda # :nodoc:
3
3
  module ActiveRecord # :nodoc:
4
4
  module AssociationMatchers
5
5
  class ModelReflector
6
+ delegate :associated_class, :through?, :join_table,
7
+ :association_relation, to: :reflection
8
+
6
9
  def initialize(subject, name)
7
10
  @subject = subject
8
11
  @name = name
@@ -13,55 +16,15 @@ module Shoulda # :nodoc:
13
16
  end
14
17
 
15
18
  def reflect_on_association(name)
16
- model_class.reflect_on_association(name)
17
- end
18
-
19
- def model_class
20
- subject.class
21
- end
19
+ reflection = model_class.reflect_on_association(name)
22
20
 
23
- def associated_class
24
- reflection.klass
25
- end
26
-
27
- def through?
28
- reflection.options[:through]
29
- end
30
-
31
- def join_table
32
- if reflection.respond_to? :join_table
33
- reflection.join_table.to_s
34
- else
35
- reflection.options[:join_table].to_s
21
+ if reflection
22
+ ModelReflection.new(reflection)
36
23
  end
37
24
  end
38
25
 
39
- def association_relation
40
- if reflection.respond_to?(:scope) && reflection.scope
41
- relation_from_scope(reflection.scope)
42
- else
43
- options = reflection.options
44
- relation = RailsShim.clean_scope(reflection.klass)
45
- if options[:conditions]
46
- relation = relation.where(options[:conditions])
47
- end
48
- if options[:include]
49
- relation = relation.include(options[:include])
50
- end
51
- if options[:order]
52
- relation = relation.order(options[:order])
53
- end
54
- if options[:group]
55
- relation = relation.group(options[:group])
56
- end
57
- if options[:having]
58
- relation = relation.having(options[:having])
59
- end
60
- if options[:limit]
61
- relation = relation.limit(options[:limit])
62
- end
63
- relation
64
- end
26
+ def model_class
27
+ subject.class
65
28
  end
66
29
 
67
30
  def build_relation_with_clause(name, value)
@@ -74,24 +37,26 @@ module Shoulda # :nodoc:
74
37
 
75
38
  def extract_relation_clause_from(relation, name)
76
39
  case name
77
- when :conditions then relation.where_values_hash
78
- when :order then relation.order_values.join(', ')
79
- else raise ArgumentError, "Unknown clause '#{name}'"
40
+ when :conditions
41
+ relation.where_values_hash
42
+ when :order
43
+ relation.order_values.map { |value| value_as_sql(value) }.join(', ')
44
+ else
45
+ raise ArgumentError, "Unknown clause '#{name}'"
80
46
  end
81
47
  end
82
48
 
83
49
  private
84
50
 
85
- def relation_from_scope(scope)
86
- # Source: AR::Associations::AssociationScope#eval_scope
87
- if scope.is_a?(::Proc)
88
- associated_class.all.instance_exec(subject, &scope)
51
+ attr_reader :subject, :name
52
+
53
+ def value_as_sql(value)
54
+ if value.respond_to?(:to_sql)
55
+ value.to_sql
89
56
  else
90
- scope
57
+ value
91
58
  end
92
59
  end
93
-
94
- attr_reader :subject, :name
95
60
  end
96
61
  end
97
62
  end
@@ -0,0 +1,40 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveRecord # :nodoc:
4
+ module AssociationMatchers
5
+ class SourceMatcher
6
+ attr_accessor :missing_option
7
+
8
+ def initialize(source, name)
9
+ @source = source
10
+ @name = name
11
+ @missing_option = ''
12
+ end
13
+
14
+ def description
15
+ "source => #{source}"
16
+ end
17
+
18
+ def matches?(subject)
19
+ self.subject = ModelReflector.new(subject, name)
20
+
21
+ if option_verifier.correct_for_string?(:source, source)
22
+ true
23
+ else
24
+ self.missing_option = "#{name} should have #{source} as source option"
25
+ false
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ attr_accessor :subject, :source, :name
32
+
33
+ def option_verifier
34
+ @option_verifier ||= OptionVerifier.new(subject)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -51,13 +51,15 @@ module Shoulda # :nodoc:
51
51
  correct_primary?
52
52
  end
53
53
 
54
- def failure_message_for_should
54
+ def failure_message
55
55
  "Expected #{expectation} (#{@missing})"
56
56
  end
57
+ alias failure_message_for_should failure_message
57
58
 
58
- def failure_message_for_should_not
59
+ def failure_message_when_negated
59
60
  "Did not expect #{expectation}"
60
61
  end
62
+ alias failure_message_for_should_not failure_message_when_negated
61
63
 
62
64
  def description
63
65
  desc = "have db column named #{@column}"
@@ -37,13 +37,15 @@ module Shoulda # :nodoc:
37
37
  index_exists? && correct_unique?
38
38
  end
39
39
 
40
- def failure_message_for_should
40
+ def failure_message
41
41
  "Expected #{expectation} (#{@missing})"
42
42
  end
43
+ alias failure_message_for_should failure_message
43
44
 
44
- def failure_message_for_should_not
45
+ def failure_message_when_negated
45
46
  "Did not expect #{expectation}"
46
47
  end
48
+ alias failure_message_for_should_not failure_message_when_negated
47
49
 
48
50
  def description
49
51
  if @options.key?(:unique)
@@ -16,19 +16,22 @@ module Shoulda # :nodoc:
16
16
  @attribute = attribute.to_s
17
17
  end
18
18
 
19
- attr_reader :failure_message_for_should, :failure_message_for_should_not
19
+ attr_reader :failure_message, :failure_message_when_negated
20
+
21
+ alias failure_message_for_should failure_message
22
+ alias failure_message_for_should_not failure_message_when_negated
20
23
 
21
24
  def matches?(subject)
22
25
  @subject = subject
23
26
  if readonly_attributes.include?(@attribute)
24
- @failure_message_for_should_not = "Did not expect #{@attribute} to be read-only"
27
+ @failure_message_when_negated = "Did not expect #{@attribute} to be read-only"
25
28
  true
26
29
  else
27
30
  if readonly_attributes.empty?
28
- @failure_message_for_should = "#{class_name} attribute #{@attribute} " <<
31
+ @failure_message = "#{class_name} attribute #{@attribute} " <<
29
32
  'is not read-only'
30
33
  else
31
- @failure_message_for_should = "#{class_name} is making " <<
34
+ @failure_message = "#{class_name} is making " <<
32
35
  "#{readonly_attributes.to_a.to_sentence} " <<
33
36
  "read-only, but not #{@attribute}."
34
37
  end
@@ -36,13 +36,15 @@ module Shoulda # :nodoc:
36
36
  serialization_valid? && type_valid?
37
37
  end
38
38
 
39
- def failure_message_for_should
39
+ def failure_message
40
40
  "Expected #{expectation} (#{@missing})"
41
41
  end
42
+ alias failure_message_for_should failure_message
42
43
 
43
- def failure_message_for_should_not
44
+ def failure_message_when_negated
44
45
  "Did not expect #{expectation}"
45
46
  end
47
+ alias failure_message_for_should_not failure_message_when_negated
46
48
 
47
49
  def description
48
50
  description = "serialize :#{@name}"
@@ -1,4 +1,4 @@
1
- if defined?(ActionController)
1
+ if defined?(ActionController) && defined?(ActionController::TestCase)
2
2
  require 'shoulda/matchers/action_controller'
3
3
 
4
4
  ActionController::TestCase.class_eval do
@@ -11,7 +11,7 @@ if defined?(ActionController)
11
11
  end
12
12
  end
13
13
 
14
- if defined?(ActiveRecord)
14
+ if defined?(ActiveRecord) && defined?(ActiveSupport::TestCase)
15
15
  require 'shoulda/matchers/active_record'
16
16
 
17
17
  ActiveSupport::TestCase.class_eval do
@@ -20,7 +20,7 @@ if defined?(ActiveRecord)
20
20
  end
21
21
  end
22
22
 
23
- if defined?(ActiveModel)
23
+ if defined?(ActiveModel) && defined?(ActiveSupport::TestCase)
24
24
  require 'shoulda/matchers/active_model'
25
25
 
26
26
  ActiveSupport::TestCase.class_eval do
@@ -2,7 +2,7 @@ module Shoulda # :nodoc:
2
2
  module Matchers
3
3
  class RailsShim # :nodoc:
4
4
  def self.layouts_ivar
5
- if rails_major_version >= 4
5
+ if action_pack_major_version >= 4
6
6
  '@_layouts'
7
7
  else
8
8
  '@layouts'
@@ -10,7 +10,7 @@ module Shoulda # :nodoc:
10
10
  end
11
11
 
12
12
  def self.flashes_ivar
13
- if rails_major_version >= 4
13
+ if action_pack_major_version >= 4
14
14
  :@flashes
15
15
  else
16
16
  :@used
@@ -18,7 +18,7 @@ module Shoulda # :nodoc:
18
18
  end
19
19
 
20
20
  def self.clean_scope(klass)
21
- if rails_major_version == 4
21
+ if active_record_major_version == 4
22
22
  klass.all
23
23
  else
24
24
  klass.scoped
@@ -26,15 +26,23 @@ module Shoulda # :nodoc:
26
26
  end
27
27
 
28
28
  def self.validates_confirmation_of_error_attribute(matcher)
29
- if rails_major_version == 4
29
+ if active_model_major_version == 4
30
30
  matcher.confirmation_attribute
31
31
  else
32
32
  matcher.attribute
33
33
  end
34
34
  end
35
35
 
36
- def self.rails_major_version
37
- Rails::VERSION::MAJOR
36
+ def self.active_record_major_version
37
+ ::ActiveRecord::VERSION::MAJOR
38
+ end
39
+
40
+ def self.active_model_major_version
41
+ ::ActiveModel::VERSION::MAJOR
42
+ end
43
+
44
+ def self.action_pack_major_version
45
+ ::ActionPack::VERSION::MAJOR
38
46
  end
39
47
  end
40
48
  end
@@ -1,5 +1,5 @@
1
1
  module Shoulda
2
2
  module Matchers
3
- VERSION = '2.4.0'.freeze
3
+ VERSION = '2.5.0'.freeze
4
4
  end
5
5
  end
@@ -13,7 +13,7 @@ describe Shoulda::Matchers::ActionController::FilterParamMatcher do
13
13
 
14
14
  matcher.matches?(nil).should be_false
15
15
 
16
- matcher.failure_message_for_should.should =~ /Expected other to be filtered.*secret/
16
+ matcher.failure_message.should =~ /Expected other to be filtered.*secret/
17
17
  end
18
18
 
19
19
  def filter(param)
@@ -18,7 +18,7 @@ describe Shoulda::Matchers::ActionController::RescueFromMatcher do
18
18
  it "asserts the controller responds to the handler method" do
19
19
  matcher = rescue_from(RuntimeError).with(:error_method)
20
20
  matcher.matches?(controller_with_rescue_from_and_invalid_method).should be_false
21
- matcher.failure_message_for_should.should =~ /does not respond to/
21
+ matcher.failure_message.should =~ /does not respond to/
22
22
  end
23
23
  end
24
24
 
@@ -35,7 +35,7 @@ describe Shoulda::Matchers::ActionController::RescueFromMatcher do
35
35
  it "asserts controller is not setup with rescue_from" do
36
36
  matcher = rescue_from RuntimeError
37
37
  define_controller("RandomController").should_not matcher
38
- matcher.failure_message_for_should_not.should =~ /Did not expect \w+ to rescue from/
38
+ matcher.failure_message_when_negated.should =~ /Did not expect \w+ to rescue from/
39
39
  end
40
40
  end
41
41
 
@@ -54,6 +54,11 @@ describe Shoulda::Matchers::ActionController::RouteMatcher, type: :controller do
54
54
  to(:action => 'other', :id => '1')
55
55
  end
56
56
 
57
+ it "accepts a string as first parameter" do
58
+ route_examples_to_examples.should route(:get, '/examples/1').
59
+ to("examples#example", id: '1')
60
+ end
61
+
57
62
  def route_examples_to_examples
58
63
  define_routes do
59
64
  get 'examples/:id', :to => 'examples#example'
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActionController::RouteParams do
4
+ describe "#normalize" do
5
+ context "when the route parameters is a hash" do
6
+ it "stringifies the values in the hash" do
7
+ build_route_params(:controller => :examples, :action => 'example', :id => '1').normalize.
8
+ should eq({ :controller => "examples", :action => "example", :id => "1" })
9
+ end
10
+ end
11
+
12
+ context "when the route parameters is a string and a hash" do
13
+ it "produces a hash of route parameters" do
14
+ build_route_params("examples#example", id: '1').normalize.
15
+ should eq({ :controller => "examples", :action => "example", :id => "1" })
16
+ end
17
+ end
18
+
19
+ context "when the route params is a string" do
20
+ it "produces a hash of route params" do
21
+ build_route_params("examples#index").normalize.
22
+ should eq({ :controller => "examples", :action => "index"})
23
+ end
24
+ end
25
+ end
26
+
27
+ def build_route_params(*params)
28
+ Shoulda::Matchers::ActionController::RouteParams.new(params)
29
+ end
30
+ end
@@ -70,7 +70,7 @@ describe Shoulda::Matchers::ActiveModel::AllowMassAssignmentOfMatcher do
70
70
 
71
71
  matcher.matches?(no_protected_attributes).should be_true
72
72
 
73
- matcher.failure_message_for_should_not.should_not be_nil
73
+ matcher.failure_message_when_negated.should_not be_nil
74
74
  end
75
75
  end
76
76
 
@@ -162,7 +162,7 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
162
162
 
163
163
  matcher.matches?(validating_format(:with => /abc/, :strict => true))
164
164
 
165
- matcher.failure_message_for_should_not.should eq 'Expected exception to include /abc/ ' +
165
+ matcher.failure_message_when_negated.should eq 'Expected exception to include /abc/ ' +
166
166
  'when attr is set to "xyz", got Attr is invalid'
167
167
  end
168
168
  end
@@ -40,13 +40,13 @@ describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher do
40
40
  end
41
41
 
42
42
  it "delegates its failure message to its allow matcher's negative failure message" do
43
- allow_matcher = stub_everything(:failure_message_for_should_not => 'allow matcher failure')
43
+ allow_matcher = stub_everything(:failure_message_when_negated => 'allow matcher failure')
44
44
  Shoulda::Matchers::ActiveModel::AllowValueMatcher.stubs(:new).returns(allow_matcher)
45
45
 
46
46
  matcher = matcher('abcde').for(:attr).with_message('good message')
47
47
  matcher.matches?(validating_format(:with => /abc/, :message => 'good message'))
48
48
 
49
- matcher.failure_message_for_should.should eq 'allow matcher failure'
49
+ matcher.failure_message.should eq 'allow matcher failure'
50
50
  end
51
51
 
52
52
  it 'matches if the message is correct but the value is not' do
@@ -18,6 +18,16 @@ describe Shoulda::Matchers::ActiveModel::EnsureInclusionOfMatcher do
18
18
  end
19
19
  end
20
20
 
21
+ context 'with an decimal column' do
22
+ it 'can verify decimal values' do
23
+ model = define_model(:example, :attr => :decimal) do
24
+ validates_inclusion_of :attr, :in => [0.0, 0.1]
25
+ end.new
26
+
27
+ model.should ensure_inclusion_of(:attr).in_array([0.0, 0.1])
28
+ end
29
+ end
30
+
21
31
  context 'with true/false values' do
22
32
  it 'can verify outside values to ensure the negative case' do
23
33
  define_model(:example, :attr => :string).new.