riakrest 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,78 +3,45 @@ module RiakRest
3
3
  # All end-user data stored via RiakRest is contained in user-defined data
4
4
  # objects. To make a user-defined data object, include module JiakData into
5
5
  # your class definition. This allows creating a class that can be used to
6
- # create instances of your user-defined data. Note JiakData does create
7
- # user-data instances, rather it facilitates creating the class you use to
6
+ # create instances of your user-defined data. Note JiakData does not create
7
+ # user-data instances, rather it facilitates creating the class used to
8
8
  # create user-data instances.
9
9
  #
10
- # The four class methods <code>allowed, required, readable, writable</code>
11
- # defined in JiakData#ClassMethods are used to declare the schema for
12
- # structured Jiak interaction with user-defined data. The <code>allowed</code>
13
- # method is mandatory; the other methods take the defaults as described in
14
- # JiakSchema. See JiakSchema for discussion on structured data interaction.
10
+ # The class methods <code>jattr_reader,jattr_writer</code>, and
11
+ # <code>jattr_accessor</code> are used to declare the Jiak readable and
12
+ # writable fields for a JiakData. The method <code>keygen</code> is used
13
+ # to specify a block for generating the key for a data instance. By default,
14
+ # JiakData generates an empty key which is interpreted by the Jiak server as
15
+ # a signal to generate a random key.
16
+ # ====Example
17
+ # class FooBar
18
+ # include JiakData
19
+ # jattr_accessor :foo, :bar
20
+ # keygen { foo.downcase }
21
+ # end
15
22
  #
16
- # User-defined data classes must override JiakData#ClassMethods#jiak_create
17
- # (creating your user-defined data from the information returned by Jiak) and
18
- # JiakData#for_jiak (providing the information to be sent to Jiak). The
19
- # default implementations of these methods throw JiakDataException to
20
- # enforce this override.
23
+ # The four class methods <code>allow, require, readable, writable</code> are
24
+ # used for more specific data schema control. See JiakSchema for a
25
+ # discussion of the schema fields.
26
+ # ====Example
27
+ # class FooBarBaz
28
+ # include JiakData
21
29
  #
22
- # JiakData provides a default data key generator that returns nil, which
23
- # instructs the Jiak server to generate a random key on first data
24
- # storage. To explicitly set the key override JiakData#keygen to return
25
- # whatever string you want to use for the key. Keys need to be unique within
26
- # each bucket on the Jiak server but can be the same across distinct buckets.
30
+ # allow :foo, :bar, :baz
31
+ # require :foo
32
+ # readable :foo, :bar
33
+ # writable :foo, :baz
34
+ # end
27
35
  #
28
- # ===Example
29
- # <code>
30
- # class FooBarBaz
31
- # include JiakData
36
+ # The methods used in the above examples can be used together as well.
32
37
  #
33
- # allowed :foo, :bar, :baz
34
- # required :foo
35
- # readable :foo, :bar
36
- # writable :foo, :baz
37
- #
38
- # def initialize(foo,bar,baz)
39
- # @foo = foo
40
- # @bar = bar
41
- # @baz = baz
42
- # end
43
- #
44
- # def self.jiak_create(jiak)
45
- # new(jiak['foo'],jiak['bar'])
46
- # end
47
- #
48
- # def for_jiak
49
- # { :foo => @foo,
50
- # :baz => @baz
51
- # }
52
- # end
38
+ # The Core Client framework uses a JiakData class to marshall data to and
39
+ # from the Jiak server. Marshalling to the Jiak server uses JiakData#to_jiak
40
+ # and marshalling from the Jiak server uses
41
+ # JiakData#ClassMethods#jiak_create. Implementations of these methods are
42
+ # automatically generated to marshall writable fields to Jiak and initialize
43
+ # from readable fields.
53
44
  #
