shoulda-matchers 3.0.1 → 3.1.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -3
  4. data/CONTRIBUTING.md +60 -28
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +15 -12
  7. data/NEWS.md +111 -0
  8. data/README.md +94 -6
  9. data/Rakefile +10 -8
  10. data/custom_plan.rb +88 -0
  11. data/gemfiles/4.0.0.gemfile +1 -0
  12. data/gemfiles/4.0.0.gemfile.lock +21 -18
  13. data/gemfiles/4.0.1.gemfile +1 -0
  14. data/gemfiles/4.0.1.gemfile.lock +21 -18
  15. data/gemfiles/4.1.gemfile +1 -0
  16. data/gemfiles/4.1.gemfile.lock +21 -18
  17. data/gemfiles/4.2.gemfile +1 -0
  18. data/gemfiles/4.2.gemfile.lock +24 -21
  19. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -11
  20. data/lib/shoulda/matchers/active_model.rb +10 -1
  21. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +258 -180
  22. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  23. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  24. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +236 -0
  25. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  26. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  27. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  28. data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  29. data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  30. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +34 -14
  31. data/lib/shoulda/matchers/active_model/helpers.rb +9 -17
  32. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -6
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +13 -2
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +19 -35
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +13 -2
  36. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +12 -2
  37. data/lib/shoulda/matchers/active_model/qualifiers.rb +12 -0
  38. data/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  39. data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -32
  41. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -8
  42. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -22
  43. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +27 -16
  44. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +58 -15
  45. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +22 -12
  46. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +165 -87
  47. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +7 -9
  48. data/lib/shoulda/matchers/active_model/validation_matcher.rb +111 -49
  49. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +60 -0
  50. data/lib/shoulda/matchers/active_model/validator.rb +71 -52
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +19 -5
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +450 -124
  53. data/lib/shoulda/matchers/util.rb +43 -0
  54. data/lib/shoulda/matchers/util/word_wrap.rb +59 -31
  55. data/lib/shoulda/matchers/version.rb +1 -1
  56. data/script/update_gem_in_all_appraisals +1 -1
  57. data/script/update_gems_in_all_appraisals +1 -1
  58. data/spec/acceptance/multiple_libraries_integration_spec.rb +5 -2
  59. data/spec/acceptance/rails_integration_spec.rb +6 -2
  60. data/spec/spec_helper.rb +1 -3
  61. data/spec/support/acceptance/helpers/step_helpers.rb +4 -1
  62. data/spec/support/tests/current_bundle.rb +21 -7
  63. data/spec/support/unit/active_record/create_table.rb +54 -0
  64. data/spec/support/unit/attribute.rb +47 -0
  65. data/spec/support/unit/capture.rb +6 -0
  66. data/spec/support/unit/change_value.rb +111 -0
  67. data/spec/support/unit/create_model_arguments/basic.rb +135 -0
  68. data/spec/support/unit/create_model_arguments/has_many.rb +15 -0
  69. data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +74 -0
  70. data/spec/support/unit/helpers/active_record_versions.rb +1 -1
  71. data/spec/support/unit/helpers/class_builder.rb +61 -47
  72. data/spec/support/unit/helpers/database_helpers.rb +5 -3
  73. data/spec/support/unit/helpers/model_builder.rb +77 -97
  74. data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +44 -0
  75. data/spec/support/unit/load_environment.rb +12 -0
  76. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +2 -2
  77. data/spec/support/unit/matchers/fail_with_message_matcher.rb +12 -1
  78. data/spec/support/unit/model_creation_strategies/active_model.rb +111 -0
  79. data/spec/support/unit/model_creation_strategies/active_record.rb +77 -0
  80. data/spec/support/unit/model_creators.rb +19 -0
  81. data/spec/support/unit/model_creators/active_model.rb +39 -0
  82. data/spec/support/unit/model_creators/active_record.rb +43 -0
  83. data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +95 -0
  84. data/spec/support/unit/model_creators/active_record/has_many.rb +67 -0
  85. data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +42 -0
  86. data/spec/support/unit/model_creators/basic.rb +97 -0
  87. data/spec/support/unit/rails_application.rb +1 -1
  88. data/spec/support/unit/record_validating_confirmation_builder.rb +3 -7
  89. data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +79 -0
  90. data/spec/support/unit/validation_matcher_scenario.rb +62 -0
  91. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +4 -0
  92. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +575 -140
  93. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +115 -15
  94. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +42 -4
  95. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +92 -6
  96. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +122 -10
  97. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +306 -58
  98. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +122 -3
  99. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +805 -131
  100. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +196 -29
  101. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +82 -40
  102. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +600 -101
  103. data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +88 -33
  104. data/spec/unit_spec_helper.rb +10 -22
  105. data/zeus.json +11 -0
  106. metadata +64 -23
  107. data/lib/shoulda/matchers/active_model/strict_validator.rb +0 -51
  108. data/spec/support/unit/shared_examples/numerical_type_submatcher.rb +0 -15
  109. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +0 -288
  110. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +0 -100
  111. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +0 -100
  112. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +0 -100
