hari 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|