shoulda-matchers 3.0.1 → 3.1.0

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