super_diff 0.2.0 → 0.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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +108 -74
  3. data/lib/super_diff.rb +20 -11
  4. data/lib/super_diff/active_record.rb +21 -23
  5. data/lib/super_diff/active_record/diff_formatters/active_record_relation.rb +3 -3
  6. data/lib/super_diff/active_record/differs/active_record_relation.rb +3 -5
  7. data/lib/super_diff/active_record/monkey_patches.rb +9 -0
  8. data/lib/super_diff/active_record/object_inspection/inspectors/active_record_model.rb +32 -22
  9. data/lib/super_diff/active_record/object_inspection/inspectors/active_record_relation.rb +17 -7
  10. data/lib/super_diff/active_record/operation_tree_builders.rb +14 -0
  11. data/lib/super_diff/active_record/{operational_sequencers → operation_tree_builders}/active_record_model.rb +2 -2
  12. data/lib/super_diff/active_record/{operational_sequencers → operation_tree_builders}/active_record_relation.rb +4 -4
  13. data/lib/super_diff/active_record/{operation_sequences.rb → operation_trees.rb} +2 -2
  14. data/lib/super_diff/active_record/{operation_sequences → operation_trees}/active_record_relation.rb +2 -2
  15. data/lib/super_diff/active_support.rb +16 -19
  16. data/lib/super_diff/active_support/diff_formatters/hash_with_indifferent_access.rb +3 -3
  17. data/lib/super_diff/active_support/differs/hash_with_indifferent_access.rb +3 -5
  18. data/lib/super_diff/active_support/object_inspection/inspectors/hash_with_indifferent_access.rb +17 -7
  19. data/lib/super_diff/active_support/operation_tree_builders.rb +10 -0
  20. data/lib/super_diff/active_support/{operational_sequencers → operation_tree_builders}/hash_with_indifferent_access.rb +2 -2
  21. data/lib/super_diff/active_support/{operation_sequences.rb → operation_trees.rb} +2 -2
  22. data/lib/super_diff/active_support/{operation_sequences → operation_trees}/hash_with_indifferent_access.rb +2 -2
  23. data/lib/super_diff/configuration.rb +60 -0
  24. data/lib/super_diff/csi.rb +4 -0
  25. data/lib/super_diff/diff_formatters.rb +3 -3
  26. data/lib/super_diff/diff_formatters/array.rb +3 -3
  27. data/lib/super_diff/diff_formatters/base.rb +3 -2
  28. data/lib/super_diff/diff_formatters/collection.rb +2 -2
  29. data/lib/super_diff/diff_formatters/custom_object.rb +3 -3
  30. data/lib/super_diff/diff_formatters/default_object.rb +6 -8
  31. data/lib/super_diff/diff_formatters/defaults.rb +10 -0
  32. data/lib/super_diff/diff_formatters/hash.rb +3 -3
  33. data/lib/super_diff/diff_formatters/main.rb +41 -0
  34. data/lib/super_diff/diff_formatters/multiline_string.rb +3 -3
  35. data/lib/super_diff/differs.rb +4 -9
  36. data/lib/super_diff/differs/array.rb +2 -11
  37. data/lib/super_diff/differs/base.rb +20 -3
  38. data/lib/super_diff/differs/custom_object.rb +2 -11
  39. data/lib/super_diff/differs/default_object.rb +2 -8
  40. data/lib/super_diff/differs/defaults.rb +12 -0
  41. data/lib/super_diff/differs/hash.rb +2 -11
  42. data/lib/super_diff/differs/main.rb +48 -0
  43. data/lib/super_diff/differs/multiline_string.rb +2 -14
  44. data/lib/super_diff/differs/time_like.rb +15 -0
  45. data/lib/super_diff/equality_matchers.rb +3 -9
  46. data/lib/super_diff/equality_matchers/array.rb +1 -7
  47. data/lib/super_diff/equality_matchers/base.rb +1 -1
  48. data/lib/super_diff/equality_matchers/default.rb +2 -8
  49. data/lib/super_diff/equality_matchers/defaults.rb +12 -0
  50. data/lib/super_diff/equality_matchers/hash.rb +1 -7
  51. data/lib/super_diff/equality_matchers/main.rb +21 -0
  52. data/lib/super_diff/equality_matchers/multiline_string.rb +1 -7
  53. data/lib/super_diff/errors.rb +16 -0
  54. data/lib/super_diff/errors/no_diff_formatter_available_error.rb +21 -0
  55. data/lib/super_diff/errors/no_differ_available_error.rb +24 -0
  56. data/lib/super_diff/errors/no_operational_sequencer_available_error.rb +22 -0
  57. data/lib/super_diff/implementation_checks.rb +19 -0
  58. data/lib/super_diff/object_inspection.rb +1 -10
  59. data/lib/super_diff/object_inspection/inspection_tree.rb +6 -2
  60. data/lib/super_diff/object_inspection/inspectors.rb +5 -0
  61. data/lib/super_diff/object_inspection/inspectors/array.rb +20 -10
  62. data/lib/super_diff/object_inspection/inspectors/base.rb +36 -0
  63. data/lib/super_diff/object_inspection/inspectors/custom_object.rb +24 -14
  64. data/lib/super_diff/object_inspection/inspectors/default_object.rb +44 -30
  65. data/lib/super_diff/object_inspection/inspectors/defaults.rb +15 -0
  66. data/lib/super_diff/object_inspection/inspectors/hash.rb +20 -10
  67. data/lib/super_diff/object_inspection/inspectors/main.rb +35 -0
  68. data/lib/super_diff/object_inspection/inspectors/primitive.rb +20 -5
  69. data/lib/super_diff/object_inspection/inspectors/string.rb +15 -5
  70. data/lib/super_diff/object_inspection/inspectors/time_like.rb +23 -0
  71. data/lib/super_diff/object_inspection/nodes/inspection.rb +9 -2
  72. data/lib/super_diff/operation_tree_builders.rb +18 -0
  73. data/lib/super_diff/{operational_sequencers → operation_tree_builders}/array.rb +38 -59
  74. data/lib/super_diff/operation_tree_builders/base.rb +98 -0
  75. data/lib/super_diff/{operational_sequencers → operation_tree_builders}/custom_object.rb +3 -3
  76. data/lib/super_diff/{operational_sequencers → operation_tree_builders}/default_object.rb +8 -3
  77. data/lib/super_diff/operation_tree_builders/defaults.rb +5 -0
  78. data/lib/super_diff/operation_tree_builders/hash.rb +226 -0
  79. data/lib/super_diff/operation_tree_builders/main.rb +42 -0
  80. data/lib/super_diff/{operational_sequencers → operation_tree_builders}/multiline_string.rb +3 -3
  81. data/lib/super_diff/operation_tree_builders/time_like.rb +34 -0
  82. data/lib/super_diff/operation_trees.rb +13 -0
  83. data/lib/super_diff/{operation_sequences → operation_trees}/array.rb +5 -1
  84. data/lib/super_diff/operation_trees/base.rb +31 -0
  85. data/lib/super_diff/{operation_sequences → operation_trees}/custom_object.rb +5 -1
  86. data/lib/super_diff/{operation_sequences → operation_trees}/default_object.rb +10 -8
  87. data/lib/super_diff/operation_trees/defaults.rb +5 -0
  88. data/lib/super_diff/{operation_sequences → operation_trees}/hash.rb +5 -1
  89. data/lib/super_diff/operation_trees/main.rb +35 -0
  90. data/lib/super_diff/operation_trees/multiline_string.rb +18 -0
  91. data/lib/super_diff/operations/unary_operation.rb +3 -0
  92. data/lib/super_diff/rspec.rb +54 -22
  93. data/lib/super_diff/rspec/augmented_matcher.rb +1 -1
  94. data/lib/super_diff/rspec/differ.rb +2 -17
  95. data/lib/super_diff/rspec/differs.rb +9 -3
  96. data/lib/super_diff/rspec/differs/collection_containing_exactly.rb +3 -8
  97. data/lib/super_diff/rspec/differs/collection_including.rb +18 -0
  98. data/lib/super_diff/rspec/differs/hash_including.rb +18 -0
  99. data/lib/super_diff/rspec/differs/object_having_attributes.rb +17 -0
  100. data/lib/super_diff/rspec/matcher_text_builders.rb +4 -0
  101. data/lib/super_diff/rspec/matcher_text_builders/be_predicate.rb +26 -7
  102. data/lib/super_diff/rspec/matcher_text_builders/have_predicate.rb +61 -0
  103. data/lib/super_diff/rspec/matcher_text_builders/match.rb +1 -1
  104. data/lib/super_diff/rspec/matcher_text_builders/raise_error.rb +13 -1
  105. data/lib/super_diff/rspec/matcher_text_builders/respond_to.rb +1 -1
  106. data/lib/super_diff/rspec/matcher_text_template.rb +1 -1
  107. data/lib/super_diff/rspec/monkey_patches.rb +226 -115
  108. data/lib/super_diff/rspec/object_inspection.rb +0 -1
  109. data/lib/super_diff/rspec/object_inspection/inspectors.rb +22 -6
  110. data/lib/super_diff/rspec/object_inspection/inspectors/collection_containing_exactly.rb +17 -8
  111. data/lib/super_diff/rspec/object_inspection/inspectors/collection_including.rb +28 -0
  112. data/lib/super_diff/rspec/object_inspection/inspectors/hash_including.rb +31 -0
  113. data/lib/super_diff/rspec/object_inspection/inspectors/instance_of.rb +23 -0
  114. data/lib/super_diff/rspec/object_inspection/inspectors/kind_of.rb +23 -0
  115. data/lib/super_diff/rspec/object_inspection/inspectors/object_having_attributes.rb +31 -0
  116. data/lib/super_diff/rspec/object_inspection/inspectors/primitive.rb +13 -0
  117. data/lib/super_diff/rspec/object_inspection/inspectors/value_within.rb +29 -0
  118. data/lib/super_diff/rspec/operation_tree_builders.rb +22 -0
  119. data/lib/super_diff/rspec/{operational_sequencers → operation_tree_builders}/collection_containing_exactly.rb +6 -6
  120. data/lib/super_diff/rspec/{operational_sequencers/partial_array.rb → operation_tree_builders/collection_including.rb} +4 -3
  121. data/lib/super_diff/rspec/operation_tree_builders/hash_including.rb +25 -0
  122. data/lib/super_diff/rspec/{operational_sequencers/partial_object.rb → operation_tree_builders/object_having_attributes.rb} +5 -11
  123. data/lib/super_diff/version.rb +1 -1
  124. data/spec/examples.txt +397 -328
  125. data/spec/integration/rails/active_record_spec.rb +1 -1
  126. data/spec/integration/rails/hash_with_indifferent_access_spec.rb +1 -1
  127. data/spec/integration/rspec/be_predicate_matcher_spec.rb +111 -59
  128. data/spec/integration/rspec/eq_matcher_spec.rb +139 -3
  129. data/spec/integration/rspec/have_attributes_matcher_spec.rb +354 -227
  130. data/spec/integration/rspec/have_predicate_matcher_spec.rb +484 -0
  131. data/spec/integration/rspec/include_matcher_spec.rb +2 -2
  132. data/spec/integration/rspec/match_array_matcher_spec.rb +372 -0
  133. data/spec/integration/rspec/match_matcher_spec.rb +8 -8
  134. data/spec/integration/rspec/raise_error_matcher_spec.rb +605 -226
  135. data/spec/integration/rspec/third_party_matcher_spec.rb +241 -0
  136. data/spec/integration/rspec/unhandled_errors_spec.rb +110 -58
  137. data/spec/spec_helper.rb +18 -7
  138. data/spec/support/command_runner.rb +3 -0
  139. data/spec/support/integration/helpers.rb +14 -90
  140. data/spec/support/integration/matchers.rb +143 -0
  141. data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +14 -29
  142. data/spec/support/integration/test_programs/base.rb +120 -0
  143. data/spec/support/integration/test_programs/plain.rb +13 -0
  144. data/spec/support/integration/test_programs/rspec_active_record.rb +17 -0
  145. data/spec/support/integration/test_programs/rspec_rails.rb +17 -0
  146. data/spec/support/models/active_record/person.rb +4 -11
  147. data/spec/support/models/active_record/query.rb +15 -0
  148. data/spec/support/models/active_record/shipping_address.rb +10 -14
  149. data/spec/support/object_id.rb +27 -0
  150. data/spec/support/ruby_versions.rb +4 -0
  151. data/spec/support/shared_examples/active_record.rb +71 -0
  152. data/spec/support/shared_examples/hash_with_indifferent_access.rb +724 -208
  153. data/spec/tmp/integration_spec.rb +15 -0
  154. data/spec/unit/{equality_matcher_spec.rb → equality_matchers/main_spec.rb} +165 -9
  155. data/spec/unit/object_inspection_spec.rb +94 -18
  156. data/spec/unit/rspec/matchers/have_predicate_spec.rb +21 -0
  157. data/spec/unit/rspec/matchers/match_array_spec.rb +11 -0
  158. data/spec/unit/rspec/matchers/raise_error_spec.rb +16 -0
  159. data/super_diff.gemspec +3 -6
  160. metadata +99 -91
  161. data/lib/super_diff/active_record/object_inspection/map_extension.rb +0 -18
  162. data/lib/super_diff/active_record/operational_sequencers.rb +0 -14
  163. data/lib/super_diff/active_support/object_inspection/map_extension.rb +0 -15
  164. data/lib/super_diff/active_support/operational_sequencers.rb +0 -10
  165. data/lib/super_diff/diff_formatter.rb +0 -32
  166. data/lib/super_diff/differ.rb +0 -51
  167. data/lib/super_diff/equality_matcher.rb +0 -32
  168. data/lib/super_diff/no_differ_available_error.rb +0 -22
  169. data/lib/super_diff/no_operational_sequencer_available_error.rb +0 -20
  170. data/lib/super_diff/object_inspection/inspector.rb +0 -27
  171. data/lib/super_diff/object_inspection/map.rb +0 -28
  172. data/lib/super_diff/operation_sequences.rb +0 -9
  173. data/lib/super_diff/operation_sequences/base.rb +0 -11
  174. data/lib/super_diff/operational_sequencer.rb +0 -48
  175. data/lib/super_diff/operational_sequencers.rb +0 -16
  176. data/lib/super_diff/operational_sequencers/base.rb +0 -89
  177. data/lib/super_diff/operational_sequencers/hash.rb +0 -85
  178. data/lib/super_diff/rspec/configuration.rb +0 -31
  179. data/lib/super_diff/rspec/differs/partial_array.rb +0 -22
  180. data/lib/super_diff/rspec/differs/partial_hash.rb +0 -22
  181. data/lib/super_diff/rspec/differs/partial_object.rb +0 -22
  182. data/lib/super_diff/rspec/object_inspection/inspectors/partial_array.rb +0 -22
  183. data/lib/super_diff/rspec/object_inspection/inspectors/partial_hash.rb +0 -21
  184. data/lib/super_diff/rspec/object_inspection/inspectors/partial_object.rb +0 -21
  185. data/lib/super_diff/rspec/object_inspection/map_extension.rb +0 -23
  186. data/lib/super_diff/rspec/operational_sequencers.rb +0 -22
  187. data/lib/super_diff/rspec/operational_sequencers/partial_hash.rb +0 -32
