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