shoulda-matchers 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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.