riakrest 0.0.4 → 0.1.0

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