hari 0.0.1 → 0.0.3
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 +15 -0
- data/.travis.yml +15 -0
- data/LICENSE +15 -19
- data/README.md +22 -0
- data/hari.gemspec +2 -1
- data/lib/hari.rb +56 -1
- data/lib/hari/entity.rb +8 -11
- data/lib/hari/entity/property.rb +2 -2
- data/lib/hari/entity/property/builder.rb +9 -1
- data/lib/hari/entity/repository.rb +3 -2
- data/lib/hari/entity/serialization/float.rb +2 -0
- data/lib/hari/entity/serialization/integer.rb +3 -10
- data/lib/hari/node.rb +20 -3
- data/lib/hari/node/queries.rb +33 -0
- data/lib/hari/node/queries/list.rb +147 -0
- data/lib/hari/node/queries/relation.rb +100 -0
- data/lib/hari/node/queries/relation/backend/list.rb +35 -0
- data/lib/hari/node/queries/relation/backend/sorted_set.rb +95 -0
- data/lib/hari/node/queries/relation/runnable.rb +24 -0
- data/lib/hari/node/queries/relation/start.rb +19 -0
- data/lib/hari/node/queries/relation/step.rb +17 -0
- data/lib/hari/node/queries/set.rb +116 -0
- data/lib/hari/node/queries/sorted_set.rb +130 -0
- data/lib/hari/node/repository.rb +24 -0
- data/lib/hari/node/serialization.rb +24 -0
- data/lib/hari/relation.rb +72 -0
- data/lib/hari/relation/linked_list.rb +16 -0
- data/lib/hari/relation/sorted_set.rb +16 -0
- data/lib/hari/version.rb +1 -1
- data/spec/hari/configuration_spec.rb +5 -0
- data/spec/hari/entity/repository_spec.rb +4 -4
- data/spec/hari/entity/serialization_spec.rb +2 -2
- data/spec/hari/node/lists_spec.rb +126 -0
- data/spec/hari/node/sets_spec.rb +85 -0
- data/spec/hari/node/sorted_sets_spec.rb +89 -0
- data/spec/hari/node_spec.rb +79 -0
- data/spec/hari_spec.rb +37 -0
- data/spec/spec_helper.rb +11 -1
- metadata +48 -32
- data/lib/hari/relationship.rb +0 -72
- data/lib/hari/relationship/linked_list.rb +0 -22
- data/lib/hari/relationship/sorted_set.rb +0 -22
- data/lib/hari/scripts.rb +0 -26
@@ -0,0 +1,24 @@
|
|
1
|
+
module Hari
|
2
|
+
class Node < Entity
|
3
|
+
module Repository
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def find_one(id, options = {})
|
9
|
+
id = "#{node_type}##{id}" unless id.to_s.include?('#')
|
10
|
+
super id, options
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_many(ids, options = {})
|
14
|
+
ids = ids.map do |id|
|
15
|
+
id.to_s.include?('#') ? id : "#{node_type}##{id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
super ids, options
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Hari
|
2
|
+
class Node < Entity
|
3
|
+
module Serialization
|
4
|
+
|
5
|
+
def from_source(source)
|
6
|
+
return if source.blank?
|
7
|
+
|
8
|
+
hash = Yajl::Parser.parse(source)
|
9
|
+
klass = hash['id'].split('#').first.camelize.constantize
|
10
|
+
|
11
|
+
attrs = hash.inject({}) do |buffer, (key, value)|
|
12
|
+
if prop = klass.properties.find { |p| p.name == key }
|
13
|
+
buffer[key] = prop.desserialize(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
buffer
|
17
|
+
end
|
18
|
+
|
19
|
+
klass.new attrs
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'hari/relation/linked_list'
|
2
|
+
require 'hari/relation/sorted_set'
|
3
|
+
|
4
|
+
module Hari
|
5
|
+
class Relation < Entity
|
6
|
+
|
7
|
+
DIRECTIONS = %w(in out)
|
8
|
+
|
9
|
+
attr_accessor :label, :start_node_id, :end_node_id
|
10
|
+
|
11
|
+
validates :label, :start_node_id, :end_node_id, presence: true
|
12
|
+
|
13
|
+
def start_node
|
14
|
+
@start_node ||= Hari::Node.find(start_node_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def end_node
|
18
|
+
@end_node ||= Hari::Node.find(end_node_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def key(direction = nil)
|
22
|
+
case direction.try :to_s
|
23
|
+
when nil then "#{start_node_id}:#{label}:#{end_node_id}"
|
24
|
+
when 'out' then "#{start_node_id}:#{label}:out"
|
25
|
+
when 'in' then "#{end_node_id}:#{label}:in"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :generate_id :key
|
30
|
+
|
31
|
+
def self.create(label, start_node, end_node, attrs = {})
|
32
|
+
new(attrs).tap do |r|
|
33
|
+
r.label = label
|
34
|
+
r.start_node_id = Hari.node_key(start_node)
|
35
|
+
r.end_node_id = Hari.node_key(end_node)
|
36
|
+
r.save
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.use!(backend)
|
41
|
+
@backend = backend.kind_of?(Module) ? backend
|
42
|
+
: "Hari::Relation::#{backend.to_s.camelize}".constantize
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.backend
|
46
|
+
@backend ||= Hari::Relation::SortedSet
|
47
|
+
end
|
48
|
+
|
49
|
+
def backend
|
50
|
+
self.class.backend
|
51
|
+
end
|
52
|
+
|
53
|
+
def weight(direction)
|
54
|
+
::Time.now.to_f
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def create
|
60
|
+
super
|
61
|
+
backend.create self
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete
|
66
|
+
backend.delete self
|
67
|
+
super
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hari
|
2
|
+
class Relation < Entity
|
3
|
+
module LinkedList
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def create(rel)
|
7
|
+
Relation::DIRECTIONS.each { |d| Hari.redis.lpush rel.key(d), rel.id }
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete(rel)
|
11
|
+
Relation::DIRECTIONS.each { |d| Hari.redis.lrem rel.key(d), 1, rel.id }
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hari
|
2
|
+
class Relation < Entity
|
3
|
+
module SortedSet
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def create(rel)
|
7
|
+
Relation::DIRECTIONS.each { |d| Hari.redis.zadd rel.key(d), rel.weight(d), rel.id }
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete(rel)
|
11
|
+
Relation::DIRECTIONS.each { |d| Hari.redis.zrem rel.key(d), rel.id }
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/hari/version.rb
CHANGED
@@ -3,19 +3,19 @@ require 'spec_helper'
|
|
3
3
|
describe Hari::Entity::Repository do
|
4
4
|
|
5
5
|
specify '.create, .find' do
|
6
|
-
model =
|
6
|
+
model = TestEntity.create(name: 'Ze', birth: '2012-04-20', points: '300')
|
7
7
|
model.id.should be
|
8
8
|
|
9
|
-
found =
|
9
|
+
found = TestEntity.find(model.id)
|
10
10
|
found.birth.year.should == 2012
|
11
11
|
found.birth.month.should == 4
|
12
12
|
found.birth.day.should == 20
|
13
13
|
found.points.should == 300
|
14
14
|
found.name.should == 'Ze'
|
15
15
|
|
16
|
-
model2 =
|
16
|
+
model2 = TestEntity.create(name: 'Jo', birth: '2009-03-21', points: '404')
|
17
17
|
|
18
|
-
founds =
|
18
|
+
founds = TestEntity.find(model.id, model2.id)
|
19
19
|
founds.size.should == 2
|
20
20
|
end
|
21
21
|
|
@@ -4,7 +4,7 @@ describe Hari::Entity::Serialization do
|
|
4
4
|
|
5
5
|
describe '#to_json' do
|
6
6
|
it 'serializes instance to json' do
|
7
|
-
model =
|
7
|
+
model = TestEntity.new(name: 'Ze',
|
8
8
|
birth: Date.new(1986, 01, 23),
|
9
9
|
points: '200')
|
10
10
|
|
@@ -14,7 +14,7 @@ describe Hari::Entity::Serialization do
|
|
14
14
|
|
15
15
|
describe '.from_json' do
|
16
16
|
it 'desserializes instance from json' do
|
17
|
-
model =
|
17
|
+
model = TestEntity.from_json('{"name":"Ze","birth":"1986-01-23","points":200}')
|
18
18
|
model.name.should == 'Ze'
|
19
19
|
model.birth.should == Date.new(1986, 01, 23)
|
20
20
|
model.points.should == 200
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Node::Queries::List do
|
4
|
+
|
5
|
+
let(:node) { Hari.node user: 10 }
|
6
|
+
subject { node.list :friends }
|
7
|
+
|
8
|
+
before { subject.add 10, 20, 30, 40, 50, 60 }
|
9
|
+
|
10
|
+
specify '#name' do
|
11
|
+
subject.name.should eq(:friends)
|
12
|
+
end
|
13
|
+
|
14
|
+
specify '#range + #members + #list! + ' +
|
15
|
+
'#[i] + #[i, j], + #[i..j] + ' +
|
16
|
+
'#at + #index + #from + #to' do
|
17
|
+
node.list!(:friends).should eq %w(10 20 30 40 50 60)
|
18
|
+
subject.members.should eq %w(10 20 30 40 50 60)
|
19
|
+
subject.range.should eq %w(10 20 30 40 50 60)
|
20
|
+
|
21
|
+
subject.range(1, 3).should eq %w(20 30 40)
|
22
|
+
subject[1, 3].should eq %w(20 30 40)
|
23
|
+
subject[1..3].should eq %w(20 30 40)
|
24
|
+
subject[1].should eq('20')
|
25
|
+
|
26
|
+
subject.at(1).should eq('20')
|
27
|
+
subject.index(1).should eq('20')
|
28
|
+
subject.from(2).should eq %w(30 40 50 60)
|
29
|
+
subject.to(3).should eq %w(10 20 30 40)
|
30
|
+
end
|
31
|
+
|
32
|
+
specify '#include? + #member?' do
|
33
|
+
subject.include?('20').should be_true
|
34
|
+
subject.member?('20').should be_true
|
35
|
+
subject.include?('70').should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
specify '#count + #size + #length' do
|
39
|
+
subject.count.should eq(6)
|
40
|
+
subject.size.should eq(6)
|
41
|
+
subject.length.should eq(6)
|
42
|
+
end
|
43
|
+
|
44
|
+
specify '#one? + #many?' do
|
45
|
+
subject.should_not be_one
|
46
|
+
subject.should be_many
|
47
|
+
|
48
|
+
list = node.list(:enemies)
|
49
|
+
list.add 90
|
50
|
+
list.should be_one
|
51
|
+
list.should_not be_many
|
52
|
+
end
|
53
|
+
|
54
|
+
specify '#empty?' do
|
55
|
+
subject.should_not be_empty
|
56
|
+
node.list(:wtfs).should be_empty
|
57
|
+
end
|
58
|
+
|
59
|
+
specify '#delete' do
|
60
|
+
subject.delete 30
|
61
|
+
subject.members.should eq %w(10 20 40 50 60)
|
62
|
+
end
|
63
|
+
|
64
|
+
specify '#<<' do
|
65
|
+
subject << 90
|
66
|
+
subject.include?('90').should be_true
|
67
|
+
subject.pop.should eq('90')
|
68
|
+
end
|
69
|
+
|
70
|
+
specify '#pop + #rpop + #shift + #lpop' do
|
71
|
+
subject.pop.should eq('60')
|
72
|
+
subject.count.should eq(5)
|
73
|
+
subject.include?('60').should be_false
|
74
|
+
|
75
|
+
subject.rpop.should eq('50')
|
76
|
+
subject.count.should eq(4)
|
77
|
+
subject.include?('50').should be_false
|
78
|
+
|
79
|
+
subject.lpop.should eq('10')
|
80
|
+
subject.count.should eq(3)
|
81
|
+
subject.include?('10').should be_false
|
82
|
+
|
83
|
+
subject.shift.should eq('20')
|
84
|
+
subject.count.should eq(2)
|
85
|
+
subject.include?('20').should be_false
|
86
|
+
end
|
87
|
+
|
88
|
+
specify '#<< + #push + #rpush + #add + #lpush + #first + #last' do
|
89
|
+
subject << 80
|
90
|
+
subject.last.should eq('80')
|
91
|
+
|
92
|
+
subject.push 90
|
93
|
+
subject.last.should eq('90')
|
94
|
+
|
95
|
+
subject.rpush 100
|
96
|
+
subject.last.should eq('100')
|
97
|
+
|
98
|
+
subject.add 200
|
99
|
+
subject.last.should eq('200')
|
100
|
+
|
101
|
+
subject.lpush 300
|
102
|
+
subject.first.should eq('300')
|
103
|
+
end
|
104
|
+
|
105
|
+
specify '#[]=' do
|
106
|
+
subject[1] = 77
|
107
|
+
subject.members.should eq %w(10 77 30 40 50 60)
|
108
|
+
end
|
109
|
+
|
110
|
+
specify '#trim' do
|
111
|
+
subject.trim 2, 4
|
112
|
+
subject.members.should eq %w(30 40 50)
|
113
|
+
end
|
114
|
+
|
115
|
+
specify '#insert + #insert_before + #insert_after' do
|
116
|
+
subject.insert '20', '55'
|
117
|
+
subject.members.should eq %w(10 20 55 30 40 50 60)
|
118
|
+
|
119
|
+
subject.insert_after '40', '66'
|
120
|
+
subject.members.should eq %w(10 20 55 30 40 66 50 60)
|
121
|
+
|
122
|
+
subject.insert_before '66', '99'
|
123
|
+
subject.members.should eq %w(10 20 55 30 40 99 66 50 60)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Node::Queries::Set do
|
4
|
+
|
5
|
+
let(:node) { Hari.node user: 10 }
|
6
|
+
subject { node.set :friends }
|
7
|
+
|
8
|
+
before { subject.add 10, 20, 30 }
|
9
|
+
|
10
|
+
specify '#name' do
|
11
|
+
subject.name.should eq(:friends)
|
12
|
+
end
|
13
|
+
|
14
|
+
specify '#members + #set!' do
|
15
|
+
node.set!(:friends).should eq %w(10 20 30)
|
16
|
+
subject.members.should eq %w(10 20 30)
|
17
|
+
end
|
18
|
+
|
19
|
+
specify '#include? + #member?' do
|
20
|
+
subject.include?(20).should be_true
|
21
|
+
subject.member?(20).should be_true
|
22
|
+
subject.include?(40).should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
specify '#count + #size + #length' do
|
26
|
+
subject.count.should eq(3)
|
27
|
+
subject.size.should eq(3)
|
28
|
+
subject.length.should eq(3)
|
29
|
+
end
|
30
|
+
|
31
|
+
specify '#one? + #many?' do
|
32
|
+
subject.should_not be_one
|
33
|
+
subject.should be_many
|
34
|
+
|
35
|
+
set = node.set(:enemies)
|
36
|
+
set.add 90
|
37
|
+
set.should be_one
|
38
|
+
set.should_not be_many
|
39
|
+
end
|
40
|
+
|
41
|
+
specify '#empty?' do
|
42
|
+
subject.should_not be_empty
|
43
|
+
node.set(:wtfs).should be_empty
|
44
|
+
end
|
45
|
+
|
46
|
+
specify '#delete' do
|
47
|
+
subject.delete 30, 40
|
48
|
+
subject.members.should eq %w(10 20)
|
49
|
+
end
|
50
|
+
|
51
|
+
specify '#<<' do
|
52
|
+
subject << 40
|
53
|
+
subject.include?(40).should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
specify '#rand' do
|
57
|
+
%w(10 20 30).include?(subject.rand.first).should be_true
|
58
|
+
subject.rand(3).sort.should eq %w(10 20 30)
|
59
|
+
subject.count.should eq(3)
|
60
|
+
end
|
61
|
+
|
62
|
+
specify '#intersect + #&' do
|
63
|
+
other_friends = Hari.node(user: 20).set(:friends)
|
64
|
+
other_friends.add 20, 30, 40, 50
|
65
|
+
|
66
|
+
(subject & other_friends).sort.should eq %w(20 30)
|
67
|
+
subject.intersect(other_friends).sort.should eq %w(20 30)
|
68
|
+
end
|
69
|
+
|
70
|
+
specify '#diff + #-' do
|
71
|
+
other_friends = Hari.node(user: 20).set(:friends)
|
72
|
+
other_friends.add 20, 30, 40, 50
|
73
|
+
|
74
|
+
(subject - other_friends).sort.should eq %w(10)
|
75
|
+
subject.diff(other_friends).sort.should eq %w(10)
|
76
|
+
end
|
77
|
+
|
78
|
+
specify '#pop' do
|
79
|
+
member = subject.pop
|
80
|
+
%w(10 20 30).include?(member).should be_true
|
81
|
+
subject.count.should eq(2)
|
82
|
+
subject.members.include?(member).should be_false
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hari::Node::Queries::SortedSet do
|
4
|
+
|
5
|
+
let(:node) { Hari.node user: 10 }
|
6
|
+
subject { node.sorted_set :friends }
|
7
|
+
|
8
|
+
before { subject.add 10, 'john', 30, 'bill', 50, 'jack' }
|
9
|
+
|
10
|
+
specify '#name' do
|
11
|
+
subject.name.should eq(:friends)
|
12
|
+
end
|
13
|
+
|
14
|
+
specify '#range + #range_with_scores + #members + #sorted_set!' do
|
15
|
+
node.sorted_set!(:friends).should eq %w(john bill jack)
|
16
|
+
subject.members.should eq %w(john bill jack)
|
17
|
+
subject.range.should eq %w(john bill jack)
|
18
|
+
|
19
|
+
subject.range(0, 1).should eq %w(john bill)
|
20
|
+
|
21
|
+
subject.range_with_scores.should eq [['john', 10.0], ['bill', 30.0], ['jack', 50.0]]
|
22
|
+
subject.range(0, 1, with_scores: true).should eq [['john', 10.0], ['bill', 30.0]]
|
23
|
+
|
24
|
+
subject.revrange(0, 1).should eq %w(jack bill)
|
25
|
+
subject.revrange.should eq %w(jack bill john)
|
26
|
+
subject.reverse_range.should eq %w(jack bill john)
|
27
|
+
subject.desc_range.should eq %w(jack bill john)
|
28
|
+
|
29
|
+
subject.revrange_with_scores.should eq [['jack', 50.0], ['bill', 30.0], ['john', 10.0]]
|
30
|
+
end
|
31
|
+
|
32
|
+
specify '#include? + #member?' do
|
33
|
+
subject.include?('jack').should be_true
|
34
|
+
subject.member?('bill').should be_true
|
35
|
+
subject.include?('steve').should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
specify '#count + #size + #length' do
|
39
|
+
subject.count.should eq(3)
|
40
|
+
subject.size.should eq(3)
|
41
|
+
subject.length.should eq(3)
|
42
|
+
end
|
43
|
+
|
44
|
+
specify '#one? + #many?' do
|
45
|
+
subject.should_not be_one
|
46
|
+
subject.should be_many
|
47
|
+
|
48
|
+
zset = node.sorted_set(:enemies)
|
49
|
+
zset.add 70, 'jill'
|
50
|
+
zset.should be_one
|
51
|
+
zset.should_not be_many
|
52
|
+
end
|
53
|
+
|
54
|
+
specify '#empty?' do
|
55
|
+
subject.should_not be_empty
|
56
|
+
node.sorted_set(:wtfs).should be_empty
|
57
|
+
end
|
58
|
+
|
59
|
+
specify '#delete' do
|
60
|
+
subject.delete 'jack', 'bill', 'jill'
|
61
|
+
subject.members.should eq %w(john)
|
62
|
+
end
|
63
|
+
|
64
|
+
specify '#score' do
|
65
|
+
subject.score('john').should eq(10.0)
|
66
|
+
subject.score('steve').should_not be
|
67
|
+
end
|
68
|
+
|
69
|
+
specify '#rank + #revrank' do
|
70
|
+
subject.rank('john').should eq(0)
|
71
|
+
subject.ranking('bill').should eq(1)
|
72
|
+
subject.position('jack').should eq(2)
|
73
|
+
|
74
|
+
subject.revrank('john').should eq(2)
|
75
|
+
subject.reverse_ranking('bill').should eq(1)
|
76
|
+
subject.reverse_position('jack').should eq(0)
|
77
|
+
end
|
78
|
+
|
79
|
+
specify '#trim_by_rank' do
|
80
|
+
subject.trim_by_rank 1, 1
|
81
|
+
subject.members.should eq %w(john jack)
|
82
|
+
end
|
83
|
+
|
84
|
+
specify '#trim_by_score' do
|
85
|
+
subject.trim_by_score 5, 15
|
86
|
+
subject.members.should eq %w(bill jack)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|