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.
- checksums.yaml +4 -4
- data/README.md +19 -166
- data/lib/super_diff/active_record/differs/active_record_relation.rb +1 -1
- data/lib/super_diff/active_record/inspection_tree_builders/active_record_model.rb +57 -0
- data/lib/super_diff/active_record/inspection_tree_builders/active_record_relation.rb +34 -0
- data/lib/super_diff/active_record/inspection_tree_builders.rb +14 -0
- data/lib/super_diff/active_record/monkey_patches.rb +6 -3
- data/lib/super_diff/active_record/object_inspection.rb +16 -4
- data/lib/super_diff/active_record/operation_tree_builders/active_record_model.rb +6 -2
- data/lib/super_diff/active_record/operation_tree_builders/active_record_relation.rb +1 -1
- data/lib/super_diff/active_record/operation_tree_flatteners/active_record_relation.rb +1 -1
- data/lib/super_diff/active_record/operation_trees/active_record_relation.rb +1 -1
- data/lib/super_diff/active_record.rb +12 -16
- data/lib/super_diff/active_support/differs/hash_with_indifferent_access.rb +1 -1
- data/lib/super_diff/active_support/inspection_tree_builders/hash_with_indifferent_access.rb +44 -0
- data/lib/super_diff/active_support/inspection_tree_builders/ordered_options.rb +44 -0
- data/lib/super_diff/active_support/inspection_tree_builders.rb +14 -0
- data/lib/super_diff/active_support/object_inspection.rb +16 -4
- data/lib/super_diff/active_support/operation_tree_builders/hash_with_indifferent_access.rb +1 -1
- data/lib/super_diff/active_support/operation_tree_flatteners/hash_with_indifferent_access.rb +1 -1
- data/lib/super_diff/active_support/operation_trees/hash_with_indifferent_access.rb +1 -1
- data/lib/super_diff/active_support.rb +11 -15
- data/lib/super_diff/basic/diff_formatters/collection.rb +135 -0
- data/lib/super_diff/basic/diff_formatters/multiline_string.rb +34 -0
- data/lib/super_diff/basic/diff_formatters.rb +11 -0
- data/lib/super_diff/basic/differs/array.rb +17 -0
- data/lib/super_diff/basic/differs/custom_object.rb +19 -0
- data/lib/super_diff/basic/differs/date_like.rb +17 -0
- data/lib/super_diff/basic/differs/default_object.rb +24 -0
- data/lib/super_diff/basic/differs/hash.rb +17 -0
- data/lib/super_diff/basic/differs/multiline_string.rb +18 -0
- data/lib/super_diff/basic/differs/time_like.rb +17 -0
- data/lib/super_diff/basic/differs.rb +24 -0
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/array.rb +3 -3
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/custom_object.rb +3 -3
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/date_like.rb +3 -3
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/default_object.rb +5 -7
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/hash.rb +3 -3
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/primitive.rb +3 -3
- data/lib/super_diff/{object_inspection → basic}/inspection_tree_builders/time_like.rb +3 -3
- data/lib/super_diff/basic/inspection_tree_builders.rb +20 -0
- data/lib/super_diff/basic/operation_tree_builders/array.rb +111 -0
- data/lib/super_diff/basic/operation_tree_builders/custom_object.rb +42 -0
- data/lib/super_diff/basic/operation_tree_builders/date_like.rb +17 -0
- data/lib/super_diff/basic/operation_tree_builders/default_object.rb +117 -0
- data/lib/super_diff/basic/operation_tree_builders/hash.rb +222 -0
- data/lib/super_diff/basic/operation_tree_builders/multiline_string.rb +90 -0
- data/lib/super_diff/basic/operation_tree_builders/time_like.rb +26 -0
- data/lib/super_diff/basic/operation_tree_builders.rb +34 -0
- data/lib/super_diff/basic/operation_tree_flatteners/array.rb +17 -0
- data/lib/super_diff/basic/operation_tree_flatteners/collection.rb +140 -0
- data/lib/super_diff/basic/operation_tree_flatteners/custom_object.rb +30 -0
- data/lib/super_diff/basic/operation_tree_flatteners/default_object.rb +32 -0
- data/lib/super_diff/basic/operation_tree_flatteners/hash.rb +35 -0
- data/lib/super_diff/basic/operation_tree_flatteners/multiline_string.rb +20 -0
- data/lib/super_diff/basic/operation_tree_flatteners.rb +24 -0
- data/lib/super_diff/basic/operation_trees/array.rb +17 -0
- data/lib/super_diff/basic/operation_trees/custom_object.rb +17 -0
- data/lib/super_diff/basic/operation_trees/default_object.rb +42 -0
- data/lib/super_diff/basic/operation_trees/hash.rb +17 -0
- data/lib/super_diff/basic/operation_trees/multiline_string.rb +17 -0
- data/lib/super_diff/basic/operation_trees.rb +25 -0
- data/lib/super_diff/basic.rb +48 -0
- data/lib/super_diff/{differs/base.rb → core/abstract_differ.rb} +2 -2
- data/lib/super_diff/core/abstract_inspection_tree_builder.rb +26 -0
- data/lib/super_diff/{operation_trees/base.rb → core/abstract_operation_tree.rb} +6 -2
- data/lib/super_diff/{operation_tree_builders/base.rb → core/abstract_operation_tree_builder.rb} +4 -8
- data/lib/super_diff/{operation_tree_flatteners/base.rb → core/abstract_operation_tree_flattener.rb} +2 -2
- data/lib/super_diff/{operations → core}/binary_operation.rb +1 -1
- data/lib/super_diff/core/colorized_document_extensions.rb +20 -0
- data/lib/super_diff/core/configuration.rb +192 -0
- data/lib/super_diff/core/differ_dispatcher.rb +31 -0
- data/lib/super_diff/core/gem_version.rb +47 -0
- data/lib/super_diff/core/helpers.rb +88 -0
- data/lib/super_diff/core/implementation_checks.rb +21 -0
- data/lib/super_diff/{object_inspection → core}/inspection_tree.rb +7 -6
- data/lib/super_diff/core/inspection_tree_builder_dispatcher.rb +23 -0
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_lines_when_rendering_to_lines.rb +9 -3
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_prefix_when_rendering_to_lines.rb +3 -3
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_prelude_when_rendering_to_lines.rb +3 -3
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/as_single_line.rb +3 -3
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/base.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/inspection.rb +7 -11
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/nesting.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/only_when.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/text.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_empty.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_non_empty.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_rendering_to_lines.rb +2 -2
- data/lib/super_diff/{object_inspection/nodes → core/inspection_tree_nodes}/when_rendering_to_string.rb +2 -2
- data/lib/super_diff/core/inspection_tree_nodes.rb +55 -0
- data/lib/super_diff/core/line.rb +85 -0
- data/lib/super_diff/{errors → core}/no_differ_available_error.rb +1 -1
- data/lib/super_diff/core/no_inspection_tree_builder_available_error.rb +21 -0
- data/lib/super_diff/core/no_operation_tree_available_error.rb +20 -0
- data/lib/super_diff/core/no_operation_tree_builder_available_error.rb +24 -0
- data/lib/super_diff/{operation_tree_builders/main.rb → core/operation_tree_builder_dispatcher.rb} +9 -13
- data/lib/super_diff/core/operation_tree_finder.rb +27 -0
- data/lib/super_diff/core/prefix_for_next_inspection_tree_node.rb +6 -0
- data/lib/super_diff/core/prelude_for_next_inspection_tree_node.rb +6 -0
- data/lib/super_diff/core/recursion_guard.rb +52 -0
- data/lib/super_diff/core/tiered_lines.rb +6 -0
- data/lib/super_diff/core/tiered_lines_elider.rb +472 -0
- data/lib/super_diff/core/tiered_lines_formatter.rb +77 -0
- data/lib/super_diff/{operations → core}/unary_operation.rb +1 -1
- data/lib/super_diff/core.rb +69 -0
- data/lib/super_diff/differs.rb +19 -12
- data/lib/super_diff/equality_matchers/array.rb +3 -3
- data/lib/super_diff/equality_matchers/default.rb +8 -3
- data/lib/super_diff/equality_matchers/hash.rb +3 -3
- data/lib/super_diff/equality_matchers/multiline_string.rb +3 -3
- data/lib/super_diff/equality_matchers/primitive.rb +3 -3
- data/lib/super_diff/equality_matchers/singleline_string.rb +2 -2
- data/lib/super_diff/errors.rb +12 -8
- data/lib/super_diff/object_inspection.rb +63 -14
- data/lib/super_diff/operation_tree_builders.rb +19 -15
- data/lib/super_diff/operation_tree_flatteners.rb +19 -16
- data/lib/super_diff/operation_trees.rb +19 -9
- data/lib/super_diff/operations.rb +12 -2
- data/lib/super_diff/rspec/augmented_matcher.rb +1 -1
- data/lib/super_diff/rspec/differ.rb +4 -5
- data/lib/super_diff/rspec/differs/collection_containing_exactly.rb +1 -1
- data/lib/super_diff/rspec/differs/collection_including.rb +1 -1
- data/lib/super_diff/rspec/differs/hash_including.rb +1 -1
- data/lib/super_diff/rspec/differs/object_having_attributes.rb +1 -1
- data/lib/super_diff/rspec/inspection_tree_builders/collection_containing_exactly.rb +34 -0
- data/lib/super_diff/rspec/inspection_tree_builders/collection_including.rb +40 -0
- data/lib/super_diff/rspec/inspection_tree_builders/double.rb +100 -0
- data/lib/super_diff/rspec/inspection_tree_builders/generic_describable_matcher.rb +17 -0
- data/lib/super_diff/rspec/inspection_tree_builders/hash_including.rb +40 -0
- data/lib/super_diff/rspec/inspection_tree_builders/instance_of.rb +25 -0
- data/lib/super_diff/rspec/inspection_tree_builders/kind_of.rb +25 -0
- data/lib/super_diff/rspec/inspection_tree_builders/object_having_attributes.rb +34 -0
- data/lib/super_diff/rspec/inspection_tree_builders/primitive.rb +9 -0
- data/lib/super_diff/rspec/inspection_tree_builders/value_within.rb +30 -0
- data/lib/super_diff/rspec/inspection_tree_builders.rb +40 -0
- data/lib/super_diff/rspec/object_inspection.rb +14 -4
- data/lib/super_diff/rspec/operation_tree_builders/collection_containing_exactly.rb +4 -4
- data/lib/super_diff/rspec/operation_tree_builders/collection_including.rb +1 -1
- data/lib/super_diff/rspec/operation_tree_builders/hash_including.rb +1 -1
- data/lib/super_diff/rspec/operation_tree_builders/object_having_attributes.rb +2 -2
- data/lib/super_diff/rspec.rb +20 -18
- data/lib/super_diff/version.rb +1 -1
- data/lib/super_diff.rb +69 -21
- data/spec/examples.txt +704 -543
- data/spec/support/integration/helpers.rb +4 -1
- data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +1 -1
- data/spec/support/models/active_record/person.rb +8 -1
- data/spec/support/shared_examples/active_record.rb +5 -5
- data/spec/support/unit/helpers.rb +12 -1
- data/spec/support/unit/matchers/be_deprecated_in_favor_of.rb +39 -0
- data/spec/unit/active_record/object_inspection_spec.rb +5 -5
- data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/array_spec.rb +8 -8
- data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/custom_object_spec.rb +9 -9
- data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/default_object_spec.rb +9 -9
- data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/hash_spec.rb +8 -8
- data/spec/unit/{operation_tree_flatteners → basic/operation_tree_flatteners}/multiline_string_spec.rb +4 -4
- data/spec/unit/{helpers_spec.rb → core/helpers_spec.rb} +2 -2
- data/spec/unit/{tiered_lines_elider_spec.rb → core/tiered_lines_elider_spec.rb} +2 -2
- data/spec/unit/{tiered_lines_formatter_spec.rb → core/tiered_lines_formatter_spec.rb} +1 -1
- data/spec/unit/deprecations_spec.rb +176 -0
- data/spec/unit/equality_matchers/main_spec.rb +5 -5
- data/super_diff.gemspec +6 -0
- metadata +127 -115
- data/lib/super_diff/active_record/object_inspection/inspection_tree_builders/active_record_model.rb +0 -51
- data/lib/super_diff/active_record/object_inspection/inspection_tree_builders/active_record_relation.rb +0 -36
- data/lib/super_diff/active_record/object_inspection/inspection_tree_builders.rb +0 -16
- data/lib/super_diff/active_support/object_inspection/inspection_tree_builders/hash_with_indifferent_access.rb +0 -46
- data/lib/super_diff/active_support/object_inspection/inspection_tree_builders/ordered_options.rb +0 -46
- data/lib/super_diff/active_support/object_inspection/inspection_tree_builders.rb +0 -16
- data/lib/super_diff/colorized_document_extensions.rb +0 -18
- data/lib/super_diff/configuration.rb +0 -149
- data/lib/super_diff/diff_formatters/collection.rb +0 -132
- data/lib/super_diff/diff_formatters/multiline_string.rb +0 -31
- data/lib/super_diff/differs/array.rb +0 -15
- data/lib/super_diff/differs/custom_object.rb +0 -17
- data/lib/super_diff/differs/date_like.rb +0 -15
- data/lib/super_diff/differs/default_object.rb +0 -19
- data/lib/super_diff/differs/defaults.rb +0 -13
- data/lib/super_diff/differs/empty.rb +0 -13
- data/lib/super_diff/differs/hash.rb +0 -15
- data/lib/super_diff/differs/main.rb +0 -31
- data/lib/super_diff/differs/multiline_string.rb +0 -16
- data/lib/super_diff/differs/time_like.rb +0 -15
- data/lib/super_diff/gem_version.rb +0 -45
- data/lib/super_diff/helpers.rb +0 -86
- data/lib/super_diff/implementation_checks.rb +0 -19
- data/lib/super_diff/line.rb +0 -83
- data/lib/super_diff/object_inspection/inspection_tree_builders/base.rb +0 -27
- data/lib/super_diff/object_inspection/inspection_tree_builders/defaults.rb +0 -15
- data/lib/super_diff/object_inspection/inspection_tree_builders/main.rb +0 -30
- data/lib/super_diff/object_inspection/inspection_tree_builders.rb +0 -48
- data/lib/super_diff/object_inspection/nodes.rb +0 -50
- data/lib/super_diff/object_inspection/prefix_for_next_node.rb +0 -6
- data/lib/super_diff/object_inspection/prelude_for_next_node.rb +0 -6
- data/lib/super_diff/operation_tree_builders/array.rb +0 -107
- data/lib/super_diff/operation_tree_builders/custom_object.rb +0 -40
- data/lib/super_diff/operation_tree_builders/date_like.rb +0 -15
- data/lib/super_diff/operation_tree_builders/default_object.rb +0 -119
- data/lib/super_diff/operation_tree_builders/defaults.rb +0 -5
- data/lib/super_diff/operation_tree_builders/hash.rb +0 -218
- data/lib/super_diff/operation_tree_builders/multiline_string.rb +0 -86
- data/lib/super_diff/operation_tree_builders/time_like.rb +0 -24
- data/lib/super_diff/operation_tree_flatteners/array.rb +0 -15
- data/lib/super_diff/operation_tree_flatteners/collection.rb +0 -136
- data/lib/super_diff/operation_tree_flatteners/custom_object.rb +0 -28
- data/lib/super_diff/operation_tree_flatteners/default_object.rb +0 -31
- data/lib/super_diff/operation_tree_flatteners/hash.rb +0 -33
- data/lib/super_diff/operation_tree_flatteners/multiline_string.rb +0 -18
- data/lib/super_diff/operation_trees/array.rb +0 -15
- data/lib/super_diff/operation_trees/custom_object.rb +0 -15
- data/lib/super_diff/operation_trees/default_object.rb +0 -40
- data/lib/super_diff/operation_trees/defaults.rb +0 -5
- data/lib/super_diff/operation_trees/hash.rb +0 -15
- data/lib/super_diff/operation_trees/main.rb +0 -35
- data/lib/super_diff/operation_trees/multiline_string.rb +0 -15
- data/lib/super_diff/recursion_guard.rb +0 -50
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_containing_exactly.rb +0 -36
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_including.rb +0 -42
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/double.rb +0 -102
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/generic_describable_matcher.rb +0 -19
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/hash_including.rb +0 -42
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/instance_of.rb +0 -27
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/kind_of.rb +0 -27
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/object_having_attributes.rb +0 -36
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/primitive.rb +0 -10
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/value_within.rb +0 -32
- data/lib/super_diff/rspec/object_inspection/inspection_tree_builders.rb +0 -48
- data/lib/super_diff/tiered_lines.rb +0 -4
- data/lib/super_diff/tiered_lines_elider.rb +0 -462
- data/lib/super_diff/tiered_lines_formatter.rb +0 -75
@@ -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
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SuperDiff
|
2
|
+
module Basic
|
3
|
+
module OperationTreeBuilders
|
4
|
+
class TimeLike < CustomObject
|
5
|
+
def self.applies_to?(expected, actual)
|
6
|
+
SuperDiff.time_like?(expected) && SuperDiff.time_like?(actual)
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def attribute_names
|
12
|
+
base = %w[year month day hour min sec subsec zone utc_offset]
|
13
|
+
|
14
|
+
# If timezones are different, also show a normalized timestamp at the
|
15
|
+
# end of the diff to help visualize why they are different moments in
|
16
|
+
# time.
|
17
|
+
if actual.zone != expected.zone
|
18
|
+
base + ["utc"]
|
19
|
+
else
|
20
|
+
base
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SuperDiff
|
2
|
+
module Basic
|
3
|
+
module OperationTreeBuilders
|
4
|
+
autoload :Array, "super_diff/basic/operation_tree_builders/array"
|
5
|
+
autoload(
|
6
|
+
:CustomObject,
|
7
|
+
"super_diff/basic/operation_tree_builders/custom_object"
|
8
|
+
)
|
9
|
+
autoload(
|
10
|
+
:DefaultObject,
|
11
|
+
"super_diff/basic/operation_tree_builders/default_object"
|
12
|
+
)
|
13
|
+
autoload :Hash, "super_diff/basic/operation_tree_builders/hash"
|
14
|
+
# TODO: Where is this used?
|
15
|
+
autoload(
|
16
|
+
:MultilineString,
|
17
|
+
"super_diff/basic/operation_tree_builders/multiline_string"
|
18
|
+
)
|
19
|
+
autoload :TimeLike, "super_diff/basic/operation_tree_builders/time_like"
|
20
|
+
autoload :DateLike, "super_diff/basic/operation_tree_builders/date_like"
|
21
|
+
|
22
|
+
class Main
|
23
|
+
def self.call(*args)
|
24
|
+
warn <<~EOT
|
25
|
+
WARNING: SuperDiff::OperationTreeBuilders::Main.call(...) is deprecated and will be removed in the next major release.
|
26
|
+
Please use SuperDiff.build_operation_tree_for(...) instead.
|
27
|
+
#{caller_locations.join("\n")}
|
28
|
+
EOT
|
29
|
+
SuperDiff.build_operation_tree_for(*args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|