@@ -0,0 +1,19 @@
1
+ module UnitTests
2
+ module ModelCreators
3
+ class << self
4
+ def register(name, klass)
5
+ registrations[name] = klass
6
+ end
7
+
8
+ def retrieve(name)
9
+ registrations[name]
10
+ end
11
+
12
+ private
13
+
14
+ def registrations
15
+ @_registrations ||= {}
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ require_relative '../model_creators'
2
+ require 'forwardable'
3
+
4
+ module UnitTests
5
+ module ModelCreators
6
+ class ActiveModel
7
+ def self.call(args)
8
+ new(args).call
9
+ end
10
+
11
+ extend Forwardable
12
+
13
+ def_delegators(
14
+ :arguments,
15
+ :attribute_name,
16
+ :attribute_default_values_by_name,
17
+ )
18
+
19
+ def initialize(args)
20
+ @arguments = CreateModelArguments::Basic.wrap(
21
+ args.merge(
22
+ model_creation_strategy: UnitTests::ModelCreationStrategies::ActiveModel
23
+ )
24
+ )
25
+ @model_creator = Basic.new(arguments)
26
+ end
27
+
28
+ def call
29
+ model_creator.call
30
+ end
31
+
32
+ protected
33
+
34
+ attr_reader :arguments, :model_creator
35
+ end
36
+
37
+ register(:active_model, ActiveModel)
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ require_relative '../model_creators'
2
+ require 'forwardable'
3
+
4
+ module UnitTests
5
+ module ModelCreators
6
+ class ActiveRecord
7
+ def self.call(args)
8
+ new(args).call
9
+ end
10
+
11
+ extend Forwardable
12
+
13
+ def_delegators(
14
+ :arguments,
15
+ :attribute_default_values_by_name,
16
+ :attribute_name,
17
+ :customize_model,
18
+ :model_name,
19
+ )
20
+
21
+ def_delegators :model_creator, :customize_model
22
+
23
+ def initialize(args)
24
+ @arguments = CreateModelArguments::Basic.wrap(
25
+ args.merge(
26
+ model_creation_strategy: UnitTests::ModelCreationStrategies::ActiveRecord
27
+ )
28
+ )
29
+ @model_creator = Basic.new(arguments)
30
+ end
31
+
32
+ def call
33
+ model_creator.call
34
+ end
35
+
36
+ protected
37
+
38
+ attr_reader :arguments, :model_creator
39
+ end
40
+
41
+ register(:active_record, ActiveRecord)
42
+ end
43
+ end
@@ -0,0 +1,95 @@
1
+ require_relative '../../model_creators'
2
+ require 'forwardable'
3
+
4
+ module UnitTests
5
+ module ModelCreators
6
+ class ActiveRecord
7
+ class HasAndBelongsToMany
8
+ def self.call(args)
9
+ new(args).call
10
+ end
11
+
12
+ extend Forwardable
13
+
14
+ def_delegators(
15
+ :arguments,
16
+ :attribute_name,
17
+ :attribute_default_values_by_name,
18
+ )
19
+
20
+ def initialize(args)
21
+ @arguments = CreateModelArguments::HasMany.wrap(args)
22
+ end
23
+
24
+ def call
25
+ parent_child_table_creator.call
26
+ child_model_creator.call
27
+ parent_model_creator.call
28
+ end
29
+
30
+ protected
31
+
32
+ attr_reader :arguments
33
+
34
+ private
35
+
36
+ alias_method :association_name, :attribute_name
37
+ alias_method :parent_model_creator_arguments, :arguments
38
+
39
+ def parent_child_table_creator
40
+ @_parent_child_table_creator ||=
41
+ UnitTests::ActiveRecord::CreateTable.new(
42
+ parent_child_table_name,
43
+ foreign_key_for_child_model => :integer,
44
+ foreign_key_for_parent_model => :integer,
45
+ :id => false
46
+ )
47
+ end
48
+
49
+ def child_model_creator
50
+ @_child_model_creator ||=
51
+ UnitTests::ModelCreationStrategies::ActiveRecord.new(
52
+ child_model_name
53
+ )
54
+ end
55
+
56
+ def parent_model_creator
57
+ @_parent_model_creator ||= begin
58
+ model_creator = UnitTests::ModelCreators::ActiveRecord.new(
59
+ parent_model_creator_arguments
60
+ )
61
+
62
+ # TODO: doesn't this need to be a has_many :through?
63
+ model_creator.customize_model do |model|
64
+ model.has_many(association_name)
65
+ end
66
+
67
+ model_creator
68
+ end
69
+ end
70
+
71
+ def foreign_key_for_child_model
72
+ child_model_name.foreign_key
73
+ end
74
+
75
+ def foreign_key_for_parent_model
76
+ parent_model_name.foreign_key
77
+ end
78
+
79
+ def parent_child_table_name
80
+ "#{child_model_name.pluralize}#{parent_model_name}".tableize
81
+ end
82
+
83
+ def parent_model_name
84
+ parent_model_creator.model_name
85
+ end
86
+
87
+ def child_model_name
88
+ association_name.to_s.classify
89
+ end
90
+ end
91
+ end
92
+
93
+ register(:"active_record/habtm", ActiveRecord::HasAndBelongsToMany)
94
+ end
95
+ end
@@ -0,0 +1,67 @@
1
+ require_relative '../../model_creators'
2
+ require 'forwardable'
3
+
4
+ module UnitTests
5
+ module ModelCreators
6
+ class ActiveRecord
7
+ class HasMany
8
+ def self.call(args)
9
+ new(args).call
10
+ end
11
+
12
+ extend Forwardable
13
+
14
+ def_delegators(
15
+ :arguments,
16
+ :attribute_name,
17
+ :attribute_default_values_by_name,
18
+ )
19
+
20
+ def initialize(args)
21
+ @arguments = CreateModelArguments::HasMany.wrap(args)
22
+ end
23
+
24
+ def call
25
+ child_model_creator.call
26
+ parent_model_creator.call
27
+ end
28
+
29
+ protected
30
+
31
+ attr_reader :arguments
32
+
33
+ private
34
+
35
+ alias_method :association_name, :attribute_name
36
+ alias_method :parent_model_creator_arguments, :arguments
37
+
38
+ def child_model_creator
39
+ @_child_model_creator ||=
40
+ UnitTests::ModelCreationStrategies::ActiveRecord.new(
41
+ child_model_name
42
+ )
43
+ end
44
+
45
+ def parent_model_creator
46
+ @_parent_model_creator ||= begin
47
+ model_creator = UnitTests::ModelCreators::ActiveRecord.new(
48
+ parent_model_creator_arguments
49
+ )
50
+
51
+ model_creator.customize_model do |model|
52
+ model.has_many(association_name)
53
+ end
54
+
55
+ model_creator
56
+ end
57
+ end
58
+
59
+ def child_model_name
60
+ association_name.to_s.classify
61
+ end
62
+ end
63
+ end
64
+
65
+ register(:"active_record/has_many", ActiveRecord::HasMany)
66
+ end
67
+ end
@@ -0,0 +1,42 @@
1
+ require_relative '../../model_creators'
2
+ require 'forwardable'
3
+
4
+ module UnitTests
5
+ module ModelCreators
6
+ class ActiveRecord
7
+ class UniquenessMatcher
8
+ def self.call(args)
9
+ new(args).call
10
+ end
11
+
12
+ extend Forwardable
13
+
14
+ def_delegators(
15
+ :arguments,
16
+ :attribute_name,
17
+ :attribute_default_values_by_name,
18
+ )
19
+
20
+ def initialize(args)
21
+ @arguments = CreateModelArguments::UniquenessMatcher.wrap(args)
22
+ @model_creator = UnitTests::ModelCreators::ActiveRecord.new(
23
+ arguments
24
+ )
25
+ end
26
+
27
+ def call
28
+ model_creator.call
29
+ end
30
+
31
+ protected
32
+
33
+ attr_reader :arguments, :model_creator
34
+ end
35
+ end
36
+
37
+ register(
38
+ :"active_record/uniqueness_matcher",
39
+ ActiveRecord::UniquenessMatcher
40
+ )
41
+ end
42
+ end
@@ -0,0 +1,97 @@
1
+ require 'forwardable'
2
+
3
+ module UnitTests
4
+ module ModelCreators
5
+ class Basic
6
+ def self.call(args)
7
+ new(args).call
8
+ end
9
+
10
+ extend Forwardable
11
+
12
+ def_delegators :arguments, :attribute_name, :model_name
13
+ def_delegators :model_creator, :customize_model
14
+
15
+ def initialize(arguments)
16
+ @arguments = arguments
17
+ @model_creator = build_model_creator
18
+ end
19
+
20
+ def call
21
+ model_creator.call
22
+ end
23
+
24
+ protected
25
+
26
+ attr_reader :arguments, :model_creator
27
+
28
+ private
29
+
30
+ def_delegators(
31
+ :arguments,
32
+ :additional_model_creation_strategy_args,
33
+ :all_attribute_overrides,
34
+ :columns,
35
+ :custom_validation?,
36
+ :model_creation_strategy,
37
+ :validation_name,
38
+ :validation_options,
39
+ :column_type,
40
+ )
41
+
42
+ def build_model_creator
43
+ model_creator = model_creation_strategy.new(
44
+ model_name,
45
+ columns,
46
+ arguments
47
+ )
48
+
49
+ model_creator.customize_model do |model|
50
+ add_validation_to(model)
51
+ possibly_override_attribute_writer_method_for(model)
52
+ end
53
+
54
+ model_creator
55
+ end
56
+
57
+ def add_validation_to(model)
58
+ if custom_validation?
59
+ _attribute_name = attribute_name
60
+
61
+ model.send(:define_method, :custom_validation) do
62
+ custom_validation.call(self, _attribute_name)
63
+ end
64
+
65
+ model.validate(:custom_validation)
66
+ else
67
+ model.public_send(validation_name, attribute_name, validation_options)
68
+ end
69
+ end
70
+
71
+ def possibly_override_attribute_writer_method_for(model)
72
+ all_attribute_overrides.each do |attribute_name, overrides|
73
+ if overrides.key?(:changing_values_with)
74
+ _change_value = method(:change_value)
75
+
76
+ model.send(:define_method, "#{attribute_name}=") do |value|
77
+ new_value = _change_value.call(
78
+ value,
79
+ overrides[:changing_values_with]
80
+ )
81
+
82
+ if respond_to?(:write_attribute)
83
+ write_attribute(new_value)
84
+ else
85
+ super(new_value)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def change_value(value, value_changer)
93
+ UnitTests::ChangeValue.call(column_type, value, value_changer)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -78,7 +78,7 @@ module UnitTests
78
78
  end
