shoulda-matchers 5.1.0 → 6.4.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +41 -18
  4. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +7 -9
  5. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +1 -1
  6. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +13 -15
  7. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +46 -1
  8. data/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  9. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +7 -0
  10. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +3 -5
  11. data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  12. data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
  13. data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  14. data/lib/shoulda/matchers/active_model/qualifiers.rb +1 -0
  15. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -7
  16. data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +532 -0
  17. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +5 -5
  18. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +29 -14
  19. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +65 -10
  20. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +76 -86
  21. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +29 -4
  22. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +6 -7
  23. data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
  24. data/lib/shoulda/matchers/active_model/validator.rb +4 -0
  25. data/lib/shoulda/matchers/active_model.rb +4 -1
  26. data/lib/shoulda/matchers/active_record/association_matcher.rb +543 -15
  27. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +34 -4
  28. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +9 -1
  29. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -0
  30. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +4 -0
  31. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +23 -19
  32. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +27 -23
  33. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +338 -30
  34. data/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  35. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
  36. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +24 -13
  37. data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
  38. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -1
  39. data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  40. data/lib/shoulda/matchers/active_record/uniqueness/model.rb +13 -1
  41. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +82 -70
  42. data/lib/shoulda/matchers/active_record.rb +2 -0
  43. data/lib/shoulda/matchers/doublespeak/double_collection.rb +2 -6
  44. data/lib/shoulda/matchers/doublespeak/world.rb +2 -6
  45. data/lib/shoulda/matchers/doublespeak.rb +0 -1
  46. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +13 -15
  47. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +7 -5
  48. data/lib/shoulda/matchers/integrations/libraries/routing.rb +5 -3
  49. data/lib/shoulda/matchers/rails_shim.rb +22 -6
  50. data/lib/shoulda/matchers/util/word_wrap.rb +2 -2
  51. data/lib/shoulda/matchers/util.rb +18 -20
  52. data/lib/shoulda/matchers/version.rb +1 -1
  53. data/lib/shoulda/matchers.rb +2 -2
  54. data/shoulda-matchers.gemspec +2 -2
  55. metadata +15 -9
  56. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -157
@@ -48,6 +48,30 @@ module Shoulda
48
48
  # should have_db_column(:camera_aperture).of_type(:decimal)
49
49
  # end
50
50
  #
51
+ # ##### of_sql_type
52
+ #
53
+ # Use `of_sql_type` to assert that a column is defined as a certain sql_type.
54
+ #
55
+ # class CreatePhones < ActiveRecord::Migration
56
+ # def change
57
+ # create_table :phones do |t|
58
+ # t.string :camera_aperture, limit: 36
59
+ # end
60
+ # end
61
+ # end
62
+ #
63
+ # # RSpec
64
+ # RSpec.describe Phone, type: :model do
65
+ # it do
66
+ # should have_db_column(:camera_aperture).of_sql_type('varchar(36)')
67
+ # end
68
+ # end
69
+ #
70
+ # # Minitest (Shoulda)
71
+ # class PhoneTest < ActiveSupport::TestCase
72
+ # should have_db_column(:camera_aperture).of_sql_type('varchar(36)')
73
+ # end
74
+ #
51
75
  # ##### with_options
52
76
  #
53
77
  # Use `with_options` to assert that a column has been defined with
@@ -96,6 +120,11 @@ module Shoulda
96
120
  self
97
121
  end
98
122
 
123
+ def of_sql_type(sql_column_type)
124
+ @options[:sql_column_type] = sql_column_type
125
+ self
126
+ end
127
+
99
128
  def with_options(opts = {})
100
129
  validate_options(opts)
101
130
  OPTIONS.each do |attribute|
@@ -110,6 +139,7 @@ module Shoulda
110
139
  @subject = subject
111
140
  column_exists? &&
112
141
  correct_column_type? &&
142
+ correct_sql_column_type? &&
113
143
  correct_precision? &&
114
144
  correct_limit? &&
115
145
  correct_default? &&
@@ -129,12 +159,9 @@ module Shoulda
129
159
 
130
160
  def description
131
161
  desc = "have db column named #{@column}"