@@ -3,7 +3,7 @@ module SuperDiff
3
3
  module MatcherTextBuilders
4
4
  class Match < Base
5
5
  def initialize(expected_captures:, **rest)
6
- super(rest)
6
+ super(**rest)
7
7
  @expected_captures = expected_captures
8
8
  end
9
9
 
@@ -5,7 +5,19 @@ module SuperDiff
5
5
  protected
6
6
 
7
7
  def actual_phrase
8
- "Expected raised exception"
8
+ if actual
9
+ "Expected raised exception"
10
+ else
11
+ "Expected"
12
+ end
13
+ end
14
+
15
+ def add_actual_value
16
+ if actual
17
+ template.add_text_in_color(beta_color) { actual }
18
+ else
19
+ template.add_text("block")
20
+ end
9
21
  end
10
22
  end
11
23
  end
@@ -9,7 +9,7 @@ module SuperDiff
9
9
  unlimited_arguments:,
10
10
  **rest
11
11
  )
12
- super(rest)
12
+ super(**rest)
13
13
  @expected_arity = expected_arity
14
14
  @arbitrary_keywords = arbitrary_keywords
15
15
  @expected_keywords = expected_keywords
@@ -3,7 +3,7 @@ module SuperDiff
3
3
  class MatcherTextTemplate