79
79
 
80
80
  def rails_new
81
- run_command! %W(rails new #{fs.project_directory} --skip-bundle)
81
+ run_command! %W(rails new #{fs.project_directory} --skip-bundle --no-rc)
82
82
  end
83
83
 
84
84
  def fix_available_locales_warning
@@ -25,9 +25,8 @@ module UnitTests
25
25
  end
26
26
 
27
27
  def attribute_to_confirm
28
- :attribute_to_confirm
28
+ options.fetch(:attribute, :attribute_to_confirm)
29
29
  end
30
- alias_method :attribute, :attribute_to_confirm
31
30
 
32
31
  def confirmation_attribute
33
32
  :"#{attribute_to_confirm}_confirmation"
@@ -44,11 +43,8 @@ module UnitTests
44
43
  private
45
44
 
46
45
  def create_model
47
- _attribute = attribute_to_confirm
48
- _options = options
49
-
50
- define_model(model_name, _attribute => :string) do
51
- validates_confirmation_of(_attribute, _options)
46
+ define_model(model_name, attribute_to_confirm => :string) do |model|
47
+ model.validates_confirmation_of(attribute_to_confirm, options)
52
48
  end
53
49
  end
54
50
  end
@@ -0,0 +1,79 @@
1
+ shared_examples_for 'ignoring_interference_by_writer' do |common_config|
2
+ valid_tests = [
3
+ :accept_if_qualified_but_changing_value_does_not_interfere,
4
+ :reject_if_qualified_but_changing_value_interferes
5
+ ]
6
+ tests = common_config.fetch(:tests)
7
+ tests.assert_valid_keys(valid_tests)
8
+
9
+ define_method(:common_config) { common_config }
10
+
11
+ context 'when the writer method for the attribute changes incoming values' do
12
+ context 'and the value change does not cause a test failure' do
13
+ config_for_test = tests[:accept_if_qualified_but_changing_value_does_not_interfere]
14
+
15
+ if config_for_test
16
+ it 'accepts (and does not raise an error)' do
17
+ args = build_args(config_for_test)
18
+ scenario = build_scenario_for_validation_matcher(args)
19
+ matcher = matcher_from(scenario)
20
+
21
+ expect(scenario.record).to matcher
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'and the value change causes a test failure' do
27
+ config_for_test = tests[:reject_if_qualified_but_changing_value_interferes]
28
+
29
+ if config_for_test
30
+ it 'lists how the value got changed in the failure message' do
31
+ args = build_args(config_for_test)
32
+ scenario = build_scenario_for_validation_matcher(args)
33
+ matcher = matcher_from(scenario)
34
+
35
+ assertion = lambda do
36
+ expect(scenario.record).to matcher
37
+ end
38
+
39
+ if config_for_test.key?(:expected_message_includes)
40
+ message = config_for_test[:expected_message_includes]
41
+ expect(&assertion).to fail_with_message_including(message)
42
+ else
43
+ message = config_for_test.fetch(:expected_message)
44
+ expect(&assertion).to fail_with_message(message)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def build_args(config_for_test)
52
+ args_from_common_config.merge(args_from_config_for_test(config_for_test))
53
+ end
54
+
55
+ def args_from_common_config
56
+ common_config.slice(
57
+ :column_type,
58
+ :model_creator,
59
+ )
60
+ end
61
+
62
+ def args_from_config_for_test(config)
63
+ config.slice(
64
+ :attribute_name,
65
+ :attribute_overrides,
66
+ :changing_values_with,
67
+ :default_value,
68
+ :model_name,
69
+ )
70
+ end
71
+
72
+ def matcher_from(scenario)
73
+ scenario.matcher.tap do |matcher|
74
+ if respond_to?(:configure_validation_matcher)
75
+ configure_validation_matcher(matcher)
76
+ end
77
+ end
78
+ end
79
+ end