riakrest 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +41 -0
- data/PostInstall.txt +2 -0
- data/README.rdoc +51 -0
- data/Rakefile +24 -0
- data/examples/auto_update_data.rb +50 -0
- data/examples/auto_update_links.rb +48 -0
- data/examples/basic_client.rb +33 -0
- data/examples/basic_resource.rb +34 -0
- data/examples/json_data_resource.rb +32 -0
- data/examples/linked_resource.rb +113 -0
- data/examples/multiple_resources.rb +43 -0
- data/lib/riakrest/core/exceptions.rb +73 -0
- data/lib/riakrest/core/jiak_bucket.rb +146 -0
- data/lib/riakrest/core/jiak_client.rb +316 -0
- data/lib/riakrest/core/jiak_data.rb +265 -0
- data/lib/riakrest/core/jiak_link.rb +131 -0
- data/lib/riakrest/core/jiak_object.rb +233 -0
- data/lib/riakrest/core/jiak_schema.rb +242 -0
- data/lib/riakrest/core/query_link.rb +156 -0
- data/lib/riakrest/data/jiak_data_hash.rb +182 -0
- data/lib/riakrest/resource/jiak_resource.rb +628 -0
- data/lib/riakrest/version.rb +7 -0
- data/lib/riakrest.rb +164 -0
- data/riakrest.gemspec +38 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/core/exceptions_spec.rb +18 -0
- data/spec/core/jiak_bucket_spec.rb +103 -0
- data/spec/core/jiak_client_spec.rb +358 -0
- data/spec/core/jiak_link_spec.rb +77 -0
- data/spec/core/jiak_object_spec.rb +210 -0
- data/spec/core/jiak_schema_spec.rb +184 -0
- data/spec/core/query_link_spec.rb +128 -0
- data/spec/data/jiak_data_hash_spec.rb +14 -0
- data/spec/resource/jiak_resource_spec.rb +128 -0
- data/spec/riakrest_spec.rb +17 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +12 -0
- data/tasks/rspec.rake +21 -0
- metadata +113 -0
data/lib/riakrest.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# See README.rdoc for the RiakRest license.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'json'
|
5
|
+
rescue LoadError
|
6
|
+
raise "RiakRest requires json for REST JSON messaging."
|
7
|
+
end
|
8
|
+
begin
|
9
|
+
require 'restclient'
|
10
|
+
rescue LoadError
|
11
|
+
raise <<EOM
|
12
|
+
RiakRest requires the restclient gem for making REST calls.
|
13
|
+
gem install rest-client
|
14
|
+
|
15
|
+
EOM
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'uri'
|
19
|
+
|
20
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
21
|
+
$:.include?(File.dirname(__FILE__)) ||
|
22
|
+
$:.include?(File.expand_path(File.dirname(__FILE__)))
|
23
|
+
|
24
|
+
# RiakRest provides structured, RESTful interaction with a Riak document
|
25
|
+
# store. In Riak parlance, this JSON data exchange is called Jiak. RiakRest
|
26
|
+
# provides two levels of interaction: Core Client and Resource. Core Client
|
27
|
+
# interaction works down at the Jiak level and so exposes Jiak
|
28
|
+
# internals. Resource interaction abstracts above that and is much easier to
|
29
|
+
# use. But we'll show the Core Client first since Resource is built on top of
|
30
|
+
# it.
|
31
|
+
#
|
32
|
+
# ===Core Client Interaction
|
33
|
+
# Primary Jiak constructs are represented by core Jiak classes:
|
34
|
+
# JiakClient :: Client used to make Jiak store, get, delete, and other calls.
|
35
|
+
# JiakBucket :: Bucket name and the data class stored in the bucket.
|
36
|
+
# JiakSchema :: Schema used by a Jiak server bucket.
|
37
|
+
# JiakData :: Class to define user data to be stored on a Jiak server.
|
38
|
+
# JiakObject :: Jiak object wrapper that includes the user-defined data.
|
39
|
+
# JiakLink :: Jiak link objects for associations between Jiak server data.
|
40
|
+
# QueryLink :: Link objects to query Jiak link associations.
|
41
|
+
#
|
42
|
+
# ====Example Usage
|
43
|
+
# This example works at the Jiak core layer. See the Resource example below for
|
44
|
+
# an abstraction layered on top of this core.
|
45
|
+
# <code>
|
46
|
+
# require 'riakrest'
|
47
|
+
# include RiakRest
|
48
|
+
# </code>
|
49
|
+
# Create a simple class to hold Person data.
|
50
|
+
# <code>
|
51
|
+
# Person = JiakDataHash.create(:name,:age)
|
52
|
+
# </code>
|
53
|
+
# Create a client, a bucket to hold the data, and set the bucket schema for
|
54
|
+
# structured interaction.
|
55
|
+
# <code>
|
56
|
+
# client = JiakClient.new("http://localhost:8002/jiak")
|
57
|
+
# bucket = JiakBucket.new('person',Person)
|
58
|
+
# client.set_schema(bucket)
|
59
|
+
# </code>
|
60
|
+
# Wrap a Person data object in a JiakObject and store it. Check the data on the
|
61
|
+
# server to see it's really there.
|
62
|
+
# <code>
|
63
|
+
# remy = client.store(JiakObject.new(:bucket => bucket,
|
64
|
+
# :data => Person.new(:name => "remy",
|
65
|
+
# :age => 10)),
|
66
|
+
# :object => true)
|
67
|
+
# puts client.get(bucket,remy.key).data.name # => "remy"
|
68
|
+
# </code>
|
69
|
+
# Change the data via accessors and update. Again, we print the server value.
|
70
|
+
# <code>
|
71
|
+
# remy.data.name # => "remy"
|
72
|
+
# remy.data.name = "Remy"
|
73
|
+
# client.store(remy)
|
74
|
+
# puts client.get(bucket,remy.key).data.name # => "Remy"
|
75
|
+
# </code>
|
76
|
+
# Let's add another person and a link between them.
|
77
|
+
# <code>
|
78
|
+
# callie = client.store(JiakObject.new(:bucket => bucket,
|
79
|
+
# :data => Person.new(:name => "Callie",
|
80
|
+
# :age => 12)),
|
81
|
+
# :object => true)
|
82
|
+
# remy << JiakLink.new(bucket,callie.key,'sister')
|
83
|
+
# client.store(remy)
|
84
|
+
# </code>
|
85
|
+
# Now we can get callie as the sister of remy:
|
86
|
+
# <code>
|
87
|
+
# sisters = client.walk(bucket,remy.key,QueryLink.new(bucket,'sister'),Person)
|
88
|
+
# sisters[0].eql?(callie) # => true
|
89
|
+
# </code>
|
90
|
+
# Finally, we'll delete the objects on the way out the door.
|
91
|
+
# <code>
|
92
|
+
# client.delete(bucket,remy.key)
|
93
|
+
# client.delete(bucket,callie.key)
|
94
|
+
# </code>
|
95
|
+
#
|
96
|
+
# ===Resource Interaction
|
97
|
+
# Much of the above code can be abstracted into resource-based
|
98
|
+
# interaction. RiakRest provides a module JiakResource that allows you to
|
99
|
+
# create resource objects that encapsulate a lot of the cruft of core client
|
100
|
+
# interaction. We'll do the same steps as above using resources.
|
101
|
+
# <code>
|
102
|
+
# require 'riakrest'
|
103
|
+
# include RiakRest
|
104
|
+
#
|
105
|
+
# PersonData = JiakDataHash.create(:name,:age)
|
106
|
+
# PersonData.keygen :name
|
107
|
+
#
|
108
|
+
# class Person
|
109
|
+
# include JiakResource
|
110
|
+
# server 'http://localhost:8002/jiak'
|
111
|
+
# group 'people'
|
112
|
+
# data_class PersonData
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# remy = Person.new(:name => 'remy', :age => 10)
|
116
|
+
# remy.post
|
117
|
+
# puts Person.get('remy').name # => "remy"
|
118
|
+
#
|
119
|
+
# remy.name = "Remy"
|
120
|
+
# remy.update
|
121
|
+
# puts Person.get('remy').name # => "Remy"
|
122
|
+
#
|
123
|
+
# callie = Person.new(:name => 'Callie', :age => 12).post
|
124
|
+
# remy.link(callie,'sister').update
|
125
|
+
#
|
126
|
+
# sisters = remy.walk(Person,'sister')
|
127
|
+
# sisters[0].eql?(callie) # => true
|
128
|
+
#
|
129
|
+
# remy.delete
|
130
|
+
# callie.delete
|
131
|
+
# </code>
|
132
|
+
# Ah, that feels better. Go forth and Riak!
|
133
|
+
module RiakRest
|
134
|
+
end
|
135
|
+
|
136
|
+
require 'riakrest/version'
|
137
|
+
|
138
|
+
require 'riakrest/core/exceptions'
|
139
|
+
require 'riakrest/core/jiak_bucket'
|
140
|
+
require 'riakrest/core/jiak_client'
|
141
|
+
require 'riakrest/core/jiak_data'
|
142
|
+
require 'riakrest/core/jiak_link'
|
143
|
+
require 'riakrest/core/jiak_object'
|
144
|
+
require 'riakrest/core/jiak_schema'
|
145
|
+
require 'riakrest/core/query_link'
|
146
|
+
|
147
|
+
require 'riakrest/data/jiak_data_hash'
|
148
|
+
|
149
|
+
require 'riakrest/resource/jiak_resource'
|
150
|
+
|
151
|
+
# Extend Array with convenience methods for comparing array contents.
|
152
|
+
class Array
|
153
|
+
# Compare arrays for same elements regardless of order.
|
154
|
+
def same_elements?(arr)
|
155
|
+
raise ArgumentError unless arr.is_a?(Array)
|
156
|
+
(size == arr.size) && arr.reduce(true){|same,elem| same && include?(elem)}
|
157
|
+
end
|
158
|
+
|
159
|
+
# Compare arrays for same element.to_s values regardless of order.
|
160
|
+
def same_fields?(arr)
|
161
|
+
raise ArgumentError unless arr.is_a?(Array)
|
162
|
+
(size == arr.size) && map{|f| f.to_s}.same_elements?(arr.map{|f| f.to_s})
|
163
|
+
end
|
164
|
+
end
|
data/riakrest.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{riakrest}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Paul Rogers"]
|
9
|
+
s.date = %q{2009-10-29}
|
10
|
+
s.description = %q{RiakRest provides structured, RESTful interaction with a Riak document
|
11
|
+
store. In Riak parlance, this JSON data exchange is called Jiak. RiakRest
|
12
|
+
provides two levels of interaction: Core Client and Resource. Core Client
|
13
|
+
interaction works down at the Jiak level and exposes Jiak internals. Resource
|
14
|
+
interaction is an abstraction built on top of the Core Client.}
|
15
|
+
s.email = ["paul@dingosky.com"]
|
16
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt"]
|
17
|
+
s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "examples/auto_update_data.rb", "examples/auto_update_links.rb", "examples/basic_client.rb", "examples/basic_resource.rb", "examples/json_data_resource.rb", "examples/linked_resource.rb", "examples/multiple_resources.rb", "lib/riakrest.rb", "lib/riakrest/core/exceptions.rb", "lib/riakrest/core/jiak_bucket.rb", "lib/riakrest/core/jiak_client.rb", "lib/riakrest/core/jiak_data.rb", "lib/riakrest/core/jiak_link.rb", "lib/riakrest/core/jiak_object.rb", "lib/riakrest/core/jiak_schema.rb", "lib/riakrest/core/query_link.rb", "lib/riakrest/data/jiak_data_hash.rb", "lib/riakrest/resource/jiak_resource.rb", "lib/riakrest/version.rb", "riakrest.gemspec", "script/console", "script/destroy", "script/generate", "spec/core/exceptions_spec.rb", "spec/core/jiak_bucket_spec.rb", "spec/core/jiak_client_spec.rb", "spec/core/jiak_link_spec.rb", "spec/core/jiak_object_spec.rb", "spec/core/jiak_schema_spec.rb", "spec/core/query_link_spec.rb", "spec/data/jiak_data_hash_spec.rb", "spec/resource/jiak_resource_spec.rb", "spec/riakrest_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
|
18
|
+
s.homepage = %q{http://github.com/wcpr/riakrest}
|
19
|
+
s.post_install_message = %q{PostInstall.txt}
|
20
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.rubyforge_project = %q{riakrest}
|
23
|
+
s.rubygems_version = %q{1.3.5}
|
24
|
+
s.summary = %q{RiakRest provides structured, RESTful interaction with a Riak document store}
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 3
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
31
|
+
s.add_development_dependency(%q<hoe>, [">= 2.3.3"])
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<hoe>, [">= 2.3.3"])
|
34
|
+
end
|
35
|
+
else
|
36
|
+
s.add_dependency(%q<hoe>, [">= 2.3.3"])
|
37
|
+
end
|
38
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/riakrest.rb'}"
|
9
|
+
puts "Loading riakrest gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe "Jiak exceptions" do
|
4
|
+
it "should all be a kind of RiakRest::Exception" do
|
5
|
+
JiakResourceException.new.should be_a_kind_of RiakRest::Exception
|
6
|
+
JiakResourceNotFound.new.should be_a_kind_of RiakRest::Exception
|
7
|
+
JiakClientException.new.should be_a_kind_of RiakRest::Exception
|
8
|
+
JiakObjectException.new.should be_a_kind_of RiakRest::Exception
|
9
|
+
JiakSchemaException.new.should be_a_kind_of RiakRest::Exception
|
10
|
+
JiakLinkException.new.should be_a_kind_of RiakRest::Exception
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "JiakResource hierarchy" do
|
15
|
+
it "should have resource not found" do
|
16
|
+
JiakResourceNotFound.new.should be_a_kind_of JiakResourceException
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe "JiakBucket" do
|
4
|
+
before do
|
5
|
+
@name = 'bucket_name'
|
6
|
+
@data_class = JiakDataHash.create(:f1,:f2)
|
7
|
+
@params = {:reads => 1, :writes => 2, :durable_writes => 3, :waits => 4}
|
8
|
+
@bucket = JiakBucket.new(@name,@data_class)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should respond to" do
|
12
|
+
@bucket.should respond_to(:name,:data_class,:params,:schema)
|
13
|
+
@bucket.should respond_to(:name=,:data_class=,:params=)
|
14
|
+
@bucket.should respond_to(:eql?)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should initialize with name and data class" do
|
18
|
+
@bucket.name.should eql @name
|
19
|
+
@bucket.data_class.should eql @data_class
|
20
|
+
@bucket.params.should be_empty
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should initialize with name, data class, and params" do
|
24
|
+
bucket = JiakBucket.new(@name,@data_class,@params)
|
25
|
+
bucket.name.should eql @name
|
26
|
+
bucket.data_class.should eql @data_class
|
27
|
+
bucket.params.should have_exactly(4).items
|
28
|
+
@params.each {|k,v| bucket.params[k].should == @params[k]}
|
29
|
+
|
30
|
+
@params.delete(:writes)
|
31
|
+
bucket = JiakBucket.new(@name,@data_class,@params)
|
32
|
+
bucket.params.should have_exactly(3).items
|
33
|
+
bucket.params[:waits].should == @params[:waits]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should update the name,data class, and params" do
|
37
|
+
# name = 'new_bucket_name'
|
38
|
+
# @bucket.name = name
|
39
|
+
# @bucket.name.should eql name
|
40
|
+
|
41
|
+
data_class = JiakDataHash.create(:g1)
|
42
|
+
@bucket.data_class = data_class
|
43
|
+
@bucket.data_class.should eql data_class
|
44
|
+
|
45
|
+
@bucket.params.should be_empty
|
46
|
+
@bucket.params = @params
|
47
|
+
@bucket.params.should eql @params
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should validate name, data class, and params" do
|
51
|
+
empty_name = lambda {JiakBucket.new("",@data_class)}
|
52
|
+
empty_name.should raise_error(JiakBucketException,/Name.*empty/)
|
53
|
+
|
54
|
+
empty_name = lambda {JiakBucket.new(" ",@data_class)}
|
55
|
+
empty_name.should raise_error(JiakBucketException,/Name.*empty/)
|
56
|
+
|
57
|
+
nil_name = lambda {JiakBucket.new(nil,@data_class)}
|
58
|
+
nil_name.should raise_error(JiakBucketException,/Name.*string/)
|
59
|
+
|
60
|
+
bad_data_class = lambda {JiakBucket.new(@name,Hash)}
|
61
|
+
bad_data_class.should raise_error(JiakBucketException,/JiakData/)
|
62
|
+
|
63
|
+
bad_data_class = lambda {@bucket.data_class = Hash}
|
64
|
+
bad_data_class.should raise_error(JiakBucketException,/JiakData/)
|
65
|
+
|
66
|
+
params = @params
|
67
|
+
params.delete(:writes)
|
68
|
+
params[:write] = 2
|
69
|
+
bad_params = lambda {JiakBucket.new(@name,@data_class,params)}
|
70
|
+
bad_params.should raise_error(JiakBucketException,/params/)
|
71
|
+
|
72
|
+
bad_params = lambda {@bucket.params = params}
|
73
|
+
bad_params.should raise_error(JiakBucketException,/params/)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should provide the schema for the data class" do
|
77
|
+
@bucket.schema.should eql @data_class.schema
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should eql by state" do
|
81
|
+
bucket = JiakBucket.new(@name,@data_class)
|
82
|
+
bucket.should eql @bucket
|
83
|
+
|
84
|
+
bucket = JiakBucket.new(@name.upcase,@data_class)
|
85
|
+
bucket.should_not eql @bucket
|
86
|
+
|
87
|
+
data_class = JiakDataHash.create(:g1)
|
88
|
+
bucket = JiakBucket.new(@name,data_class)
|
89
|
+
bucket.should_not eql @bucket
|
90
|
+
|
91
|
+
bucket = JiakBucket.new(@name,@data_class,@params)
|
92
|
+
bucket.should_not eql @bucket
|
93
|
+
|
94
|
+
@bucket.params = @params
|
95
|
+
bucket.should eql @bucket
|
96
|
+
|
97
|
+
params = {:reads => @params[:reads]+1}
|
98
|
+
bucket_1 = JiakBucket.new(@name,@data_class,@params)
|
99
|
+
bucket_2 = JiakBucket.new(@name,@data_class,params)
|
100
|
+
bucket_1.should_not eql bucket_2
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|