riakrest 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,265 @@
|
|
1
|
+
module RiakRest
|
2
|
+
|
3
|
+
# All end-user data stored via RiakRest is contained in user-defined data
|
4
|
+
# objects. To make a user-defined data object, include module JiakData into
|
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
|
8
|
+
# create user-data instances.
|
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.
|
15
|
+
#
|
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.
|
21
|
+
#
|
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.
|
27
|
+
#
|
28
|
+
# ===Example
|
29
|
+
# <code>
|
30
|
+
# class FooBarBaz
|
31
|
+
# include JiakData
|
32
|
+
#
|
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
|
53
|
+
#
|
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
|
+
module JiakData
|
79
|
+
|
80
|
+
# ----------------------------------------------------------------------
|
81
|
+
# Class methods
|
82
|
+
# ----------------------------------------------------------------------
|
83
|
+
|
84
|
+
# 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.
|
88
|
+
module ClassMethods
|
89
|
+
|
90
|
+
# :call-seq:
|
91
|
+
# allowed :f1, ..., :fn -> array
|
92
|
+
#
|
93
|
+
# Fields allowed in Jiak interactions. Returns an array of the allowed
|
94
|
+
# fields.
|
95
|
+
#
|
96
|
+
def allowed(*fields)
|
97
|
+
arr_fields = create_array(fields)
|
98
|
+
fields.each {|field| attr_accessor field}
|
99
|
+
@schema = JiakSchema.new(arr_fields)
|
100
|
+
arr_fields
|
101
|
+
end
|
102
|
+
|
103
|
+
# :call-seq:
|
104
|
+
# required :f1, ..., :fn -> array
|
105
|
+
#
|
106
|
+
# Fields required during in Jiak interactions. Returns an array of the
|
107
|
+
# required fields.
|
108
|
+
#
|
109
|
+
def required(*fields)
|
110
|
+
set_fields('required_fields',*fields)
|
111
|
+
end
|
112
|
+
|
113
|
+
# :call-seq:
|
114
|
+
# readable :f1, ..., :fn -> array
|
115
|
+
#
|
116
|
+
# Fields returned by Jiak on retrieval. Returns an array of the fields in
|
117
|
+
# the read mask.
|
118
|
+
#
|
119
|
+
def readable(*fields)
|
120
|
+
set_fields('read_mask',*fields)
|
121
|
+
end
|
122
|
+
|
123
|
+
# :call-seq:
|
124
|
+
# writable :f1, ..., :fn -> arry
|
125
|
+
#
|
126
|
+
# Fields that can be written during Jiak interaction. Returns an array of
|
127
|
+
# the fields in the write mask.
|
128
|
+
#
|
129
|
+
def writable(*fields)
|
130
|
+
set_fields('write_mask',*fields)
|
131
|
+
end
|
132
|
+
|
133
|
+
# :call-seq:
|
134
|
+
# readwrite :f1, ..., :fn -> array
|
135
|
+
#
|
136
|
+
# Set the read and write masks to the same fields. Returns an array of
|
137
|
+
# the fields in the masks.
|
138
|
+
def readwrite(*fields)
|
139
|
+
arr_fields = set_fields('readwrite',*fields)
|
140
|
+
arr_fields
|
141
|
+
end
|
142
|
+
|
143
|
+
def set_fields(which,*fields)
|
144
|
+
arr_fields = create_array(fields)
|
145
|
+
check_allowed(arr_fields)
|
146
|
+
@schema.send("#{which}=",arr_fields)
|
147
|
+
arr_fields
|
148
|
+
end
|
149
|
+
private :set_fields
|
150
|
+
|
151
|
+
# :call-seq:
|
152
|
+
# JiakData.schema -> JiakSchema
|
153
|
+
#
|
154
|
+
# Get a JiakSchema representation determined by
|
155
|
+
# <code>allowed, required, readable, writable</code>.
|
156
|
+
#
|
157
|
+
def schema
|
158
|
+
@schema
|
159
|
+
end
|
160
|
+
|
161
|
+
# :call-seq:
|
162
|
+
# JiakData.jiak_create(jiak) -> JiakData
|
163
|
+
#
|
164
|
+
# Create an instance of user-defined data object from the fields read
|
165
|
+
# by Jiak. These fields are determined by the read mask of the
|
166
|
+
# structured Jiak interaction. See JiakSchema for read mask discussion.
|
167
|
+
#
|
168
|
+
# User-defined data classes must override this method. The method is
|
169
|
+
# called during the creation of a JiakObject from information returned by
|
170
|
+
# Jiak. The JiakObject contains the user-defined data itself. You do not
|
171
|
+
# call this method explicitly.
|
172
|
+
#
|
173
|
+
# ====Example
|
174
|
+
# <code>
|
175
|
+
# def initialize(f1,f2)
|
176
|
+
# @f1 = f1
|
177
|
+
# @f2 = f2
|
178
|
+
# end
|
179
|
+
# def jiak_create(jiak)
|
180
|
+
# new(jiak['f1'], jiak['f2'])
|
181
|
+
# end
|
182
|
+
# </code>
|
183
|
+
#
|
184
|
+
# Raise JiakDataException if not explicitly defined by user-defined data class.
|
185
|
+
def jiak_create(json)
|
186
|
+
raise JiakDataException, "#{self} must define jiak_create"
|
187
|
+
end
|
188
|
+
|
189
|
+
def create_array(*fields)
|
190
|
+
if(fields.size == 1 && fields[0].is_a?(Array))
|
191
|
+
array = fields[0]
|
192
|
+
else
|
193
|
+
array = fields
|
194
|
+
end
|
195
|
+
array.map {|field| field}
|
196
|
+
array
|
197
|
+
end
|
198
|
+
private :create_array
|
199
|
+
|
200
|
+
def check_allowed(fields)
|
201
|
+
allowed_fields = @schema.allowed_fields
|
202
|
+
fields.each do |field|
|
203
|
+
unless allowed_fields.include?(field)
|
204
|
+
raise JiakDataException, "field '#{field}' not allowed"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
private :check_allowed
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.included(including_class) # :nodoc:
|
212
|
+
including_class.extend(ClassMethods)
|
213
|
+
end
|
214
|
+
|
215
|
+
# ----------------------------------------------------------------------
|
216
|
+
# Instance methods
|
217
|
+
# ----------------------------------------------------------------------
|
218
|
+
|
219
|
+
# :call-seq:
|
220
|
+
# for_jiak -> hash
|
221
|
+
#
|
222
|
+
# Provide a hash structure of the data to write to Jiak. The fields for
|
223
|
+
# this structure should come from the write mask of the structured Jiak
|
224
|
+
# interaction. See JiakSchema for write mask discussion.
|
225
|
+
#
|
226
|
+
# User-defined data classes must override this method. The method is called
|
227
|
+
# during the creation of a JiakObject to send information to Jiak. The
|
228
|
+
# JiakObject contains the user-defined data itself. You do not call this
|
229
|
+
# method explicitly.
|
230
|
+
#
|
231
|
+
# ====Example
|
232
|
+
# <code>
|
233
|
+
# def for_jiak
|
234
|
+
# { :writable_f1 => @writable_f1,
|
235
|
+
# :writable_f2 => @writable_f2
|
236
|
+
# }
|
237
|
+
# end
|
238
|
+
# </code>
|
239
|
+
#
|
240
|
+
# Raise JiakDataException if not explicitly defined by user-defined data class.
|
241
|
+
def for_jiak
|
242
|
+
raise JiakDataException, "#{self} must define for_jiak"
|
243
|
+
end
|
244
|
+
|
245
|
+
# :call-seq:
|
246
|
+
# keygen -> string
|
247
|
+
#
|
248
|
+
# Generate Jiak key for data. Default implementation returns
|
249
|
+
# <code>nil</code> which instructs the Jiak server to generate a random
|
250
|
+
# key. Override for user-defined data behaviour.
|
251
|
+
#
|
252
|
+
# ====Example
|
253
|
+
# A simple implementation would look like:
|
254
|
+
# <code>
|
255
|
+
# def keygen
|
256
|
+
# f1.to_s
|
257
|
+
# end
|
258
|
+
# </code>
|
259
|
+
def keygen
|
260
|
+
nil
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module RiakRest
|
2
|
+
|
3
|
+
# Represents a link between object in Jiak. JiakLinks are used to link a
|
4
|
+
# JiakObject to another JiakObject at a bucket/key. The tag allows the link
|
5
|
+
# to be traversed later using a QueryLink.
|
6
|
+
#
|
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
|
14
|
+
# <code>QueryLink.new('people','sister')</code>
|
15
|
+
#
|
16
|
+
class JiakLink
|
17
|
+
|
18
|
+
attr_accessor :bucket, :key, :tag
|
19
|
+
|
20
|
+
# :call-seq:
|
21
|
+
# JiakLink.new(bucket,key,tag) -> JiakLink
|
22
|
+
# JiakLink.new([bucket,key,tag]) -> JiakLink
|
23
|
+
#
|
24
|
+
# Create a link (designated by tag) to an object at bucket/key. Bucket can
|
25
|
+
# be either a JiakBucket or a string bucket name; key and tag must both be
|
26
|
+
# strings.
|
27
|
+
#
|
28
|
+
def initialize(*args)
|
29
|
+
case args.size
|
30
|
+
when 1
|
31
|
+
if args[0].is_a? Array
|
32
|
+
bucket, key, tag = args[0][0], args[0][1], args[0][2]
|
33
|
+
elsif args[0].is_a? JiakLink
|
34
|
+
bucket, key, tag = args[0].bucket, args[0].key, args[0].tag
|
35
|
+
else
|
36
|
+
raise JiakLinkException, "argument error"
|
37
|
+
end
|
38
|
+
when 3
|
39
|
+
bucket, key, tag = args[0], args[1], args[2]
|
40
|
+
else
|
41
|
+
raise JiakLinkException, "argument error"
|
42
|
+
end
|
43
|
+
|
44
|
+
@bucket, @key, @tag = transform_args(bucket,key,tag)
|
45
|
+
end
|
46
|
+
|
47
|
+
# :call-seq:
|
48
|
+
# link.bucket = bucket
|
49
|
+
#
|
50
|
+
# Set the bucket field.
|
51
|
+
def bucket=(bucket)
|
52
|
+
bucket = bucket.name if bucket.is_a? JiakBucket
|
53
|
+
@bucket = transform_arg(bucket)
|
54
|
+
end
|
55
|
+
|
56
|
+
# :call-seq:
|
57
|
+
# link.key = key
|
58
|
+
#
|
59
|
+
# Set the key field.
|
60
|
+
def key=(key)
|
61
|
+
@key = transform_arg(key)
|
62
|
+
end
|
63
|
+
|
64
|
+
# :call-seq:
|
65
|
+
# link.tag = tag
|
66
|
+
#
|
67
|
+
# Set the tag field.
|
68
|
+
def tag=(tag)
|
69
|
+
@tag = transform_arg(tag)
|
70
|
+
end
|
71
|
+
|
72
|
+
# :call-seq:
|
73
|
+
# link.for_jiak -> JSON
|
74
|
+
#
|
75
|
+
# Representation of this JiakLink for transport to Jiak.
|
76
|
+
def for_jiak
|
77
|
+
[@bucket, @key, @tag]
|
78
|
+
end
|
79
|
+
|
80
|
+
# :call-seq:
|
81
|
+
# link == other -> true or false
|
82
|
+
#
|
83
|
+
# Equality -- JiakLinks are equal if they contain the same attribute values.
|
84
|
+
def ==(other)
|
85
|
+
(@bucket == other.bucket &&
|
86
|
+
@key == other.key &&
|
87
|
+
@tag == other.tag) rescue false
|
88
|
+
end
|
89
|
+
|
90
|
+
# :call-seq:
|
91
|
+
# eql?(other) -> true or false
|
92
|
+
#
|
93
|
+
# Returns <code>true</code> if <code>other</code> is a JiakLink with the
|
94
|
+
# same attribute values.
|
95
|
+
def eql?(other)
|
96
|
+
other.is_a?(JiakLink) &&
|
97
|
+
@bucket.eql?(other.bucket) &&
|
98
|
+
@key.eql?(other.key) &&
|
99
|
+
@tag.eql?(other.tag)
|
100
|
+
end
|
101
|
+
|
102
|
+
def hash # :nodoc:
|
103
|
+
@bucket.hash + @key.hash + @tag.hash
|
104
|
+
end
|
105
|
+
|
106
|
+
# String representation of this JiakLink.
|
107
|
+
def to_s
|
108
|
+
"[#{bucket},#{key},#{tag}]"
|
109
|
+
end
|
110
|
+
|
111
|
+
def transform_args(b,k,t)
|
112
|
+
b = b.name if b.is_a? JiakBucket
|
113
|
+
[transform_arg(b),transform_arg(k),transform_arg(t)]
|
114
|
+
end
|
115
|
+
private :transform_args
|
116
|
+
|
117
|
+
def transform_arg(arg)
|
118
|
+
unless arg.is_a? String
|
119
|
+
raise JiakLinkException, "Link elements must be Strings."
|
120
|
+
end
|
121
|
+
value = arg.dup
|
122
|
+
value.strip!
|
123
|
+
if value.empty?
|
124
|
+
raise JiakLinkException, "Link elements can't be empty."
|
125
|
+
end
|
126
|
+
value
|
127
|
+
end
|
128
|
+
private :transform_arg
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
module RiakRest
|
2
|
+
|
3
|
+
# Wrapper for JiakData.
|
4
|
+
class JiakObject
|
5
|
+
|
6
|
+
attr_accessor :bucket, :key, :data, :links, :riak
|
7
|
+
|
8
|
+
# :call-seq:
|
9
|
+
# JiakObject.new(opts) -> JiakObject
|
10
|
+
#
|
11
|
+
# Create a object for Jiak storage. Valid options:
|
12
|
+
# <code>:bucket</code> :: JiakBucket for storage.
|
13
|
+
# <code>:data</code> :: Object JiakData to be stored.
|
14
|
+
# <code>:key</code> :: Object key.
|
15
|
+
# <code>:links</code> :: Object JiakLink array.
|
16
|
+
#
|
17
|
+
# The bucket and data options are required.
|
18
|
+
#
|
19
|
+
# The key and links options are optional. If no key is provided, the
|
20
|
+
# <code>keygen</code> method of <code>data</code> is used to provide the
|
21
|
+
# key. The default implementation of JiakData#keygen is an empty string,
|
22
|
+
# which instructs the Jiak server to generate a random key. If no links are
|
23
|
+
# provided, the default uses an empty array.
|
24
|
+
#
|
25
|
+
# There are other options used by the system to maintain the context of the
|
26
|
+
# JiakObject on the Riak cluster. These options should not be manually
|
27
|
+
# altered and are purposely not described here.
|
28
|
+
def initialize(opts)
|
29
|
+
opts[:links] ||= []
|
30
|
+
check_opts(opts)
|
31
|
+
|
32
|
+
@bucket = check_bucket(opts[:bucket])
|
33
|
+
@data = check_data(opts[:data])
|
34
|
+
@key = transform_key(opts[:key] || @data.keygen)
|
35
|
+
@links = check_links(opts[:links])
|
36
|
+
|
37
|
+
# The Riak context for the object if provided.
|
38
|
+
if opts[:vclock]
|
39
|
+
@riak = opts.select {|k,v| [:vclock,:vtag,:lastmod].include?(k)}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# :call-seq:
|
44
|
+
# JiakObject.from_jiak(jiak) -> JiakObject
|
45
|
+
#
|
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
|
+
jiak[:bucket] = JiakBucket.new(jiak.delete('bucket'),klass)
|
51
|
+
jiak[:data] = klass.jiak_create(jiak.delete('object'))
|
52
|
+
jiak[:links] = jiak.delete('links').map {|link| JiakLink.new(link)}
|
53
|
+
|
54
|
+
new(jiak.inject({}) do |build, (key, value)|
|
55
|
+
build[key.to_sym] = value
|
56
|
+
build
|
57
|
+
end)
|
58
|
+
end
|
59
|
+
|
60
|
+
# :call-seq:
|
61
|
+
# jiak_object.to_jiak -> JSON
|
62
|
+
#
|
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
|
+
def to_jiak
|
67
|
+
jiak = {
|
68
|
+
:bucket => @bucket.name,
|
69
|
+
:key => @key,
|
70
|
+
:object => @data.for_jiak,
|
71
|
+
:links => @links.map {|link| link.for_jiak}
|
72
|
+
}
|
73
|
+
if(@riak)
|
74
|
+
jiak[:vclock] = @riak[:vclock]
|
75
|
+
jiak[:vtag] = @riak[:vtag]
|
76
|
+
jiak[:lastmod] = @riak[:lastmod]
|
77
|
+
end
|
78
|
+
jiak.to_json
|
79
|
+
end
|
80
|
+
|
81
|
+
# :call-seq:
|
82
|
+
# jiak_object.bucket = bucket
|
83
|
+
#
|
84
|
+
# Set the bucket for a JiakObject. Bucket must be a JiakBucket.
|
85
|
+
def bucket=(bucket)
|
86
|
+
@bucket = check_bucket(bucket)
|
87
|
+
end
|
88
|
+
|
89
|
+
# :call-seq:
|
90
|
+
# jiak_object.key = string or nil
|
91
|
+
#
|
92
|
+
# Set the key for a JiakObject. Key string is stripped of leading and
|
93
|
+
# trailing blanks. A nil key is interpreted as an empty string.
|
94
|
+
def key=(key)
|
95
|
+
@key = transform_key(key)
|
96
|
+
end
|
97
|
+
|
98
|
+
# :call-seq:
|
99
|
+
# jiak_object.data = {}
|
100
|
+
#
|
101
|
+
# Set the data wrapped by a JiakObject. The data must be a JiakData object.
|
102
|
+
def data=(data)
|
103
|
+
@data = check_data(data)
|
104
|
+
end
|
105
|
+
|
106
|
+
# :call-seq:
|
107
|
+
# jiak_object.links = []
|
108
|
+
#
|
109
|
+
# Set the links array for JiakObject. Each array element must be a
|
110
|
+
# JiakLink.
|
111
|
+
def links=(links)
|
112
|
+
@links = check_links(links)
|
113
|
+
end
|
114
|
+
|
115
|
+
# :call-seq:
|
116
|
+
# jiak_object << JiakLink -> JiakObject
|
117
|
+
#
|
118
|
+
# Convenience method for adding a JiakLink to the links for a
|
119
|
+
# JiakObject. Duplicate links are ignored. Returns the JiakObject for
|
120
|
+
# chaining.
|
121
|
+
def <<(link)
|
122
|
+
link = check_link(link)
|
123
|
+
@links << link unless @links.include?(link)
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# :call-seq:
|
128
|
+
# jiak_object.riak = riak
|
129
|
+
#
|
130
|
+
# Set the Riak context for a JiakObject.
|
131
|
+
def riak=(riak)
|
132
|
+
@riak = check_riak(riak)
|
133
|
+
end
|
134
|
+
|
135
|
+
# :call-seq:
|
136
|
+
# jiak_object == other -> true or false
|
137
|
+
#
|
138
|
+
# Equality -- Two JiakObjects are equal if they contain the same values
|
139
|
+
# for all attributes.
|
140
|
+
def ==(other)
|
141
|
+
(@bucket == other.bucket &&
|
142
|
+
@key == other.key &&
|
143
|
+
@data == other.data &&
|
144
|
+
@links == other.links
|
145
|
+
) rescue false
|
146
|
+
end
|
147
|
+
|
148
|
+
# :call-seq:
|
149
|
+
# jiak_object.eql?(other) -> true or false
|
150
|
+
#
|
151
|
+
# Returns <code>true</code> if <code>other</code> is a JiakObject with the
|
152
|
+
# same the same attribute values.
|
153
|
+
def eql?(other)
|
154
|
+
other.is_a?(JiakObject) &&
|
155
|
+
@bucket.eql?(other.bucket) &&
|
156
|
+
@key.eql?(other.key) &&
|
157
|
+
@data.eql?(other.data) &&
|
158
|
+
@links.eql?(other.links)
|
159
|
+
end
|
160
|
+
|
161
|
+
def hash # :nodoc:
|
162
|
+
@bucket.name.hash + @key.hash + @data.hash + @links.hash + @riak.hash
|
163
|
+
end
|
164
|
+
|
165
|
+
def check_opts(opts)
|
166
|
+
err = opts.select {|k,v| !VALID_OPTS.include?(k)}
|
167
|
+
unless err.empty?
|
168
|
+
raise JiakObjectException, "unrecognized options: #{err.keys}"
|
169
|
+
end
|
170
|
+
opts
|
171
|
+
end
|
172
|
+
private :check_opts
|
173
|
+
|
174
|
+
def check_bucket(bucket)
|
175
|
+
unless bucket.is_a?(JiakBucket)
|
176
|
+
raise JiakObjectException, "Bucket must be a JiakBucket."
|
177
|
+
end
|
178
|
+
bucket
|
179
|
+
end
|
180
|
+
private :check_bucket
|
181
|
+
|
182
|
+
def transform_key(key)
|
183
|
+
# Change nil key to empty
|
184
|
+
o_key = key.nil? ? '' : key.dup
|
185
|
+
unless o_key.is_a?(String)
|
186
|
+
raise JiakObjectException, "Key must be a String"
|
187
|
+
end
|
188
|
+
o_key.strip!
|
189
|
+
o_key
|
190
|
+
end
|
191
|
+
private :transform_key
|
192
|
+
|
193
|
+
def check_data(data)
|
194
|
+
unless data.is_a?(JiakData)
|
195
|
+
raise JiakObjectException, "Data must be a JiakData."
|
196
|
+
end
|
197
|
+
data
|
198
|
+
end
|
199
|
+
private :check_data
|
200
|
+
|
201
|
+
def check_links(links)
|
202
|
+
unless links.is_a? Array
|
203
|
+
raise JiakObjectException, "Links must be an array"
|
204
|
+
end
|
205
|
+
links.each do |link|
|
206
|
+
check_link(link)
|
207
|
+
end
|
208
|
+
links
|
209
|
+
end
|
210
|
+
private :check_links
|
211
|
+
|
212
|
+
def check_link(link)
|
213
|
+
unless link.is_a? JiakLink
|
214
|
+
raise JiakObjectException, "Each link must be a JiakLink"
|
215
|
+
end
|
216
|
+
link
|
217
|
+
end
|
218
|
+
private :check_link
|
219
|
+
|
220
|
+
def check_riak(riak)
|
221
|
+
err = riak.select {|k,v| !VALID_RIAK.include?(k)}
|
222
|
+
unless err.empty?
|
223
|
+
raise JiakObjectException, "unrecognized options: #{err.keys}"
|
224
|
+
end
|
225
|
+
riak
|
226
|
+
end
|
227
|
+
|
228
|
+
private
|
229
|
+
VALID_RIAK = [:vclock,:vtag,:lastmod]
|
230
|
+
VALID_OPTS = [:bucket,:key,:data,:links] + VALID_RIAK
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|