rico 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -1
- data/Guardfile +5 -0
- data/lib/rico/array.rb +31 -9
- data/lib/rico/object.rb +7 -2
- data/lib/rico/resolver.rb +18 -0
- data/lib/rico/value.rb +14 -1
- data/lib/rico/version.rb +1 -1
- data/lib/rico.rb +12 -0
- data/rico.gemspec +1 -0
- data/spec/array_spec.rb +23 -0
- data/spec/resolver_spec.rb +45 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/value_spec.rb +40 -0
- metadata +22 -2
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--color
|
1
|
+
--tty --color --format nested
|
data/Guardfile
ADDED
data/lib/rico/array.rb
CHANGED
@@ -4,7 +4,7 @@ module Rico
|
|
4
4
|
include Enumerable
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
def_delegators :members, :each, :[]
|
7
|
+
def_delegators :members, :each, :[], :length, :count
|
8
8
|
|
9
9
|
public
|
10
10
|
|
@@ -14,7 +14,7 @@ module Rico
|
|
14
14
|
#
|
15
15
|
# Returns the result of the store operation
|
16
16
|
def add(*items)
|
17
|
-
mutate
|
17
|
+
mutate build_map_add(items)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Removes the requested items from the array and stores the object
|
@@ -23,14 +23,14 @@ module Rico
|
|
23
23
|
#
|
24
24
|
# Returns the result of the store operation
|
25
25
|
def remove(*items)
|
26
|
-
mutate
|
26
|
+
mutate build_map_remove(items)
|
27
27
|
end
|
28
28
|
|
29
29
|
# Obtains the items in the array
|
30
30
|
#
|
31
31
|
# Returns the data in the object as an array
|
32
32
|
def members
|
33
|
-
Array(data)
|
33
|
+
Array((data || {})["_values"])
|
34
34
|
end
|
35
35
|
|
36
36
|
# Tests whether or not an item exists in the array
|
@@ -42,16 +42,38 @@ module Rico
|
|
42
42
|
members.include? item
|
43
43
|
end
|
44
44
|
|
45
|
-
#
|
45
|
+
# Resolve conflict between one or more RObject siblings
|
46
46
|
#
|
47
|
-
#
|
48
|
-
|
49
|
-
|
47
|
+
# robjects - array of RObjects to merge
|
48
|
+
#
|
49
|
+
# Returns a single RObject result or nil
|
50
|
+
def self.resolve(robject)
|
51
|
+
siblings = robject.siblings
|
52
|
+
values = siblings.map {|r| Array(r.data["_values"]) }
|
53
|
+
deletions = siblings.map {|r| Array(r.data["_deletes"]) }.flatten
|
54
|
+
|
55
|
+
result = []
|
56
|
+
values.each do |v|
|
57
|
+
result += (v - result)
|
58
|
+
end
|
59
|
+
|
60
|
+
result -= deletions
|
61
|
+
|
62
|
+
obj = Riak::RObject.new(robject.bucket, robject.key)
|
63
|
+
obj.data = { "_values" => result, "_deletes" => deletions }
|
64
|
+
obj
|
50
65
|
end
|
51
|
-
alias_method :count, :length
|
52
66
|
|
53
67
|
protected
|
54
68
|
|
69
|
+
def build_map_add(items)
|
70
|
+
{ "_type" => type_key, "_values" => compute_add(items) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_map_remove(items)
|
74
|
+
{ "_type" => type_key, "_values" => compute_remove(items), "_deletes" => items }
|
75
|
+
end
|
76
|
+
|
55
77
|
def compute_add(items)
|
56
78
|
members + items
|
57
79
|
end
|
data/lib/rico/object.rb
CHANGED
@@ -2,7 +2,7 @@ module Rico
|
|
2
2
|
module Object
|
3
3
|
extend Forwardable
|
4
4
|
|
5
|
-
def_delegators :riak_object, :
|
5
|
+
def_delegators :riak_object, :conflict?, :delete, :store
|
6
6
|
|
7
7
|
# Initialize an object with a bucket and key
|
8
8
|
#
|
@@ -26,7 +26,7 @@ module Rico
|
|
26
26
|
def mutate(value)
|
27
27
|
@data = value
|
28
28
|
riak_object.data = value
|
29
|
-
|
29
|
+
store
|
30
30
|
end
|
31
31
|
|
32
32
|
# Determine whether an object exists or not
|
@@ -38,6 +38,11 @@ module Rico
|
|
38
38
|
|
39
39
|
protected
|
40
40
|
|
41
|
+
def type_key
|
42
|
+
name = self.class.name.split("::").last
|
43
|
+
Rico::TYPES[name]
|
44
|
+
end
|
45
|
+
|
41
46
|
def riak_object
|
42
47
|
@riak_object ||= Rico.bucket(@bucket).get_or_new @key
|
43
48
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rico
|
2
|
+
class Resolver
|
3
|
+
def self.to_proc
|
4
|
+
@to_proc ||= lambda do |robject|
|
5
|
+
klasses = robject.siblings.map{|s| s.data && s.data["_type"] }.compact.uniq
|
6
|
+
return nil unless klasses.length == 1
|
7
|
+
|
8
|
+
klass_name = Rico::TYPES.invert[klasses.first]
|
9
|
+
return nil unless klass_name
|
10
|
+
|
11
|
+
klass = Rico.const_get(klass_name)
|
12
|
+
return nil unless klass.respond_to?(:resolve)
|
13
|
+
|
14
|
+
klass.resolve(robject.siblings)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/rico/value.rb
CHANGED
@@ -20,12 +20,25 @@ module Rico
|
|
20
20
|
#
|
21
21
|
# Returns true if stored, false if not
|
22
22
|
def setnx(value)
|
23
|
-
if
|
23
|
+
if exists?
|
24
24
|
false
|
25
25
|
else
|
26
26
|
set value
|
27
27
|
true
|
28
28
|
end
|
29
29
|
end
|
30
|
+
|
31
|
+
# Resolve conflict between one or more RObject siblings
|
32
|
+
#
|
33
|
+
# This currently just returns the first sibling
|
34
|
+
#
|
35
|
+
# robjects - array of RObjects to resolve
|
36
|
+
#
|
37
|
+
# Returns a single RObject result or nil
|
38
|
+
def self.resolve(robject)
|
39
|
+
obj = Riak::RObject.new(robject.bucket, robject.key)
|
40
|
+
obj.data = robject.siblings.first.data
|
41
|
+
obj
|
42
|
+
end
|
30
43
|
end
|
31
44
|
end
|
data/lib/rico/version.rb
CHANGED
data/lib/rico.rb
CHANGED
@@ -8,10 +8,20 @@ require "rico/set"
|
|
8
8
|
require "rico/sorted_set"
|
9
9
|
require "rico/value"
|
10
10
|
|
11
|
+
require "rico/resolver"
|
12
|
+
|
11
13
|
require "rico/version"
|
12
14
|
|
13
15
|
module Rico
|
14
16
|
|
17
|
+
TYPES = {
|
18
|
+
"Array" => "array",
|
19
|
+
"List" => "list",
|
20
|
+
"Set" => "set",
|
21
|
+
"SortedSet" => "sset",
|
22
|
+
"Value" => "value"
|
23
|
+
}
|
24
|
+
|
15
25
|
def self.configure
|
16
26
|
yield self if block_given?
|
17
27
|
end
|
@@ -38,3 +48,5 @@ module Rico
|
|
38
48
|
@riak = riak
|
39
49
|
end
|
40
50
|
end
|
51
|
+
|
52
|
+
Riak::RObject.on_conflict(&Rico::Resolver.to_proc)
|
data/rico.gemspec
CHANGED
data/spec/array_spec.rb
CHANGED
@@ -133,6 +133,29 @@ describe Rico::Array do
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
+
describe ".resolve" do
|
137
|
+
it "properly resolves missing values" do
|
138
|
+
datas = [
|
139
|
+
{ "_type" => "array", "_values" => [1,2,3] },
|
140
|
+
{ "_type" => "array", "_values" => [1,2,3,4] }
|
141
|
+
]
|
142
|
+
conflicted = RiakHelpers.build_conflicted_robject "array_resolve_simple", datas
|
143
|
+
result = Rico::Array.resolve(conflicted)
|
144
|
+
result.data["_values"].should eql [1,2,3,4]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "properly deletes deleted values after resolve" do
|
148
|
+
datas = [
|
149
|
+
{ "_type" => "array", "_values" => [1,2,3,4] },
|
150
|
+
{ "_type" => "array", "_values" => [1,2,3], "_deletes" => [4] }
|
151
|
+
]
|
152
|
+
conflicted = RiakHelpers.build_conflicted_robject "array_resolve_delete", datas
|
153
|
+
result = Rico::Array.resolve(conflicted)
|
154
|
+
result.data["_values"].should eql [1,2,3]
|
155
|
+
result.data["_deletes"].should eql [4]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
136
159
|
it "is enumerable" do
|
137
160
|
a = Rico::Array.new RiakHelpers.bucket, "enumerable"
|
138
161
|
a.add(3, 1, 4, 1, 5, 9)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Rico::Resolver do
|
4
|
+
describe ".to_proc" do
|
5
|
+
it "returns nil for data without type" do
|
6
|
+
datas = [
|
7
|
+
{ "a" => "b" },
|
8
|
+
{ "c" => "d" },
|
9
|
+
{ "f" => "e" }
|
10
|
+
]
|
11
|
+
conflicted = RiakHelpers.build_conflicted_robject "resolver_proc_no_type", datas
|
12
|
+
Rico::Resolver.to_proc.call(conflicted).should eql nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns nil for data with conflicted types" do
|
16
|
+
datas = [
|
17
|
+
{ "_type" => "array", "_values" => [1,2,3,4,1] },
|
18
|
+
{ "_type" => "set", "_values" => [1,2,3,4] }
|
19
|
+
]
|
20
|
+
conflicted = RiakHelpers.build_conflicted_robject "resolver_proc_conflicted_types", datas
|
21
|
+
Rico::Resolver.to_proc.call(conflicted).should eql nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns nil for data without a known rico class" do
|
25
|
+
datas = [
|
26
|
+
{ "_type" => "unknown", "_values" => [1,2,3] },
|
27
|
+
{ "_type" => "unknown", "_values" => [1,2,3,4] }
|
28
|
+
]
|
29
|
+
conflicted = RiakHelpers.build_conflicted_robject "resolver_proc_unknown_type", datas
|
30
|
+
Rico::Resolver.to_proc.call(conflicted).should eql nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns the result of the resolve function on known types" do
|
34
|
+
datas = [
|
35
|
+
{ "_type" => "array", "_values" => [1,2,3] },
|
36
|
+
{ "_type" => "array", "_values" => [1,2,3,4] }
|
37
|
+
]
|
38
|
+
conflicted = RiakHelpers.build_conflicted_robject "resolver_proc_no_resolve_function", datas
|
39
|
+
|
40
|
+
Rico::Array.stub(:resolve).and_return(conflicted.siblings.last)
|
41
|
+
Rico::Resolver.to_proc.call(conflicted).should eql conflicted.siblings.last
|
42
|
+
Rico::Array.unstub(:resolve)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -17,5 +17,30 @@ module RiakHelpers
|
|
17
17
|
b.keys.each do |k|
|
18
18
|
b.delete k
|
19
19
|
end
|
20
|
+
|
21
|
+
a = Rico::Array.new bucket, "visual_array"
|
22
|
+
a.add 1,2,3,4,5,6
|
23
|
+
a.remove 6
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.build_conflicted_robject(key, values)
|
27
|
+
bucket = Rico.bucket RiakHelpers.bucket
|
28
|
+
o = Riak::RObject.new bucket, key
|
29
|
+
siblings = values.map do |v|
|
30
|
+
x = Riak::RObject.new bucket, key
|
31
|
+
x.data = v
|
32
|
+
x
|
33
|
+
end
|
34
|
+
|
35
|
+
o = Riak::RObject.new bucket, key
|
36
|
+
o.siblings = siblings
|
37
|
+
o
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build_conflicted_rico_object(klass, key, values=["v1", "v2", "v3"])
|
41
|
+
o = build_conflicted_robject key, values
|
42
|
+
v = klass.new RiakHelpers.bucket, key
|
43
|
+
v.instance_variable_set("@riak_object", o)
|
44
|
+
v
|
20
45
|
end
|
21
46
|
end
|
data/spec/value_spec.rb
CHANGED
@@ -42,6 +42,25 @@ describe Rico::Value do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe "#setnx" do
|
46
|
+
it "writes the value, returns true if new" do
|
47
|
+
a = Rico::Value.new RiakHelpers.bucket, "setnx_new"
|
48
|
+
a.setnx("value").should eql true
|
49
|
+
b = Rico::Value.new RiakHelpers.bucket, "setnx_new"
|
50
|
+
b.get.should eql "value"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "does nothing, returns false if the value exists" do
|
54
|
+
a = Rico::Value.new RiakHelpers.bucket, "setnx_exists"
|
55
|
+
a.set "value"
|
56
|
+
b = Rico::Value.new RiakHelpers.bucket, "setnx_exists"
|
57
|
+
b.setnx("other").should eql false
|
58
|
+
b.get.should eql "value"
|
59
|
+
c = Rico::Value.new RiakHelpers.bucket, "setnx_exists"
|
60
|
+
c.get.should eql "value"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
45
64
|
describe "#delete" do
|
46
65
|
it "deletes an existing object" do
|
47
66
|
a = Rico::Value.new RiakHelpers.bucket, "delete_existing"
|
@@ -52,4 +71,25 @@ describe Rico::Value do
|
|
52
71
|
b.get.should eql nil
|
53
72
|
end
|
54
73
|
end
|
74
|
+
|
75
|
+
describe ".resolve" do
|
76
|
+
it "just returns the first sibling" do
|
77
|
+
datas = ["Tom", "Jerry"]
|
78
|
+
conflicted = RiakHelpers.build_conflicted_robject "value_resolve_simple", datas
|
79
|
+
result = Rico::Value.resolve(conflicted)
|
80
|
+
result.data.should eql "Tom"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "properly deletes deleted values after resolve" do
|
84
|
+
datas = [
|
85
|
+
{ "_type" => "array", "_values" => [1,2,3,4] },
|
86
|
+
{ "_type" => "array", "_values" => [1,2,3], "_deletes" => [4] }
|
87
|
+
]
|
88
|
+
conflicted = RiakHelpers.build_conflicted_robject "array_resolve_delete", datas
|
89
|
+
result = Rico::Array.resolve(conflicted)
|
90
|
+
result.data["_values"].should eql [1,2,3]
|
91
|
+
result.data["_deletes"].should eql [4]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
55
95
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rico
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: riak-client
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '2.12'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: guard-rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.3'
|
46
62
|
description: Primative data types on Riak
|
47
63
|
email:
|
48
64
|
- jcoene@gmail.com
|
@@ -53,6 +69,7 @@ files:
|
|
53
69
|
- .gitignore
|
54
70
|
- .rspec
|
55
71
|
- Gemfile
|
72
|
+
- Guardfile
|
56
73
|
- LICENSE
|
57
74
|
- README.md
|
58
75
|
- Rakefile
|
@@ -61,6 +78,7 @@ files:
|
|
61
78
|
- lib/rico/counter.rb
|
62
79
|
- lib/rico/list.rb
|
63
80
|
- lib/rico/object.rb
|
81
|
+
- lib/rico/resolver.rb
|
64
82
|
- lib/rico/set.rb
|
65
83
|
- lib/rico/sorted_set.rb
|
66
84
|
- lib/rico/value.rb
|
@@ -68,6 +86,7 @@ files:
|
|
68
86
|
- rico.gemspec
|
69
87
|
- spec/array_spec.rb
|
70
88
|
- spec/list_spec.rb
|
89
|
+
- spec/resolver_spec.rb
|
71
90
|
- spec/rico_spec.rb
|
72
91
|
- spec/set_spec.rb
|
73
92
|
- spec/sorted_set_spec.rb
|
@@ -100,6 +119,7 @@ summary: Primative data types on Riak
|
|
100
119
|
test_files:
|
101
120
|
- spec/array_spec.rb
|
102
121
|
- spec/list_spec.rb
|
122
|
+
- spec/resolver_spec.rb
|
103
123
|
- spec/rico_spec.rb
|
104
124
|
- spec/set_spec.rb
|
105
125
|
- spec/sorted_set_spec.rb
|