132
- if @options.key?(:column_type)
133
- desc << " of type #{@options[:column_type]}"
134
- end
135
- if @options.key?(:precision)
136
- desc << " of precision #{@options[:precision]}"
137
- end
162
+ desc << " of type #{@options[:column_type]}" if @options.key?(:column_type)
163
+ desc << " of sql_type #{@options[:sql_column_type]}" if @options.key?(:sql_column_type)
164
+ desc << " of precision #{@options[:precision]}" if @options.key?(:precision)
138
165
  desc << " of limit #{@options[:limit]}" if @options.key?(:limit)
139
166
  desc << " of default #{@options[:default]}" if @options.key?(:default)
140
167
  desc << " of null #{@options[:null]}" if @options.key?(:null)
@@ -178,6 +205,19 @@ module Shoulda
178
205
  end
179
206
  end
180
207
 
208
+ def correct_sql_column_type?
209
+ return true unless @options.key?(:sql_column_type)
210
+
211
+ if matched_column.sql_type.to_s == @options[:sql_column_type].to_s
212
+ true
213
+ else
214
+ @missing =
215
+ "#{model_class} has a db column named #{@column} " <<
216
+ "of sql type #{matched_column.sql_type}, not #{@options[:sql_column_type]}."
217
+ false
218
+ end
219
+ end
220
+
181
221
  def correct_precision?
182
222
  return true unless @options.key?(:precision)
183
223
 
@@ -152,18 +152,18 @@ module Shoulda
152
152
  end
153
153
 
154
154
  def description
155
- description = 'have '
156
-
157
- description <<
158
- if qualifiers.include?(:unique)
159
- "#{Shoulda::Matchers::Util.a_or_an(index_type)} "
160
- else
161
- 'an '
162
- end
155
+ String.new('have ').tap do |description|
156
+ description <<
157
+ if qualifiers.include?(:unique)
158
+ "#{Shoulda::Matchers::Util.a_or_an(index_type)} "
159
+ else
160
+ 'an '
161
+ end
163
162
 
164
- description << 'index on '
163
+ description << 'index on '
165
164
 
166
- description << inspected_expected_columns
165
+ description << inspected_expected_columns
166
+ end
167
167
  end
168
168
 
169
169
  private
@@ -197,17 +197,28 @@ module Shoulda
197
197
  def matched_index
198
198
  @_matched_index ||=
199
199
  if expected_columns.one?
200
- actual_indexes.detect do |index|
200
+ sorted_indexes.detect do |index|
201
201
  Array.wrap(index.columns) == expected_columns
202
202
  end
203
203
  else
204
- actual_indexes.detect do |index|
204
+ sorted_indexes.detect do |index|
205
205
  index.columns == expected_columns
206
206
  end
207
207
  end
208
208
  end
209
209
 
210
- def actual_indexes
210
+ def sorted_indexes
211
+ if qualifiers.include?(:unique)
212
+ # return indexes with unique matching the qualifier first
213
+ unsorted_indexes.sort_by do |index|
214
+ index.unique == qualifiers[:unique] ? 0 : 1
215
+ end
216
+ else
217
+ unsorted_indexes
218
+ end
219
+ end
220
+
221
+ def unsorted_indexes
211
222
  model.connection.indexes(table_name)
212
223
  end
213
224
 
@@ -2,7 +2,7 @@ module Shoulda
2
2
  module Matchers
3
3
  module ActiveRecord
4
4
  # The `have_implicit_order_column` matcher tests that the model has `implicit_order_column`
5
- # assigned to one of the table columns. (Rails 6+ only)
5
+ # assigned to one of the table columns.
6
6
  #
7
7
  # class Product < ApplicationRecord
8
8
  # self.implicit_order_column = :created_at
@@ -20,10 +20,8 @@ module Shoulda
20
20
  #
21
21
  # @return [HaveImplicitOrderColumnMatcher]
22
22
  #
23
- if RailsShim.active_record_gte_6?
24
- def have_implicit_order_column(column_name)
25
- HaveImplicitOrderColumnMatcher.new(column_name)
26
- end
23
+ def have_implicit_order_column(column_name)
24
+ HaveImplicitOrderColumnMatcher.new(column_name)
27
25
  end
28
26
 
29
27
  # @private
@@ -59,7 +59,7 @@ module Shoulda
59
59
  private
60
60
 
61
61
  def readonly_attributes
62
- @_readonly_attributes ||= (@subject.class.readonly_attributes || [])
62
+ @_readonly_attributes ||= @subject.class.readonly_attributes || []
63
63
  end
64
64
 
65
65
  def class_name
@@ -0,0 +1,151 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ # The `normalize` matcher is used to ensure attribute normalizations
5
+ # are transforming attribute values as expected.
6
+ #
7
+ # Take this model for example:
8
+ #
9
+ # class User < ActiveRecord::Base
10
+ # normalizes :email, with: -> email { email.strip.downcase }
11
+ # end
12
+ #
13
+ # You can use `normalize` providing an input and defining the expected
14
+ # normalization output:
15
+ #
16
+ # # RSpec
17
+ # RSpec.describe User, type: :model do
18
+ # it do
19
+ # should normalize(:email).from(" ME@XYZ.COM\n").to("me@xyz.com")
20
+ # end
21
+ # end
22
+ #
23
+ # # Minitest (Shoulda)
24
+ # class User < ActiveSupport::TestCase
25
+ # should normalize(:email).from(" ME@XYZ.COM\n").to("me@xyz.com")
26
+ # end
27
+ #
28
+ # You can use `normalize` to test multiple attributes at once:
29
+ #
30
+ # class User < ActiveRecord::Base
31
+ # normalizes :email, :handle, with: -> value { value.strip.downcase }
32
+ # end
33
+ #
34
+ # # RSpec
35
+ # RSpec.describe User, type: :model do
36
+ # it do
37
+ # should normalize(:email, :handle).from(" Example\n").to("example")
38
+ # end
39
+ # end
40
+ #
41
+ # # Minitest (Shoulda)
42
+ # class User < ActiveSupport::TestCase
43
+ # should normalize(:email, :handle).from(" Example\n").to("example")
44
+ # end
45
+ #
46
+ # If the normalization accepts nil values with the `apply_to_nil` option,
47
+ # you just need to use `.from(nil).to("Your expected value here")`.
48
+ #
49
+ # class User < ActiveRecord::Base
50
+ # normalizes :name, with: -> name { name&.titleize || 'Untitled' },
51
+ # apply_to_nil: true
52
+ # end
53
+ #
54
+ # # RSpec
55
+ # RSpec.describe User, type: :model do
56
+ # it { should normalize(:name).from("jane doe").to("Jane Doe") }
57
+ # it { should normalize(:name).from(nil).to("Untitled") }
58
+ # end
59
+ #
60
+ # # Minitest (Shoulda)
61
+ # class User < ActiveSupport::TestCase
62
+ # should normalize(:name).from("jane doe").to("Jane Doe")
63
+ # should normalize(:name).from(nil).to("Untitled")
64
+ # end
65
+ #
66
+ # @return [NormalizeMatcher]
67
+ #
68
+ def normalize(*attributes)
69
+ if attributes.empty?
70
+ raise ArgumentError, 'need at least one attribute'
71
+ else
72
+ NormalizeMatcher.new(*attributes)
73
+ end
74
+ end
75
+
76
+ # @private
77
+ class NormalizeMatcher
78
+ attr_reader :attributes, :from_value, :to_value, :failure_message,
79
+ :failure_message_when_negated
80
+
81
+ def initialize(*attributes)
82
+ @attributes = attributes
83
+ end
84
+
85
+ def description
86
+ %(
87
+ normalize #{attributes.to_sentence(last_word_connector: ' and ')} from
88
+ ‹#{from_value.inspect}› to ‹#{to_value.inspect}›
89
+ ).squish
90
+ end
91
+
92
+ def from(value)
93
+ @from_value = value
94
+
95
+ self
96
+ end
97
+
98
+ def to(value)
99
+ @to_value = value
100
+
101
+ self
102
+ end
103
+
104
+ def matches?(subject)
105
+ attributes.all? { |attribute| attribute_matches?(subject, attribute) }
106
+ end
107
+
108
+ def does_not_match?(subject)
109
+ attributes.all? { |attribute| attribute_does_not_match?(subject, attribute) }
110
+ end
111
+
112
+ private
113
+
114
+ def attribute_matches?(subject, attribute)
115
+ return true if normalize_attribute?(subject, attribute)
116
+
117
+ @failure_message = build_failure_message(
118
+ attribute,
119
+ subject.class.normalize_value_for(attribute, from_value),
120
+ )
121
+ false
122
+ end
123
+
124
+ def attribute_does_not_match?(subject, attribute)
125
+ return true unless normalize_attribute?(subject, attribute)
126
+
127
+ @failure_message_when_negated = build_failure_message_when_negated(attribute)
128
+ false
129
+ end
130
+
131
+ def normalize_attribute?(subject, attribute)
132
+ subject.class.normalize_value_for(attribute, from_value) == to_value
133
+ end
134
+
135
+ def build_failure_message(attribute, attribute_value)
136
+ %(
137
+ Expected to normalize #{attribute.inspect} from ‹#{from_value.inspect}› to
138
+ ‹#{to_value.inspect}› but it was normalized to ‹#{attribute_value.inspect}›
139
+ ).squish
140
+ end
141
+
142
+ def build_failure_message_when_negated(attribute)
143
+ %(
144
+ Expected to not normalize #{attribute.inspect} from ‹#{from_value.inspect}› to
145
+ ‹#{to_value.inspect}› but it was normalized
146
+ ).squish
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -29,7 +29,19 @@ module Shoulda
29
29
  end