54
- # def keygen
55
- # "#{foo}"
56
- # end
57
- # end
58
- # </code>
59
- #
60
- # Note that FooBarBaz <code>bar</code> is readable but not writable and
61
- # <code>baz</code> is writable but not readable. Also note
62
- # <code>for_jiak</code> only provides the <code>writable</code> fields for
63
- # writing to the Jiak server and <code>jiak_create</code> only initializes
64
- # from the <code>readable</code> fields returned by the Jiak server. The
65
- # above definition means a user of FooBarBaz could change <code>baz</code>
66
- # but not see that change and could access <code>bar</code> but not change
67
- # it. This could be useful if either another JiakData class (with a different
68
- # schema) created access into the same data, allowing <code>bar</code> writes
69
- # and <code>baz</code> reads, or if Riak server-side manipulation affected
70
- # those fields. The constraints declared in FooBarBaz simply provide
71
- # a particular structured interaction of data on a Jiak server.
72
- #
73
- # If only one JiakData will be used for a particular type of data on the Jiak
74
- # server it is desirable to have the <code>readable</code> and
75
- # <code>writable</code> fields be the same as <code>allowed</code>. Setting
76
- # only <code>allowed</code> fields provide this reasonable default, hence only
77
- # that call is mandatory.
78
45
  module JiakData
79
46
 
80
47
  # ----------------------------------------------------------------------
@@ -82,91 +49,126 @@ module RiakRest
82
49
  # ----------------------------------------------------------------------
83
50
 
84
51
  # Class methods for use in creating a user-defined JiakData. The methods
85
- # <code>allowed, required, readable, writable</code> define the JiakSchema
86
- # for this JiakData. See JiakSchema for discussion on the use of schemas in
87
- # Riak.
52
+ # <code>allow, require, readable, writable</code> delegate to JiakSchema.
53
+ # See JiakSchema for discussion on the use of schemas in Riak.
88
54
  module ClassMethods
89
55
 
90
56
  # :call-seq:
91
- # allowed :f1, ..., :fn -> array
92
- # allowed [:f1, ..., :fn] -> array
57
+ # jattr_reader :f1,...,:fn
58
+ #
59
+ # Add read accessible fields.
60
+ def jattr_reader(*fields)
61
+ readable *fields
62
+ nil
63
+ end
64
+ alias :jattr :jattr_reader
65
+
66
+ # :call-seq:
67
+ # jattr_writer :f1,...,:fn
68
+ #
69
+ # Add write accessible fields.
70
+ def jattr_writer(*fields)
71
+ writable *fields
72
+ nil
73
+ end
74
+
75
+ # :call-seq:
76
+ # jattr_accessor :f1,...,:fn
77
+ #
78
+ # Add read/write accessible fields.
79
+ def jattr_accessor(*fields)
80
+ readable *fields
81
+ writable *fields
82
+ end
83
+
84
+ # :call-seq:
85
+ # allow :f1, ..., :fn -> array
86
+ # allow [:f1, ..., :fn] -> array
93
87
  #
94
- # Fields allowed in Jiak interactions. Returns an array of the allowed
95
- # fields.
88
+ # Adds to the fields allowed in Jiak interactions.
96
89
  #
97
- # The field <code>jiak</code> is reserved for RiakRest.
90
+ # Returns an array of added fields.
98
91
  #
99
92
  # Raise JiakDataException if the fields include <code>jiak</code>.
100
- def allowed(*fields)
101
- if(fields.include?(:jiak) || fields.include?('jiak'))
102
- raise JiakDataException, "jiak field name reserved for RiakRest"
103
- end
104
- arr_fields = transform_fields(*fields)
105
- arr_fields.each {|field| attr_accessor field}
106
- @schema = JiakSchema.new(arr_fields)
107
- arr_fields
93
+ def allow(*fields)
94
+ delegate_schema("allow",*fields)
108
95
  end
109
96
 
110
97
  # :call-seq:
111
- # required :f1, ..., :fn -> array
112
- # required [:f1, ..., :fn] -> array
98
+ # require :f1, ..., :fn -> array
99
+ # require [:f1, ..., :fn] -> array
113
100
  #
