super_diff 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/super_diff/active_record.rb +2 -0
  4. data/lib/super_diff/active_record/monkey_patches.rb +9 -0
  5. data/lib/super_diff/csi.rb +4 -0
  6. data/lib/super_diff/equality_matchers/default.rb +1 -1
  7. data/lib/super_diff/operation_sequences/base.rb +14 -0
  8. data/lib/super_diff/rspec.rb +9 -9
  9. data/lib/super_diff/rspec/differ.rb +6 -6
  10. data/lib/super_diff/rspec/differs.rb +9 -3
  11. data/lib/super_diff/rspec/differs/collection_containing_exactly.rb +1 -1
  12. data/lib/super_diff/rspec/differs/{partial_hash.rb → collection_including.rb} +4 -3
  13. data/lib/super_diff/rspec/differs/{partial_array.rb → hash_including.rb} +4 -3
  14. data/lib/super_diff/rspec/differs/{partial_object.rb → object_having_attributes.rb} +3 -3
  15. data/lib/super_diff/rspec/matcher_text_builders.rb +4 -0
  16. data/lib/super_diff/rspec/matcher_text_builders/be_predicate.rb +26 -7
  17. data/lib/super_diff/rspec/matcher_text_builders/have_predicate.rb +61 -0
  18. data/lib/super_diff/rspec/matcher_text_builders/raise_error.rb +13 -1
  19. data/lib/super_diff/rspec/monkey_patches.rb +218 -111
  20. data/lib/super_diff/rspec/object_inspection/inspectors.rb +6 -6
  21. data/lib/super_diff/rspec/object_inspection/inspectors/{partial_array.rb → collection_including.rb} +2 -2
  22. data/lib/super_diff/rspec/object_inspection/inspectors/{partial_hash.rb → hash_including.rb} +1 -1
  23. data/lib/super_diff/rspec/object_inspection/inspectors/object_having_attributes.rb +22 -0
  24. data/lib/super_diff/rspec/object_inspection/map_extension.rb +7 -7
  25. data/lib/super_diff/rspec/operational_sequencers.rb +6 -6
  26. data/lib/super_diff/rspec/operational_sequencers/collection_containing_exactly.rb +1 -1
  27. data/lib/super_diff/rspec/operational_sequencers/{partial_array.rb → collection_including.rb} +3 -2
  28. data/lib/super_diff/rspec/operational_sequencers/{partial_hash.rb → hash_including.rb} +3 -2
  29. data/lib/super_diff/rspec/operational_sequencers/{partial_object.rb → object_having_attributes.rb} +2 -4
  30. data/lib/super_diff/version.rb +1 -1
  31. data/spec/integration/rails/active_record_spec.rb +1 -1
  32. data/spec/integration/rails/hash_with_indifferent_access_spec.rb +1 -1
  33. data/spec/integration/rspec/be_predicate_matcher_spec.rb +111 -59
  34. data/spec/integration/rspec/eq_matcher_spec.rb +1 -1
  35. data/spec/integration/rspec/have_predicate_matcher_spec.rb +484 -0
  36. data/spec/integration/rspec/match_array_matcher_spec.rb +372 -0
  37. data/spec/integration/rspec/match_matcher_spec.rb +8 -8
  38. data/spec/integration/rspec/raise_error_matcher_spec.rb +605 -226
  39. data/spec/integration/rspec/third_party_matcher_spec.rb +241 -0
  40. data/spec/integration/rspec/unhandled_errors_spec.rb +56 -81
  41. data/spec/spec_helper.rb +18 -7
  42. data/spec/support/integration/helpers.rb +10 -2
  43. data/spec/support/integration/matchers.rb +143 -0
  44. data/spec/support/models/active_record/query.rb +15 -0
  45. data/spec/support/object_id.rb +26 -0
  46. data/spec/support/ruby_versions.rb +4 -0
  47. data/spec/support/shared_examples/active_record.rb +71 -0
  48. data/spec/unit/equality_matcher_spec.rb +8 -8
  49. data/spec/unit/object_inspection_spec.rb +17 -17
  50. data/spec/unit/rspec/matchers/have_predicate_spec.rb +21 -0
  51. data/spec/unit/rspec/matchers/match_array_spec.rb +11 -0
  52. data/super_diff.gemspec +0 -1
  53. metadata +30 -34
  54. data/lib/super_diff/rspec/object_inspection/inspectors/partial_object.rb +0 -21
  55. data/spec/examples.txt +0 -350
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7c5f949ffe359d395ca46825b8bf8be046c4c0e940710c82acc4777e3f239e9
4
- data.tar.gz: 222f3864ffe1a7d5e0e1fc5d01b66a1820bf0a3e0125e2f0e6de6771febeadef
3
+ metadata.gz: 8efa30260e2d32c10ea6571d37a393756b216c1ba2c991d28a4124a0822c7e6f
4
+ data.tar.gz: a98a146c39eda8aeb3735869ffadacb279c26b76f2bf3f4b2ebc212fd6e8dbe1
5
5
  SHA512:
6
- metadata.gz: 6c05bd06aab01df5c8ed84af2b38ebaaae913436e16cf11566c23b7afd9cc893a59c30fdb8c666c8e088c0101f3e970e0f549da9a3df4c44482142a3d649cd4d
7
- data.tar.gz: 0d4040820fa2633d106c2f8dc5e4462393c7b12a2cb250444755d77a333bc5ea9375660f8e8b3bab1d3985e63d0c25e27eb4e8aaac7a991d9858af9821106b08
6
+ metadata.gz: a0fdf3017f5a38865bb8cfbb15dfacf3a070f42c23883a6821b39a4b2bb00da3c9beb1e65c417b5094693ab14de18edf9934f23c7f242849cc2dfb950ee60649
7
+ data.tar.gz: 6484896862126281d2f051d7ef17b760426083bfba3f9ba7f93936f331dca3cee187cbd8e75e46e6a9f86cbcd17d38e47fd9c2096a7ffdb6dc4290bed763c92f
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  SuperDiff is a gem that hooks into RSpec to intelligently display the
11
11
  differences between two data structures of any type.
12
12
 
13
- 📢 **[See what's changed in the latest version (0.2.0)][changelog].**
13
+ 📢 **[See what's changed in the latest version (0.3.0)][changelog].**
14
14
 
15
15
  [changelog]: CHANGELOG.md
16
16
 
@@ -84,13 +84,13 @@ expect(actual).to eq(expected)
84
84
 
85
85
  You would get output that looks like this:
86
86
 
87
- ![Before super_diff](doc/before_super_diff.png)
87
+ ![Before super_diff](doc/before.png)
88
88
 
89
89
  What this library does is to provide a diff engine that knows how to figure out
90
90
  the differences between any two data structures and display them in a sensible
91
91
  way. So, using the example above, you'd get this instead:
92
92
 
93
- ![After super_diff](doc/after_super_diff.png)
93
+ ![After super_diff](doc/after.png)
94
94
 
95
95
  ## Installation
96
96
 
@@ -36,6 +36,8 @@ if defined?(SuperDiff::RSpec)
36
36
  end
37
37
  end
38
38
 
39
+ require "super_diff/active_record/monkey_patches"
40
+
39
41
  SuperDiff::ObjectInspection.map.prepend(
40
42
  SuperDiff::ActiveRecord::ObjectInspection::MapExtension,
41
43
  )
@@ -0,0 +1,9 @@
1
+ # rubocop:disable Style/BracesAroundHashParameters, Style/ClassAndModuleChildren
2
+ class ActiveRecord::Base
3
+ def attributes_for_super_diff
4
+ (attributes.keys.sort - ["id"]).reduce({ id: id }) do |hash, key|
5
+ hash.merge(key.to_sym => attributes[key])
6
+ end
7
+ end
8
+ end
9
+ # rubocop:enable Style/BracesAroundHashParameters, Style/ClassAndModuleChildren
@@ -36,6 +36,10 @@ module SuperDiff
36
36
  text.gsub(/\e\[\d+(?:;\d+)*m(.+?)\e\[0m/, '\1')
37
37
  end
38
38
 
39
+ def self.already_colorized?(text)
40
+ text.match?(/\e\[\d+m/)
41
+ end
42
+
39
43
  def self.inspect_colors_in(text)
40
44
  [FourBitColor, EightBitColor, TwentyFourBitColor].
41
45
  reduce(text) do |str, klass|
@@ -34,7 +34,7 @@ module SuperDiff
34
34
  end
35
35
 
36
36
  def diff_section
37
- if diff.blank?
37
+ if diff.empty?
38
38
  ""
39
39
  else
40
40
  <<~SECTION
@@ -6,6 +6,20 @@ module SuperDiff
6
6
  raise NotImplementedError
7
7
  end
8
8
  # rubocop:enable Lint/UnusedMethodArgument
9
+
10
+ def pretty_print(pp)
11
+ pp.text "#{self.class.name}.new(["
12
+ pp.group_sub do
13
+ pp.nest(2) do
14
+ pp.breakable
15
+ pp.seplist(self) do |value|
16
+ pp.pp value
17
+ end
18
+ end
19
+ end
20
+ pp.breakable
21
+ pp.text("])")
22
+ end
9
23
  end
10
24
  end
11
25
  end
@@ -25,30 +25,30 @@ module SuperDiff
25
25
  @_configuration ||= Configuration.new
26
26
  end
27
27
 
28
- def self.partial_hash?(value)
29
- partial_placeholder?(value) &&
28
+ def self.a_hash_including_something?(value)
29
+ fuzzy_object?(value) &&
30
30
  value.respond_to?(:expecteds) &&
31
31
  value.expecteds.one? &&
32
32
  value.expecteds.first.is_a?(::Hash)
33
33
  end
34
34
 
35
- def self.partial_array?(value)
36
- partial_placeholder?(value) &&
35
+ def self.a_collection_including_something?(value)
36
+ fuzzy_object?(value) &&
37
37
  value.respond_to?(:expecteds) &&
38
38
  !(value.expecteds.one? && value.expecteds.first.is_a?(::Hash))
39
39
  end
40
40
 
41
- def self.partial_object?(value)
42
- partial_placeholder?(value) &&
41
+ def self.an_object_having_some_attributes?(value)
42
+ fuzzy_object?(value) &&
43
43
  value.base_matcher.is_a?(::RSpec::Matchers::BuiltIn::HaveAttributes)
44
44
  end
45
45
 
46
- def self.collection_containing_exactly?(value)
47
- partial_placeholder?(value) &&
46
+ def self.a_collection_containing_exactly_something?(value)
47
+ fuzzy_object?(value) &&
48
48
  value.base_matcher.is_a?(::RSpec::Matchers::BuiltIn::ContainExactly)
49
49
  end
50
50
 
51
- def self.partial_placeholder?(value)
51
+ def self.fuzzy_object?(value)
52
52
  value.is_a?(::RSpec::Matchers::AliasedMatcher)
53
53
  end
54
54
  end
@@ -14,16 +14,16 @@ module SuperDiff
14
14
  extra_classes: [
15
15
  *RSpec.configuration.extra_differ_classes,
16
16
  Differs::CollectionContainingExactly,
17
- Differs::PartialArray,
18
- Differs::PartialHash,
19
- Differs::PartialObject,
17
+ Differs::CollectionIncluding,
18
+ Differs::HashIncluding,
19
+ Differs::ObjectHavingAttributes,
20
20
  ],
21
21
  extra_operational_sequencer_classes: [
22
22
  *RSpec.configuration.extra_operational_sequencer_classes,
23
23
  OperationalSequencers::CollectionContainingExactly,
24
- OperationalSequencers::PartialArray,
25
- OperationalSequencers::PartialHash,
26
- OperationalSequencers::PartialObject,
24
+ OperationalSequencers::CollectionIncluding,
25
+ OperationalSequencers::HashIncluding,
26
+ OperationalSequencers::ObjectHavingAttributes,
27
27
  ],
28
28
  extra_diff_formatter_classes: RSpec.configuration.extra_diff_formatter_classes,
29
29
  )
@@ -5,9 +5,15 @@ module SuperDiff
5
5
  :CollectionContainingExactly,
6
6
  "super_diff/rspec/differs/collection_containing_exactly",
7
7
  )
8
- autoload :PartialArray, "super_diff/rspec/differs/partial_array"
9
- autoload :PartialHash, "super_diff/rspec/differs/partial_hash"
10
- autoload :PartialObject, "super_diff/rspec/differs/partial_object"
8
+ autoload(
9
+ :CollectionIncluding,
10
+ "super_diff/rspec/differs/collection_including",
11
+ )
12
+ autoload :HashIncluding, "super_diff/rspec/differs/hash_including"
13
+ autoload(
14
+ :ObjectHavingAttributes,
15
+ "super_diff/rspec/differs/object_having_attributes",
16
+ )
11
17
  end
12
18
  end
13
19
  end
@@ -3,7 +3,7 @@ module SuperDiff
3
3
  module Differs
4
4
  class CollectionContainingExactly < SuperDiff::Differs::Array
5
5
  def self.applies_to?(expected, actual)
6
- SuperDiff::RSpec.collection_containing_exactly?(expected) &&
6
+ SuperDiff::RSpec.a_collection_containing_exactly_something?(expected) &&
7
7
  actual.is_a?(::Array)
8
8
  end
9
9
 
@@ -1,15 +1,16 @@
1
1
  module SuperDiff
2
2
  module RSpec
3
3
  module Differs
4
- class PartialHash < SuperDiff::Differs::Hash
4
+ class CollectionIncluding < SuperDiff::Differs::Array
5
5
  def self.applies_to?(expected, actual)
6
- SuperDiff::RSpec.partial_hash?(expected) && actual.is_a?(::Hash)
6
+ SuperDiff::RSpec.a_collection_including_something?(expected) &&
7
+ actual.is_a?(::Array)
7
8
  end
8
9
 
9
10
  private
10
11
 
11
12
  def operations
12
- OperationalSequencers::PartialHash.call(
13
+ OperationalSequencers::CollectionIncluding.call(
13
14
  expected: expected,
14
15
  actual: actual,
15
16
  extra_operational_sequencer_classes: extra_operational_sequencer_classes,
@@ -1,15 +1,16 @@
1
1
  module SuperDiff
2
2
  module RSpec
3
3
  module Differs
4
- class PartialArray < SuperDiff::Differs::Array
4
+ class HashIncluding < SuperDiff::Differs::Hash
5
5
  def self.applies_to?(expected, actual)
6
- SuperDiff::RSpec.partial_array?(expected) && actual.is_a?(::Array)
6
+ SuperDiff::RSpec.a_hash_including_something?(expected) &&
7
+ actual.is_a?(::Hash)
7
8
  end
8
9
 
9
10
  private
10
11
 
11
12
  def operations
12
- OperationalSequencers::PartialArray.call(
13
+ OperationalSequencers::HashIncluding.call(
13
14
  expected: expected,
14
15
  actual: actual,
15
16
  extra_operational_sequencer_classes: extra_operational_sequencer_classes,
@@ -1,15 +1,15 @@
1
1
  module SuperDiff
2
2
  module RSpec
3
3
  module Differs
4
- class PartialObject < SuperDiff::Differs::DefaultObject
4
+ class ObjectHavingAttributes < SuperDiff::Differs::DefaultObject
5
5
  def self.applies_to?(expected, _actual)
6
- SuperDiff::RSpec.partial_object?(expected)
6
+ SuperDiff::RSpec.an_object_having_some_attributes?(expected)
7
7
  end
8
8
 
9
9
  private
10
10
 
11
11
  def operations
12
- OperationalSequencers::PartialObject.call(
12
+ OperationalSequencers::ObjectHavingAttributes.call(
13
13
  expected: expected,
14
14
  actual: actual,
15
15
  extra_operational_sequencer_classes: extra_operational_sequencer_classes,
@@ -10,6 +10,10 @@ module SuperDiff
10
10
  :ContainExactly,
11
11
  "super_diff/rspec/matcher_text_builders/contain_exactly",
12
12
  )
13
+ autoload(
14
+ :HavePredicate,
15
+ "super_diff/rspec/matcher_text_builders/have_predicate",
16
+ )
13
17
  autoload :Match, "super_diff/rspec/matcher_text_builders/match"
14
18
  autoload(
15
19
  :RaiseError,
@@ -36,27 +36,46 @@ module SuperDiff
36
36
  end
37
37
  end
38
38
 
39
- def add_expected_value_to(template, expected)
39
+ def add_expected_value_to_failure_message(template)
40
40
  template.add_text " "
41
- template.add_text_in_color(alpha_color, "`#{expected}?`")
41
+ template.add_text_in_color(
42
+ alpha_color,
43
+ "#{expected_for_failure_message}?",
44
+ )
42
45
  template.add_text " or "
43
- template.add_text_in_color(alpha_color, "`#{expected}s?`")
46
+ template.add_text_in_color(
47
+ alpha_color,
48
+ "#{expected_for_failure_message}s?",
49
+ )
50
+ end
51
+
52
+ def add_expected_value_to_description(template)
53
+ template.add_text " "
54
+ template.add_text_in_color(
55
+ alpha_color,
56
+ "`#{expected_for_description}?`",
57
+ )
58
+ template.add_text " or "
59
+ template.add_text_in_color(
60
+ alpha_color,
61
+ "`#{expected_for_description}s?`",
62
+ )
44
63
  end
45
64
 
46
65
  def add_extra_after_error
47
66
  if expected_predicate_method_name == :true?
48
67
  template.add_text "\n\n"
49
68
  template.add_text "(Perhaps you want to use "
50
- template.add_text_in_color(:blue, "`be(true)`")
69
+ template.add_text_in_color(:blue, "be(true)")
51
70
  template.add_text " or "
52
- template.add_text_in_color(:blue, "`be_truthy`")
71
+ template.add_text_in_color(:blue, "be_truthy")
53
72
  template.add_text " instead?)"
54
73
  elsif expected_predicate_method_name == :false?
55
74
  template.add_text "\n\n"
56
75
  template.add_text "(Perhaps you want to use "
57
- template.add_text_in_color(:blue, "`be(false)`")
76
+ template.add_text_in_color(:blue, "be(false)")
58
77
  template.add_text " or "
59
- template.add_text_in_color(:blue, "`be_falsey`")
78
+ template.add_text_in_color(:blue, "be_falsey")
60
79
  template.add_text " instead?)"
61
80
  end
62
81
  end
@@ -0,0 +1,61 @@
1
+ module SuperDiff
2
+ module RSpec
3
+ module MatcherTextBuilders
4
+ class HavePredicate < Base
5
+ def initialize(predicate_accessible:, private_predicate:, **rest)
6
+ super(**rest)
7
+ @predicate_accessible = predicate_accessible
8
+ @private_predicate = private_predicate
9
+ end
10
+
11
+ protected
12
+
13
+ def expected_action_for_failure_message
14
+ if predicate_accessible?
15
+ "return a truthy result for"
16
+ elsif private_predicate?
17
+ "have a public method"
18
+ else
19
+ "respond to"
20
+ end
21
+ end
22
+
23
+ def beta_color
24
+ :yellow
25
+ end
26
+
27
+ def add_actual_value
28
+ template.add_text_in_color(beta_color) do
29
+ description_of(actual)
30
+ end
31
+ end
32
+
33
+ def add_expected_value_to_failure_message(template)
34
+ template.add_text " "
35
+ template.add_text_in_color(
36
+ alpha_color,
37
+ expected_for_failure_message,
38
+ )
39
+ end
40
+
41
+ def add_expected_value_to_description(template)
42
+ template.add_text " "
43
+ template.add_text_in_color(
44
+ alpha_color,
45
+ "`#{expected_for_description}`",
46
+ )
47
+ end
48
+
49
+ private
50
+
51
+ def private_predicate?
52
+ @private_predicate
53
+ end
54
+
55
+ def predicate_accessible?
56
+ @predicate_accessible
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -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
@@ -93,19 +93,31 @@ module RSpec
93
93
  if options.include?(:failure_lines)
94
94
  @failure_line_groups = {
95
95
  lines: options[:failure_lines],
96
- already_colored: false
96
+ already_colorized: false
97
97
  }
98
98
  end
99
99
  end
100
100
 
101
101
  # Override to only color uncolored lines in red
102
- def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
102
+ # and to not color empty lines
103
+ def colorized_message_lines(colorizer = ::RSpec::Core::Formatters::ConsoleCodes)
103
104
  lines = failure_line_groups.flat_map do |group|
104
- if group[:already_colored]
105
+ if group[:already_colorized]
105
106
  group[:lines]
106
107
  else
107
108
  group[:lines].map do |line|
108
- colorizer.wrap(line, message_color)
109
+ if line.strip.empty?
110
+ line
111
+ else
112
+ indentation = line[/^[ ]+/]
113
+ rest = colorizer.wrap(line.sub(/^[ ]+/, ''), message_color)
114
+
115
+ if indentation
116
+ indentation + rest
117
+ else
118
+ rest
119
+ end
120
+ end
109
121
  end
110
122
  end
111
123
  end
@@ -129,33 +141,68 @@ module RSpec
129
141
  # Considering that `failure_slash_error_lines` is already colored,
130
142
  # extract this from the other lines so that they, too, can be colored,
131
143
  # later
144
+ #
145
+ # TODO: Refactor this somehow
146
+ #
132
147
  def failure_line_groups
133
- @failure_line_groups ||= [].tap do |groups|
134
- groups << {
135
- lines: failure_slash_error_lines,
136
- already_colored: true
137
- }
148
+ if defined?(@failure_line_groups)
149
+ @failure_line_groups
150
+ else
151
+ @failure_line_groups = [
152
+ {
153
+ lines: failure_slash_error_lines,
154
+ already_colorized: true
155
+ }
156
+ ]
138
157
 
139
158
  sections = [failure_slash_error_lines, exception_lines]
140
159
  separate_groups = (
141
160
  sections.any? { |section| section.size > 1 } &&
142
161
  !exception_lines.first.empty?
143
162
  )
163
+
144
164
  if separate_groups
145
- groups << { lines: [''], already_colored: true }
165
+ @failure_line_groups << { lines: [''], already_colorized: true }
146
166
  end
147
- already_has_coloration = exception_lines.any? do |line|
148
- line.match?(/\e\[\d+m/)
167
+
168
+ already_colorized = exception_lines.any? do |line|
169
+ SuperDiff::Csi.already_colorized?(line)
149
170
  end
150
171
 
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
- }
172
+ if already_colorized
173
+ @failure_line_groups << {
174
+ lines: exception_lines,
175
+ already_colorized: true
176
+ }
177
+ else
178
+ locatable_exception_lines =
179
+ exception_lines.each_with_index.map do |line, index|
180
+ { text: line, index: index }
181
+ end
182
+
183
+ boundary_line =
184
+ locatable_exception_lines.find do |line, index|
185
+ line[:text].strip.empty? || line[:text].match?(/^ /)
186
+ end
187
+
188
+ if boundary_line
189
+ @failure_line_groups << {
190
+ lines: exception_lines[0..boundary_line[:index] - 1],
191
+ already_colorized: false
192
+ }
193
+ @failure_line_groups << {
194
+ lines: exception_lines[boundary_line[:index]..-1],
195
+ already_colorized: true
196
+ }
197
+ else
198
+ @failure_line_groups << {
199
+ lines: exception_lines,
200
+ already_colorized: false
201
+ }
202
+ end
203
+ end
204
+
205
+ @failure_line_groups
159
206
  end
160
207
  end
161
208
 
@@ -204,6 +251,11 @@ module RSpec
204
251
 
205
252
  module Support
206
253
  class ObjectFormatter
254
+ # Override to use our formatting algorithm
255
+ def self.format(value)
256
+ SuperDiff::ObjectInspection.inspect(value, as_single_line: true)
257
+ end
258
+
207
259
  # Override to use our formatting algorithm
208
260
  def format(value)
209
261
  SuperDiff::ObjectInspection.inspect(value, as_single_line: true)
@@ -303,20 +355,6 @@ module RSpec
303
355
  end)
304
356
  end
305
357
 
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
358
  class BeFalsey
321
359
  prepend SuperDiff::RSpec::AugmentedMatcher
322
360
 
@@ -375,12 +413,18 @@ module RSpec
375
413
  end)
376
414
  end
377
415
 
378
- class Eq
416
+ class BeTruthy
379
417
  prepend SuperDiff::RSpec::AugmentedMatcher
380
- end
381
418
 
382
- class Equal
383
- prepend SuperDiff::RSpec::AugmentedMatcher
419
+ prepend(Module.new do
420
+ def expected_action_for_matcher_text
421
+ "be"
422
+ end
423
+
424
+ def expected_for_matcher_text
425
+ "truthy"
426
+ end
427
+ end)
384
428
  end
385
429
 
386
430
  class ContainExactly
@@ -405,72 +449,12 @@ module RSpec
405
449
  end)
406
450
  end
407
451
 
408
- class Include
452
+ class Eq
409
453
  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
454
  end
461
455
 
462
- class Match
456
+ class Equal
463
457
  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
458
  end
475
459
 
476
460
  class HaveAttributes
@@ -550,33 +534,113 @@ module RSpec
550
534
  end
551
535
  end
552
536
 
553
- class RespondTo
537
+ class Has
554
538
  prepend SuperDiff::RSpec::AugmentedMatcher
555
539
 
556
540
  prepend(Module.new do
541
+ def actual_for_matcher_text
542
+ actual
543
+ end
544
+
545
+ def expected_for_matcher_text
546
+ "#{predicate}#{failure_message_args_description}"
547
+ end
548
+
549
+ def expected_action_for_matcher_text
550
+ "return true for"
551
+ end
552
+
557
553
  def matcher_text_builder_class
558
- SuperDiff::RSpec::MatcherTextBuilders::RespondTo
554
+ SuperDiff::RSpec::MatcherTextBuilders::HavePredicate
559
555
  end
560
556
 
561
557
  def matcher_text_builder_args
562
558
  super.merge(
563
- expected_arity: @expected_arity,
564
- arbitrary_keywords: @arbitrary_keywords,
565
- expected_keywords: @expected_keywords,
566
- unlimited_arguments: @unlimited_arguments
559
+ predicate_accessible: predicate_accessible?,
560
+ private_predicate: private_predicate?
567
561
  )
568
562
  end
563
+ end)
564
+ end
569
565
 
566
+ class Include
567
+ prepend SuperDiff::RSpec::AugmentedMatcher
568
+
569
+ prepend(Module.new do
570
+ # Override this method so that the differ knows that this is a partial
571
+ # array or hash
572
+ def expected_for_diff
573
+ if expecteds.all? { |item| item.is_a?(Hash) }
574
+ matchers.a_collection_including(expecteds.first)
575
+ else
576
+ matchers.a_collection_including(*expecteds)
577
+ end
578
+ end
579
+
580
+ private
581
+
582
+ # Override to capitalize message and add period at end
583
+ def build_failure_message(negated:)
584
+ message = super
585
+
586
+ if actual.respond_to?(:include?)
587
+ message
588
+ elsif message.end_with?(".")
589
+ message.sub("\.$", ", ") + "but it does not respond to `include?`."
590
+ else
591
+ message + "\n\nBut it does not respond to `include?`."
592
+ end
593
+ end
594
+
595
+ # Override to use readable_list_of
570
596
  def expected_for_description
571
- @names
597
+ readable_list_of(expecteds).lstrip
572
598
  end
573
599
 
600
+ # Override to use readable_list_of
574
601
  def expected_for_failure_message
575
- @failing_method_names
602
+ # TODO: Switch to using @divergent_items and handle this in the text
603
+ # builder
604
+ readable_list_of(@divergent_items).lstrip
605
+ end
606
+
607
+ # Update to use (...) as delimiter instead of {...}
608
+ def readable_list_of(items)
609
+ if items && items.all? { |item| item.is_a?(Hash) }
610
+ description_of(items.inject(:merge)).
611
+ sub(/^\{ /, '(').
612
+ sub(/ \}$/, ')')
613
+ else
614
+ super
615
+ end
576
616
  end
577
617
  end)
578
618
  end
579
619
 
620
+ class Match
621
+ prepend SuperDiff::RSpec::AugmentedMatcher
622
+
623
+ prepend(Module.new do
624
+ def matcher_text_builder_class
625
+ SuperDiff::RSpec::MatcherTextBuilders::Match
626
+ end
627
+
628
+ def matcher_text_builder_args
629
+ super.merge(expected_captures: @expected_captures)
630
+ end
631
+ end)
632
+ end
633
+
634
+ class MatchArray < ContainExactly
635
+ def expected_for_diff
636
+ matchers.an_array_matching(expected)
637
+ end
638
+
639
+ def expected_action_for_matcher_text
640
+ "match array with"
641
+ end
642
+ end
643
+
580
644
  class RaiseError
581
645
  prepend SuperDiff::RSpec::AugmentedMatcher
582
646
 
@@ -588,7 +652,9 @@ module RSpec
588
652
  end
589
653
 
590
654
  def actual_for_diff
591
- @actual_error.message
655
+ if @actual_error
656
+ @actual_error.message
657
+ end
592
658
  end
593
659
 
594
660
  def expected_for_matcher_text
@@ -608,7 +674,11 @@ module RSpec
608
674
  end
609
675
 
610
676
  def expected_action_for_failure_message
611
- "match"
677
+ if @actual_error
678
+ "match"
679
+ else
680
+ "raise error"
681
+ end
612
682
  end
613
683
 
614
684
  def matcher_text_builder_class
@@ -617,9 +687,46 @@ module RSpec
617
687
  end)
618
688
 
619
689
  def self.matcher_name
620
- 'raise error'
690
+ "raise error"
621
691
  end
622
692
  end
693
+
694
+ class RespondTo
695
+ prepend SuperDiff::RSpec::AugmentedMatcher
696
+
697
+ prepend(Module.new do
698
+ def initialize(*)
699
+ super
700
+ @failing_method_names = nil
701
+ end
702
+
703
+ def matcher_text_builder_class
704
+ SuperDiff::RSpec::MatcherTextBuilders::RespondTo
705
+ end
706
+
707
+ def matcher_text_builder_args
708
+ super.merge(
709
+ expected_arity: @expected_arity,
710
+ arbitrary_keywords: @arbitrary_keywords,
711
+ expected_keywords: @expected_keywords,
712
+ unlimited_arguments: @unlimited_arguments
713
+ )
714
+ end
715
+
716
+ def expected_for_description
717
+ @names
718
+ end
719
+
720
+ def expected_for_failure_message
721
+ @failing_method_names
722
+ end
723
+ end)
724
+ end
725
+ end
726
+
727
+ def match_array(items)
728
+ BuiltIn::MatchArray.new(items)
623
729
  end
730
+ alias_matcher :an_array_matching, :match_array
624
731
  end
625
732
  end