4
4
  MAX_LINE_LENGTH = 100
5
5
 
6
- def self.generate(&block)
6
+ def self.render(&block)
7
7
  new(&block).to_s
8
8
  end
9
9
 
@@ -91,21 +91,35 @@ module RSpec
91
91
  @skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false)
92
92
  # Patch to convert options[:failure_lines] to groups
93
93
  if options.include?(:failure_lines)
94
- @failure_line_groups = {
95
- lines: options[:failure_lines],
96
- already_colored: false
97
- }
94
+ @failure_line_groups = [
95
+ {
96
+ lines: options[:failure_lines],
97
+ already_colorized: false
98
+ }
99
+ ]
98
100
  end
99
101
  end
100
102
 
101
103
  # Override to only color uncolored lines in red
102
- def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
104
+ # and to not color empty lines
105
+ def colorized_message_lines(colorizer = ::RSpec::Core::Formatters::ConsoleCodes)
103
106
  lines = failure_line_groups.flat_map do |group|
104
- if group[:already_colored]
107
+ if group[:already_colorized]
105
108
  group[:lines]
106
109
  else
107
110
  group[:lines].map do |line|
108
- colorizer.wrap(line, message_color)
111
+ if line.strip.empty?
112
+ line
113
+ else
114
+ indentation = line[/^[ ]+/]
115
+ rest = colorizer.wrap(line.sub(/^[ ]+/, ''), message_color)
116
+
117
+ if indentation
118
+ indentation + rest
119
+ else
120
+ rest
121
+ end
122
+ end
109
123
  end