114
- # Fields required during in Jiak interactions. Returns an array of the
115
- # required fields.
101
+ # Adds to the fields required during in Jiak interactions.
116
102
  #
117
- def required(*fields)
118
- set_fields('required_fields',*fields)
103
+ # Returns an array of added fields.
104
+ def require(*fields)
105
+ delegate_schema("require",*fields)
119
106
  end
120
107
 
121
108
  # :call-seq:
122
109
  # readable :f1, ..., :fn -> array
123
110
  # readable [:f1, ..., :fn] -> array
124
111
  #
125
- # Fields returned by Jiak on retrieval. Returns an array of the fields in
126
- # the read mask.
112
+ # Adds to the fields that can be read from Jiak.
127
113
  #
114
+ # Returns an array of added fields.
128
115
  def readable(*fields)
129
- set_fields('read_mask',*fields)
116
+ delegate_schema("readable",*fields)
130
117
  end
131
118
 
132
119
  # :call-seq:
133
120
  # writable :f1, ..., :fn -> arry
134
121
  # writable [:f1, ..., :fn] -> arry
135
122
  #
136
- # Fields that can be written during Jiak interaction. Returns an array of
137
- # the fields in the write mask.
123
+ # Adds to the fields that can be written to Jiak.
138
124
  #
125
+ # Returns an array of added fields.
139
126
  def writable(*fields)
140
- set_fields('write_mask',*fields)
127
+ delegate_schema("writable",*fields)
141
128
  end
142
129
 
143
130
  # :call-seq:
144
- # readwrite :f1, ..., :fn -> array
145
- # readwrite [:f1, ..., :fn] -> array
131
+ # readwrite :f1, ..., :fn -> nil
132
+ # readwrite [:f1, ..., :fn] -> nil
133
+ #
134
+ # Adds to the fields that can be read and written.
146
135
  #
147
- # Set the read and write masks to the same fields. Returns an array of
148
- # the fields in the masks.
136
+ # Returns nil
149
137
  def readwrite(*fields)
150
- arr_fields = set_fields('readwrite',*fields)
151
- arr_fields
138
+ readable(*fields)
139
+ writable(*fields)
140
+ nil
152
141
  end
153
142
 
154
- def set_fields(which,*fields)
155
- arr_fields = transform_fields(*fields)
156
- check_allowed(arr_fields)
157
- @schema.send("#{which}=",arr_fields)
158
- arr_fields
143
+ # Delegates adding fields to the schema, then creates attr accessors for
144
+ # each field added.
145
+ def delegate_schema(method,*fields)
146
+ @schema ||= JiakSchema.new
147
+ prev_allowed = @schema.allowed_fields
148
+ added_fields = @schema.send(method,*fields)
149
+ added_allowed = @schema.allowed_fields - prev_allowed
150
+ added_allowed.each {|field| attr_accessor field}
151
+ added_fields
159
152
  end
160
- private :set_fields
153
+ private :delegate_schema
161
154
 
162
155
  # :call-seq:
163
156
  # JiakData.schema -> JiakSchema
164
157
  #
165
- # Get a JiakSchema representation determined by
166
- # <code>allowed, required, readable, writable</code>.
167
- #
158
+ # Get a JiakSchema representation this data.
168
159
  def schema
169
- @schema
160
+ @schema ||= JiakSchema.new
161
+ @schema.dup
162
+ end
163
+
164
+ # :call-seq:
165
+ # JiakData.keygen(&block) -> nil
166
+ #
167
+ # Define the key generation for an instance of the created JiakData
168
+ # class. Key generation will call the specified block in the scope of the
169
+ # current instance.
170
+ def keygen(&block)
171
+ define_method(:keygen,&block)
170
172
  end
171
173
 
172
174
  # :call-seq:
@@ -176,46 +178,57 @@ module RiakRest
176
178
  # by Jiak. These fields are determined by the read mask of the
177
179
  # structured Jiak interaction. See JiakSchema for read mask discussion.
