structure_compare 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 535404ee3a6c198af0c7788bee7f0a31d15fee6c
4
+ data.tar.gz: c133617419e3d5b8e3338e63ae08b123b74a3ad3
5
+ SHA512:
6
+ metadata.gz: 4decfee7c6aafed708585478a69be43475a2a52db8b1484bc179df16c53610f7c47dbf98b77a8d0249615f9a9d5832b882ad1c4d0235b3cffb9b1bdbb506693c
7
+ data.tar.gz: 78f2b98c8c8af06034ffada10178cd343ee479e4004732caff6a8bb70f47ae122fe299755d5247435e8389ca7c62158cc91a0d45a0a3d36716fae96f6d6c5c14
data/.gitignore ADDED
@@ -0,0 +1,40 @@
1
+ # SimpleCov
2
+ test/coverage
3
+
4
+ # Bundler
5
+ Gemfile.lock
6
+
7
+ *.gem
8
+ *.rbc
9
+ /.config
10
+ /coverage/
11
+ /InstalledFiles
12
+ /pkg/
13
+ /spec/reports/
14
+ /test/tmp/
15
+ /test/version_tmp/
16
+ /tmp/
17
+
18
+ ## Specific to RubyMotion:
19
+ .dat*
20
+ .repl_history
21
+ build/
22
+
23
+ ## Documentation cache and generated files:
24
+ /.yardoc/
25
+ /_yardoc/
26
+ /doc/
27
+ /rdoc/
28
+
29
+ ## Environment normalisation:
30
+ /.bundle/
31
+ /lib/bundler/man/
32
+
33
+ # for a library or gem, you might want to ignore these files since the code is
34
+ # intended to run in multiple environments; otherwise, check them in:
35
+ Gemfile.lock
36
+ .ruby-version
37
+ .ruby-gemset
38
+
39
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
40
+ .rvmrc
data/.simplecov ADDED
@@ -0,0 +1,13 @@
1
+ SimpleCov.start do
2
+ coverage_dir "test/coverage"
3
+ command_name "tests"
4
+
5
+ use_merging true
6
+ merge_timeout 3600
7
+
8
+ add_filter "test/"
9
+
10
+ add_group "All", "/lib/*.rb"
11
+
12
+ formatter SimpleCov::Formatter::HTMLFormatter
13
+ end
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ guard :minitest do
2
+
3
+ # run all tests when lib main module file changes
4
+ watch(%r{^lib/structure_compare.rb$}) { "test" }
5
+
6
+ # run accompanying test for single source file if it changes
7
+ watch(%r{^lib/(.*)\.rb$}) {|m| "test/#{m[1]}_test.rb" }
8
+
9
+ # run test whenever it changes
10
+ watch(%r{^test/(.*)\/?(.*)?_test\.rb$})
11
+
12
+ end
13
+
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Markus Seeger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # structure_compare
2
+ Compares the structure of two deep nested Ruby structures
3
+
4
+ Useful for testing API responses.
5
+
6
+ TODO: usage, example
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+ require 'rubocop/rake_task'
7
+
8
+ task :default => [:test]
9
+
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.libs << 'test'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = false
15
+ end
16
+
17
+ RuboCop::RakeTask.new(:rubocop) do |task|
18
+ # NOTE: do not define task.patterns here, since this
19
+ # would override the .rubocop.yml include/exclude
20
+ # definition
21
+
22
+ task.formatters = ['offenses', 'simple']
23
+ task.options = ['-D'] # Display cop name
24
+
25
+ # don't abort rake on failure
26
+ task.fail_on_error = false
27
+ end
28
+
29
+ task default: [:test, :rubocop]
@@ -0,0 +1,156 @@
1
+ # TODO rename to structure_compare
2
+ # TODO Enumerable to support non-array enumerables
3
+ # TODO doc
4
+ module StructureCompare
5
+
6
+ class StructuresNotEqualError < RuntimeError; end
7
+ class ArgumentError < ArgumentError; end
8
+
9
+ class StructureComparison
10
+ def initialize(options = {})
11
+ @options = {
12
+ strict_key_order: false,
13
+ check_values: false,
14
+ treat_hash_symbols_as_strings: false,
15
+ float_tolerance_factor: 0
16
+ }.merge(options)
17
+ end
18
+
19
+ # TODO doc
20
+ def structures_are_equal?(expected, actual)
21
+ @path = []
22
+ @error = nil
23
+
24
+ begin
25
+ check_structures_equal!(expected, actual)
26
+ rescue StructuresNotEqualError => _error
27
+ return false
28
+ end
29
+
30
+ return true
31
+ end
32
+
33
+ # TODO doc
34
+ def error
35
+ "#{@path.join} : #{@error}"
36
+ end
37
+
38
+ protected
39
+
40
+ def check_structures_equal!(expected, actual)
41
+ check_kind_of!(expected, actual)
42
+
43
+ case expected
44
+ when Array
45
+ check_arrays_equal!(expected, actual)
46
+ when Hash
47
+ check_hashes_equal!(expected, actual)
48
+ else
49
+ check_leafs_equal!(expected, actual)
50
+ end
51
+ end
52
+
53
+ def check_arrays_equal!(expected, actual)
54
+ check_equal!(
55
+ expected.count, actual.count, "array length: #{expected.count} != #{actual.count}"
56
+ )
57
+
58
+ expected.each_with_index do |expected_value, index|
59
+ path_segment = "[#{index}]"
60
+ @path.push(path_segment)
61
+
62
+ check_structures_equal!(expected_value, actual[index])
63
+ @path.pop
64
+ end
65
+ end
66
+
67
+ def check_hashes_equal!(expected, actual)
68
+ check_hash_keys_equal!(expected, actual)
69
+
70
+ expected_values = expected.values
71
+ actual_values = actual.values
72
+
73
+ expected_values.each_with_index do |expected_value, index|
74
+ key = expected.keys[index]
75
+ path_segment = key.is_a?(Symbol) ? "[:#{key}]" : "[\"#{key}\"]"
76
+ @path.push(path_segment)
77
+
78
+ check_structures_equal!(expected_value, actual_values[index])
79
+ @path.pop
80
+ end
81
+ end
82
+
83
+ def check_hash_keys_equal!(expected, actual)
84
+ expected_keys = expected.keys
85
+ actual_keys = actual.keys
86
+
87
+ if @options[:treat_hash_symbols_as_strings]
88
+ # NOTE: not all hash keys are symbols/strings, only convert symbols
89
+ expected_keys.map! { |key| key.is_a?(Symbol) ? key.to_s : key }
90
+ actual_keys.map! { |key| key.is_a?(Symbol) ? key.to_s : key }
91
+ end
92
+
93
+ if @options[:strict_key_order]
94
+ check_equal!(expected_keys, actual_keys)
95
+ else
96
+ begin
97
+ expected_keys.sort!
98
+ actual_keys.sort!
99
+ rescue ::ArgumentError => error
100
+ raise StructureCompare::ArgumentError.new(
101
+ "Unable to sort hash keys: \"#{error}\"." +
102
+ 'Try enabling :strict_key_order option to prevent sorting of mixed-type hash keys.'
103
+ )
104
+ end
105
+
106
+ check_equal!(expected_keys, actual_keys)
107
+ end
108
+ end
109
+
110
+ def check_leafs_equal!(expected, actual)
111
+ check_equal!(expected, actual) if @options[:check_values]
112
+ end
113
+
114
+ def check_kind_of!(expected, actual)
115
+ unless actual.kind_of?(expected.class)
116
+ failure_message = "expected #{actual.class.to_s} to be kind of #{expected.class.to_s}"
117
+ not_equal_error!(expected, actual, failure_message)
118
+ end
119
+ end
120
+
121
+ def check_equal!(expected, actual, failure_message = nil)
122
+ if expected.is_a?(Float) && actual.is_a?(Float)
123
+ is_equal = float_equal_with_tolerance_factor?(
124
+ expected, actual, @options[:float_tolerance_factor]
125
+ )
126
+ else
127
+ is_equal = (expected == actual)
128
+ end
129
+
130
+ if !is_equal
131
+ failure_message ||= "expected: #{expected.inspect}, got: #{actual.inspect}"
132
+ not_equal_error!(expected, actual, failure_message)
133
+ end
134
+ end
135
+
136
+ def float_equal_with_tolerance_factor?(expected, actual, tolerance_factor)
137
+ raise StructureCompare::ArgumentError.new("tolerance_factor must be > 0") if tolerance_factor < 0
138
+
139
+ lower_bound = (expected * (1.0 - tolerance_factor) - Float::EPSILON)
140
+ upper_bound = (expected * (1.0 + tolerance_factor) + Float::EPSILON)
141
+
142
+ return (lower_bound <= actual) && (actual <= upper_bound)
143
+ end
144
+
145
+ # TODO make this part of an overridden exception?
146
+ def not_equal_error!(expected, actual, failure_message)
147
+ @error = failure_message
148
+ raise StructuresNotEqualError.new() # TODO error message, path
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ # TODO test if monkeypatching MiniTest::Assertions works
155
+
156
+ require 'version'
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module StructureCompare
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "structure_compare"
8
+ gem.version = StructureCompare::VERSION
9
+ gem.authors = ["Markus Seeger"]
10
+ gem.email = ["mail@codegourmet.de"]
11
+ gem.description = "Compares the structure of two deep nested Ruby structures"
12
+ gem.summary = ""
13
+ gem.homepage = "https://github.com/codegourmet/structure_compare"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency "rake", "~> 10.1"
22
+ gem.add_development_dependency "guard"
23
+ gem.add_development_dependency "guard-minitest"
24
+ gem.add_development_dependency "minitest"
25
+ gem.add_development_dependency "minitest-reporters"
26
+ gem.add_development_dependency "simplecov"
27
+ gem.add_development_dependency "rubocop"
28
+ end
@@ -0,0 +1,176 @@
1
+ require_relative "test_helper"
2
+ require_relative "../lib/structure_compare"
3
+
4
+ class StructureCompareTest < MiniTest::Test
5
+
6
+ def setup
7
+ end
8
+
9
+ def test_compares_keys_of_hashes
10
+ refute_structures_equal({a: 1, b: 2}, {a: 1, other_key: 2})
11
+ refute_structures_equal({a: 1, b: 2}, {a: 1, b: 2, c: 3})
12
+ end
13
+
14
+ def test_hash_key_order_is_compared_if_option_set
15
+ assert_structures_equal({a: 1, b: 2}, {b: 2, a: 1})
16
+ assert_structures_equal({a: 1, b: 2}, {b: 2, a: 1}, strict_key_order: false)
17
+ refute_structures_equal({a: 1, b: 2}, {b: 2, a: 1}, strict_key_order: true)
18
+ end
19
+
20
+ def test_compares_leaf_value_types
21
+ assert_structures_equal({a: 1, b: 2}, {b: 2, a: 1})
22
+ refute_structures_equal({a: 1, b: 2}, {a: 1, b: 2.0})
23
+
24
+ assert_structures_equal(%w(a b c d), %w(a b c d))
25
+ refute_structures_equal(%w(a b c d), ['a', 'b', 'c', 111])
26
+ end
27
+
28
+ def test_leaf_values_are_compared_if_option_set
29
+ assert_structures_equal({a: 1, b: 2}, {a: 111, b: 222})
30
+ refute_structures_equal({a: 1, b: 2}, {a: 111, b: 222}, check_values: true)
31
+
32
+ assert_structures_equal(%w(a b c d), %w(a b c d))
33
+ refute_structures_equal(%w(a b c d), %w(A b c d), check_values: true)
34
+ end
35
+
36
+ def test_compares_arrays
37
+ assert_structures_equal([1, 2, 3], [1, 2, 3])
38
+ refute_structures_equal([1, 2], [1, 2, 3])
39
+ end
40
+
41
+ def test_stores_error_with_path_in_getter
42
+ structure_a = { x: 1, a: [{ b: [1, 1, 1] }] }
43
+ structure_b = { x: 1, a: [{ b: [1, 9, 1] }] }
44
+
45
+ comparison = StructureCompare::StructureComparison.new(check_values: true)
46
+ comparison.structures_are_equal?(structure_a, structure_b)
47
+ assert_match "[:a][0][:b][1]", comparison.error
48
+ end
49
+
50
+ def test_shows_values_for_expected_and_actual_if_error
51
+ structure_a = { x: 1, a: [{ b: [1, "FOO", 1] }] }
52
+ structure_b = { x: 1, a: [{ b: [1, "BAR", 1] }] }
53
+
54
+ assert_structures_equal(structure_a, structure_b)
55
+ comparison = StructureCompare::StructureComparison.new(check_values: true)
56
+ comparison.structures_are_equal?(structure_a, structure_b)
57
+ assert_match "FOO", comparison.error
58
+ assert_match "BAR", comparison.error
59
+ end
60
+
61
+ def test_compares_deep_structures
62
+ structure_a = { x: 1, a: [{ b: [1, 1, 1] }] }
63
+ structure_b = { x: 1, a: [{ b: [1, 9, 1] }] }
64
+
65
+ assert_structures_equal(structure_a, structure_a, check_values: true)
66
+ refute_structures_equal(structure_a, structure_b, check_values: true)
67
+ end
68
+
69
+ def test_mixed_type_hash_keys_trigger_error_unless_strict_order_set
70
+ hash = { a: 1, 5 => 6 }
71
+
72
+ assert_raises StructureCompare::ArgumentError do
73
+ assert_structures_equal(hash, hash)
74
+ end
75
+
76
+ assert_structures_equal(hash, hash, strict_key_order: true)
77
+ end
78
+
79
+ def test_symbol_keys_are_strings_if_option_set
80
+ string_hash = {"a" => 1, "b" => 2, 5 => 6}
81
+ symbol_hash = {a: 1, b: 2, 5 => 6}
82
+
83
+ refute_structures_equal(string_hash, symbol_hash, strict_key_order: true)
84
+ assert_structures_equal(
85
+ string_hash, symbol_hash,
86
+ treat_hash_symbols_as_strings: true, strict_key_order: true
87
+ )
88
+ end
89
+
90
+ def test_compares_floats_correctly
91
+ assert_structures_equal([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], check_values: true)
92
+ assert_structures_equal(
93
+ [1.0 - Float::EPSILON * 0.5, 2.0 + Float::EPSILON * 0.5, 3.0],
94
+ [1.0, 2.0, 3.0],
95
+ check_values: true
96
+ )
97
+
98
+ refute_structures_equal(
99
+ [1.0 - Float::EPSILON * 2, 2.0, 3.0],
100
+ [1.0, 2.0, 3.0],
101
+ check_values: true
102
+ )
103
+
104
+ refute_structures_equal(
105
+ [1.0 + Float::EPSILON * 2, 2.0, 3.0],
106
+ [1.0, 2.0, 3.0],
107
+ check_values: true
108
+ )
109
+ end
110
+
111
+ def test_compares_with_tolerance_if_options_set
112
+ assert_structures_equal(%w(a b c d), %w(a b c d), {
113
+ # just to catch bugs with this option concerning non-floats
114
+ check_values: true, float_tolerance_factor: 0.1
115
+ })
116
+
117
+ refute_structures_equal([1.0, 2.0, 3.0], [1.0001, 2.0001, 3.0001], {
118
+ check_values: true
119
+ })
120
+
121
+ assert_structures_equal([1.0, 2.0, 3.0], [1.0001, 2.0001, 3.0001], {
122
+ check_values: true, float_tolerance_factor: 0.1
123
+ })
124
+ assert_structures_equal([1.0, 2.0, 3.0], [1.1, 2.1, 3.1], {
125
+ check_values: true, float_tolerance_factor: 0.1
126
+ })
127
+ assert_structures_equal([1.0, 2.0, 3.0], [0.9, 1.9, 2.9], {
128
+ check_values: true, float_tolerance_factor: 0.1
129
+ })
130
+
131
+ assert_structures_equal(
132
+ [1.0, 2.0, 3.0],
133
+ [0.9 - Float::EPSILON * 0.5, 2.1, 3.1],
134
+ { check_values: true, float_tolerance_factor: 0.1 }
135
+ )
136
+ refute_structures_equal(
137
+ [1.0, 2.0, 3.0],
138
+ [0.9 - Float::EPSILON * 2, 2.1, 3.1],
139
+ { check_values: true, float_tolerance_factor: 0.1 }
140
+ )
141
+
142
+ assert_structures_equal(
143
+ [1.0, 2.0, 3.0],
144
+ [1.1 + Float::EPSILON * 0.5, 2.1, 3.1],
145
+ { check_values: true, float_tolerance_factor: 0.1 }
146
+ )
147
+ refute_structures_equal(
148
+ [1.0, 2.0, 3.0],
149
+ [1.1 + Float::EPSILON * 2, 2.1, 3.1],
150
+ { check_values: true, float_tolerance_factor: 0.1 }
151
+ )
152
+ end
153
+
154
+ def test_ensures_tolerance_factor_is_gt_0
155
+ assert_raises StructureCompare::ArgumentError do
156
+ assert_structures_equal(
157
+ [1.0], [1.0], check_values: true, float_tolerance_factor: -0.1
158
+ )
159
+ end
160
+ end
161
+
162
+ protected
163
+
164
+ def assert_structures_equal(expected, actual, options = {})
165
+ comparison = StructureCompare::StructureComparison.new(options)
166
+ is_equal = comparison.structures_are_equal?(expected, actual)
167
+ assert is_equal, comparison.error
168
+ end
169
+
170
+ def refute_structures_equal(expected, actual, options = {})
171
+ comparison = StructureCompare::StructureComparison.new(options)
172
+ is_equal = comparison.structures_are_equal?(expected, actual)
173
+ refute is_equal, "structures are not expected to be equal"
174
+ end
175
+
176
+ end
@@ -0,0 +1,12 @@
1
+ require 'simplecov' # has to be at top, will execute ../.simplecov
2
+
3
+ require 'structure_compare'
4
+
5
+ require 'minitest/autorun'
6
+ require 'minitest/reporters'
7
+
8
+ if ENV["RM_INFO"] || ENV["TEAMCITY_VERSION"]
9
+ MiniTest::Reporters.use! MiniTest::Reporters::RubyMineReporter.new
10
+ else
11
+ MiniTest::Reporters.use! MiniTest::Reporters::SpecReporter.new
12
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: structure_compare
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Markus Seeger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: guard-minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest-reporters
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Compares the structure of two deep nested Ruby structures
112
+ email:
113
+ - mail@codegourmet.de
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".simplecov"
120
+ - Gemfile
121
+ - Guardfile
122
+ - LICENSE
123
+ - README.md
124
+ - Rakefile
125
+ - lib/structure_compare.rb
126
+ - lib/version.rb
127
+ - structure_compare.gemspec
128
+ - test/structure_compare_test.rb
129
+ - test/test_helper.rb
130
+ homepage: https://github.com/codegourmet/structure_compare
131
+ licenses:
132
+ - MIT
133
+ metadata: {}
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.4.5
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: ''
154
+ test_files:
155
+ - test/structure_compare_test.rb
156
+ - test/test_helper.rb