super_diff 0.3.0 → 0.5.1

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -73
  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 -11
  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 -1
  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 +5 -350
  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 +1 -1
  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/unit/{equality_matcher_spec.rb → equality_matchers/main_spec.rb} +165 -9
  154. data/spec/unit/object_inspection_spec.rb +94 -18
  155. data/spec/unit/rspec/matchers/have_predicate_spec.rb +21 -0
  156. data/spec/unit/rspec/matchers/match_array_spec.rb +11 -0
  157. data/spec/unit/rspec/matchers/raise_error_spec.rb +16 -0
  158. data/super_diff.gemspec +4 -6
  159. metadata +99 -82
  160. data/lib/super_diff/active_record/object_inspection/map_extension.rb +0 -18
  161. data/lib/super_diff/active_record/operational_sequencers.rb +0 -14
  162. data/lib/super_diff/active_support/object_inspection/map_extension.rb +0 -15
  163. data/lib/super_diff/active_support/operational_sequencers.rb +0 -10
  164. data/lib/super_diff/diff_formatter.rb +0 -32
  165. data/lib/super_diff/differ.rb +0 -51
  166. data/lib/super_diff/differs/time.rb +0 -24
  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/inspectors/time.rb +0 -13
  172. data/lib/super_diff/object_inspection/map.rb +0 -30
  173. data/lib/super_diff/operation_sequences.rb +0 -9
  174. data/lib/super_diff/operation_sequences/base.rb +0 -11
  175. data/lib/super_diff/operational_sequencer.rb +0 -48
  176. data/lib/super_diff/operational_sequencers.rb +0 -17
  177. data/lib/super_diff/operational_sequencers/base.rb +0 -89
  178. data/lib/super_diff/operational_sequencers/hash.rb +0 -85
  179. data/lib/super_diff/operational_sequencers/time_like.rb +0 -30
  180. data/lib/super_diff/rspec/configuration.rb +0 -31
  181. data/lib/super_diff/rspec/differs/partial_array.rb +0 -22
  182. data/lib/super_diff/rspec/differs/partial_hash.rb +0 -22
  183. data/lib/super_diff/rspec/differs/partial_object.rb +0 -22
  184. data/lib/super_diff/rspec/object_inspection/inspectors/partial_array.rb +0 -22
  185. data/lib/super_diff/rspec/object_inspection/inspectors/partial_hash.rb +0 -21
  186. data/lib/super_diff/rspec/object_inspection/inspectors/partial_object.rb +0 -21
  187. data/lib/super_diff/rspec/object_inspection/map_extension.rb +0 -23
  188. data/lib/super_diff/rspec/operational_sequencers.rb +0 -22
  189. data/lib/super_diff/rspec/operational_sequencers/partial_hash.rb +0 -32
@@ -0,0 +1,15 @@
1
+ module SuperDiff
2
+ module ObjectInspection
3
+ module Inspectors
4
+ DEFAULTS = [
5
+ CustomObject,
6
+ Array,
7
+ Hash,
8
+ Primitive,
9
+ String,
10
+ TimeLike,
11
+ DefaultObject,
12
+ ].freeze
13
+ end
14
+ end
15
+ end
@@ -1,20 +1,30 @@
1
1
  module SuperDiff
2
2
  module ObjectInspection
3
3
  module Inspectors
4
- Hash = InspectionTree.new do
5
- when_empty do
6
- add_text "{}"
4
+ class Hash < Base
5
+ def self.applies_to?(value)
6
+ value.is_a?(::Hash)
7
7
  end
8
8
 
9
- when_non_empty do
10
- add_text "{"
9
+ protected
11
10
 
12
- nested do |hash|
13
- insert_hash_inspection_of(hash)
14
- end
11
+ def inspection_tree
12
+ InspectionTree.new do
13
+ when_empty do
14
+ add_text "{}"
15
+ end
16
+
17
+ when_non_empty do
18
+ add_text "{"
15
19
 
16
- add_break " "
17
- add_text "}"
20
+ nested do |hash|
21
+ insert_hash_inspection_of(hash)
22
+ end
23
+
24
+ add_break " "
25
+ add_text "}"
26
+ end
27
+ end
18
28
  end
19
29
  end
20
30
  end