110
124
  end
111
125
  end
@@ -129,33 +143,68 @@ module RSpec
129
143
  # Considering that `failure_slash_error_lines` is already colored,
130
144
  # extract this from the other lines so that they, too, can be colored,
131
145
  # later
146
+ #
147
+ # TODO: Refactor this somehow
148
+ #
132
149
  def failure_line_groups
133
- @failure_line_groups ||= [].tap do |groups|
134
- groups << {
135
- lines: failure_slash_error_lines,
136
- already_colored: true
137
- }
150
+ if defined?(@failure_line_groups)
151
+ @failure_line_groups
152
+ else
153
+ @failure_line_groups = [
154
+ {
155
+ lines: failure_slash_error_lines,
156
+ already_colorized: true
157
+ }
158
+ ]
138
159
 
139
160
  sections = [failure_slash_error_lines, exception_lines]
140
161
  separate_groups = (
141
162
  sections.any? { |section| section.size > 1 } &&
142
163
  !exception_lines.first.empty?
143
164
  )
165
+
144
166
  if separate_groups
145
- groups << { lines: [''], already_colored: true }
167
+ @failure_line_groups << { lines: [''], already_colorized: true }
168
+ end
169
+
170
+ already_colorized = exception_lines.any? do |line|
171
+ SuperDiff::Csi.already_colorized?(line)
146
172
  end