178
180
  #
179
- # User-defined data classes must override this method. The method is
180
- # called during the creation of a JiakObject from information returned by
181
- # Jiak. The JiakObject contains the user-defined data itself. You do not
182
- # call this method explicitly.
181
+ # User-defined data classes must either override this method explicitly
182
+ # or use the <code>jattr_*</code> methods which implicitly override this
183
+ # method. The method is automatically called to marshall data from
184
+ # Jiak. You do not call this method explicitly.
183
185
  #
184
186
  # ====Example
185
- # <code>
186
- # def initialize(f1,f2)
187
- # @f1 = f1
188
- # @f2 = f2
189
- # end
190
- # def jiak_create(jiak)
191
- # new(jiak['f1'], jiak['f2'])
192
- # end
193
- # </code>
194
- #
195
- # Raise JiakDataException if not explicitly defined by user-data class.
196
- def jiak_create(json)
197
- raise JiakDataException, "#{self} must define jiak_create"
187
+ # def initialize(f1,f2)
188
+ # @f1 = f1
189
+ # @f2 = f2
190
+ # end
191
+ # def jiak_create(jiak)
192
+ # new(jiak['f1'], jiak['f2'])
193
+ # end
194
+ def jiak_create(jiak)
195
+ new(jiak)
198
196
  end
199
197
 
200
- def transform_fields(*fields)
201
- fields = fields[0] if(fields[0].is_a?(Array))
202
- fields.map {|f| f.to_sym}
198
+ end
199
+
200
+ def self.included(including_class) # :nodoc:
201
+ including_class.extend(ClassMethods)
202
+
203
+ define_method(:initialize) do |hash={}|
204
+ hash.each {|k,v| instance_variable_set("@#{k}", v)}
203
205
  end
204
- private :transform_fields
205
206
 
206
- def check_allowed(fields)
207
- allowed_fields = @schema.allowed_fields
208
- fields.each do |field|
209
- unless allowed_fields.include?(field)
210
- raise JiakDataException, "field '#{field}' not allowed"
211
- end
207
+ define_method(:to_jiak) do
208
+ self.class.schema.write_mask.inject({}) do |build,field|
209
+ build[field] = send("#{field}")
210
+ build
212
211
  end
213
212
  end
214
- private :check_allowed
215
- end
216
213
 
217
- def self.included(including_class) # :nodoc:
218
- including_class.extend(ClassMethods)
214
+ define_method(:eql?) do |other|
215
+ other.is_a?(self.class) &&
216
+ self.class.schema.allowed_fields.reduce(true) do |same,field|
217
+ same && other.send("#{field}").eql?(send("#{field}"))
218
+ end
219
+ end
220
+
221
+ define_method(:==) do |other|
222
+ self.class.schema.allowed_fields.reduce(true) do |same,field|
223
+ same && (other.send("#{field}") == (send("#{field}")))
224
+ end
225
+ end
226
+
227
+ define_method(:hash) do
228
+ self.class.schema.allowed_fields.inject(0) do |hsh,field|
229
+ hsh += send("#{field}").hash
230
+ end
231
+ end
219
232
  end
220
233
 
221
234
  # ----------------------------------------------------------------------
@@ -223,29 +236,31 @@ module RiakRest
223
236
  # ----------------------------------------------------------------------
224
237
 
225
238
  # :call-seq:
226
- # for_jiak -> hash
239
+ # to_jiak -> hash
227
240
  #
228
241
  # Provide a hash structure of the data to write to Jiak. The fields for
229
- # this structure should come from the write mask of the structured Jiak
230
- # interaction. See JiakSchema for write mask discussion.
242
+ # this structure should come from the JiakData write mask. See JiakSchema
243
+ # for shema discussion.
231
244
  #