@@ -0,0 +1,35 @@
1
+ module SuperDiff
2
+ module ObjectInspection
3
+ module Inspectors
4
+ class Main
5
+ extend AttrExtras.mixin
6
+
7
+ method_object :object, [:as_single_line!, :indent_level!]
8
+
9
+ def call
10
+ if resolved_class
11
+ resolved_class.call(
12
+ object,
13
+ as_single_line: as_single_line?,
14
+ indent_level: indent_level,
15
+ )
16
+ else
17
+ raise NoInspectorAvailableError.create(object)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_query :as_single_line?
24
+
25
+ def resolved_class
26
+ available_classes.find { |klass| klass.applies_to?(object) }
27
+ end
28
+
29
+ def available_classes
30
+ SuperDiff.configuration.extra_inspector_classes + DEFAULTS
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,12 +1,27 @@
1
1
  module SuperDiff
2
2
  module ObjectInspection
3
3
  module Inspectors
4
- Primitive = InspectionTree.new do
5
- # rubocop:disable Style/SymbolProc
6
- add_text do |object|
7
- object.inspect
4
+ class Primitive < Base
5
+ def self.applies_to?(value)
6
+ case value
7
+ when true, false, nil, Symbol, Numeric, Regexp, Class
8
+ true
9
+ else
10
+ false
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def inspection_tree
17
+ InspectionTree.new do
18
+ # rubocop:disable Style/SymbolProc
19
+ add_text do |object|
20
+ object.inspect
21
+ end
22
+ # rubocop:enable Style/SymbolProc
23
+ end
8
24
  end
9
- # rubocop:enable Style/SymbolProc
10
25
  end
11
26
  end
12
27
  end
@@ -1,12 +1,22 @@
1
1
  module SuperDiff
2
2
  module ObjectInspection
3
3
  module Inspectors
4
- String = InspectionTree.new do
5
- # rubocop:disable Style/SymbolProc
6
- add_text do |string|
7
- string.inspect
4
+ class String < Base
5
+ def self.applies_to?(value)
6
+ value.is_a?(::String)
7
+ end
8
+
9
+ protected
10
+
11
+ def inspection_tree
12
+ InspectionTree.new do
13
+ # rubocop:disable Style/SymbolProc
14
+ add_text do |string|
15
+ string.inspect
16
+ end
17
+ # rubocop:enable Style/SymbolProc
18
+ end
8
19
  end
9
- # rubocop:enable Style/SymbolProc
10
20
  end
11
21
  end
12
22
  end
@@ -0,0 +1,23 @@
1
+ module SuperDiff
2
+ module ObjectInspection
3
+ module Inspectors
4
+ class TimeLike < Base
5
+ def self.applies_to?(value)
6
+ SuperDiff.time_like?(value)
7
+ end
8
+
9
+ TIME_FORMAT = "%Y-%m-%d %H:%M:%S.%3N %Z %:z".freeze
10
+
11
+ protected
12
+
13
+ def inspection_tree
14
+ InspectionTree.new do
15
+ add_text do |time|
16
+ "#{time.strftime(TIME_FORMAT)} (#{time.class})"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,9 +2,16 @@ module SuperDiff
2
2
  module ObjectInspection
3
3
  module Nodes
4
4
  class Inspection < Base
5
- def evaluate(_object, indent_level:, as_single_line:)
5
+ def evaluate(object, indent_level:, as_single_line:)
6
+ value =
7
+ if block
8
+ tree.evaluate_block(object, &block)
9
+ else
10
+ immediate_value
11
+ end
12
+
6
13
  SuperDiff::ObjectInspection.inspect(
7
- immediate_value,
14
+ value,
8
15
  indent_level: indent_level,
9
16
  as_single_line: as_single_line,
10
17
  )
@@ -0,0 +1,18 @@
1
+ module SuperDiff
2
+ module OperationTreeBuilders
3
+ autoload :Array, "super_diff/operation_tree_builders/array"
4
+ autoload :Base, "super_diff/operation_tree_builders/base"
5
+ autoload :CustomObject, "super_diff/operation_tree_builders/custom_object"
6
+ autoload :DefaultObject, "super_diff/operation_tree_builders/default_object"
7
+ autoload :Hash, "super_diff/operation_tree_builders/hash"
8
+ autoload :Main, "super_diff/operation_tree_builders/main"
9
+ # TODO: Where is this used?
10
+ autoload(
11
+ :MultilineString,
12
+ "super_diff/operation_tree_builders/multiline_string",
13
+ )
14
+ autoload :TimeLike, "super_diff/operation_tree_builders/time_like"
15
+ end
16
+ end
17
+
18
+ require "super_diff/operation_tree_builders/defaults"
@@ -1,5 +1,5 @@
1
1
  module SuperDiff
2
- module OperationalSequencers
2
+ module OperationTreeBuilders
3
3
  class Array < Base
4
4
  def self.applies_to?(expected, actual)
5
5
  expected.is_a?(::Array) && actual.is_a?(::Array)
@@ -7,72 +7,40 @@ module SuperDiff
7
7
 
8
8
  def call
9
9
  Diff::LCS.traverse_balanced(expected, actual, lcs_callbacks)
10
- operations
10
+ operation_tree
11
11
  end
12
12
 
13
13
  private
14
14
 
15
15
  def lcs_callbacks
16
16
  @_lcs_callbacks ||= LcsCallbacks.new(
17
- operations: operations,
17
+ operation_tree: operation_tree,
18
18
  expected: expected,
19
19
  actual: actual,
20
- extra_operational_sequencer_classes: extra_operational_sequencer_classes,
21
- extra_diff_formatter_classes: extra_diff_formatter_classes,
22
20
  sequence: method(:sequence),
23
21
  )
24
22
  end
25
23
 
26
- def operations
27
- @_operations ||= OperationSequences::Array.new([])
24
+ def operation_tree
25
+ @_operation_tree ||= OperationTrees::Array.new([])
28
26
  end
29
27
 
30
28
  class LcsCallbacks
31
29
  extend AttrExtras.mixin
32
30
 
33
- pattr_initialize(
34
- [
35
- :operations!,
36
- :expected!,
37
- :actual!,
38
- :extra_operational_sequencer_classes!,
39
- :extra_diff_formatter_classes!,
40
- :sequence!
41
- ],
42
- )
43
- public :operations
31
+ pattr_initialize [:operation_tree!, :expected!, :actual!, :sequence!]
32
+ public :operation_tree
44
33
 
45
34
  def match(event)
46
- operations << ::SuperDiff::Operations::UnaryOperation.new(
47
- name: :noop,
48
- collection: actual,
49
- key: event.new_position,
50
- value: event.new_element,
51
- index: event.new_position,
52
- index_in_collection: actual.index(event.new_element),
53
- )
35
+ add_noop_operation(event)
54
36
  end
55
37
 
56
38
  def discard_a(event)
57
- operations << ::SuperDiff::Operations::UnaryOperation.new(
58
- name: :delete,
59
- collection: expected,
60
- key: event.old_position,
61
- value: event.old_element,
62
- index: event.old_position,
63
- index_in_collection: expected.index(event.old_element),
64
- )
39
+ add_delete_operation(event)
65
40
  end
66
41
 
67
42
  def discard_b(event)
68
- operations << ::SuperDiff::Operations::UnaryOperation.new(
69
- name: :insert,
70
- collection: actual,
71
- key: event.new_position,
72
- value: event.new_element,
73
- index: event.new_position,
74
- index_in_collection: actual.index(event.new_element),
75
- )
43
+ add_insert_operation(event)
76
44
  end
77
45
 
78
46
  def change(event)
@@ -88,23 +56,8 @@ module SuperDiff
88
56
 
89
57
  private
90
58
 
91
- def add_change_operation(event, child_operations)
92
- operations << ::SuperDiff::Operations::BinaryOperation.new(
93
- name: :change,
94
- left_collection: expected,
95
- right_collection: actual,
96
- left_key: event.old_position,
97
- right_key: event.new_position,
98
- left_value: event.old_element,
99
- right_value: event.new_element,
100
- left_index: event.old_position,
101
- right_index: event.new_position,
102
- child_operations: child_operations,
103
- )
104
- end
105
-
106
59
  def add_delete_operation(event)
107
- operations << Operations::UnaryOperation.new(
60
+ operation_tree << Operations::UnaryOperation.new(
108
61
  name: :delete,
109
62
  collection: expected,
110
63
  key: event.old_position,
@@ -115,7 +68,7 @@ module SuperDiff
115
68
  end
116
69
 
117
70
  def add_insert_operation(event)
118
- operations << Operations::UnaryOperation.new(
71
+ operation_tree << Operations::UnaryOperation.new(
119
72
  name: :insert,
120
73
  collection: actual,
121
74
  key: event.new_position,
@@ -124,6 +77,32 @@ module SuperDiff
124
77
  index_in_collection: actual.index(event.new_element),
125
78
  )
126
79
  end
80
+
81
+ def add_noop_operation(event)
82
+ operation_tree << Operations::UnaryOperation.new(
83
+ name: :noop,
84
+ collection: actual,
85
+ key: event.new_position,
86
+ value: event.new_element,
87
+ index: event.new_position,
88
+ index_in_collection: actual.index(event.new_element),
89
+ )
90
+ end
91
+
92
+ def add_change_operation(event, child_operations)
93
+ operation_tree << Operations::BinaryOperation.new(
94
+ name: :change,
95
+ left_collection: expected,
96
+ right_collection: actual,
97
+ left_key: event.old_position,
98
+ right_key: event.new_position,
99
+ left_value: event.old_element,
100
+ right_value: event.new_element,
101
+ left_index: event.old_position,
102
+ right_index: event.new_position,
103
+ child_operations: child_operations,
104
+ )
105
+ end
127
106
  end
128
107
  end
129
108
  end
@@ -0,0 +1,98 @@
1
+ module SuperDiff
2
+ module OperationTreeBuilders
3
+ class Base
4
+ def self.applies_to?(_expected, _actual)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ extend AttrExtras.mixin
9
+ include ImplementationChecks
10
+
11
+ method_object [:expected!, :actual!]
12
+
13
+ def call
14
+ operation_tree
15
+ end
16
+
17
+ protected
18
+
19
+ def unary_operations
20
+ unimplemented_instance_method!
21
+ end
22
+
23
+ def build_operation_tree
24
+ unimplemented_instance_method!
25
+ end
26
+
27
+ private
28
+
29
+ def operation_tree
30
+ unary_operations = self.unary_operations
31
+ operation_tree = build_operation_tree
32
+ unmatched_delete_operations = []
33
+
34
+ unary_operations.each_with_index do |operation, index|
35
+ if (
36
+ operation.name == :insert &&
37
+ (delete_operation = unmatched_delete_operations.find { |op| op.key == operation.key }) &&
38
+ (insert_operation = operation)
39
+ )
40
+ unmatched_delete_operations.delete(delete_operation)
41
+
42
+ if (child_operations = possible_comparison_of(
43
+ delete_operation,
44
+ insert_operation,
45
+ ))
46
+ operation_tree.delete(delete_operation)
47
+ operation_tree << Operations::BinaryOperation.new(
48
+ name: :change,
49
+ left_collection: delete_operation.collection,
50
+ right_collection: insert_operation.collection,
51
+ left_key: delete_operation.key,
52
+ right_key: insert_operation.key,
53
+ left_value: delete_operation.collection[operation.key],
54
+ right_value: insert_operation.collection[operation.key],
55
+ left_index: delete_operation.index_in_collection,
56
+ right_index: insert_operation.index_in_collection,
57
+ child_operations: child_operations,
58
+ )
59
+ else
60
+ operation_tree << insert_operation
61
+ end
62
+ else
63
+ if operation.name == :delete
64
+ unmatched_delete_operations << operation
65
+ end
66
+
67
+ operation_tree << operation
68
+ end
69
+ end
70
+
71
+ operation_tree
72
+ end
73
+
74
+ def possible_comparison_of(operation, next_operation)
75
+ if should_compare?(operation, next_operation)
76
+ sequence(operation.value, next_operation.value)
77
+ else
78
+ nil
79
+ end
80
+ end
81
+
82
+ def should_compare?(operation, next_operation)
83
+ next_operation &&
84
+ operation.name == :delete &&
85
+ next_operation.name == :insert &&
86
+ next_operation.key == operation.key
87
+ end
88
+
89
+ def sequence(expected, actual)
90
+ OperationTreeBuilders::Main.call(
91
+ expected: expected,
92
+ actual: actual,
93
+ all_or_nothing: false,
94
+ )
95
+ end
96
+ end
97
+ end
98
+ end