hash_dealer 1.3.6 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ source "http://rubygems.org"
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
6
  gem "activesupport", :require => "active_support"
7
+ gem "i18n"
7
8
 
8
9
  # Add dependencies to develop your gem here.
9
10
  # Include everything needed to run rake, tests, features, etc.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.6
1
+ 1.4.0
data/hash_dealer.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "hash_dealer"
8
- s.version = "1.3.6"
8
+ s.version = "1.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dan Langevin"]
12
- s.date = "2011-09-15"
12
+ s.date = "2011-10-05"
13
13
  s.description = "Like Factory Girl but for Hashes only"
14
14
  s.email = "dan.langevin@lifebooker.com"
15
15
  s.extra_rdoc_files = [
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
  "Rakefile",
27
27
  "VERSION",
28
28
  "hash_dealer.gemspec",
29
+ "lib/comparator.rb",
29
30
  "lib/core_extensions.rb",
30
31
  "lib/hash_dealer.rb",
31
32
  "lib/matcher.rb",
@@ -47,6 +48,7 @@ Gem::Specification.new do |s|
47
48
 
48
49
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
50
  s.add_runtime_dependency(%q<activesupport>, [">= 0"])
51
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
50
52
  s.add_development_dependency(%q<bundler>, [">= 0"])
51
53
  s.add_development_dependency(%q<jeweler>, [">= 0"])
52
54
  s.add_development_dependency(%q<rspec>, [">= 0"])
@@ -56,6 +58,7 @@ Gem::Specification.new do |s|
56
58
  s.add_development_dependency(%q<simplecov>, [">= 0"])
57
59
  else
58
60
  s.add_dependency(%q<activesupport>, [">= 0"])
61
+ s.add_dependency(%q<i18n>, [">= 0"])
59
62
  s.add_dependency(%q<bundler>, [">= 0"])
60
63
  s.add_dependency(%q<jeweler>, [">= 0"])
61
64
  s.add_dependency(%q<rspec>, [">= 0"])
@@ -66,6 +69,7 @@ Gem::Specification.new do |s|
66
69
  end
67
70
  else
68
71
  s.add_dependency(%q<activesupport>, [">= 0"])
72
+ s.add_dependency(%q<i18n>, [">= 0"])
69
73
  s.add_dependency(%q<bundler>, [">= 0"])
70
74
  s.add_dependency(%q<jeweler>, [">= 0"])
71
75
  s.add_dependency(%q<rspec>, [">= 0"])
data/lib/comparator.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'active_support/core_ext'
2
+
3
+ class Comparator
4
+
5
+ def self.normalize_value(val)
6
+ return val if val.is_a?(TimeDateMatcher)
7
+ val = ActiveSupport::JSON.decode(val) if val.is_a?(String)
8
+ val = val.stringify_keys if val.is_a?(Hash)
9
+ val = val.collect{|v| v.is_a?(Hash) || n.is_a?(Array) ? self.normalize_value(v) : v} if val.is_a?(Array)
10
+ val
11
+ end
12
+
13
+ def self.diff(obj1, obj2)
14
+ return {} if obj1 == obj2
15
+ return self.array_diff(obj1, obj2) if obj1.is_a?(Array) && obj2.is_a?(Array)
16
+ return self.hash_diff(obj1, obj2) if obj1.is_a?(Hash) && obj2.is_a?(Hash)
17
+ return [obj1, obj2]
18
+ end
19
+
20
+ def self.array_diff(obj1, obj2)
21
+ {}.tap do |ret|
22
+ obj1.each_index do |k|
23
+ ret[k] = self.diff(obj1[k], obj2[k]) unless obj1[k] == obj2[k]
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.hash_diff(obj1, obj2)
29
+ obj1, obj2 = self.stringify_keys(obj1), self.stringify_keys(obj2)
30
+ (obj1.keys + obj2.keys).uniq.inject({}) do |memo, key|
31
+ unless obj1[key] == obj2[key]
32
+ if obj1[key].kind_of?(Hash) && obj2[key].kind_of?(Hash)
33
+ memo[key] = self.diff(obj1[key],obj2[key])
34
+ elsif obj1[key].kind_of?(Array) && obj2[key].kind_of?(Array)
35
+ memo[key] = [].tap do |arr|
36
+ obj1[key].each_index do |i|
37
+ arr << self.diff(obj1[key][i], obj2[key][i])
38
+ end
39
+ end
40
+ else
41
+ memo[key] = self.diff(obj1[key], obj2[key])
42
+ end
43
+ end
44
+ memo
45
+ end
46
+ end
47
+
48
+ def self.stringify_keys(hash)
49
+ {}.tap do |ret|
50
+ hash.keys.each do |k|
51
+ ret[k.to_s] = hash[k].is_a?(Hash) ? self.stringify_keys(hash[k]) : hash[k]
52
+ end
53
+ end
54
+ end
55
+
56
+ end
@@ -61,8 +61,18 @@ end
61
61
  class String
62
62
  def matcher(opts = {})
63
63
  # if we have a leading : or a /:xyz - a matcher is already defined
64
- self =~ /(^:|\/:)/ ? self : ":#{self}"
64
+ self =~ /(^:|\/:)/ ? PathString.new(self) : PathString.new(":#{self}")
65
65
  end
66
+ define_method "==_with_path_string" do |other|
67
+ if other.is_a?(PathString)
68
+ return true if self.is_a?(PathString)
69
+ return other == self
70
+ end
71
+ return self.send("==_without_path_string", other)
72
+ end
73
+ alias_method "==_without_path_string", :==
74
+ alias_method :==, "==_with_path_string"
75
+ alias_method :eql?, :==
66
76
  end
67
77
 
68
78
  class Hash
data/lib/hash_dealer.rb CHANGED
@@ -2,6 +2,7 @@ require File.expand_path('../path_string', __FILE__)
2
2
  require File.expand_path('../variable_array', __FILE__)
3
3
  require File.expand_path('../core_extensions', __FILE__)
4
4
  require File.expand_path('../matcher', __FILE__)
5
+ require File.expand_path('../comparator', __FILE__)
5
6
 
6
7
  class HashDealer
7
8
 
data/lib/matcher.rb CHANGED
@@ -4,43 +4,40 @@ require 'pp'
4
4
 
5
5
  RSpec::Matchers.define(:match_response) do |actual|
6
6
 
7
+ actual = Comparator.normalize_value(actual)
8
+
7
9
  match do |expected|
8
- PathString.as_sorted_json(actual) == PathString.as_sorted_json(expected)
10
+ expected = Comparator.normalize_value(expected)
11
+ Comparator.diff(actual, expected) == {}
9
12
  end
10
13
 
11
14
  failure_message_for_should do |container|
12
- "expected #{PathString.as_sorted_json(actual).pretty_inspect}\n to equal\n #{PathString.as_sorted_json(container).pretty_inspect} \n\n Diff: \n #{PathString.diff(container, actual)}"
15
+ Comparator.diff(actual, expected).pretty_inspect
13
16
  end
14
17
 
15
18
  failure_message_for_should_not do |container|
16
- "expected #{PathString.as_sorted_json(actual).pretty_inspect}\n not to equal\n #{PathString.as_sorted_json(container).pretty_inspect} \n\n Diff: \n #{PathString.diff(container, actual)}"
19
+ Comparator.diff(actual, expected).pretty_inspect
17
20
  end
18
21
  end
19
22
 
20
23
  # alias as match_json
21
24
  RSpec::Matchers.define(:match_list) do |actual|
22
25
 
23
- def normalize(val)
24
- val = JSON.parse(val) if val.is_a?(String)
25
- # if it's an array, we want just the first value
26
- if val.is_a?(Array)
27
- # we append :matcher to account for variable length arrays - that causes a problem
28
- # when the array is at the root and we are looking for the first element
29
- val = val.first == ":matcher" ? val[1] : val.first
30
- end
31
- val
32
- end
26
+ actual = Comparator.normalize_value(actual)
33
27
 
34
28
  match do |expected|
35
- PathString.as_sorted_json(normalize(actual)) == PathString.as_sorted_json(normalize(expected))
29
+ expected = Comparator.normalize_value(expected)
30
+ expected = expected.first if expected.is_a?(Array)
31
+ actual = actual.first if actual.is_a?(Array)
32
+ Comparator.diff(actual, expected) == {}
36
33
  end
37
34
 
38
35
  failure_message_for_should do |container|
39
- "expected #{PathString.as_sorted_json(normalize(actual))}\n to equal\n #{PathString.as_sorted_json(normalize(container))} \n\n Diff: \n #{PathString.diff(container, actual)}"
36
+ Comparator.diff(actual, expected).pretty_inspect
40
37
  end
41
38
 
42
39
  failure_message_for_should_not do |container|
43
- "expected #{PathString.as_sorted_json(normalize(actual))}\n not to equal\n #{PathString.as_sorted_json(normalize(container))} \n\n Diff: \n #{PathString.diff(container, actual)}"
40
+ Comparator.diff(actual, expected).pretty_inspect
44
41
  end
45
42
 
46
43
  end
@@ -121,7 +121,7 @@ describe HashDealer do
121
121
  updated_at(Time.now)
122
122
  end
123
123
 
124
- JSON.unparse({:created_at => Time.now, :updated_at => Time.now, :id => 1}).should match_response(HashDealer.roll(:var).matcher)
124
+ ActiveSupport::JSON.encode({:created_at => Time.now, :updated_at => Time.now, :id => 1}).should match_response(HashDealer.roll(:var).matcher)
125
125
 
126
126
  end
127
127
 
@@ -4,19 +4,20 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
4
4
  describe "match_response Matcher" do
5
5
 
6
6
  it "should match hashes" do
7
- {:a => "b"}.should match_response({"a" => ":test"})
7
+ {:a => "b"}.should match_response({"a" => "test"}.matcher)
8
8
  end
9
9
 
10
10
  it "should match paths within hashes" do
11
- {:a => {:b => "/test/test"}}.should match_response({"a" =>{"b" => "/:a/:b"}})
11
+ String.new({"a" =>{"b" => "/:a/:b"}}.matcher["a"]["b"]).should eql "/:a/:b"
12
+ {:a => {:b => "/test/test"}}.should match_response({"a" =>{"b" => "/:a/:b"}}.matcher)
12
13
  end
13
14
 
14
15
  it "should match the first element in a list" do
15
- {"a" => ":b"}.should match_list([{"a" => "test"}, {"a" => "test2"}])
16
+ [{"a" => "test"}, {"a" => "test2"}].should match_list({"a" => "b"}.matcher)
16
17
  end
17
18
 
18
19
  it "should account for the first :matcher param when it's at the root" do
19
- JSON.unparse([{"a" => "b"}, {"a" => "c"}]).should match_list({"a" => ":b"})
20
+ JSON.unparse([{"a" => "b"}, {"a" => "c"}]).should match_list({"a" => "b"}.matcher)
20
21
  end
21
22
 
22
23
  it "should match using wildcards for variable length arrays" do
@@ -29,4 +30,18 @@ describe "match_response Matcher" do
29
30
  {:a => {:b => "c"}}.should match_response({"a" => {"b" => "c"}})
30
31
  end
31
32
 
33
+ it "should provide meaningful diffs" do
34
+ diff = Comparator.diff({:a => {:b => "c", :d => "e"}}, {:a => {:b => "d", :d => "e"}, :b => "test"})
35
+ diff.should eql({"a" => {"b" => ["c", "d"]}, "b" => [nil, "test"]})
36
+ end
37
+
38
+ it "should match hashes regardless of the order of the keys" do
39
+ Comparator.diff({"a" => {"b" => "c", "d" => "e"}, "b" => "c"}, {:b => "c", :a => {"d" => "e", :b => "c"}}).should eql({})
40
+ end
41
+
42
+ it "should use the matcher comparison inside of Comparator" do
43
+ Comparator.diff({"a" => "dkddk"}.matcher, {"a" => "test"}).should eql({})
44
+ Comparator.diff({"a" => "dkddk"}, {"a" => "test"}.matcher).should eql({})
45
+ end
46
+
32
47
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: hash_dealer
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.3.6
5
+ version: 1.4.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Dan Langevin
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-09-15 00:00:00 Z
13
+ date: 2011-10-05 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -24,18 +24,18 @@ dependencies:
24
24
  prerelease: false
25
25
  version_requirements: *id001
26
26
  - !ruby/object:Gem::Dependency
27
- name: bundler
27
+ name: i18n
28
28
  requirement: &id002 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: "0"
34
- type: :development
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: *id002
37
37
  - !ruby/object:Gem::Dependency
38
- name: jeweler
38
+ name: bundler
39
39
  requirement: &id003 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
@@ -46,7 +46,7 @@ dependencies:
46
46
  prerelease: false
47
47
  version_requirements: *id003
48
48
  - !ruby/object:Gem::Dependency
49
- name: rspec
49
+ name: jeweler
50
50
  requirement: &id004 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
@@ -57,7 +57,7 @@ dependencies:
57
57
  prerelease: false
58
58
  version_requirements: *id004
59
59
  - !ruby/object:Gem::Dependency
60
- name: rcov
60
+ name: rspec
61
61
  requirement: &id005 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
@@ -68,8 +68,19 @@ dependencies:
68
68
  prerelease: false
69
69
  version_requirements: *id005
70
70
  - !ruby/object:Gem::Dependency
71
- name: guard-rspec
71
+ name: rcov
72
72
  requirement: &id006 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *id006
81
+ - !ruby/object:Gem::Dependency
82
+ name: guard-rspec
83
+ requirement: &id007 !ruby/object:Gem::Requirement
73
84
  none: false
74
85
  requirements:
75
86
  - - "="
@@ -77,10 +88,10 @@ dependencies:
77
88
  version: 0.4.0
78
89
  type: :development
79
90
  prerelease: false
80
- version_requirements: *id006
91
+ version_requirements: *id007
81
92
  - !ruby/object:Gem::Dependency
82
93
  name: ruby-debug19
83
- requirement: &id007 !ruby/object:Gem::Requirement
94
+ requirement: &id008 !ruby/object:Gem::Requirement
84
95
  none: false
85
96
  requirements:
86
97
  - - ">="
@@ -88,10 +99,10 @@ dependencies:
88
99
  version: "0"
89
100
  type: :development
90
101
  prerelease: false
91
- version_requirements: *id007
102
+ version_requirements: *id008
92
103
  - !ruby/object:Gem::Dependency
93
104
  name: simplecov
94
- requirement: &id008 !ruby/object:Gem::Requirement
105
+ requirement: &id009 !ruby/object:Gem::Requirement
95
106
  none: false
96
107
  requirements:
97
108
  - - ">="
@@ -99,7 +110,7 @@ dependencies:
99
110
  version: "0"
100
111
  type: :development
101
112
  prerelease: false
102
- version_requirements: *id008
113
+ version_requirements: *id009
103
114
  description: Like Factory Girl but for Hashes only
104
115
  email: dan.langevin@lifebooker.com
105
116
  executables: []
@@ -119,6 +130,7 @@ files:
119
130
  - Rakefile
120
131
  - VERSION
121
132
  - hash_dealer.gemspec
133
+ - lib/comparator.rb
122
134
  - lib/core_extensions.rb
123
135
  - lib/hash_dealer.rb
124
136
  - lib/matcher.rb
@@ -141,7 +153,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
141
153
  requirements:
142
154
  - - ">="
143
155
  - !ruby/object:Gem::Version
144
- hash: -498563270830469616
156
+ hash: -3208818064426284269
145
157
  segments:
146
158
  - 0
147
159
  version: "0"