super_diff 0.11.0 → 0.12.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 (231) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -166
  3. data/lib/super_diff/active_record/differs/active_record_relation.rb +1 -1
  4. data/lib/super_diff/active_record/inspection_tree_builders/active_record_model.rb +57 -0
  5. data/lib/super_diff/active_record/inspection_tree_builders/active_record_relation.rb +34 -0
  6. data/lib/super_diff/active_record/inspection_tree_builders.rb +14 -0
  7. data/lib/super_diff/active_record/monkey_patches.rb +6 -3
  8. data/lib/super_diff/active_record/object_inspection.rb +16 -4
  9. data/lib/super_diff/active_record/operation_tree_builders/active_record_model.rb +6 -2
  10. data/lib/super_diff/active_record/operation_tree_builders/active_record_relation.rb +1 -1
  11. data/lib/super_diff/active_record/operation_tree_flatteners/active_record_relation.rb +1 -1
  12. data/lib/super_diff/active_record/operation_trees/active_record_relation.rb +1 -1
  13. data/lib/super_diff/active_record.rb +12 -16
  14. data/lib/super_diff/active_support/differs/hash_with_indifferent_access.rb +1 -1
  15. data/lib/super_diff/active_support/inspection_tree_builders/hash_with_indifferent_access.rb +44 -0
  16. data/lib/super_diff/active_support/inspection_tree_builders/ordered_options.rb +44 -0
  17. data/lib/super_diff/active_support/inspection_tree_builders.rb +14 -0
  18. data/lib/super_diff/active_support/object_inspection.rb +16 -4
  19. data/lib/super_diff/active_support/operation_tree_builders/hash_with_indifferent_access.rb +1 -1
  20. data/lib/super_diff/active_support/operation_tree_flatteners/hash_with_indifferent_access.rb +1 -1
  21. data/lib/super_diff/active_support/operation_trees/hash_with_indifferent_access.rb +1 -1
  22. data/lib/super_diff/active_support.rb +11 -15
  23. data/lib/super_diff/basic/diff_formatters/collection.rb +135 -0
  24. data/lib/super_diff/basic/diff_formatters/multiline_string.rb +34 -0
  25. data/lib/super_diff/basic/diff_formatters.rb +11 -0
  26. data/lib/super_diff/basic/differs/array.rb +17 -0
  27. data/lib/super_diff/basic/differs/custom_object.rb +19 -0
  28. data/lib/super_diff/basic/differs/date_like.rb +17 -0
  29. data/lib/super_diff/basic/differs/default_object.rb +24 -0
  30. data/lib/super_diff/basic/differs/hash.rb +17 -0
  31. data/lib/super_diff/basic/differs/multiline_string.rb +18 -0
  32. data/lib/super_diff/basic/differs/time_like.rb +17 -0
  33. data/lib/super_diff/basic/differs.rb +24 -0
  34. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/array.rb +3 -3
  35. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/custom_object.rb +3 -3
  36. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/date_like.rb +3 -3
  37. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/default_object.rb +5 -7
  38. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/hash.rb +3 -3
  39. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/primitive.rb +3 -3
  40. data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/time_like.rb +3 -3
  41. data/lib/super_diff/basic/inspection_tree_builders.rb +20 -0
  42. data/lib/super_diff/basic/operation_tree_builders/array.rb +111 -0
  43. data/lib/super_diff/basic/operation_tree_builders/custom_object.rb +42 -0
  44. data/lib/super_diff/basic/operation_tree_builders/date_like.rb +17 -0
  45. data/lib/super_diff/basic/operation_tree_builders/default_object.rb +117 -0
  46. data/lib/super_diff/basic/operation_tree_builders/hash.rb +222 -0
  47. data/lib/super_diff/basic/operation_tree_builders/multiline_string.rb +90 -0
  48. data/lib/super_diff/basic/operation_tree_builders/time_like.rb +26 -0
  49. data/lib/super_diff/basic/operation_tree_builders.rb +34 -0
  50. data/lib/super_diff/basic/operation_tree_flatteners/array.rb +17 -0
  51. data/lib/super_diff/basic/operation_tree_flatteners/collection.rb +140 -0
  52. data/lib/super_diff/basic/operation_tree_flatteners/custom_object.rb +30 -0
  53. data/lib/super_diff/basic/operation_tree_flatteners/default_object.rb +32 -0
  54. data/lib/super_diff/basic/operation_tree_flatteners/hash.rb +35 -0
  55. data/lib/super_diff/basic/operation_tree_flatteners/multiline_string.rb +20 -0
  56. data/lib/super_diff/basic/operation_tree_flatteners.rb +24 -0
  57. data/lib/super_diff/basic/operation_trees/array.rb +17 -0
  58. data/lib/super_diff/basic/operation_trees/custom_object.rb +17 -0
  59. data/lib/super_diff/basic/operation_trees/default_object.rb +42 -0
  60. data/lib/super_diff/basic/operation_trees/hash.rb +17 -0
  61. data/lib/super_diff/basic/operation_trees/multiline_string.rb +17 -0
  62. data/lib/super_diff/basic/operation_trees.rb +25 -0
  63. data/lib/super_diff/basic.rb +48 -0
  64. data/lib/super_diff/{differs/base.rb → core/abstract_differ.rb} +2 -2
  65. data/lib/super_diff/core/abstract_inspection_tree_builder.rb +26 -0
  66. data/lib/super_diff/{operation_trees/base.rb → core/abstract_operation_tree.rb} +6 -2
  67. data/lib/super_diff/{operation_tree_builders/base.rb → core/abstract_operation_tree_builder.rb} +4 -8
  68. data/lib/super_diff/{operation_tree_flatteners/base.rb → core/abstract_operation_tree_flattener.rb} +2 -2
  69. data/lib/super_diff/{operations → core}/binary_operation.rb +1 -1
  70. data/lib/super_diff/core/colorized_document_extensions.rb +20 -0
  71. data/lib/super_diff/core/configuration.rb +192 -0
  72. data/lib/super_diff/core/differ_dispatcher.rb +31 -0
  73. data/lib/super_diff/core/gem_version.rb +47 -0
  74. data/lib/super_diff/core/helpers.rb +88 -0
  75. data/lib/super_diff/core/implementation_checks.rb +21 -0
  76. data/lib/super_diff/{object_inspection → core}/inspection_tree.rb +7 -6
  77. data/lib/super_diff/core/inspection_tree_builder_dispatcher.rb +23 -0
  78. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_lines_when_rendering_to_lines.rb +9 -3
  79. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_prefix_when_rendering_to_lines.rb +3 -3
  80. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_prelude_when_rendering_to_lines.rb +3 -3
  81. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_single_line.rb +3 -3
  82. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/base.rb +2 -2
  83. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/inspection.rb +7 -11
  84. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/nesting.rb +2 -2
  85. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/only_when.rb +2 -2
  86. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/text.rb +2 -2
  87. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_empty.rb +2 -2
  88. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_non_empty.rb +2 -2
  89. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_rendering_to_lines.rb +2 -2
  90. data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_rendering_to_string.rb +2 -2
  91. data/lib/super_diff/core/inspection_tree_nodes.rb +55 -0
  92. data/lib/super_diff/core/line.rb +85 -0
  93. data/lib/super_diff/{errors → core}/no_differ_available_error.rb +1 -1
  94. data/lib/super_diff/core/no_inspection_tree_builder_available_error.rb +21 -0
  95. data/lib/super_diff/core/no_operation_tree_available_error.rb +20 -0
  96. data/lib/super_diff/core/no_operation_tree_builder_available_error.rb +24 -0
  97. data/lib/super_diff/{operation_tree_builders/main.rb → core/operation_tree_builder_dispatcher.rb} +9 -13
  98. data/lib/super_diff/core/operation_tree_finder.rb +27 -0
  99. data/lib/super_diff/core/prefix_for_next_inspection_tree_node.rb +6 -0
  100. data/lib/super_diff/core/prelude_for_next_inspection_tree_node.rb +6 -0
  101. data/lib/super_diff/core/recursion_guard.rb +52 -0
  102. data/lib/super_diff/core/tiered_lines.rb +6 -0
  103. data/lib/super_diff/core/tiered_lines_elider.rb +472 -0
  104. data/lib/super_diff/core/tiered_lines_formatter.rb +77 -0
  105. data/lib/super_diff/{operations → core}/unary_operation.rb +1 -1
  106. data/lib/super_diff/core.rb +69 -0
  107. data/lib/super_diff/differs.rb +19 -12
  108. data/lib/super_diff/equality_matchers/array.rb +3 -3
  109. data/lib/super_diff/equality_matchers/default.rb +8 -3
  110. data/lib/super_diff/equality_matchers/hash.rb +3 -3
  111. data/lib/super_diff/equality_matchers/multiline_string.rb +3 -3
  112. data/lib/super_diff/equality_matchers/primitive.rb +3 -3
  113. data/lib/super_diff/equality_matchers/singleline_string.rb +2 -2
  114. data/lib/super_diff/errors.rb +12 -8
  115. data/lib/super_diff/object_inspection.rb +63 -14
  116. data/lib/super_diff/operation_tree_builders.rb +19 -15
  117. data/lib/super_diff/operation_tree_flatteners.rb +19 -16
  118. data/lib/super_diff/operation_trees.rb +19 -9
  119. data/lib/super_diff/operations.rb +12 -2
  120. data/lib/super_diff/rspec/augmented_matcher.rb +1 -1
  121. data/lib/super_diff/rspec/differ.rb +4 -5
  122. data/lib/super_diff/rspec/differs/collection_containing_exactly.rb +1 -1
  123. data/lib/super_diff/rspec/differs/collection_including.rb +1 -1
  124. data/lib/super_diff/rspec/differs/hash_including.rb +1 -1
  125. data/lib/super_diff/rspec/differs/object_having_attributes.rb +1 -1
  126. data/lib/super_diff/rspec/inspection_tree_builders/collection_containing_exactly.rb +34 -0
  127. data/lib/super_diff/rspec/inspection_tree_builders/collection_including.rb +40 -0
  128. data/lib/super_diff/rspec/inspection_tree_builders/double.rb +100 -0
  129. data/lib/super_diff/rspec/inspection_tree_builders/generic_describable_matcher.rb +17 -0
  130. data/lib/super_diff/rspec/inspection_tree_builders/hash_including.rb +40 -0
  131. data/lib/super_diff/rspec/inspection_tree_builders/instance_of.rb +25 -0
  132. data/lib/super_diff/rspec/inspection_tree_builders/kind_of.rb +25 -0
  133. data/lib/super_diff/rspec/inspection_tree_builders/object_having_attributes.rb +34 -0
  134. data/lib/super_diff/rspec/inspection_tree_builders/primitive.rb +9 -0
  135. data/lib/super_diff/rspec/inspection_tree_builders/value_within.rb +30 -0
  136. data/lib/super_diff/rspec/inspection_tree_builders.rb +40 -0
  137. data/lib/super_diff/rspec/object_inspection.rb +14 -4
  138. data/lib/super_diff/rspec/operation_tree_builders/collection_containing_exactly.rb +4 -4
  139. data/lib/super_diff/rspec/operation_tree_builders/collection_including.rb +1 -1
  140. data/lib/super_diff/rspec/operation_tree_builders/hash_including.rb +1 -1
  141. data/lib/super_diff/rspec/operation_tree_builders/object_having_attributes.rb +2 -2
  142. data/lib/super_diff/rspec.rb +20 -18
  143. data/lib/super_diff/version.rb +1 -1
  144. data/lib/super_diff.rb +69 -21
  145. data/spec/examples.txt +704 -543
  146. data/spec/support/integration/helpers.rb +4 -1
  147. data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +1 -1
  148. data/spec/support/models/active_record/person.rb +8 -1
  149. data/spec/support/shared_examples/active_record.rb +5 -5
  150. data/spec/support/unit/helpers.rb +12 -1
  151. data/spec/support/unit/matchers/be_deprecated_in_favor_of.rb +39 -0
  152. data/spec/unit/active_record/object_inspection_spec.rb +5 -5
  153. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/array_spec.rb +8 -8
  154. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/custom_object_spec.rb +9 -9
  155. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/default_object_spec.rb +9 -9
  156. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/hash_spec.rb +8 -8
  157. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/multiline_string_spec.rb +4 -4
  158. data/spec/unit/{helpers_spec.rb → core/helpers_spec.rb} +2 -2
  159. data/spec/unit/{tiered_lines_elider_spec.rb → core/tiered_lines_elider_spec.rb} +2 -2
  160. data/spec/unit/{tiered_lines_formatter_spec.rb → core/tiered_lines_formatter_spec.rb} +1 -1
  161. data/spec/unit/deprecations_spec.rb +176 -0
  162. data/spec/unit/equality_matchers/main_spec.rb +5 -5
  163. data/super_diff.gemspec +6 -0
  164. metadata +127 -115
  165. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders/active_record_model.rb +0 -51
  166. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders/active_record_relation.rb +0 -36
  167. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders.rb +0 -16
  168. data/lib/super_diff/active_support/object_inspection/inspection_tree_builders/hash_with_indifferent_access.rb +0 -46
  169. data/lib/super_diff/active_support/object_inspection/inspection_tree_builders/ordered_options.rb +0 -46
  170. data/lib/super_diff/active_support/object_inspection/inspection_tree_builders.rb +0 -16
  171. data/lib/super_diff/colorized_document_extensions.rb +0 -18
  172. data/lib/super_diff/configuration.rb +0 -149
  173. data/lib/super_diff/diff_formatters/collection.rb +0 -132
  174. data/lib/super_diff/diff_formatters/multiline_string.rb +0 -31
  175. data/lib/super_diff/differs/array.rb +0 -15
  176. data/lib/super_diff/differs/custom_object.rb +0 -17
  177. data/lib/super_diff/differs/date_like.rb +0 -15
  178. data/lib/super_diff/differs/default_object.rb +0 -19
  179. data/lib/super_diff/differs/defaults.rb +0 -13
  180. data/lib/super_diff/differs/empty.rb +0 -13
  181. data/lib/super_diff/differs/hash.rb +0 -15
  182. data/lib/super_diff/differs/main.rb +0 -31
  183. data/lib/super_diff/differs/multiline_string.rb +0 -16
  184. data/lib/super_diff/differs/time_like.rb +0 -15
  185. data/lib/super_diff/gem_version.rb +0 -45
  186. data/lib/super_diff/helpers.rb +0 -86
  187. data/lib/super_diff/implementation_checks.rb +0 -19
  188. data/lib/super_diff/line.rb +0 -83
  189. data/lib/super_diff/object_inspection/inspection_tree_builders/base.rb +0 -27
  190. data/lib/super_diff/object_inspection/inspection_tree_builders/defaults.rb +0 -15
  191. data/lib/super_diff/object_inspection/inspection_tree_builders/main.rb +0 -30
  192. data/lib/super_diff/object_inspection/inspection_tree_builders.rb +0 -48
  193. data/lib/super_diff/object_inspection/nodes.rb +0 -50
  194. data/lib/super_diff/object_inspection/prefix_for_next_node.rb +0 -6
  195. data/lib/super_diff/object_inspection/prelude_for_next_node.rb +0 -6
  196. data/lib/super_diff/operation_tree_builders/array.rb +0 -107
  197. data/lib/super_diff/operation_tree_builders/custom_object.rb +0 -40
  198. data/lib/super_diff/operation_tree_builders/date_like.rb +0 -15
  199. data/lib/super_diff/operation_tree_builders/default_object.rb +0 -119
  200. data/lib/super_diff/operation_tree_builders/defaults.rb +0 -5
  201. data/lib/super_diff/operation_tree_builders/hash.rb +0 -218
  202. data/lib/super_diff/operation_tree_builders/multiline_string.rb +0 -86
  203. data/lib/super_diff/operation_tree_builders/time_like.rb +0 -24
  204. data/lib/super_diff/operation_tree_flatteners/array.rb +0 -15
  205. data/lib/super_diff/operation_tree_flatteners/collection.rb +0 -136
  206. data/lib/super_diff/operation_tree_flatteners/custom_object.rb +0 -28
  207. data/lib/super_diff/operation_tree_flatteners/default_object.rb +0 -31
  208. data/lib/super_diff/operation_tree_flatteners/hash.rb +0 -33
  209. data/lib/super_diff/operation_tree_flatteners/multiline_string.rb +0 -18
  210. data/lib/super_diff/operation_trees/array.rb +0 -15
  211. data/lib/super_diff/operation_trees/custom_object.rb +0 -15
  212. data/lib/super_diff/operation_trees/default_object.rb +0 -40
  213. data/lib/super_diff/operation_trees/defaults.rb +0 -5
  214. data/lib/super_diff/operation_trees/hash.rb +0 -15
  215. data/lib/super_diff/operation_trees/main.rb +0 -35
  216. data/lib/super_diff/operation_trees/multiline_string.rb +0 -15
  217. data/lib/super_diff/recursion_guard.rb +0 -50
  218. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_containing_exactly.rb +0 -36
  219. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_including.rb +0 -42
  220. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/double.rb +0 -102
  221. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/generic_describable_matcher.rb +0 -19
  222. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/hash_including.rb +0 -42
  223. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/instance_of.rb +0 -27
  224. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/kind_of.rb +0 -27
  225. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/object_having_attributes.rb +0 -36
  226. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/primitive.rb +0 -10
  227. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/value_within.rb +0 -32
  228. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders.rb +0 -48
  229. data/lib/super_diff/tiered_lines.rb +0 -4
  230. data/lib/super_diff/tiered_lines_elider.rb +0 -462
  231. data/lib/super_diff/tiered_lines_formatter.rb +0 -75
@@ -1,119 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeBuilders
3
- class DefaultObject < Base
4
- def self.applies_to?(_expected, _actual)
5
- true
6
- end
7
-
8
- def initialize(*args)
9
- super(*args)
10
-
11
- establish_expected_and_actual_attributes
12
- end
13
-
14
- protected
15
-
16
- def unary_operations
17
- attribute_names.reduce([]) do |operations, name|
18
- possibly_add_noop_operation_to(operations, name)
19
- possibly_add_delete_operation_to(operations, name)
20
- possibly_add_insert_operation_to(operations, name)
21
- operations
22
- end
23
- end
24
-
25
- def build_operation_tree
26
- # XXX This assumes that `expected` and `actual` are the same
27
- # TODO: Does this need to be find_operation_tree_for?
28
- OperationTrees::DefaultObject.new([], underlying_object: actual)
29
- end
30
-
31
- def find_operation_tree_for(value)
32
- OperationTrees::Main.call(value)
33
- end
34
-
35
- def attribute_names
36
- (
37
- expected.instance_variables.sort & actual.instance_variables.sort
38
- ).map { |variable_name| variable_name[1..-1] }
39
- end
40
-
41
- private
42
-
43
- attr_reader :expected_attributes, :actual_attributes
44
-
45
- def establish_expected_and_actual_attributes
46
- @expected_attributes =
47
- attribute_names.reduce({}) do |hash, name|
48
- hash.merge(name => expected.instance_variable_get("@#{name}"))
49
- end
50
-
51
- @actual_attributes =
52
- attribute_names.reduce({}) do |hash, name|
53
- hash.merge(name => actual.instance_variable_get("@#{name}"))
54
- end
55
- end
56
-
57
- def possibly_add_noop_operation_to(operations, attribute_name)
58
- if should_add_noop_operation?(attribute_name)
59
- operations << Operations::UnaryOperation.new(
60
- name: :noop,
61
- collection: actual_attributes,
62
- key: attribute_name,
63
- index: attribute_names.index(attribute_name),
64
- value: actual_attributes[attribute_name]
65
- )
66
- end
67
- end
68
-
69
- def should_add_noop_operation?(attribute_name)
70
- expected_attributes.include?(attribute_name) &&
71
- actual_attributes.include?(attribute_name) &&
72
- expected_attributes[attribute_name] ==
73
- actual_attributes[attribute_name]
74
- end
75
-
76
- def possibly_add_delete_operation_to(operations, attribute_name)
77
- if should_add_delete_operation?(attribute_name)
78
- operations << Operations::UnaryOperation.new(
79
- name: :delete,
80
- collection: expected_attributes,
81
- key: attribute_name,
82
- index: attribute_names.index(attribute_name),
83
- value: expected_attributes[attribute_name]
84
- )
85
- end
86
- end
87
-
88
- def should_add_delete_operation?(attribute_name)
89
- expected_attributes.include?(attribute_name) &&
90
- (
91
- !actual_attributes.include?(attribute_name) ||
92
- expected_attributes[attribute_name] !=
93
- actual_attributes[attribute_name]
94
- )
95
- end
96
-
97
- def possibly_add_insert_operation_to(operations, attribute_name)
98
- if should_add_insert_operation?(attribute_name)
99
- operations << Operations::UnaryOperation.new(
100
- name: :insert,
101
- collection: actual_attributes,
102
- key: attribute_name,
103
- index: attribute_names.index(attribute_name),
104
- value: actual_attributes[attribute_name]
105
- )
106
- end
107
- end
108
-
109
- def should_add_insert_operation?(attribute_name)
110
- !expected_attributes.include?(attribute_name) ||
111
- (
112
- actual_attributes.include?(attribute_name) &&
113
- expected_attributes[attribute_name] !=
114
- actual_attributes[attribute_name]
115
- )
116
- end
117
- end
118
- end
119
- end
@@ -1,5 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeBuilders
3
- DEFAULTS = [Array, Hash, TimeLike, DateLike, CustomObject].freeze
4
- end
5
- end
@@ -1,218 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeBuilders
3
- class Hash < Base
4
- def self.applies_to?(expected, actual)
5
- expected.is_a?(::Hash) && actual.is_a?(::Hash)
6
- end
7
-
8
- protected
9
-
10
- def unary_operations
11
- unary_operations_using_variant_of_patience_algorithm
12
- end
13
-
14
- def build_operation_tree
15
- OperationTrees::Hash.new([])
16
- end
17
-
18
- private
19
-
20
- def unary_operations_using_variant_of_patience_algorithm
21
- operations = []
22
- aks, eks = actual.keys, expected.keys
23
- previous_ei, ei = nil, 0
24
- ai = 0
25
-
26
- # When diffing a hash, we're more interested in the 'actual' version
27
- # than the 'expected' version, because that's the ultimate truth.
28
- # Therefore, the diff is presented from the perspective of the 'actual'
29
- # hash, and we start off by looping over it.
30
- while ai < aks.size
31
- ak = aks[ai]
32
- av, ev = actual[ak], expected[ak]
33
- # While we iterate over 'actual' in order, we jump all over
34
- # 'expected', trying to match up its keys with the keys in 'actual' as
35
- # much as possible.
36
- ei = eks.index(ak)
37
-
38
- if should_add_noop_operation?(ak)
39
- # (If we're here, it probably means that the key we're pointing to
40
- # in the 'actual' and 'expected' hashes have the same value.)
41
-
42
- if ei && previous_ei && (ei - previous_ei) > 1
43
- # If we've jumped from one operation in the 'expected' hash to
44
- # another operation later in 'expected' (due to the fact that the
45
- # 'expected' hash is in a different order than 'actual'), collect
46
- # any delete operations in between and add them to our operations
47
- # array as deletes before adding the noop. If we don't do this
48
- # now, then those deletes will disappear. (Again, we are mainly
49
- # iterating over 'actual', so this is the only way to catch all of
50
- # the keys in 'expected'.)
51
- (previous_ei + 1).upto(ei - 1) do |ei2|
52
- ek = eks[ei2]
53
- ev2, av2 = expected[ek], actual[ek]
54
-
55
- if (
56
- (!actual.include?(ek) || ev2 != av2) &&
57
- operations.none? do |operation|
58
- %i[delete noop].include?(operation.name) &&
59
- operation.key == ek
60
- end
61
- )
62
- operations << Operations::UnaryOperation.new(
63
- name: :delete,
64
- collection: expected,
65
- key: ek,
66
- value: ev2,
67
- index: ei2
68
- )
69
- end
70
- end
71
- end
72
-
73
- operations << Operations::UnaryOperation.new(
74
- name: :noop,
75
- collection: actual,
76
- key: ak,
77
- value: av,
78
- index: ai
79
- )
80
- else
81
- # (If we're here, it probably means that the key in 'actual' isn't
82
- # present in 'expected' or the values don't match.)
83
-
84
- if (
85
- (operations.empty? || operations.last.name == :noop) &&
86
- (ai == 0 || eks.include?(aks[ai - 1]))
87
- )
88
- # If we go from a match in the last iteration to a missing or
89
- # extra key in this one, or we're at the first key in 'actual' and
90
- # it's missing or extra, look for deletes in the 'expected' hash
91
- # and add them to our list of operations before we add the
92
- # inserts. In most cases we will accomplish this by backtracking a
93
- # bit to the key in 'expected' that matched the key in 'actual' we
94
- # processed in the previous iteration (or just the first key in
95
- # 'expected' if this is the first key in 'actual'), and then
96
- # iterating from there through 'expected' until we reach the end
97
- # or we hit some other condition (see below).
98
-
99
- start_index =
100
- if ai > 0
101
- eks.index(aks[ai - 1]) + 1
102
- else
103
- 0
104
- end
105
-
106
- start_index.upto(eks.size - 1) do |ei2|
107
- ek = eks[ei2]
108
- ev, av2 = expected[ek], actual[ek]
109
-
110
- if actual.include?(ek) && ev == av2
111
- # If the key in 'expected' we've landed on happens to be a
112
- # match in 'actual', then stop, because it's going to be
113
- # handled in some future iteration of the 'actual' loop.
114
- break
115
- elsif (
116
- aks[ai + 1..-1].any? do |k|
117
- expected.include?(k) && expected[k] != actual[k]
118
- end
119
- )
120
- # While we backtracked a bit to iterate over 'expected', we
121
- # now have to look ahead. If we will end up encountering a
122
- # insert that matches this delete later, stop and go back to
123
- # iterating over 'actual'. This is because the delete we would
124
- # have added now will be added later when we encounter the
125
- # associated insert, so we don't want to add it twice.
126
- break
127
- else
128
- operations << Operations::UnaryOperation.new(
129
- name: :delete,
130
- collection: expected,
131
- key: ek,
132
- value: ev,
133
- index: ei2
134
- )
135
- end
136
-
137
- if ek == ak && ev != av
138
- # If we're pointing to the same key in 'expected' as in
139
- # 'actual', but with different values, go ahead and add an
140
- # insert now to accompany the delete added above. That way
141
- # they appear together, which will be easier to read.
142
- operations << Operations::UnaryOperation.new(
143
- name: :insert,
144
- collection: actual,
145
- key: ak,
146
- value: av,
147
- index: ai
148
- )
149
- end
150
- end
151
- end
152
-
153
- if (
154
- expected.include?(ak) && ev != av &&
155
- operations.none? { |op| op.name == :delete && op.key == ak }
156
- )
157
- # If we're here, it means that we didn't encounter any delete
158
- # operations above for whatever reason and so we need to add a
159
- # delete to represent the fact that the value for this key has
160
- # changed.
161
- operations << Operations::UnaryOperation.new(
162
- name: :delete,
163
- collection: expected,
164
- key: ak,
165
- value: expected[ak],
166
- index: ei
167
- )
168
- end
169
-
170
- if operations.none? { |op| op.name == :insert && op.key == ak }
171
- # If we're here, it means that we didn't encounter any insert
172
- # operations above. Since we already handled delete, the only
173
- # alternative is that this key must not exist in 'expected', so
174
- # we need to add an insert.
175
- operations << Operations::UnaryOperation.new(
176
- name: :insert,
177
- collection: actual,
178
- key: ak,
179
- value: av,
180
- index: ai
181
- )
182
- end
183
- end
184
-
185
- ai += 1
186
- previous_ei = ei
187
- end
188
-
189
- # The last thing to do is this: if there are keys in 'expected' that
190
- # aren't in 'actual', and they aren't associated with any inserts to
191
- # where they would have been added above, tack those deletes onto the
192
- # end of our operations array.
193
- (eks - aks - operations.map(&:key)).each do |ek|
194
- ei = eks.index(ek)
195
- ev = expected[ek]
196
-
197
- operations << Operations::UnaryOperation.new(
198
- name: :delete,
199
- collection: expected,
200
- key: ek,
201
- value: ev,
202
- index: ei
203
- )
204
- end
205
-
206
- operations
207
- end
208
-
209
- def should_add_noop_operation?(key)
210
- expected.include?(key) && expected[key] == actual[key]
211
- end
212
-
213
- def all_keys
214
- actual.keys | expected.keys
215
- end
216
- end
217
- end
218
- end
@@ -1,86 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeBuilders
3
- class MultilineString < Base
4
- def self.applies_to?(expected, actual)
5
- expected.is_a?(::String) && actual.is_a?(::String) &&
6
- (expected.include?("\n") || actual.include?("\n"))
7
- end
8
-
9
- def initialize(*args)
10
- super(*args)
11
-
12
- @original_expected = @expected
13
- @original_actual = @actual
14
- @expected = split_into_lines(@expected)
15
- @actual = split_into_lines(@actual)
16
- @sequence_matcher = PatienceDiff::SequenceMatcher.new
17
- end
18
-
19
- protected
20
-
21
- def unary_operations
22
- opcodes.flat_map do |code, a_start, a_end, b_start, b_end|
23
- if code == :delete
24
- add_delete_operations(a_start..a_end)
25
- elsif code == :insert
26
- add_insert_operations(b_start..b_end)
27
- else
28
- add_noop_operations(b_start..b_end)
29
- end
30
- end
31
- end
32
-
33
- def build_operation_tree
34
- OperationTrees::MultilineString.new([])
35
- end
36
-
37
- private
38
-
39
- attr_reader :sequence_matcher, :original_expected, :original_actual
40
-
41
- def split_into_lines(string)
42
- string.scan(/.+(?:\r|\n|\r\n|\Z)/)
43
- end
44
-
45
- def opcodes
46
- sequence_matcher.diff_opcodes(expected, actual)
47
- end
48
-
49
- def add_delete_operations(indices)
50
- indices.map do |index|
51
- Operations::UnaryOperation.new(
52
- name: :delete,
53
- collection: expected,
54
- key: index,
55
- index: index,
56
- value: expected[index]
57
- )
58
- end
59
- end
60
-
61
- def add_insert_operations(indices)
62
- indices.map do |index|
63
- Operations::UnaryOperation.new(
64
- name: :insert,
65
- collection: actual,
66
- key: index,
67
- index: index,
68
- value: actual[index]
69
- )
70
- end
71
- end
72
-
73
- def add_noop_operations(indices)
74
- indices.map do |index|
75
- Operations::UnaryOperation.new(
76
- name: :noop,
77
- collection: actual,
78
- key: index,
79
- index: index,
80
- value: actual[index]
81
- )
82
- end
83
- end
84
- end
85
- end
86
- end
@@ -1,24 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeBuilders
3
- class TimeLike < CustomObject
4
- def self.applies_to?(expected, actual)
5
- SuperDiff.time_like?(expected) && SuperDiff.time_like?(actual)
6
- end
7
-
8
- protected
9
-
10
- def attribute_names
11
- base = %w[year month day hour min sec subsec zone utc_offset]
12
-
13
- # If timezones are different, also show a normalized timestamp at the
14
- # end of the diff to help visualize why they are different moments in
15
- # time.
16
- if actual.zone != expected.zone
17
- base + ["utc"]
18
- else
19
- base
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,15 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeFlatteners
3
- class Array < Collection
4
- protected
5
-
6
- def open_token
7
- "["
8
- end
9
-
10
- def close_token
11
- "]"
12
- end
13
- end
14
- end
15
- end
@@ -1,136 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeFlatteners
3
- class Collection < Base
4
- protected
5
-
6
- def build_tiered_lines
7
- [
8
- Line.new(
9
- type: :noop,
10
- indentation_level: indentation_level,
11
- value: open_token,
12
- collection_bookend: :open
13
- ),
14
- *inner_lines,
15
- Line.new(
16
- type: :noop,
17
- indentation_level: indentation_level,
18
- value: close_token,
19
- collection_bookend: :close
20
- )
21
- ]
22
- end
23
-
24
- def inner_lines
25
- @_inner_lines ||=
26
- operation_tree.flat_map do |operation|
27
- lines =
28
- if operation.name == :change
29
- build_lines_for_change_operation(operation)
30
- else
31
- build_lines_for_non_change_operation(operation)
32
- end
33
-
34
- maybe_add_prefix_at_beginning_of_lines(
35
- maybe_add_comma_at_end_of_lines(lines, operation),
36
- operation
37
- )
38
- end
39
- end
40
-
41
- def maybe_add_prefix_at_beginning_of_lines(lines, operation)
42
- if add_prefix_at_beginning_of_lines?(operation)
43
- add_prefix_at_beginning_of_lines(lines, operation)
44
- else
45
- lines
46
- end
47
- end
48
-
49
- def add_prefix_at_beginning_of_lines?(operation)
50
- !!item_prefix_for(operation)
51
- end
52
-
53
- def add_prefix_at_beginning_of_lines(lines, operation)
54
- [lines[0].prefixed_with(item_prefix_for(operation))] + lines[1..-1]
55
- end
56
-
57
- def maybe_add_comma_at_end_of_lines(lines, operation)
58
- if last_item_in_collection?(operation)
59
- lines
60
- else
61
- add_comma_at_end_of_lines(lines)
62
- end
63
- end
64
-
65
- def last_item_in_collection?(operation)
66
- if operation.name == :change
67
- operation.left_index == operation.left_collection.size - 1 &&
68
- operation.right_index == operation.right_collection.size - 1
69
- else
70
- operation.index == operation.collection.size - 1
71
- end
72
- end
73
-
74
- def add_comma_at_end_of_lines(lines)
75
- lines[0..-2] + [lines[-1].with_comma]
76
- end
77
-
78
- def build_lines_for_change_operation(operation)
79
- SuperDiff::RecursionGuard.guarding_recursion_of(
80
- operation.left_collection,
81
- operation.right_collection
82
- ) do |already_seen|
83
- if already_seen
84
- raise InfiniteRecursionError
85
- else
86
- operation.children.flatten(indentation_level: indentation_level + 1)
87
- end
88
- end
89
- end
90
-
91
- def build_lines_for_non_change_operation(operation)
92
- indentation_level = @indentation_level + 1
93
-
94
- if recursive_operation?(operation)
95
- [
96
- Line.new(
97
- type: operation.name,
98
- indentation_level: indentation_level,
99
- value: SuperDiff::RecursionGuard::PLACEHOLDER
100
- )
101
- ]
102
- else
103
- build_lines_from_inspection_of(
104
- operation.value,
105
- type: operation.name,
106
- indentation_level: indentation_level
107
- )
108
- end
109
- end
110
-
111
- def recursive_operation?(operation)
112
- operation.value.equal?(operation.collection) ||
113
- SuperDiff::RecursionGuard.already_seen?(operation.value)
114
- end
115
-
116
- def item_prefix_for(_operation)
117
- ""
118
- end
119
-
120
- def build_lines_from_inspection_of(value, type:, indentation_level:)
121
- SuperDiff.inspect_object(
122
- value,
123
- as_lines: true,
124
- type: type,
125
- indentation_level: indentation_level
126
- )
127
- end
128
-
129
- class InfiniteRecursionError < StandardError
130
- def initialize(_message = nil)
131
- super("Unhandled recursive data structure encountered!")
132
- end
133
- end
134
- end
135
- end
136
- end
@@ -1,28 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeFlatteners
3
- class CustomObject < Collection
4
- protected
5
-
6
- def open_token
7
- "#<%<class>s {" % { class: operation_tree.underlying_object.class }
8
- end
9
-
10
- def close_token
11
- "}>"
12
- end
13
-
14
- def item_prefix_for(operation)
15
- key =
16
- # Note: We could have used the right_key here too, they're both the
17
- # same keys
18
- if operation.respond_to?(:left_key)
19
- operation.left_key
20
- else
21
- operation.key
22
- end
23
-
24
- "#{key}: "
25
- end
26
- end
27
- end
28
- end
@@ -1,31 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeFlatteners
3
- class DefaultObject < Collection
4
- protected
5
-
6
- def open_token
7
- "#<#{operation_tree.underlying_object.class.name}:" +
8
- SuperDiff::Helpers.object_address_for(
9
- operation_tree.underlying_object
10
- ) + " {"
11
- end
12
-
13
- def close_token
14
- "}>"
15
- end
16
-
17
- def item_prefix_for(operation)
18
- key =
19
- # Note: We could have used the right_key here too, they're both the
20
- # same keys
21
- if operation.respond_to?(:left_key)
22
- operation.left_key
23
- else
24
- operation.key
25
- end
26
-
27
- "@#{key}="
28
- end
29
- end
30
- end
31
- end
@@ -1,33 +0,0 @@
1
- module SuperDiff
2
- module OperationTreeFlatteners
3
- class Hash < Collection
4
- protected
5
-
6
- def open_token
7
- "{"
8
- end
9
-
10
- def close_token
11
- "}"
12
- end
13
-
14
- def item_prefix_for(operation)
15
- key = key_for(operation)
16
-
17
- format_keys_as_kwargs? ? "#{key}: " : "#{key.inspect} => "
18
- end
19
-
20
- private
21
-
22
- def format_keys_as_kwargs?
23
- operation_tree.all? { |operation| key_for(operation).is_a?(Symbol) }
24
- end
25
-
26
- def key_for(operation)
27
- # Note: We could have used the right_key here too, they're both the
28
- # same keys
29
- operation.respond_to?(:left_key) ? operation.left_key : operation.key
30
- end
31
- end
32
- end
33
- end