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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/hari.gemspec +10 -2
  3. data/lib/hari.rb +8 -4
  4. data/lib/hari/configuration/redis.rb +6 -2
  5. data/lib/hari/entity.rb +10 -20
  6. data/lib/hari/entity/property.rb +19 -4
  7. data/lib/hari/entity/property/builder.rb +18 -3
  8. data/lib/hari/entity/repository.rb +11 -3
  9. data/lib/hari/entity/serialization.rb +53 -9
  10. data/lib/hari/entity/serialization/array.rb +21 -0
  11. data/lib/hari/entity/serialization/hash.rb +31 -0
  12. data/lib/hari/keys.rb +5 -0
  13. data/lib/hari/keys/hash.rb +67 -0
  14. data/lib/hari/keys/key.rb +24 -3
  15. data/lib/hari/keys/list.rb +10 -8
  16. data/lib/hari/keys/set.rb +8 -8
  17. data/lib/hari/keys/sorted_set.rb +20 -8
  18. data/lib/hari/node.rb +19 -2
  19. data/lib/hari/node/index.rb +152 -0
  20. data/lib/hari/node/queries.rb +32 -17
  21. data/lib/hari/node/queries/relation.rb +14 -1
  22. data/lib/hari/node/queries/relation/backend/sorted_set.rb +41 -90
  23. data/lib/hari/node/queries/relation/backend/sorted_set/count_step.rb +16 -0
  24. data/lib/hari/node/queries/relation/backend/sorted_set/node_step.rb +91 -0
  25. data/lib/hari/node/queries/type.rb +69 -4
  26. data/lib/hari/node/repository.rb +36 -0
  27. data/lib/hari/node/serialization.rb +11 -10
  28. data/lib/hari/object.rb +6 -0
  29. data/lib/hari/serialization.rb +3 -0
  30. data/lib/hari/version.rb +1 -1
  31. data/spec/hari/entity/repository_spec.rb +17 -0
  32. data/spec/hari/entity/serialization/hash_spec.rb +16 -0
  33. data/spec/hari/entity/serialization_spec.rb +14 -4
  34. data/spec/hari/keys/hash_spec.rb +55 -0
  35. data/spec/hari/keys/lists_spec.rb +27 -0
  36. data/spec/hari/node/index_spec.rb +199 -0
  37. data/spec/hari/node_spec.rb +84 -0
  38. data/spec/hari/serialization_spec.rb +41 -0
  39. data/spec/spec_helper.rb +6 -2
  40. metadata +27 -4
@@ -0,0 +1,6 @@
1
+ module Hari
2
+ class Object
3
+ include Hari::Serialization
4
+
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Hari
2
+ Serialization = Hari::Entity::Serialization
3
+ end
@@ -1,3 +1,3 @@
1
1
  module Hari
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.5'
3
3
  end
@@ -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
- birth: Date.new(1986, 01, 23),
9
- points: '200')
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,"name":"Ze","birth":"1986-01-23","points":200}'
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
@@ -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