232
- # User-defined data classes must override this method. The method is called
233
- # during the creation of a JiakObject to send information to Jiak. The
234
- # JiakObject contains the user-defined data itself. You do not call this
235
- # method explicitly.
245
+ # User-defined data classes must either override this method explicitly or
246
+ # use the <code>jattr_*</code> methods which implicitly provide an implicit
247
+ # override. The method is automatically called to marshall data to
248
+ # Jiak. You do not call this method explicitly.
249
+
250
+ # Data classes that do not used the jattr_* methods to specify attributes
251
+ # must override this method.
236
252
  #
237
253
  # ====Example
238
- # <code>
239
- # def for_jiak
240
- # { :writable_f1 => @writable_f1,
241
- # :writable_f2 => @writable_f2
242
- # }
243
- # end
244
- # </code>
245
- #
246
- # Raise JiakDataException if not explicitly defined by user-defined data class.
247
- def for_jiak
248
- raise JiakDataException, "#{self} must define for_jiak"
254
+ # def to_jiak
255
+ # { :writable_f1 => @writable_f1,
256
+ # :writable_f2 => @writable_f2
257
+ # }
258
+ # end
259
+ def to_jiak
260
+ self.class.schema.write_mask.inject({}) do |build,field|
261
+ build[field] = send("#{field}")
262
+ build
263
+ end
249
264
  end
250
265
 
251
266
  # :call-seq:
@@ -257,15 +272,15 @@ module RiakRest
257
272
  #
258
273
  # ====Example
259
274
  # A simple implementation would look like:
260
- # <code>
261
- # def keygen
262
- # f1.to_s
263
- # end
264
- # </code>
275
+ # def keygen
276
+ # f1.to_s
277
+ # end
278
+ #
279
+ # The JiakData#ClassMethods#keygen class method can also be used to
280
+ # override this default implement method.
265
281
  def keygen
266
282
  nil
267
283
  end
268
284
 
269
285
  end
270
286
  end
271
-
@@ -0,0 +1,69 @@
1
+ module RiakRest
2
+ # JiakDataFields provides a easy-to-create JiakData implementation.
3
+ #
4
+ # ===Usage
5
+ # <code>
6
+ # Dog = JiakDataFields.create(:name,:weight)
7
+ #
8
+ # addie = Dog.new(:name => "Adelaide", :weight => 45)
9
+ # addie.name # => "Adeliade"
10
+ # addie.weight # => 45
11
+ #
12
+ # addie.weight = 47 # => 47
13
+ # </code>
14
+ #
15
+ class JiakDataFields
16
+
17
+ # :call-seq:
18
+ # JiakDataFields.create(:f1,...,fn) -> JiakDataFields
19
+ # JiakDataFields.create([:f1,...,fn]) -> JiakDataFields
20
+ # JiakDataFields.create(schema) -> JiakDataFields
21
+ #
22
+ # Creates a JiakDataFields class that can be used to create JiakData objects
23
+ # containing the specified fields.
24
+ def self.create(*args)
25
+ Class.new do
26
+ include JiakData
27
+
28
+ if(args.size == 1)
29
+ case args[0]
30
+ when Symbol, Array
31
+ jattr_accessor *args[0]
32
+ when JiakSchema
33
+ jattr_reader *args[0].read_mask
34
+ jattr_writer *args[0].write_mask
35
+ allow *args[0].allowed_fields
36
+ require *args[0].required_fields
37
+ end
38
+ else
39
+ jattr_accessor *args
40
+ end
41
+
42
+ # :call-seq:
43
+ # data[field] -> value
44
+ #
45
+ # Get the value of a field.
46
+ #
47
+ # Returns <code>nil</code> if <code>field</code> was not declared for
48
+ # this class. <code>field</code> can be in either string or symbol
49
+ # form.
50
+ def [](key)
51
+ send("#{key}") rescue nil
52
+ end
53
+
54
+ # :call-seq:
55
+ # data[field] = value
56
+ #
57
+ # Set the value of a field.
58
+ #
59
+ # Returns the value set, or <code>nil</code> if <code>field</code> was
60
+ # not declared for this class.
61
+ def []=(key,value)
62
+ send("#{key}=",value) rescue nil
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -5,12 +5,10 @@ module RiakRest
5
5
  # to be traversed later using a QueryLink.
