riakrest 0.0.1
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.
- 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
|