hash19 0.0.3 → 0.0.4
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/.gitignore +3 -0
- data/lib/hash19/core.rb +1 -1
- data/lib/hash19/resolvers.rb +13 -5
- data/lib/hash19/version.rb +1 -1
- data/spec/hash19/access_spec.rb +14 -11
- data/spec/hash19/associations_spec.rb +30 -7
- data/spec/hash19/contains_spec.rb +5 -0
- data/spec/hash19/core_spec.rb +27 -20
- data/spec/hash19/injection_spec.rb +21 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e3af7ef38c2b83b16643922fdb0b9e4f17e9a02
|
4
|
+
data.tar.gz: 1ce1e8d0f3355773c09d224ec1e6f8de21afb184
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ceb2cc897448d847ab762fd5e63b355962f9b52bd3788c12d5a0451c26bf7b2c30b80d354f08fcbd682a239e84c1282905796dfd950e7e4b9eefa92f50db929
|
7
|
+
data.tar.gz: 76740b66f18d888d6630faaa9f5eebf4ce9786f395eece15e87caa09fdee1ab3619e86dc97e6aa628938662e0db810d252165c2a07c8827a0861684d6fe5c431
|
data/.gitignore
CHANGED
data/lib/hash19/core.rb
CHANGED
@@ -11,7 +11,7 @@ module Hash19
|
|
11
11
|
|
12
12
|
module Initializer
|
13
13
|
def initialize(payload={})
|
14
|
-
if
|
14
|
+
if payload.is_a? Array
|
15
15
|
@hash19 = payload.map do |el|
|
16
16
|
klass = resolve_class(self.class.contains_klass.to_s.camelize.singularize)
|
17
17
|
klass.send(:new, el).to_h(lazy: true)
|
data/lib/hash19/resolvers.rb
CHANGED
@@ -21,8 +21,11 @@ module Hash19
|
|
21
21
|
association.map { |hash| klass.send(:new, hash).to_h(lazy: true) }
|
22
22
|
end
|
23
23
|
else
|
24
|
-
|
25
|
-
|
24
|
+
puts "warning: Association:<#{name}> is not present in #{self.class.name}. Possible specify a trigger" unless opts[:trigger]
|
25
|
+
puts "warning: Key:<#{opts[:using]}> not present in #{self.class.name}. Cannot map association:<#{name}>" unless @hash19.has_key? opts[:using]
|
26
|
+
if opts[:trigger] and @hash19.has_key? opts[:using]
|
27
|
+
@hash19[opts[:alias] || name] = LazyValue.new(-> { opts[:trigger].call(@hash19.delete(opts[:using])) }) if opts[:trigger]
|
28
|
+
end
|
26
29
|
end
|
27
30
|
end
|
28
31
|
end
|
@@ -38,11 +41,13 @@ module Hash19
|
|
38
41
|
self.class.injections.each do |opts|
|
39
42
|
async do
|
40
43
|
entries = JsonPath.new(opts[:at]).on(hash).flatten
|
41
|
-
ids = entries.map { |el| el[opts[:using]] }
|
44
|
+
ids = entries.map { |el| el[opts[:using]] }.compact
|
45
|
+
next unless ids.present?
|
42
46
|
to_inject = opts[:trigger].call(ids).map(&:with_indifferent_access)
|
43
47
|
key = opts[:as] || opts[:using].to_s.gsub(/_id$|Id$/, '')
|
44
48
|
entries.each do |entry|
|
45
49
|
id = entry.delete(opts[:using])
|
50
|
+
next unless id.present?
|
46
51
|
target = to_inject.find { |el| el[opts[:reference] || opts[:using]] == id }
|
47
52
|
entry[key] = target
|
48
53
|
end
|
@@ -51,12 +56,15 @@ module Hash19
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
|
-
|
55
59
|
def resolve_class(assoc_name)
|
56
60
|
full_class_name = self.class.name
|
57
61
|
new_class = full_class_name.gsub(full_class_name.demodulize, assoc_name)
|
58
62
|
new_class.split('::').inject(Object) do |mod, class_name|
|
59
|
-
mod.
|
63
|
+
if mod.const_defined?(class_name)
|
64
|
+
mod.const_get(class_name)
|
65
|
+
else
|
66
|
+
raise("Class:<#{new_class}> not defined! Unable to resolve association:<#{assoc_name.downcase}>")
|
67
|
+
end
|
60
68
|
end
|
61
69
|
end
|
62
70
|
|
data/lib/hash19/version.rb
CHANGED
data/spec/hash19/access_spec.rb
CHANGED
@@ -2,27 +2,30 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'Access Patterns for Hash19' do
|
4
4
|
|
5
|
-
class Hash1
|
6
|
-
include Hash19
|
7
|
-
attributes :a, :b, :c
|
8
|
-
end
|
9
|
-
|
10
5
|
it 'should be able to access hash keys using dot notation' do
|
6
|
+
|
7
|
+
class Hash1
|
8
|
+
include Hash19
|
9
|
+
attributes :a, :b, :c
|
10
|
+
end
|
11
|
+
|
11
12
|
hash1 = Hash1.new({a: 1, b: 2, c: 3})
|
12
13
|
expect(hash1[:a]).to eq(1)
|
13
14
|
expect(hash1[:b]).to eq(2)
|
14
15
|
expect(hash1[:c]).to eq(3)
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
it 'should be able to access keys recursively' do
|
19
|
+
|
20
|
+
class Hash11
|
21
|
+
include Hash19
|
22
|
+
attributes :x, :y
|
23
|
+
has_one :hash1, alias: :h
|
24
|
+
end
|
22
25
|
|
23
|
-
it 'should be able to work recursively' do
|
24
26
|
hash11 = Hash11.new(hash1: {a: 1, b: 2, c: 3}, x: -1, y: -2)
|
25
27
|
expect(hash11[:h][:a]).to eq(1)
|
28
|
+
expect(hash11[:y]).to eq(-2)
|
26
29
|
end
|
27
30
|
|
28
31
|
end
|
@@ -21,7 +21,19 @@ describe 'Associations' do
|
|
21
21
|
expect(parent.to_h).to eq('p' => 1, 'q' => 2, 'child' => {'x' => 1})
|
22
22
|
end
|
23
23
|
|
24
|
-
it 'should
|
24
|
+
it 'should flag error when target class not present' do
|
25
|
+
module Computer
|
26
|
+
class Laptop < Testable
|
27
|
+
attributes :model, :year
|
28
|
+
has_one :keyboard
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
expect { Computer::Laptop.new(model: 'MacBook Pro', year: 2013, keyboard: {keys: 101}) }.
|
33
|
+
to raise_error('Class:<Computer::Keyboard> not defined! Unable to resolve association:<keyboard>')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should support alternate key in payload for has_one' do
|
25
37
|
class OtherParent < Testable
|
26
38
|
attributes :x
|
27
39
|
has_one :child, key: :offspring, class: 'Child'
|
@@ -31,7 +43,7 @@ describe 'Associations' do
|
|
31
43
|
expect(parent.to_h).to eq('x' => true, 'child' => {'x' => 1})
|
32
44
|
end
|
33
45
|
|
34
|
-
it 'should support alias for has_one' do
|
46
|
+
it 'should support association alias for has_one' do
|
35
47
|
class AnotherParent < Testable
|
36
48
|
attributes :x
|
37
49
|
has_one :child, key: :offspring, alias: :junior
|
@@ -57,7 +69,7 @@ describe 'Associations' do
|
|
57
69
|
expect(bike.to_h).to eq('cc' => 150, 'wheels' => [{'flat' => false}, {'flat' => true}])
|
58
70
|
end
|
59
71
|
|
60
|
-
it 'should support key for has_many' do
|
72
|
+
it 'should support alternate key in payload for has_many' do
|
61
73
|
class OtherBike < Testable
|
62
74
|
attributes :cc
|
63
75
|
has_many :wheels, key: :discs
|
@@ -67,7 +79,7 @@ describe 'Associations' do
|
|
67
79
|
expect(bike.to_h).to eq('cc' => 150, 'wheels' => [{'flat' => false}, {'flat' => true}])
|
68
80
|
end
|
69
81
|
|
70
|
-
it 'should support alias for has_many' do
|
82
|
+
it 'should support association alias for has_many' do
|
71
83
|
class AnotherBike < Testable
|
72
84
|
attributes :cc
|
73
85
|
has_many :wheels, key: :discs, alias: :rings
|
@@ -91,8 +103,8 @@ describe 'Associations' do
|
|
91
103
|
end
|
92
104
|
|
93
105
|
it 'should be able to resolve has_many relationship within modules' do
|
94
|
-
duck = Bird::Duck.new(quack: true, feathers: [{light: true}, {light:
|
95
|
-
expect(duck.to_h).to eq('quack' => true, 'feathers' => [{'light' => true}, {'light' =>
|
106
|
+
duck = Bird::Duck.new(quack: true, feathers: [{light: true}, {light: false}])
|
107
|
+
expect(duck.to_h).to eq('quack' => true, 'feathers' => [{'light' => true}, {'light' => false}])
|
96
108
|
end
|
97
109
|
end
|
98
110
|
|
@@ -132,10 +144,21 @@ describe 'Associations' do
|
|
132
144
|
it 'should be able to call the trigger on has_one association' do
|
133
145
|
packet = UDPPacket.new(code: 500, error_id: 500)
|
134
146
|
expect(packet.to_h).to eq('code' => 500, 'error_id' => 500, 'all_errors' => [{'error_id' => 500, 'desc' => 'fatal error'},
|
135
|
-
|
147
|
+
{'error_id' => 404, 'desc' => 'not found'}])
|
136
148
|
end
|
137
149
|
|
138
150
|
end
|
139
151
|
|
152
|
+
context 'no id to lookup' do
|
153
|
+
it 'should not fail if association key is not present' do
|
154
|
+
class Jedi < Testable
|
155
|
+
attributes :name
|
156
|
+
has_one :padawan, using: :padawan_id, trigger: ->(id) { Padawan.find }
|
157
|
+
end
|
158
|
+
|
159
|
+
jedi = Jedi.new(name: 'Obi Wan Kenobi')
|
160
|
+
expect(jedi.to_h).to eq({"name" => 'Obi Wan Kenobi'})
|
161
|
+
end
|
162
|
+
end
|
140
163
|
|
141
164
|
end
|
@@ -37,5 +37,10 @@ describe 'Containers' do
|
|
37
37
|
{'name' => 'hulk', 'power' => 'bulk', 'weapon' => {'name' => 'hands', 'id' => 3}}])
|
38
38
|
end
|
39
39
|
|
40
|
+
it 'should not fail when container is empty' do
|
41
|
+
super_heroes = SuperHeroes.new([])
|
42
|
+
expect(super_heroes.to_h).to eq([])
|
43
|
+
end
|
44
|
+
|
40
45
|
end
|
41
46
|
end
|
data/spec/hash19/core_spec.rb
CHANGED
@@ -12,7 +12,6 @@ describe Hash19::Core do
|
|
12
12
|
attributes :a, :b, :c
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
15
|
it 'should be able to assign whitelisted attributes' do
|
17
16
|
test = Test1.new(a: 1, b: 2, d: 4)
|
18
17
|
expect(test.to_h).to eq('a' => 1, 'b' => 2)
|
@@ -23,32 +22,40 @@ describe Hash19::Core do
|
|
23
22
|
expect(test.to_h).to eq({})
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
class Test2 < Testable
|
31
|
-
attributes :a, :b, :c
|
32
|
-
attribute :fake, key: :actual
|
33
|
-
attribute :d
|
25
|
+
it 'should not break when an empty hash is passed' do
|
26
|
+
test = Test1.new({})
|
27
|
+
expect(test.to_h).to eq({})
|
34
28
|
end
|
35
29
|
|
36
|
-
it 'should
|
37
|
-
test =
|
38
|
-
expect(test.to_h).to eq(
|
30
|
+
it 'should not break when an empty array is passed' do
|
31
|
+
test = Test1.new([])
|
32
|
+
expect(test.to_h).to eq([])
|
39
33
|
end
|
40
34
|
|
41
|
-
|
42
|
-
test = Test2.new(actual: 1, a: 2, d: 3)
|
43
|
-
expect(test.to_h).to eq('fake' => 1, 'a' => 2, 'd' => 3)
|
44
|
-
end
|
35
|
+
end
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
context 'Single attribute and aliases' do
|
38
|
+
|
39
|
+
class Test2 < Testable
|
40
|
+
attributes :a, :b, :c
|
41
|
+
attribute :fake, key: :actual
|
42
|
+
attribute :d
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should be able to assign attributes based on alias' do
|
46
|
+
test = Test2.new(actual: 1)
|
47
|
+
expect(test.to_h).to eq('fake' => 1)
|
50
48
|
end
|
51
49
|
|
50
|
+
it 'should be able to use both attribute and attributes constructs' do
|
51
|
+
test = Test2.new(actual: 1, a: 2, d: 3)
|
52
|
+
expect(test.to_h).to eq('fake' => 1, 'a' => 2, 'd' => 3)
|
53
|
+
end
|
52
54
|
|
55
|
+
it 'should ignore alias if key not present' do
|
56
|
+
test = Test2.new(a: 2)
|
57
|
+
expect(test.to_h).to eq('a' => 2)
|
58
|
+
end
|
59
|
+
end
|
53
60
|
|
54
61
|
end
|
@@ -36,6 +36,27 @@ describe 'Injections' do
|
|
36
36
|
{'name' => 'Kanpai', 'eats' => {'id' => 3, 'name' => 'orange'}}
|
37
37
|
])
|
38
38
|
end
|
39
|
+
|
40
|
+
it "should not fail when no keys for injection present" do
|
41
|
+
gru_team = SuperVillain.new(name: 'Gru', minions: [{name: 'Poppadom'},
|
42
|
+
{name: 'Gelato'},
|
43
|
+
{name: 'Kanpai'}])
|
44
|
+
expect(gru_team.to_h).to eq('name' => 'Gru', 'minions' => [{'name' => 'Poppadom'},
|
45
|
+
{'name' => 'Gelato'},
|
46
|
+
{'name' => 'Kanpai'}
|
47
|
+
])
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should only map associations with keys" do
|
51
|
+
gru_team = SuperVillain.new(name: 'Gru', minions: [{name: 'Poppadom', fruit_id: 1},
|
52
|
+
{name: 'Gelato'},
|
53
|
+
{name: 'Kanpai'}])
|
54
|
+
expect(gru_team.to_h).to eq('name' => 'Gru', 'minions' => [{'name' => 'Poppadom', 'eats' => {'id' => 1, 'name' => 'banana'}},
|
55
|
+
{'name' => 'Gelato'},
|
56
|
+
{'name' => 'Kanpai'}
|
57
|
+
])
|
58
|
+
end
|
59
|
+
|
39
60
|
end
|
40
61
|
|
41
62
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash19
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|