super_diff 0.3.0 → 0.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 (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