6
6
  #
7
7
  # ===Usage
8
- # <code>
9
- # link = JiakLink.new('people','callie','sister')
10
- # </code>
11
- # If the above link were added to a JiakObject in the same bucket and keyed
12
- # by the string 'remy', the link from remy to the sister callie would be
13
- # retrieve using JiakClient#walk and the query link
8
+ # link = JiakLink.new('people','callie','sister')
9
+ # If the above link were added to a JiakObject <code>remy</code> , then
10
+ # <code>remy</code> could retrieve the sister object <code>callie</code>
11
+ # using JiakClient#walk with a query link
14
12
  # <code>QueryLink.new('people','sister')</code>
15
13
  #
16
14
  class JiakLink
@@ -70,10 +68,10 @@ module RiakRest
70
68
  end
71
69
 
72
70
  # :call-seq:
73
- # link.for_jiak -> JSON
71
+ # link.to_jiak -> JSON
74
72
  #
75
73
  # Representation of this JiakLink for transport to Jiak.
76
- def for_jiak
74
+ def to_jiak
77
75
  [@bucket, @key, @tag]
78
76
  end
79
77
 
@@ -1,6 +1,10 @@
1
1
  module RiakRest
2
2
 
3
- # Wrapper for JiakData.
3
+ # JiakObject wraps a JiakData and also maintains Jiak information, Riak
4
+ # context, and a set a links used in conjunction with the Jiak link
5
+ # map/reduce facility.
6
+ #
7
+ # See JiakClient for example usage.
4
8
  class JiakObject
5
9
 
6
10
  attr_accessor :bucket, :key, :data, :links, :riak
@@ -41,12 +45,11 @@ module RiakRest
41
45
  end
42
46
 
43
47
  # :call-seq:
44
- # JiakObject.from_jiak(jiak) -> JiakObject
48
+ # JiakObject.jiak_create(jiak) -> JiakObject
45
49
  #
46
- # Create a JiakObject from parsed JSON returned by the Jiak server. Calls
47
- # the <code>jiak_create</code> of the JiakData class passed as the second
48
- # argument to inflate the data into the user-defined data class.
49
- def self.from_jiak(jiak,klass)
50
+ # Called by the Core Client framework when marshalling from Jiak. This
51
+ # method does not need to be call explicitly.
52
+ def self.jiak_create(jiak,klass)
50
53
  jiak[:bucket] = JiakBucket.new(jiak.delete('bucket'),klass)
51
54
  jiak[:data] = klass.jiak_create(jiak.delete('object'))
52
55
  jiak[:links] = jiak.delete('links').map {|link| JiakLink.new(link)}
@@ -58,24 +61,23 @@ module RiakRest
58
61
  end
59
62
 
60
63
  # :call-seq:
61
- # jiak_object.to_jiak -> JSON
64
+ # to_jiak -> hash
62
65
  #
63
- # Create a representation suitable for sending to a Jiak server. Calls the
64
- # <code>for_jiak</code> method of the wrapped JiakData. Called by
65
- # JiakClient when transporting an object to Jiak.
66
+ # Called by the Core Client framework when marshalling to Jiak. This method
67
+ # does not need to be call explicitly.
66
68
  def to_jiak
67
69
  jiak = {
68
70
  :bucket => @bucket.name,
69
71
  :key => @key,
70
- :object => @data.for_jiak,
71
- :links => @links.map {|link| link.for_jiak}
72
+ :object => @data.to_jiak,
73
+ :links => @links.map {|link| link.to_jiak}
72
74
  }
73
75
  if(@riak)
74
76
  jiak[:vclock] = @riak[:vclock]
75
77
  jiak[:vtag] = @riak[:vtag]
76
78
  jiak[:lastmod] = @riak[:lastmod]
77
79
  end
78
- jiak.to_json
80
+ jiak
79
81
  end
80
82
 
81
83
  # :call-seq: