super_diff 0.10.0 → 0.12.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.

Potentially problematic release.


This version of super_diff might be problematic. Click here for more details.

Files changed (260) 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 -14
  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/basic/inspection_tree_builders/array.rb +53 -0
  35. data/lib/super_diff/basic/inspection_tree_builders/custom_object.rb +41 -0
  36. data/lib/super_diff/basic/inspection_tree_builders/date_like.rb +51 -0
  37. data/lib/super_diff/basic/inspection_tree_builders/default_object.rb +77 -0
  38. data/lib/super_diff/basic/inspection_tree_builders/hash.rb +63 -0
  39. data/lib/super_diff/basic/inspection_tree_builders/primitive.rb +19 -0
  40. data/lib/super_diff/basic/inspection_tree_builders/time_like.rb +58 -0
  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 +33 -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 +32 -26
  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 +12 -5
  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/{errors/no_operational_sequencer_available_error.rb → core/no_operation_tree_builder_available_error.rb} +6 -4
  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 -11
  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 -12
  115. data/lib/super_diff/object_inspection.rb +63 -14
  116. data/lib/super_diff/operation_tree_builders.rb +19 -14
  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/matcher_text_builders/raise_error.rb +3 -7
  138. data/lib/super_diff/rspec/monkey_patches.rb +59 -8
  139. data/lib/super_diff/rspec/object_inspection.rb +14 -4
  140. data/lib/super_diff/rspec/operation_tree_builders/collection_containing_exactly.rb +4 -4
  141. data/lib/super_diff/rspec/operation_tree_builders/collection_including.rb +1 -1
  142. data/lib/super_diff/rspec/operation_tree_builders/hash_including.rb +1 -1
  143. data/lib/super_diff/rspec/operation_tree_builders/object_having_attributes.rb +2 -2
  144. data/lib/super_diff/rspec.rb +28 -25
  145. data/lib/super_diff/version.rb +1 -1
  146. data/lib/super_diff.rb +78 -21
  147. data/spec/examples.txt +704 -493
  148. data/spec/integration/rails/engines_spec.rb +8 -3
  149. data/spec/integration/rspec/contain_exactly_matcher_spec.rb +19 -19
  150. data/spec/integration/rspec/eq_matcher_spec.rb +111 -39
  151. data/spec/integration/rspec/generic_describable_matchers_spec.rb +177 -0
  152. data/spec/integration/rspec/have_attributes_matcher_spec.rb +25 -25
  153. data/spec/integration/rspec/include_matcher_spec.rb +23 -23
  154. data/spec/integration/rspec/magic_metadata_spec.rb +51 -0
  155. data/spec/integration/rspec/match_array_matcher_spec.rb +30 -30
  156. data/spec/integration/rspec/match_matcher_spec.rb +93 -93
  157. data/spec/integration/rspec/raise_error_matcher_spec.rb +813 -69
  158. data/spec/internal/log/test.log +0 -0
  159. data/spec/spec_helper.rb +3 -0
  160. data/spec/support/integration/helpers.rb +19 -11
  161. data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +1 -1
  162. data/spec/support/integration/matchers.rb +34 -0
  163. data/spec/support/integration/test_programs/base.rb +6 -6
  164. data/spec/support/integration/test_programs/rspec_rails_engine.rb +3 -13
  165. data/spec/support/models/active_record/person.rb +8 -1
  166. data/spec/support/shared_examples/active_record.rb +38 -38
  167. data/spec/support/shared_examples/active_support.rb +125 -4
  168. data/spec/support/shared_examples/elided_diffs.rb +48 -48
  169. data/spec/support/shared_examples/hash_with_indifferent_access.rb +88 -88
  170. data/spec/support/shared_examples/key.rb +10 -10
  171. data/spec/support/unit/helpers.rb +12 -1
  172. data/spec/support/unit/matchers/be_deprecated_in_favor_of.rb +39 -0
  173. data/spec/unit/active_record/object_inspection_spec.rb +5 -5
  174. data/spec/unit/active_support/object_inspection_spec.rb +170 -0
  175. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/array_spec.rb +8 -8
  176. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/custom_object_spec.rb +9 -9
  177. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/default_object_spec.rb +9 -9
  178. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/hash_spec.rb +8 -8
  179. data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/multiline_string_spec.rb +4 -4
  180. data/spec/unit/{helpers_spec.rb → core/helpers_spec.rb} +2 -2
  181. data/spec/unit/{tiered_lines_elider_spec.rb → core/tiered_lines_elider_spec.rb} +2 -2
  182. data/spec/unit/{tiered_lines_formatter_spec.rb → core/tiered_lines_formatter_spec.rb} +1 -1
  183. data/spec/unit/deprecations_spec.rb +176 -0
  184. data/spec/unit/equality_matchers/main_spec.rb +5 -5
  185. data/spec/unit/rspec/matchers/raise_error_spec.rb +43 -11
  186. data/spec/unit/rspec/object_inspection/rspec_matcher_spec.rb +91 -0
  187. data/spec/unit/rspec/object_inspection_spec.rb +2 -2
  188. data/spec/unit/super_diff_spec.rb +64 -0
  189. data/super_diff.gemspec +6 -0
  190. metadata +137 -112
  191. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders/active_record_model.rb +0 -39
  192. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders/active_record_relation.rb +0 -27
  193. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders.rb +0 -16
  194. data/lib/super_diff/active_support/object_inspection/inspection_tree_builders/hash_with_indifferent_access.rb +0 -31
  195. data/lib/super_diff/active_support/object_inspection/inspection_tree_builders.rb +0 -12
  196. data/lib/super_diff/colorized_document_extensions.rb +0 -18
  197. data/lib/super_diff/configuration.rb +0 -149
  198. data/lib/super_diff/diff_formatters/collection.rb +0 -132
  199. data/lib/super_diff/diff_formatters/multiline_string.rb +0 -31
  200. data/lib/super_diff/differs/array.rb +0 -15
  201. data/lib/super_diff/differs/custom_object.rb +0 -17
  202. data/lib/super_diff/differs/default_object.rb +0 -19
  203. data/lib/super_diff/differs/defaults.rb +0 -12
  204. data/lib/super_diff/differs/empty.rb +0 -13
  205. data/lib/super_diff/differs/hash.rb +0 -15
  206. data/lib/super_diff/differs/main.rb +0 -31
  207. data/lib/super_diff/differs/multiline_string.rb +0 -16
  208. data/lib/super_diff/differs/time_like.rb +0 -15
  209. data/lib/super_diff/errors/no_diff_formatter_available_error.rb +0 -21
  210. data/lib/super_diff/gem_version.rb +0 -45
  211. data/lib/super_diff/helpers.rb +0 -86
  212. data/lib/super_diff/implementation_checks.rb +0 -19
  213. data/lib/super_diff/line.rb +0 -83
  214. data/lib/super_diff/object_inspection/inspection_tree_builders/array.rb +0 -34
  215. data/lib/super_diff/object_inspection/inspection_tree_builders/base.rb +0 -27
  216. data/lib/super_diff/object_inspection/inspection_tree_builders/custom_object.rb +0 -31
  217. data/lib/super_diff/object_inspection/inspection_tree_builders/default_object.rb +0 -54
  218. data/lib/super_diff/object_inspection/inspection_tree_builders/defaults.rb +0 -14
  219. data/lib/super_diff/object_inspection/inspection_tree_builders/hash.rb +0 -38
  220. data/lib/super_diff/object_inspection/inspection_tree_builders/main.rb +0 -30
  221. data/lib/super_diff/object_inspection/inspection_tree_builders/primitive.rb +0 -21
  222. data/lib/super_diff/object_inspection/inspection_tree_builders/time_like.rb +0 -47
  223. data/lib/super_diff/object_inspection/inspection_tree_builders.rb +0 -44
  224. data/lib/super_diff/object_inspection/nodes.rb +0 -50
  225. data/lib/super_diff/object_inspection/prefix_for_next_node.rb +0 -6
  226. data/lib/super_diff/object_inspection/prelude_for_next_node.rb +0 -6
  227. data/lib/super_diff/operation_tree_builders/array.rb +0 -107
  228. data/lib/super_diff/operation_tree_builders/custom_object.rb +0 -40
  229. data/lib/super_diff/operation_tree_builders/default_object.rb +0 -119
  230. data/lib/super_diff/operation_tree_builders/defaults.rb +0 -5
  231. data/lib/super_diff/operation_tree_builders/hash.rb +0 -218
  232. data/lib/super_diff/operation_tree_builders/multiline_string.rb +0 -86
  233. data/lib/super_diff/operation_tree_builders/time_like.rb +0 -24
  234. data/lib/super_diff/operation_tree_flatteners/array.rb +0 -15
  235. data/lib/super_diff/operation_tree_flatteners/collection.rb +0 -136
  236. data/lib/super_diff/operation_tree_flatteners/custom_object.rb +0 -28
  237. data/lib/super_diff/operation_tree_flatteners/default_object.rb +0 -31
  238. data/lib/super_diff/operation_tree_flatteners/hash.rb +0 -33
  239. data/lib/super_diff/operation_tree_flatteners/multiline_string.rb +0 -18
  240. data/lib/super_diff/operation_trees/array.rb +0 -15
  241. data/lib/super_diff/operation_trees/custom_object.rb +0 -15
  242. data/lib/super_diff/operation_trees/default_object.rb +0 -40
  243. data/lib/super_diff/operation_trees/defaults.rb +0 -5
  244. data/lib/super_diff/operation_trees/hash.rb +0 -15
  245. data/lib/super_diff/operation_trees/main.rb +0 -35
  246. data/lib/super_diff/operation_trees/multiline_string.rb +0 -15
  247. data/lib/super_diff/recursion_guard.rb +0 -50
  248. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_containing_exactly.rb +0 -29
  249. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_including.rb +0 -36
  250. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/double.rb +0 -98
  251. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/hash_including.rb +0 -37
  252. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/instance_of.rb +0 -28
  253. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/kind_of.rb +0 -28
  254. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/object_having_attributes.rb +0 -29
  255. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/primitive.rb +0 -10
  256. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/value_within.rb +0 -33
  257. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders.rb +0 -44
  258. data/lib/super_diff/tiered_lines.rb +0 -4
  259. data/lib/super_diff/tiered_lines_elider.rb +0 -462
  260. data/lib/super_diff/tiered_lines_formatter.rb +0 -75
@@ -0,0 +1,19 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module InspectionTreeBuilders
4
+ class Primitive < Core::AbstractInspectionTreeBuilder
5
+ def self.applies_to?(value)
6
+ SuperDiff.primitive?(value) || value.is_a?(::String)
7
+ end
8
+
9
+ def call
10
+ Core::InspectionTree.new do |t1|
11
+ t1.as_lines_when_rendering_to_lines do |t2|
12
+ t2.add_text object.inspect
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,58 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module InspectionTreeBuilders
4
+ class TimeLike < Core::AbstractInspectionTreeBuilder
5
+ def self.applies_to?(value)
6
+ SuperDiff.time_like?(value)
7
+ end
8
+
9
+ def call
10
+ Core::InspectionTree.new do |t1|
11
+ t1.as_lines_when_rendering_to_lines(
12
+ collection_bookend: :open
13
+ ) do |t2|
14
+ t2.add_text "#<#{object.class} "
15
+
16
+ # stree-ignore
17
+ t2.when_rendering_to_lines do |t3|
18
+ t3.add_text "{"
19
+ end
20
+ end
21
+
22
+ t1.when_rendering_to_string do |t2|
23
+ t2.add_text(
24
+ object.strftime("%Y-%m-%d %H:%M:%S") +
25
+ (object.subsec == 0 ? "" : "+#{object.subsec.inspect}") +
26
+ " " + object.strftime("%:z") +
27
+ (object.zone ? " (#{object.zone})" : "")
28
+ )
29
+ end
30
+
31
+ t1.when_rendering_to_lines do |t2|
32
+ t2.nested do |t3|
33
+ t3.insert_separated_list(
34
+ %i[year month day hour min sec subsec zone utc_offset]
35
+ ) do |t4, name|
36
+ t4.add_text name.to_s
37
+ t4.add_text ": "
38
+ t4.add_inspection_of object.public_send(name)
39
+ end
40
+ end
41
+ end
42
+
43
+ t1.as_lines_when_rendering_to_lines(
44
+ collection_bookend: :close
45
+ ) do |t2|
46
+ # stree-ignore
47
+ t2.when_rendering_to_lines do |t3|
48
+ t3.add_text "}"
49
+ end
50
+
51
+ t2.add_text ">"
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module InspectionTreeBuilders
4
+ autoload :Array, "super_diff/basic/inspection_tree_builders/array"
5
+ autoload(
6
+ :CustomObject,
7
+ "super_diff/basic/inspection_tree_builders/custom_object"
8
+ )
9
+ autoload(
10
+ :DefaultObject,
11
+ "super_diff/basic/inspection_tree_builders/default_object"
12
+ )
13
+ autoload :Hash, "super_diff/basic/inspection_tree_builders/hash"
14
+ autoload :Primitive, "super_diff/basic/inspection_tree_builders/primitive"
15
+ autoload :String, "super_diff/basic/inspection_tree_builders/string"
16
+ autoload :TimeLike, "super_diff/basic/inspection_tree_builders/time_like"
17
+ autoload :DateLike, "super_diff/basic/inspection_tree_builders/date_like"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,111 @@
1
+ require "diff-lcs"
2
+
3
+ module SuperDiff
4
+ module Basic
5
+ module OperationTreeBuilders
6
+ class Array < Core::AbstractOperationTreeBuilder
7
+ def self.applies_to?(expected, actual)
8
+ expected.is_a?(::Array) && actual.is_a?(::Array)
9
+ end
10
+
11
+ def call
12
+ Diff::LCS.traverse_balanced(expected, actual, lcs_callbacks)
13
+ operation_tree
14
+ end
15
+
16
+ private
17
+
18
+ def lcs_callbacks
19
+ @_lcs_callbacks ||=
20
+ LcsCallbacks.new(
21
+ operation_tree: operation_tree,
22
+ expected: expected,
23
+ actual: actual,
24
+ compare: method(:compare)
25
+ )
26
+ end
27
+
28
+ def operation_tree
29
+ @_operation_tree ||= OperationTrees::Array.new([])
30
+ end
31
+
32
+ class LcsCallbacks
33
+ extend AttrExtras.mixin
34
+
35
+ pattr_initialize %i[operation_tree! expected! actual! compare!]
36
+ public :operation_tree
37
+
38
+ def match(event)
39
+ add_noop_operation(event)
40
+ end
41
+
42
+ def discard_a(event)
43
+ add_delete_operation(event)
44
+ end
45
+
46
+ def discard_b(event)
47
+ add_insert_operation(event)
48
+ end
49
+
50
+ def change(event)
51
+ children = compare.(event.old_element, event.new_element)
52
+
53
+ if children
54
+ add_change_operation(event, children)
55
+ else
56
+ add_delete_operation(event)
57
+ add_insert_operation(event)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def add_delete_operation(event)
64
+ operation_tree << Core::UnaryOperation.new(
65
+ name: :delete,
66
+ collection: expected,
67
+ key: event.old_position,
68
+ value: event.old_element,
69
+ index: event.old_position
70
+ )
71
+ end
72
+
73
+ def add_insert_operation(event)
74
+ operation_tree << Core::UnaryOperation.new(
75
+ name: :insert,
76
+ collection: actual,
77
+ key: event.new_position,
78
+ value: event.new_element,
79
+ index: event.new_position
80
+ )
81
+ end
82
+
83
+ def add_noop_operation(event)
84
+ operation_tree << Core::UnaryOperation.new(
85
+ name: :noop,
86
+ collection: actual,
87
+ key: event.new_position,
88
+ value: event.new_element,
89
+ index: event.new_position
90
+ )
91
+ end
92
+
93
+ def add_change_operation(event, children)
94
+ operation_tree << Core::BinaryOperation.new(
95
+ name: :change,
96
+ left_collection: expected,
97
+ right_collection: actual,
98
+ left_key: event.old_position,
99
+ right_key: event.new_position,
100
+ left_value: event.old_element,
101
+ right_value: event.new_element,
102
+ left_index: event.old_position,
103
+ right_index: event.new_position,
104
+ children: children
105
+ )
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,42 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module OperationTreeBuilders
4
+ class CustomObject < DefaultObject
5
+ def self.applies_to?(expected, actual)
6
+ expected.class == actual.class &&
7
+ expected.respond_to?(:attributes_for_super_diff) &&
8
+ actual.respond_to?(:attributes_for_super_diff)
9
+ end
10
+
11
+ protected
12
+
13
+ def build_operation_tree
14
+ # NOTE: It doesn't matter whether we use expected or actual here,
15
+ # because all we care about is the name of the class
16
+ OperationTrees::CustomObject.new([], underlying_object: actual)
17
+ end
18
+
19
+ def attribute_names
20
+ expected.attributes_for_super_diff.keys &
21
+ actual.attributes_for_super_diff.keys
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :expected_attributes, :actual_attributes
27
+
28
+ def establish_expected_and_actual_attributes
29
+ @expected_attributes =
30
+ attribute_names.reduce({}) do |hash, name|
31
+ hash.merge(name => expected.public_send(name))
32
+ end
33
+
34
+ @actual_attributes =
35
+ attribute_names.reduce({}) do |hash, name|
36
+ hash.merge(name => actual.public_send(name))
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module OperationTreeBuilders
4
+ class DateLike < CustomObject
5
+ def self.applies_to?(expected, actual)
6
+ SuperDiff.date_like?(expected) && SuperDiff.date_like?(actual)
7
+ end
8
+
9
+ protected
10
+
11
+ def attribute_names
12
+ %w[year month day]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,117 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module OperationTreeBuilders
4
+ class DefaultObject < Core::AbstractOperationTreeBuilder
5
+ def self.applies_to?(_expected, _actual)
6
+ true
7
+ end
8
+
9
+ def initialize(*args)
10
+ super(*args)
11
+
12
+ establish_expected_and_actual_attributes
13
+ end
14
+
15
+ protected
16
+
17
+ def unary_operations
18
+ attribute_names.reduce([]) do |operations, name|
19
+ possibly_add_noop_operation_to(operations, name)
20
+ possibly_add_delete_operation_to(operations, name)
21
+ possibly_add_insert_operation_to(operations, name)
22
+ operations
23
+ end
24
+ end
25
+
26
+ def build_operation_tree
27
+ # XXX This assumes that `expected` and `actual` are the same
28
+ # TODO: Does this need to find the operation tree matching `actual`?
29
+ OperationTrees::DefaultObject.new([], underlying_object: actual)
30
+ end
31
+
32
+ def attribute_names
33
+ (
34
+ expected.instance_variables.sort & actual.instance_variables.sort
35
+ ).map { |variable_name| variable_name[1..-1] }
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :expected_attributes, :actual_attributes
41
+
42
+ def establish_expected_and_actual_attributes
43
+ @expected_attributes =
44
+ attribute_names.reduce({}) do |hash, name|
45
+ hash.merge(name => expected.instance_variable_get("@#{name}"))
46
+ end
47
+
48
+ @actual_attributes =
49
+ attribute_names.reduce({}) do |hash, name|
50
+ hash.merge(name => actual.instance_variable_get("@#{name}"))
51
+ end
52
+ end
53
+
54
+ def possibly_add_noop_operation_to(operations, attribute_name)
55
+ if should_add_noop_operation?(attribute_name)
56
+ operations << Core::UnaryOperation.new(
57
+ name: :noop,
58
+ collection: actual_attributes,
59
+ key: attribute_name,
60
+ index: attribute_names.index(attribute_name),
61
+ value: actual_attributes[attribute_name]
62
+ )
63
+ end
64
+ end
65
+
66
+ def should_add_noop_operation?(attribute_name)
67
+ expected_attributes.include?(attribute_name) &&
68
+ actual_attributes.include?(attribute_name) &&
69
+ expected_attributes[attribute_name] ==
70
+ actual_attributes[attribute_name]
71
+ end
72
+
73
+ def possibly_add_delete_operation_to(operations, attribute_name)
74
+ if should_add_delete_operation?(attribute_name)
75
+ operations << Core::UnaryOperation.new(
76
+ name: :delete,
77
+ collection: expected_attributes,
78
+ key: attribute_name,
79
+ index: attribute_names.index(attribute_name),
80
+ value: expected_attributes[attribute_name]
81
+ )
82
+ end
83
+ end
84
+
85
+ def should_add_delete_operation?(attribute_name)
86
+ expected_attributes.include?(attribute_name) &&
87
+ (
88
+ !actual_attributes.include?(attribute_name) ||
89
+ expected_attributes[attribute_name] !=
90
+ actual_attributes[attribute_name]
91
+ )
92
+ end
93
+
94
+ def possibly_add_insert_operation_to(operations, attribute_name)
95
+ if should_add_insert_operation?(attribute_name)
96
+ operations << Core::UnaryOperation.new(
97
+ name: :insert,
98
+ collection: actual_attributes,
99
+ key: attribute_name,
100
+ index: attribute_names.index(attribute_name),
101
+ value: actual_attributes[attribute_name]
102
+ )
103
+ end
104
+ end
105
+
106
+ def should_add_insert_operation?(attribute_name)
107
+ !expected_attributes.include?(attribute_name) ||
108
+ (
109
+ actual_attributes.include?(attribute_name) &&
110
+ expected_attributes[attribute_name] !=
111
+ actual_attributes[attribute_name]
112
+ )
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,222 @@
1
+ module SuperDiff
2
+ module Basic
3
+ module OperationTreeBuilders
4
+ class Hash < Core::AbstractOperationTreeBuilder
5
+ def self.applies_to?(expected, actual)
6
+ expected.is_a?(::Hash) && actual.is_a?(::Hash)
7
+ end
8
+
9
+ protected
10
+
11
+ def unary_operations
12
+ unary_operations_using_variant_of_patience_algorithm
13
+ end
14
+
15
+ def build_operation_tree
16
+ OperationTrees::Hash.new([])
17
+ end
18
+
19
+ private
20
+
21
+ def unary_operations_using_variant_of_patience_algorithm
22
+ operations = []
23
+ aks, eks = actual.keys, expected.keys
24
+ previous_ei, ei = nil, 0
25
+ ai = 0
26
+
27
+ # When diffing a hash, we're more interested in the 'actual' version
28
+ # than the 'expected' version, because that's the ultimate truth.
29
+ # Therefore, the diff is presented from the perspective of the 'actual'
30
+ # hash, and we start off by looping over it.
31
+ while ai < aks.size
32
+ ak = aks[ai]
33
+ av, ev = actual[ak], expected[ak]
34
+ # While we iterate over 'actual' in order, we jump all over
35
+ # 'expected', trying to match up its keys with the keys in 'actual' as
36
+ # much as possible.
37
+ ei = eks.index(ak)
38
+
39
+ if should_add_noop_operation?(ak)
40
+ # (If we're here, it probably means that the key we're pointing to
41
+ # in the 'actual' and 'expected' hashes have the same value.)
42
+
43
+ if ei && previous_ei && (ei - previous_ei) > 1
44
+ # If we've jumped from one operation in the 'expected' hash to
45
+ # another operation later in 'expected' (due to the fact that the
46
+ # 'expected' hash is in a different order than 'actual'), collect
47
+ # any delete operations in between and add them to our operations
48
+ # array as deletes before adding the noop. If we don't do this
49
+ # now, then those deletes will disappear. (Again, we are mainly
50
+ # iterating over 'actual', so this is the only way to catch all of
51
+ # the keys in 'expected'.)
52
+ (previous_ei + 1).upto(ei - 1) do |ei2|
53
+ ek = eks[ei2]
54
+ ev2, av2 = expected[ek], actual[ek]
55
+
56
+ if (
57
+ (!actual.include?(ek) || ev2 != av2) &&
58
+ operations.none? do |operation|
59
+ %i[delete noop].include?(operation.name) &&
60
+ operation.key == ek
61
+ end
62
+ )
63
+ operations << Core::UnaryOperation.new(
64
+ name: :delete,
65
+ collection: expected,
66
+ key: ek,
67
+ value: ev2,
68
+ index: ei2
69
+ )
70
+ end
71
+ end
72
+ end
73
+
74
+ operations << Core::UnaryOperation.new(
75
+ name: :noop,
76
+ collection: actual,
77
+ key: ak,
78
+ value: av,
79
+ index: ai
80
+ )
81
+ else
82
+ # (If we're here, it probably means that the key in 'actual' isn't
83
+ # present in 'expected' or the values don't match.)
84
+
85
+ if (
86
+ (operations.empty? || operations.last.name == :noop) &&
87
+ (ai == 0 || eks.include?(aks[ai - 1]))
88
+ )
89
+ # If we go from a match in the last iteration to a missing or
90
+ # extra key in this one, or we're at the first key in 'actual' and
91
+ # it's missing or extra, look for deletes in the 'expected' hash
92
+ # and add them to our list of operations before we add the
93
+ # inserts. In most cases we will accomplish this by backtracking a
94
+ # bit to the key in 'expected' that matched the key in 'actual' we
95
+ # processed in the previous iteration (or just the first key in
96
+ # 'expected' if this is the first key in 'actual'), and then
97
+ # iterating from there through 'expected' until we reach the end
98
+ # or we hit some other condition (see below).
99
+
100
+ start_index =
101
+ if ai > 0
102
+ eks.index(aks[ai - 1]) + 1
103
+ else
104
+ 0
105
+ end
106
+
107
+ start_index.upto(eks.size - 1) do |ei2|
108
+ ek = eks[ei2]
109
+ ev, av2 = expected[ek], actual[ek]
110
+
111
+ if actual.include?(ek) && ev == av2
112
+ # If the key in 'expected' we've landed on happens to be a
113
+ # match in 'actual', then stop, because it's going to be
114
+ # handled in some future iteration of the 'actual' loop.
115
+ break
116
+ elsif (
117
+ aks[ai + 1..-1].any? do |k|
118
+ expected.include?(k) && expected[k] != actual[k]
119
+ end
120
+ )
121
+ # While we backtracked a bit to iterate over 'expected', we
122
+ # now have to look ahead. If we will end up encountering a
123
+ # insert that matches this delete later, stop and go back to
124
+ # iterating over 'actual'. This is because the delete we would
125
+ # have added now will be added later when we encounter the
126
+ # associated insert, so we don't want to add it twice.
127
+ break
128
+ else
129
+ operations << Core::UnaryOperation.new(
130
+ name: :delete,
131
+ collection: expected,
132
+ key: ek,
133
+ value: ev,
134
+ index: ei2
135
+ )
136
+ end
137
+
138
+ if ek == ak && ev != av
139
+ # If we're pointing to the same key in 'expected' as in
140
+ # 'actual', but with different values, go ahead and add an
141
+ # insert now to accompany the delete added above. That way
142
+ # they appear together, which will be easier to read.
143
+ operations << Core::UnaryOperation.new(
144
+ name: :insert,
145
+ collection: actual,
146
+ key: ak,
147
+ value: av,
148
+ index: ai
149
+ )
150
+ end
151
+ end
152
+ end
153
+
154
+ if (
155
+ expected.include?(ak) && ev != av &&
156
+ operations.none? do |op|
157
+ op.name == :delete && op.key == ak
158
+ end
159
+ )
160
+ # If we're here, it means that we didn't encounter any delete
161
+ # operations above for whatever reason and so we need to add a
162
+ # delete to represent the fact that the value for this key has
163
+ # changed.
164
+ operations << Core::UnaryOperation.new(
165
+ name: :delete,
166
+ collection: expected,
167
+ key: ak,
168
+ value: expected[ak],
169
+ index: ei
170
+ )
171
+ end
172
+
173
+ if operations.none? { |op| op.name == :insert && op.key == ak }
174
+ # If we're here, it means that we didn't encounter any insert
175
+ # operations above. Since we already handled delete, the only
176
+ # alternative is that this key must not exist in 'expected', so
177
+ # we need to add an insert.
178
+ operations << Core::UnaryOperation.new(
179
+ name: :insert,
180
+ collection: actual,
181
+ key: ak,
182
+ value: av,
183
+ index: ai
184
+ )
185
+ end
186
+ end
187
+
188
+ ai += 1
189
+ previous_ei = ei
190
+ end
191
+
192
+ # The last thing to do is this: if there are keys in 'expected' that
193
+ # aren't in 'actual', and they aren't associated with any inserts to
194
+ # where they would have been added above, tack those deletes onto the
195
+ # end of our operations array.
196
+ (eks - aks - operations.map(&:key)).each do |ek|
197
+ ei = eks.index(ek)
198
+ ev = expected[ek]
199
+
200
+ operations << Core::UnaryOperation.new(
201
+ name: :delete,
202
+ collection: expected,
203
+ key: ek,
204
+ value: ev,
205
+ index: ei
206
+ )
207
+ end
208
+
209
+ operations
210
+ end
211
+
212
+ def should_add_noop_operation?(key)
213
+ expected.include?(key) && expected[key] == actual[key]
214
+ end
215
+
216
+ def all_keys
217
+ actual.keys | expected.keys
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,90 @@
1
+ require "patience_diff"
2
+
3
+ module SuperDiff
4
+ module Basic
5
+ module OperationTreeBuilders
6
+ class MultilineString < Core::AbstractOperationTreeBuilder
7
+ def self.applies_to?(expected, actual)
8
+ expected.is_a?(::String) && actual.is_a?(::String) &&
9
+ (expected.include?("\n") || actual.include?("\n"))
10
+ end
11
+
12
+ def initialize(*args)
13
+ super(*args)
14
+
15
+ @original_expected = @expected
16
+ @original_actual = @actual
17
+ @expected = split_into_lines(@expected)
18
+ @actual = split_into_lines(@actual)
19
+ @sequence_matcher = PatienceDiff::SequenceMatcher.new
20
+ end
21
+
22
+ protected
23
+
24
+ def unary_operations
25
+ opcodes.flat_map do |code, a_start, a_end, b_start, b_end|
26
+ if code == :delete
27
+ add_delete_operations(a_start..a_end)
28
+ elsif code == :insert
29
+ add_insert_operations(b_start..b_end)
30
+ else
31
+ add_noop_operations(b_start..b_end)
32
+ end
33
+ end
34
+ end
35
+
36
+ def build_operation_tree
37
+ OperationTrees::MultilineString.new([])
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :sequence_matcher, :original_expected, :original_actual
43
+
44
+ def split_into_lines(string)
45
+ string.scan(/.+(?:\r|\n|\r\n|\Z)/)
46
+ end
47
+
48
+ def opcodes
49
+ sequence_matcher.diff_opcodes(expected, actual)
50
+ end
51
+
52
+ def add_delete_operations(indices)
53
+ indices.map do |index|
54
+ Core::UnaryOperation.new(
55
+ name: :delete,
56
+ collection: expected,
57
+ key: index,
58
+ index: index,
59
+ value: expected[index]
60
+ )
61
+ end
62
+ end
63
+
64
+ def add_insert_operations(indices)
65
+ indices.map do |index|
66
+ Core::UnaryOperation.new(
67
+ name: :insert,
68
+ collection: actual,
69
+ key: index,
70
+ index: index,
71
+ value: actual[index]
72
+ )
73
+ end
74
+ end
75
+
76
+ def add_noop_operations(indices)
77
+ indices.map do |index|
78
+ Core::UnaryOperation.new(
79
+ name: :noop,
80
+ collection: actual,
81
+ key: index,
82
+ index: index,
83
+ value: actual[index]
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end