147
- already_has_coloration = exception_lines.any? do |line|
148
- line.match?(/\e\[\d+m/)
173
+
174
+ if already_colorized
175
+ @failure_line_groups << {
176
+ lines: exception_lines,
177
+ already_colorized: true
178
+ }
179
+ else
180
+ locatable_exception_lines =
181
+ exception_lines.each_with_index.map do |line, index|
182
+ { text: line, index: index }
183
+ end
184
+
185
+ boundary_line =
186
+ locatable_exception_lines.find do |line, index|
187
+ line[:text].strip.empty? || line[:text].match?(/^ /)
188
+ end
189
+
190
+ if boundary_line
191
+ @failure_line_groups << {
192
+ lines: exception_lines[0..boundary_line[:index] - 1],
193
+ already_colorized: false
194
+ }
195
+ @failure_line_groups << {
196
+ lines: exception_lines[boundary_line[:index]..-1],
197
+ already_colorized: true
198
+ }
199
+ else
200
+ @failure_line_groups << {
201
+ lines: exception_lines,
202
+ already_colorized: false
203
+ }
204
+ end
149
205
  end
150
206
 
151
- groups << {
152
- lines: exception_lines[0..0],
153
- already_colored: already_has_coloration
154
- }
155
- groups << {
156
- lines: exception_lines[1..-1] + extra_failure_lines,
157
- already_colored: true
158
- }
207
+ @failure_line_groups
159
208
  end
160
209
  end
161
210
 
@@ -204,6 +253,11 @@ module RSpec
204
253
 
205
254
  module Support
206
255
  class ObjectFormatter
256
+ # Override to use our formatting algorithm
257
+ def self.format(value)
258
+ SuperDiff::ObjectInspection.inspect(value, as_single_line: true)
259
+ end
260
+
207
261
  # Override to use our formatting algorithm
208
262
  def format(value)
209
263
  SuperDiff::ObjectInspection.inspect(value, as_single_line: true)
@@ -303,20 +357,6 @@ module RSpec
303
357
  end)
