riakrest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +41 -0
  3. data/PostInstall.txt +2 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +24 -0
  6. data/examples/auto_update_data.rb +50 -0
  7. data/examples/auto_update_links.rb +48 -0
  8. data/examples/basic_client.rb +33 -0
  9. data/examples/basic_resource.rb +34 -0
  10. data/examples/json_data_resource.rb +32 -0
  11. data/examples/linked_resource.rb +113 -0
  12. data/examples/multiple_resources.rb +43 -0
  13. data/lib/riakrest/core/exceptions.rb +73 -0
  14. data/lib/riakrest/core/jiak_bucket.rb +146 -0
  15. data/lib/riakrest/core/jiak_client.rb +316 -0
  16. data/lib/riakrest/core/jiak_data.rb +265 -0
  17. data/lib/riakrest/core/jiak_link.rb +131 -0
  18. data/lib/riakrest/core/jiak_object.rb +233 -0
  19. data/lib/riakrest/core/jiak_schema.rb +242 -0
  20. data/lib/riakrest/core/query_link.rb +156 -0
  21. data/lib/riakrest/data/jiak_data_hash.rb +182 -0
  22. data/lib/riakrest/resource/jiak_resource.rb +628 -0
  23. data/lib/riakrest/version.rb +7 -0
  24. data/lib/riakrest.rb +164 -0
  25. data/riakrest.gemspec +38 -0
  26. data/script/console +10 -0
  27. data/script/destroy +14 -0
  28. data/script/generate +14 -0
  29. data/spec/core/exceptions_spec.rb +18 -0
  30. data/spec/core/jiak_bucket_spec.rb +103 -0
  31. data/spec/core/jiak_client_spec.rb +358 -0
  32. data/spec/core/jiak_link_spec.rb +77 -0
  33. data/spec/core/jiak_object_spec.rb +210 -0
  34. data/spec/core/jiak_schema_spec.rb +184 -0
  35. data/spec/core/query_link_spec.rb +128 -0
  36. data/spec/data/jiak_data_hash_spec.rb +14 -0
  37. data/spec/resource/jiak_resource_spec.rb +128 -0
  38. data/spec/riakrest_spec.rb +17 -0
  39. data/spec/spec.opts +5 -0
  40. data/spec/spec_helper.rb +12 -0
  41. data/tasks/rspec.rake +21 -0
  42. 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