structure_compare 0.1.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.
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