304
358
  end
305
359
 
306
- class BeTruthy
307
- prepend SuperDiff::RSpec::AugmentedMatcher
308
-
309
- prepend(Module.new do
310
- def expected_action_for_matcher_text
311
- "be"
312
- end
313
-
314
- def expected_for_matcher_text
315
- "truthy"
316
- end
317
- end)
318
- end
319
-
320
360
  class BeFalsey
321
361
  prepend SuperDiff::RSpec::AugmentedMatcher
322
362
 
@@ -375,12 +415,18 @@ module RSpec
375
415
  end)
376
416
  end
377
417
 
378
- class Eq
418
+ class BeTruthy
379
419
  prepend SuperDiff::RSpec::AugmentedMatcher
380
- end
381
420
 
382
- class Equal
383
- prepend SuperDiff::RSpec::AugmentedMatcher
421
+ prepend(Module.new do
422
+ def expected_action_for_matcher_text
423
+ "be"
424
+ end
425
+
426
+ def expected_for_matcher_text
427
+ "truthy"
428
+ end
429
+ end)
384
430
  end
385
431
 
386
432
  class ContainExactly
@@ -405,72 +451,12 @@ module RSpec
405
451
  end)
406
452
  end
407
453
 
408
- class Include
454
+ class Eq
409
455
  prepend SuperDiff::RSpec::AugmentedMatcher
410
-
411
- prepend(Module.new do
412
- # Override this method so that the differ knows that this is a partial
413
- # array or hash
414
- def expected_for_diff
415
- if expecteds.all? { |item| item.is_a?(Hash) }
416
- matchers.a_collection_including(expecteds.first)
417
- else
418
- matchers.a_collection_including(*expecteds)
419
- end
420
- end
421
-
422
- private
423
-
424
- # Override to capitalize message and add period at end
425
- def build_failure_message(negated:)
426
- message = super
427
-
428
- if actual.respond_to?(:include?)
429
- message
430
- elsif message.end_with?(".")
431
- message.sub("\.$", ", ") + "but it does not respond to `include?`."
432
- else
433
- message + "\n\nBut it does not respond to `include?`."
434
- end
435
- end
436
-
437
- # Override to use readable_list_of
438
- def expected_for_description
439
- readable_list_of(expecteds).lstrip
440
- end
441
-
442
- # Override to use readable_list_of
443
- def expected_for_failure_message
444
- # TODO: Switch to using @divergent_items and handle this in the text
445
- # builder
446
- readable_list_of(@divergent_items).lstrip
447
- end
448
-
449
- # Update to use (...) as delimiter instead of {...}
450
- def readable_list_of(items)
451
- if items && items.all? { |item| item.is_a?(Hash) }
452
- description_of(items.inject(:merge)).
453
- sub(/^\{ /, '(').
454
- sub(/ \}$/, ')')
455
- else
456
- super
457
- end
458
- end
459
- end)
460
456
  end
461
457
 
462
- class Match
458
+ class Equal
463
459
  prepend SuperDiff::RSpec::AugmentedMatcher
464
-
465
- prepend(Module.new do
466
- def matcher_text_builder_class
467
- SuperDiff::RSpec::MatcherTextBuilders::Match
468
- end
469
-
470
- def matcher_text_builder_args
471
- super.merge(expected_captures: @expected_captures)
472
- end
473
- end)
474
460
  end
475
461
 
476
462
  class HaveAttributes
@@ -550,33 +536,113 @@ module RSpec
550
536
  end
551
537
  end
552
538
 
553
- class RespondTo
539
+ class Has
554
540
  prepend SuperDiff::RSpec::AugmentedMatcher
555
541
 
556
542
  prepend(Module.new do
543
+ def actual_for_matcher_text
544
+ actual
545
+ end
546
+
547
+ def expected_for_matcher_text
548
+ "#{predicate}#{failure_message_args_description}"
549
+ end
550
+
551
+ def expected_action_for_matcher_text
552
+ "return true for"
553
+ end
554
+
557
555
  def matcher_text_builder_class
558
- SuperDiff::RSpec::MatcherTextBuilders::RespondTo
556
+ SuperDiff::RSpec::MatcherTextBuilders::HavePredicate
559
557
  end
560
558
 
561
559
  def matcher_text_builder_args
562
560
  super.merge(
563
- expected_arity: @expected_arity,
564
- arbitrary_keywords: @arbitrary_keywords,
565
- expected_keywords: @expected_keywords,
566
- unlimited_arguments: @unlimited_arguments
561
+ predicate_accessible: predicate_accessible?,
562
+ private_predicate: private_predicate?
567
563
  )
568
564
  end
565
+ end)
566
+ end
569
567
 
568
+ class Include
569
+ prepend SuperDiff::RSpec::AugmentedMatcher
570
+
571
+ prepend(Module.new do
572
+ # Override this method so that the differ knows that this is a partial
573
+ # array or hash
574
+ def expected_for_diff
575
+ if expecteds.all? { |item| item.is_a?(Hash) }
576
+ matchers.a_collection_including(expecteds.first)
577
+ else
578
+ matchers.a_collection_including(*expecteds)
579
+ end
580
+ end
581
+
582
+ private
583
+
584
+ # Override to capitalize message and add period at end
585
+ def build_failure_message(negated:)
586
+ message = super
587
+
588
+ if actual.respond_to?(:include?)
589
+ message
590
+ elsif message.end_with?(".")
591
+ message.sub("\.$", ", ") + "but it does not respond to `include?`."
592
+ else
593
+ message + "\n\nBut it does not respond to `include?`."
594
+ end
595
+ end
596
+
597
+ # Override to use readable_list_of
570
598
  def expected_for_description
571
- @names
599
+ readable_list_of(expecteds).lstrip
572
600
  end
573
601
 
602
+ # Override to use readable_list_of
574
603
  def expected_for_failure_message
575
- @failing_method_names
604
+ # TODO: Switch to using @divergent_items and handle this in the text
605
+ # builder
606
+ readable_list_of(@divergent_items).lstrip
607
+ end
608
+
609
+ # Update to use (...) as delimiter instead of {...}
610
+ def readable_list_of(items)
611
+ if items && items.all? { |item| item.is_a?(Hash) }
612
+ description_of(items.inject(:merge)).
613
+ sub(/^\{ /, '(').
614
+ sub(/ \}$/, ')')
615
+ else
616
+ super
617
+ end
618
+ end
619
+ end)
620
+ end
621
+
622
+ class Match
623
+ prepend SuperDiff::RSpec::AugmentedMatcher
624
+
625
+ prepend(Module.new do
626
+ def matcher_text_builder_class
627
+ SuperDiff::RSpec::MatcherTextBuilders::Match
628
+ end
629
+
630
+ def matcher_text_builder_args
631
+ super.merge(expected_captures: @expected_captures)
576
632
  end
577
633
  end)
578
634
  end
579
635
 
636
+ class MatchArray < ContainExactly
637
+ def expected_for_diff
638
+ matchers.an_array_matching(expected)
639
+ end
640
+
641
+ def expected_action_for_matcher_text
642
+ "match array with"
643
+ end
644
+ end
645
+
580
646
  class RaiseError
581
647
  prepend SuperDiff::RSpec::AugmentedMatcher
582
648
 
@@ -588,12 +654,16 @@ module RSpec
588
654
  end
589
655
 
590
656
  def actual_for_diff
591
- @actual_error.message
657
+ if @actual_error
658
+ @actual_error.message
659
+ end
592
660
  end
593
661
 
594
662
  def expected_for_matcher_text
595
663
  if @expected_message
596
- "#<#{@expected_error.name} #{@expected_message.inspect}>"
664
+ "#<#{@expected_error.name} #{description_of(@expected_message)}>"
665
+ elsif @expected_error.is_a? Regexp
666
+ "#<Exception #{description_of(@expected_error)}>"
597
667
  else
598
668
  "#<#{@expected_error.name}>"
599
669
  end
@@ -608,7 +678,11 @@ module RSpec
608
678
  end
609
679
 
610
680
  def expected_action_for_failure_message
611
- "match"
681
+ if @actual_error
682
+ "match"
683
+ else
684
+ "raise error"
685
+ end
612
686
  end
613
687
 
614
688
  def matcher_text_builder_class
@@ -617,9 +691,46 @@ module RSpec
617
691
  end)
618
692
 
619
693
  def self.matcher_name
620
- 'raise error'
694
+ "raise error"
621
695
  end
622
696
  end
697
+
698
+ class RespondTo
699
+ prepend SuperDiff::RSpec::AugmentedMatcher
700
+
701
+ prepend(Module.new do
702
+ def initialize(*)
703
+ super
704
+ @failing_method_names = nil
705
+ end
706
+
707
+ def matcher_text_builder_class
708
+ SuperDiff::RSpec::MatcherTextBuilders::RespondTo
709
+ end
710
+
711
+ def matcher_text_builder_args
712
+ super.merge(
713
+ expected_arity: @expected_arity,
714
+ arbitrary_keywords: @arbitrary_keywords,
715
+ expected_keywords: @expected_keywords,
716
+ unlimited_arguments: @unlimited_arguments
717
+ )
718
+ end
719
+
720
+ def expected_for_description
721
+ @names
722
+ end
723
+
724
+ def expected_for_failure_message
725
+ @failing_method_names
726
+ end
727
+ end)
728
+ end
729
+ end
730
+
731
+ def match_array(items)
732
+ BuiltIn::MatchArray.new(items)
623
733
  end
734
+ alias_matcher :an_array_matching, :match_array
624
735
  end
625
736
  end