30
30
 
31
31
  def symlink_to(parent)
32
- namespace.set(name, parent.dup)
32
+ table_name = parent.table_name
33
+
34
+ new_class = Class.new(parent) do
35
+ define_singleton_method :table_name do
36
+ table_name
37
+ end
38
+
39
+ define_singleton_method :base_class do
40
+ self
41
+ end
42
+ end
43
+
44
+ namespace.set(name, new_class)
33
45
  end
34
46
 
35
47
  def to_s
@@ -411,9 +411,29 @@ module Shoulda
411
411
  if scopes_match?
412
412
  true
413
413
  else
414
- @failure_reason = 'Expected the validation '
414
+ @failure_reason = String.new('Expected the validation ').tap do |failure_reason_string|
415
+ failure_reason_string <<
416
+ if expected_scopes.empty?
417
+ 'not to be scoped to anything, '
418
+ else
419
+ "to be scoped to #{inspected_expected_scopes}, "
420
+ end
421
+
422
+ if actual_sets_of_scopes.any?
423
+ failure_reason_string << 'but it was scoped to '
424
+ failure_reason_string << "#{inspected_actual_scopes} instead."
425
+ else
426
+ failure_reason_string << 'but it was not scoped to anything.'
427
+ end
428
+ end
415
429
 
416
- @failure_reason <<
430
+ false
431
+ end
432
+ end
433
+
434
+ def build_failure_reason
435
+ String.new('Expected the validation ').tap do |failure_reason_string|
436
+ failure_reason_string <<
417
437
  if expected_scopes.empty?
418
438
  'not to be scoped to anything, '
419
439
  else
@@ -421,27 +441,25 @@ module Shoulda
421
441
  end
422
442
 
423
443
  if actual_sets_of_scopes.any?
424
- @failure_reason << 'but it was scoped to '
425
- @failure_reason << "#{inspected_actual_scopes} instead."
444
+ failure_reason_string << 'but it was scoped to '
445
+ failure_reason_string << "#{inspected_actual_scopes} instead."
426
446
  else
427
- @failure_reason << 'but it was not scoped to anything.'
447
+ failure_reason_string << 'but it was not scoped to anything.'
428
448
  end
429
-
430
- false
431
449
  end
432
450
  end
433
451
 
434
452
  def does_not_match_scopes_configuration?
435
453
  if scopes_match?
436
- @failure_reason = 'Expected the validation '
437
-
438
- if expected_scopes.empty?
439
- @failure_reason << 'to be scoped to nothing, '
440
- @failure_reason << 'but it was scoped to '
441
- @failure_reason << "#{inspected_actual_scopes} instead."
442
- else
443
- @failure_reason << 'not to be scoped to '
444
- @failure_reason << inspected_expected_scopes
454
+ @failure_reason = String.new('Expected the validation ').tap do |failure_reason|
455
+ if expected_scopes.empty?
456
+ failure_reason << 'to be scoped to nothing, '
457
+ failure_reason << 'but it was scoped to '
458
+ failure_reason << "#{inspected_actual_scopes} instead."
459
+ else
460
+ failure_reason << 'not to be scoped to '
461
+ failure_reason << inspected_expected_scopes
462
+ end
445
463
  end
446
464
 
447
465
  false
@@ -618,20 +636,18 @@ module Shoulda
618
636
  else
619
637
  inspected_scopes = scopes_missing_on_model.map(&:inspect)
620
638
 
621
- reason = ''
622
-
623
- reason << inspected_scopes.to_sentence
624
-
625
- reason <<
626
- if inspected_scopes.many?
627
- ' do not seem to be attributes'
628
- else
629
- ' does not seem to be an attribute'
630
- end
639
+ @failure_reason = String.new.tap do |reason|
640
+ reason << inspected_scopes.to_sentence
631
641
 
632
- reason << " on #{model.name}."
642
+ reason <<
643
+ if inspected_scopes.many?
644
+ ' do not seem to be attributes'
645
+ else
646
+ ' does not seem to be an attribute'
647
+ end
633
648
 
634
- @failure_reason = reason
649
+ reason << " on #{model.name}."
650
+ end
635
651
 
636
652
  false
637
653
  end
@@ -643,20 +659,18 @@ module Shoulda
643
659
  else
644
660
  inspected_scopes = scopes_present_on_model.map(&:inspect)
645
661
 
646
- reason = ''
647
-
648
- reason << inspected_scopes.to_sentence
649
-
650
- reason <<
651
- if inspected_scopes.many?
652
- ' seem to be attributes'
653
- else
654
- ' seems to be an attribute'
655
- end
662
+ @failure_reason = String.new.tap do |reason|
663
+ reason << inspected_scopes.to_sentence
656
664
 
657
- reason << " on #{model.name}."
665
+ reason <<
666
+ if inspected_scopes.many?
667
+ ' seem to be attributes'
668
+ else
669
+ ' seems to be an attribute'
670
+ end
658
671
 
659
- @failure_reason = reason
672
+ reason << " on #{model.name}."
673
+ end
660
674
 
661
675
  false
662
676
  end
@@ -936,45 +950,43 @@ module Shoulda
936
950
  end
937
951
 
938
952
  def failure_message_preface # rubocop:disable Metrics/MethodLength
939
- prefix = ''
940
-
941
- if @existing_record_created
942
- prefix << "After taking the given #{model.name}"
953
+ String.new.tap do |prefix|
954
+ if @existing_record_created
955
+ prefix << "After taking the given #{model.name}"
956
+
957
+ if attribute_setter_for_existing_record
958
+ prefix << ', setting '
959
+ prefix << description_for_attribute_setter(
960
+ attribute_setter_for_existing_record,
961
+ )
962
+ else
963
+ prefix << ", whose :#{attribute} is "
964
+ prefix << "‹#{existing_value_read.inspect}›"
965
+ end
943
966
 
944
- if attribute_setter_for_existing_record
945
- prefix << ', setting '
967
+ prefix << ', and saving it as the existing record, then'
968
+ elsif attribute_setter_for_existing_record
969
+ prefix << "Given an existing #{model.name},"
970
+ prefix << ' after setting '
946
971
  prefix << description_for_attribute_setter(
947
972
  attribute_setter_for_existing_record,
948
973
  )
974
+ prefix << ', then'
949
975
  else
950
- prefix << ", whose :#{attribute} is "
951
- prefix << "‹#{existing_value_read.inspect}›"
976
+ prefix << "Given an existing #{model.name} whose :#{attribute}"
977
+ prefix << ' is '
978
+ prefix << Shoulda::Matchers::Util.inspect_value(
979
+ existing_value_read,
980
+ )
981
+ prefix << ', after'
952
982
  end
953
983
 
954
- prefix << ', and saving it as the existing record, then'
955
- elsif attribute_setter_for_existing_record
956
- prefix << "Given an existing #{model.name},"
957
- prefix << ' after setting '
958
- prefix << description_for_attribute_setter(
959
- attribute_setter_for_existing_record,
960
- )
961
- prefix << ', then'
962
- else
963
- prefix << "Given an existing #{model.name} whose :#{attribute}"
964
- prefix << ' is '
965
- prefix << Shoulda::Matchers::Util.inspect_value(
966
- existing_value_read,
967
- )
968
- prefix << ', after'
969
- end
970
-
971
- prefix << " making a new #{model.name} and setting "
984
+ prefix << " making a new #{model.name} and setting "
972
985
 
973
- prefix << descriptions_for_attribute_setters_for_new_record
986
+ prefix << descriptions_for_attribute_setters_for_new_record
974
987
 
975
- prefix << ", the matcher expected the new #{model.name} to be"
976
-
977
- prefix
988
+ prefix << ", the matcher expected the new #{model.name} to be"
989
+ end
978
990
  end
979
991
 
980
992
  def attribute_changed_value_message
@@ -24,6 +24,8 @@ require 'shoulda/matchers/active_record/define_enum_for_matcher'
24
24
  require 'shoulda/matchers/active_record/uniqueness'
25
25
  require 'shoulda/matchers/active_record/validate_uniqueness_of_matcher'
26
26
  require 'shoulda/matchers/active_record/have_attached_matcher'
27
+ require 'shoulda/matchers/active_record/normalize_matcher'
28
+ require 'shoulda/matchers/active_record/encrypt_matcher'
27
29
 
28
30
  module Shoulda
29
31
  module Matchers
@@ -18,15 +18,11 @@ module Shoulda
18
18
  end
19
19
 
20
20
  def activate
21
- doubles_by_method_name.each do |_method_name, double|
22
- double.activate
23
- end
21
+ doubles_by_method_name.each_value(&:activate)
24
22
  end
25
23
 
26
24
  def deactivate
27
- doubles_by_method_name.each do |_method_name, double|
28
- double.deactivate
29
- end
25
+ doubles_by_method_name.each_value(&:deactivate)
30
26
  end
31
27
 
32
28
  def calls_by_method_name
@@ -39,15 +39,11 @@ module Shoulda
39
39
  private
40
40
 
41
41
  def activate
42
- double_collections_by_class.each do |_klass, double_collection|
43
- double_collection.activate
44
- end
42
+ double_collections_by_class.each_value(&:activate)
45
43
  end
46
44
 
47
45
  def deactivate
48
- double_collections_by_class.each do |_klass, double_collection|
49
- double_collection.deactivate
50
- end
46
+ double_collections_by_class.each_value(&:deactivate)
51
47
  end
52
48
 
53
49
  def double_collections_by_class
@@ -1,5 +1,4 @@
1
1
  require 'forwardable'
2
- require 'logger'
3
2
 
4
3
  module Shoulda
5
4
  module Matchers
@@ -400,7 +400,7 @@ module Shoulda
400
400
  false
401
401
  rescue NoMethodError => e
402
402
  if e.message =~
403
- /undefined method `#{delegate_method}' for nil:NilClass/
403
+ /undefined method `#{delegate_method}' for nil/
404
404
  false
405
405
  else
406
406
  raise e
@@ -440,22 +440,20 @@ module Shoulda
440
440
  end
441
441
 
442
442
  def formatted_calls_on_delegate_object
443
- string = ''
444
-
445
- if calls_on_delegate_object.any?
446
- string << "\n\n"
447
- calls_on_delegate_object.each_with_index do |call, i|
448
- name = call.method_name
449
- args = call.args.map(&:inspect).join(', ')
450
- string << "#{i + 1}) #{name}(#{args})\n"
443
+ String.new.tap do |string|
444
+ if calls_on_delegate_object.any?
445
+ string << "\n\n"
446
+ calls_on_delegate_object.each_with_index do |call, i|
447
+ name = call.method_name
448
+ args = call.args.map(&:inspect).join(', ')
449
+ string << "#{i + 1}) #{name}(#{args})\n"
450
+ end
451
+ else
452
+ string << ' (none)'
451
453
  end
452
- else
453
- string << ' (none)'
454
- end
455
454
 
456
- string.rstrip!
457
-
458
- string
455
+ string.rstrip!
456
+ end
459
457
  end
460
458
  end
461
459
  end