hari 0.0.4 → 0.0.5
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/hari.gemspec +10 -2
- data/lib/hari.rb +8 -4
- data/lib/hari/configuration/redis.rb +6 -2
- data/lib/hari/entity.rb +10 -20
- data/lib/hari/entity/property.rb +19 -4
- data/lib/hari/entity/property/builder.rb +18 -3
- data/lib/hari/entity/repository.rb +11 -3
- data/lib/hari/entity/serialization.rb +53 -9
- data/lib/hari/entity/serialization/array.rb +21 -0
- data/lib/hari/entity/serialization/hash.rb +31 -0
- data/lib/hari/keys.rb +5 -0
- data/lib/hari/keys/hash.rb +67 -0
- data/lib/hari/keys/key.rb +24 -3
- data/lib/hari/keys/list.rb +10 -8
- data/lib/hari/keys/set.rb +8 -8
- data/lib/hari/keys/sorted_set.rb +20 -8
- data/lib/hari/node.rb +19 -2
- data/lib/hari/node/index.rb +152 -0
- data/lib/hari/node/queries.rb +32 -17
- data/lib/hari/node/queries/relation.rb +14 -1
- data/lib/hari/node/queries/relation/backend/sorted_set.rb +41 -90
- data/lib/hari/node/queries/relation/backend/sorted_set/count_step.rb +16 -0
- data/lib/hari/node/queries/relation/backend/sorted_set/node_step.rb +91 -0
- data/lib/hari/node/queries/type.rb +69 -4
- data/lib/hari/node/repository.rb +36 -0
- data/lib/hari/node/serialization.rb +11 -10
- data/lib/hari/object.rb +6 -0
- data/lib/hari/serialization.rb +3 -0
- data/lib/hari/version.rb +1 -1
- data/spec/hari/entity/repository_spec.rb +17 -0
- data/spec/hari/entity/serialization/hash_spec.rb +16 -0
- data/spec/hari/entity/serialization_spec.rb +14 -4
- data/spec/hari/keys/hash_spec.rb +55 -0
- data/spec/hari/keys/lists_spec.rb +27 -0
- data/spec/hari/node/index_spec.rb +199 -0
- data/spec/hari/node_spec.rb +84 -0
- data/spec/hari/serialization_spec.rb +41 -0
- data/spec/spec_helper.rb +6 -2
- metadata +27 -4
data/lib/hari/object.rb
ADDED
data/lib/hari/version.rb
CHANGED
@@ -17,6 +17,23 @@ describe Hari::Entity::Repository do
|
|
17
17
|
|
18
18
|
founds = TestEntity.find(model.id, model2.id)
|
19
19
|
founds.size.should == 2
|
20
|
+
|
21
|
+
model2.update_attributes birth: '2001-01-01', points: '403', friends_ids: [1, 3, 4]
|
22
|
+
|
23
|
+
found = TestEntity.find(model2.id)
|
24
|
+
found.birth.year.should eq(2001)
|
25
|
+
found.birth.month.should eq(1)
|
26
|
+
found.birth.day.should eq(1)
|
27
|
+
found.points.should eq(403)
|
28
|
+
found.friends_ids.should eq [1, 3, 4]
|
29
|
+
|
30
|
+
found.update_attributes friends_ids: [4, 5, 6]
|
31
|
+
|
32
|
+
TestEntity.find(found.id).friends_ids.should eq [4, 5, 6]
|
33
|
+
|
34
|
+
found.update_attribute :name, 'Joe'
|
35
|
+
|
36
|
+
TestEntity.find(found.id).name.should eq('Joe')
|
20
37
|
end
|
21
38
|
|
22
39
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Entity::Serialization::Hash do
|
4
|
+
|
5
|
+
specify '.serialize' do
|
6
|
+
subject.serialize(nil).should eq({})
|
7
|
+
subject.serialize('').should eq({})
|
8
|
+
subject.serialize(a: 1, b: 2).should eq(a: 1, b: 2)
|
9
|
+
|
10
|
+
struct = OpenStruct.new(a: 1, b: 2)
|
11
|
+
subject.serialize(struct).should eq(a: 1, b: 2)
|
12
|
+
|
13
|
+
expect { subject.serialize('notahash') }.to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -5,19 +5,29 @@ describe Hari::Entity::Serialization do
|
|
5
5
|
describe '#to_json' do
|
6
6
|
it 'serializes instance to json' do
|
7
7
|
model = TestEntity.new(name: 'Ze',
|
8
|
-
|
9
|
-
|
8
|
+
birth: Date.new(1986, 01, 23),
|
9
|
+
points: '200',
|
10
|
+
preferences: { soccer: 100, rugby: 30 },
|
11
|
+
friends_ids: [1, 2, 3])
|
10
12
|
|
11
|
-
model.to_json.should == '{"id":null,"created_at":null,"updated_at":null,
|
13
|
+
model.to_json.should == '{"id":null,"created_at":null,"updated_at":null,' +
|
14
|
+
'"name":"Ze","country":"US","birth":"1986-01-23","points":200,' +
|
15
|
+
'"preferences":{"soccer":100,"rugby":30},"friends_ids":[1,2,3],"male":true}'
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
19
|
describe '.from_json' do
|
16
20
|
it 'desserializes instance from json' do
|
17
|
-
model = TestEntity.from_json('{"name":"Ze","birth":"1986-01-23","points":200
|
21
|
+
model = TestEntity.from_json('{"name":"Ze","birth":"1986-01-23","points":200,' +
|
22
|
+
'"preferences":{"soccer":100,"rugby":30},"friends_ids":[1,2,3],"male":true}')
|
23
|
+
|
18
24
|
model.name.should == 'Ze'
|
19
25
|
model.birth.should == Date.new(1986, 01, 23)
|
20
26
|
model.points.should == 200
|
27
|
+
model.preferences.should eq('soccer' => 100, 'rugby' => 30)
|
28
|
+
model.friends_ids.should eq [1, 2, 3]
|
29
|
+
model.male.should be_true
|
30
|
+
model.male?.should be_true
|
21
31
|
end
|
22
32
|
end
|
23
33
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Keys::Hash do
|
4
|
+
|
5
|
+
let(:node) { Hari.node user: 10 }
|
6
|
+
subject { node.hash :preferences }
|
7
|
+
|
8
|
+
before do
|
9
|
+
subject[:genre] = 'afrobeat'
|
10
|
+
end
|
11
|
+
|
12
|
+
specify '#hash' do
|
13
|
+
node.hash.should be_a(Fixnum)
|
14
|
+
node.hash(:preferences).should be_a(Hari::Keys::Hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
specify '#hash! + #to_a' do
|
18
|
+
node.hash!(:preferences).should eq('genre' => 'afrobeat')
|
19
|
+
subject.to_h.should eq('genre' => 'afrobeat')
|
20
|
+
end
|
21
|
+
|
22
|
+
specify '#delete' do
|
23
|
+
subject.delete :genre
|
24
|
+
subject.to_h.should eq({})
|
25
|
+
end
|
26
|
+
|
27
|
+
specify '#key? + #[]' do
|
28
|
+
subject.key?(:genre).should be_true
|
29
|
+
subject.key?(:city).should be_false
|
30
|
+
subject.has_key?(:genre).should be_true
|
31
|
+
subject.member?(:genre).should be_true
|
32
|
+
|
33
|
+
subject[:genre].should eq('afrobeat')
|
34
|
+
end
|
35
|
+
|
36
|
+
specify '#keys + #values + #values_at' do
|
37
|
+
subject[:city] = 'Amsterdam'
|
38
|
+
subject.keys.sort.should eq %w(city genre)
|
39
|
+
subject.values.sort.should eq %w(Amsterdam afrobeat)
|
40
|
+
subject[:country] = 'Netherlands'
|
41
|
+
|
42
|
+
subject.values_at(:city, :country).should eq %w(Amsterdam Netherlands)
|
43
|
+
end
|
44
|
+
|
45
|
+
specify '#count + #merge!' do
|
46
|
+
subject.count.should eq(1)
|
47
|
+
subject.merge! one: 'more', and: 'another_one'
|
48
|
+
subject.keys.sort.should eq %w(and genre one)
|
49
|
+
|
50
|
+
subject.count.should eq(3)
|
51
|
+
subject.size.should eq(3)
|
52
|
+
subject.length.should eq(3)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -123,4 +123,31 @@ describe Hari::Keys::List do
|
|
123
123
|
subject.members.should eq %w(10 20 55 30 40 99 66 50 60)
|
124
124
|
end
|
125
125
|
|
126
|
+
specify 'with a type class' do
|
127
|
+
subject.delete!
|
128
|
+
friends = node.list(:friends, type: TestNode)
|
129
|
+
|
130
|
+
friends << TestNode.new(name: 'john')
|
131
|
+
friends << TestNode.new(name: 'josh')
|
132
|
+
friends << TestNode.new(name: 'jorj')
|
133
|
+
|
134
|
+
friends.to_a.map(&:name).should eq %w(john josh jorj)
|
135
|
+
|
136
|
+
friends[1].name.should eq('josh')
|
137
|
+
end
|
138
|
+
|
139
|
+
specify 'node attribution' do
|
140
|
+
builder = Class.new Hari::Node do
|
141
|
+
list :names
|
142
|
+
def self.node_type; 'list'; end
|
143
|
+
end
|
144
|
+
|
145
|
+
node = builder.create
|
146
|
+
|
147
|
+
node.names = []
|
148
|
+
node.names.should be_empty
|
149
|
+
node.names.push 'lol'
|
150
|
+
node.names!.should eq ['lol']
|
151
|
+
end
|
152
|
+
|
126
153
|
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Node::Index do
|
4
|
+
|
5
|
+
class Customer < Hari::Node
|
6
|
+
property :name
|
7
|
+
property :status, index: true
|
8
|
+
property :age, index: true
|
9
|
+
|
10
|
+
property :active, type: Boolean, default: false, index: true
|
11
|
+
end
|
12
|
+
|
13
|
+
let!(:joao) { Customer.create name: 'Joao', status: 'pending', age: '20' }
|
14
|
+
|
15
|
+
let!(:maria) { Customer.create name: 'Maria', status: 'pending', age: '21' }
|
16
|
+
|
17
|
+
let! :antonio do
|
18
|
+
Delorean.time_travel_to 20.minutes.ago do
|
19
|
+
Customer.create name: 'Antonio', status: 'pending', age: '21'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let! :joaquim do
|
24
|
+
Delorean.time_travel_to 40.minutes.ago do
|
25
|
+
Customer.create name: 'Joaquim', status: 'pending', age: '21'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
let! :manoel do
|
30
|
+
Delorean.time_travel_to 40.minutes.ago do
|
31
|
+
Customer.create name: 'Manoel', status: 'active', age: '21'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.find_by' do
|
36
|
+
subject { Customer.find_by :status, 'pending' }
|
37
|
+
|
38
|
+
context 'without from' do
|
39
|
+
its(:count) { should eq(4) }
|
40
|
+
|
41
|
+
it 'retrieves elements in right order' do
|
42
|
+
subject.to_a.map(&:name).should eq %w(Maria Joao Antonio Joaquim)
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'and with pagination' do
|
46
|
+
let(:query_paginated) { subject.limit 0, 2 }
|
47
|
+
|
48
|
+
specify 'count' do
|
49
|
+
query_paginated.count.should eq(4)
|
50
|
+
end
|
51
|
+
|
52
|
+
specify 'data' do
|
53
|
+
query_paginated.to_a.map(&:name).should eq %w(Maria Joao)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with from' do
|
59
|
+
let(:query) { subject.from 25.minutes.ago }
|
60
|
+
|
61
|
+
specify 'count' do
|
62
|
+
query.count.should eq(3)
|
63
|
+
end
|
64
|
+
|
65
|
+
specify 'data' do
|
66
|
+
query.to_a.map(&:name).should eq %w(Maria Joao Antonio)
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'and pagination' do
|
70
|
+
let(:query_paginated) { query.limit 0, 2 }
|
71
|
+
|
72
|
+
specify 'count' do
|
73
|
+
query_paginated.count.should eq(3)
|
74
|
+
end
|
75
|
+
|
76
|
+
specify 'data' do
|
77
|
+
query_paginated.to_a.map(&:name).should eq %w(Maria Joao)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.where' do
|
84
|
+
subject { Customer.where status: 'pending', age: '21', active: false }
|
85
|
+
|
86
|
+
context 'without from' do
|
87
|
+
its(:count) { should eq(3) }
|
88
|
+
|
89
|
+
it 'retrieves elements in right order' do
|
90
|
+
subject.to_a.map(&:name).should eq %w(Maria Antonio Joaquim)
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'and with pagination' do
|
94
|
+
let(:query_paginated) { subject.limit 0, 2 }
|
95
|
+
|
96
|
+
specify 'count' do
|
97
|
+
query_paginated.count.should eq(3)
|
98
|
+
end
|
99
|
+
|
100
|
+
specify 'data' do
|
101
|
+
query_paginated.to_a.map(&:name).should eq %w(Maria Antonio)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'after change attribute' do
|
106
|
+
before { joao.update_attribute :active, true }
|
107
|
+
subject { Customer.where status: 'pending', active: true }
|
108
|
+
|
109
|
+
specify 'count' do
|
110
|
+
subject.count.should eq(1)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'querying nil value' do
|
115
|
+
let!(:gustavo) { Customer.create name: 'Gustavo', age: '2', status: nil }
|
116
|
+
|
117
|
+
subject { Customer.where status: nil, age: '2' }
|
118
|
+
|
119
|
+
specify 'data' do
|
120
|
+
subject.to_a.first.name.should eq(gustavo.name)
|
121
|
+
end
|
122
|
+
|
123
|
+
specify 'count' do
|
124
|
+
subject.count.should eq(1)
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'change attribute to nil' do
|
128
|
+
before { gustavo.update_attribute :age, nil }
|
129
|
+
subject { Customer.where status: nil, age: nil }
|
130
|
+
|
131
|
+
specify 'count' do
|
132
|
+
subject.count.should eq(1)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'not found' do
|
138
|
+
subject { Customer.where status: 'inexistent' }
|
139
|
+
|
140
|
+
it 'returns empty array' do
|
141
|
+
subject.to_a.should be_empty
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'with from' do
|
147
|
+
let(:query) { subject.from 25.minutes.ago }
|
148
|
+
|
149
|
+
specify 'count' do
|
150
|
+
query.count.should eq(2)
|
151
|
+
end
|
152
|
+
|
153
|
+
specify 'data' do
|
154
|
+
query.to_a.map(&:name).should eq %w(Maria Antonio)
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'and pagination' do
|
158
|
+
let(:query_paginated) { query.limit 0, 1 }
|
159
|
+
|
160
|
+
specify 'count' do
|
161
|
+
query_paginated.count.should eq(2)
|
162
|
+
end
|
163
|
+
|
164
|
+
specify 'data' do
|
165
|
+
query_paginated.to_a.first.name.should eq 'Maria'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe 'from a type query' do
|
172
|
+
before do
|
173
|
+
joao.out(:follow) << [antonio, maria, joaquim, manoel]
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'queries users' do
|
177
|
+
active = joao.out(:follow).type(:customer).where(status: 'active')
|
178
|
+
active.count.should eq(1)
|
179
|
+
active.to_a.first.name.should eq('Manoel')
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'index update' do
|
184
|
+
before do
|
185
|
+
Customer.where(active: false).to_a.each do |node|
|
186
|
+
node.update_attribute :active, true
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'does not bring in old index' do
|
191
|
+
Customer.where(active: false).to_a.should be_empty
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'brings in the new index' do
|
195
|
+
Customer.where(active: true).count.should eq(5)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
data/spec/hari/node_spec.rb
CHANGED
@@ -45,6 +45,10 @@ describe Hari::Node do
|
|
45
45
|
it 'can chain queries' do
|
46
46
|
followers_following = joao.out(:follow).out(:follow).to_a
|
47
47
|
followers_following.map(&:id).sort.should eq [raimundo, maria, joaquim].map(&:id).sort
|
48
|
+
|
49
|
+
joao.out(:follow).out(:follow).count.should eq(4)
|
50
|
+
|
51
|
+
joao.out(:follow).out(:follow).first.name.should eq('Joaquim')
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
@@ -85,6 +89,12 @@ describe Hari::Node do
|
|
85
89
|
|
86
90
|
following = teresa.out(:follow).out(:follow).limit(1).from(15.minutes.ago.to_f, 'down')
|
87
91
|
following.to_a.map(&:id).sort.should eq [maria.id]
|
92
|
+
|
93
|
+
following = teresa.out(:follow).out(:follow).limit(2)
|
94
|
+
following.to_a.map(&:id).sort.should eq [teresa.id, raimundo.id]
|
95
|
+
|
96
|
+
following = teresa.out(:follow).out(:follow).limit(3)
|
97
|
+
following.to_a.map(&:id).sort.should eq [teresa.id, raimundo.id, maria.id]
|
88
98
|
end
|
89
99
|
end
|
90
100
|
|
@@ -119,6 +129,80 @@ describe Hari::Node do
|
|
119
129
|
|
120
130
|
fans.sort_by(friends).take(5).should eq %w(7 6 5 4 1)
|
121
131
|
end
|
132
|
+
|
133
|
+
it 'has count' do
|
134
|
+
14.times { Hari.relation! :follow, 'user#2', "user##{SecureRandom.hex(2)}" }
|
135
|
+
3.times { Hari.relation! :follow, 'user#2', "celeb##{SecureRandom.hex(2)}"}
|
136
|
+
|
137
|
+
Hari('user#2').out(:follow).type(:user).count.should eq(15)
|
138
|
+
Hari('user#1').in(:follow).type(:user).count.should eq(1)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'can list nodes ids' do
|
142
|
+
Hari.relation! :follow, 'user#1', 'celeb#y'
|
143
|
+
Hari.relation! :follow, 'user#1', 'celeb#z'
|
144
|
+
Hari.relation! :follow, 'user#1', 'celeb#w'
|
145
|
+
|
146
|
+
Hari(user: 1).out(:follow) << 'celeb#k'
|
147
|
+
Hari(user: 1).out(:follow) << %w(celeb#a celeb#b)
|
148
|
+
|
149
|
+
user_celebs = Hari('user#1').out(:follow).type(:celeb)
|
150
|
+
|
151
|
+
celebs = user_celebs.nodes_ids
|
152
|
+
celebs.should eq %w(celeb#b celeb#a celeb#k celeb#w celeb#z celeb#y celeb#x)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'can list relations ids' do
|
156
|
+
Hari.relation! :follow, 'user#1', 'celeb#y'
|
157
|
+
Hari.relation! :follow, 'user#1', 'celeb#z'
|
158
|
+
Hari.relation! :follow, 'user#1', 'celeb#w'
|
159
|
+
|
160
|
+
relations = Hari('user#1').out(:follow).type(:celeb).relations_ids
|
161
|
+
relations.should eq %w(user#1:follow:celeb#w
|
162
|
+
user#1:follow:celeb#z
|
163
|
+
user#1:follow:celeb#y
|
164
|
+
user#1:follow:celeb#x)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'can list nodes' do
|
168
|
+
followings = [teresa, raimundo, maria, joaquim, lili]
|
169
|
+
x = 5
|
170
|
+
|
171
|
+
followings.each do |following|
|
172
|
+
Delorean.time_travel_to x.minutes.ago do
|
173
|
+
Hari.relation! :follow, joao, following
|
174
|
+
end
|
175
|
+
|
176
|
+
x += 5
|
177
|
+
end
|
178
|
+
|
179
|
+
people = joao.out(:follow).type(:test_node).nodes
|
180
|
+
people[0].name.should eq('Teresa')
|
181
|
+
people[1].name.should eq('Raimundo')
|
182
|
+
people[2].name.should eq('Maria')
|
183
|
+
people[3].name.should eq('Joaquim')
|
184
|
+
people[4].name.should eq('Lili')
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'paginates' do
|
188
|
+
30.times do |i|
|
189
|
+
Hari.relation! :follow, 'user#42', "celeb##{i + 1}"
|
190
|
+
end
|
191
|
+
|
192
|
+
query = Hari('user#42').out(:follow).type(:celeb).limit(13, 16)
|
193
|
+
query.ids.should eq %w(17 16 15 14)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'creates new relations by type' do
|
197
|
+
followers = Hari(user: 2).out(:follow)
|
198
|
+
followers.type(:user) << '13'
|
199
|
+
followers.nodes_ids!.include?('user#13').should be_true
|
200
|
+
|
201
|
+
followers.type(:user) << ['15', '16']
|
202
|
+
followers.nodes_ids!.include?('user#15').should be_true
|
203
|
+
followers.nodes_ids!.include?('user#16').should be_true
|
204
|
+
end
|
205
|
+
|
122
206
|
end
|
123
207
|
|
124
208
|
end
|