aws 1.10.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.
- data/History.txt +246 -0
- data/Manifest.txt +19 -0
- data/README.txt +168 -0
- data/Rakefile +17 -0
- data/lib/acf/right_acf_interface.rb +379 -0
- data/lib/awsbase/benchmark_fix.rb +39 -0
- data/lib/awsbase/right_awsbase.rb +797 -0
- data/lib/awsbase/support.rb +111 -0
- data/lib/ec2/right_ec2.rb +1737 -0
- data/lib/right_aws.rb +69 -0
- data/lib/s3/right_s3.rb +1094 -0
- data/lib/s3/right_s3_interface.rb +1175 -0
- data/lib/sdb/active_sdb.rb +936 -0
- data/lib/sdb/right_sdb_interface.rb +701 -0
- data/lib/sqs/right_sqs.rb +388 -0
- data/lib/sqs/right_sqs_gen2.rb +286 -0
- data/lib/sqs/right_sqs_gen2_interface.rb +444 -0
- data/lib/sqs/right_sqs_interface.rb +596 -0
- metadata +96 -0
@@ -0,0 +1,936 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2008 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'uuidtools'
|
26
|
+
rescue LoadError => e
|
27
|
+
STDERR.puts("RightSDB requires the uuidtools gem. Run \'gem install uuidtools\' and try again.")
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
|
31
|
+
module RightAws
|
32
|
+
|
33
|
+
# = RightAws::ActiveSdb -- RightScale SDB interface (alpha release)
|
34
|
+
# The RightAws::ActiveSdb class provides a complete interface to Amazon's Simple
|
35
|
+
# Database Service.
|
36
|
+
#
|
37
|
+
# ActiveSdb is in alpha and does not load by default with the rest of RightAws. You must use an additional require statement to load the ActiveSdb class. For example:
|
38
|
+
#
|
39
|
+
# require 'right_aws'
|
40
|
+
# require 'sdb/active_sdb'
|
41
|
+
#
|
42
|
+
# Additionally, the ActiveSdb class requires the 'uuidtools' gem; this gem is not normally required by RightAws and is not installed as a
|
43
|
+
# dependency of RightAws.
|
44
|
+
#
|
45
|
+
# Simple ActiveSdb usage example:
|
46
|
+
#
|
47
|
+
# class Client < RightAws::ActiveSdb::Base
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# # connect to SDB
|
51
|
+
# RightAws::ActiveSdb.establish_connection
|
52
|
+
#
|
53
|
+
# # create domain
|
54
|
+
# Client.create_domain
|
55
|
+
#
|
56
|
+
# # create initial DB
|
57
|
+
# Client.create 'name' => 'Bush', 'country' => 'USA', 'gender' => 'male', 'expiration' => '2009', 'post' => 'president'
|
58
|
+
# Client.create 'name' => 'Putin', 'country' => 'Russia', 'gender' => 'male', 'expiration' => '2008', 'post' => 'president'
|
59
|
+
# Client.create 'name' => 'Medvedev', 'country' => 'Russia', 'gender' => 'male', 'expiration' => '2012', 'post' => 'president'
|
60
|
+
# Client.create 'name' => 'Mary', 'country' => 'USA', 'gender' => 'female', 'hobby' => ['patchwork', 'bundle jumping']
|
61
|
+
# Client.create 'name' => 'Mary', 'country' => 'Russia', 'gender' => 'female', 'hobby' => ['flowers', 'cats', 'cooking']
|
62
|
+
# sandy_id = Client.create('name' => 'Sandy', 'country' => 'Russia', 'gender' => 'female', 'hobby' => ['flowers', 'cats', 'cooking']).id
|
63
|
+
#
|
64
|
+
# # find all Bushes in USA
|
65
|
+
# Client.find(:all, :conditions => ["['name'=?] intersection ['country'=?]",'Bush','USA']).each do |client|
|
66
|
+
# client.reload
|
67
|
+
# puts client.attributes.inspect
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# # find all Maries through the world
|
71
|
+
# Client.find_all_by_name_and_gender('Mary','female').each do |mary|
|
72
|
+
# mary.reload
|
73
|
+
# puts "#{mary[:name]}, gender: #{mary[:gender]}, hobbies: #{mary[:hobby].join(',')}"
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# # find new russian president
|
77
|
+
# medvedev = Client.find_by_post_and_country_and_expiration('president','Russia','2012')
|
78
|
+
# if medvedev
|
79
|
+
# medvedev.reload
|
80
|
+
# medvedev.save_attributes('age' => '42', 'hobby' => 'Gazprom')
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # retire old president
|
84
|
+
# Client.find_by_name('Putin').delete
|
85
|
+
#
|
86
|
+
# # Sandy disappointed in 'cooking' and decided to hide her 'gender' and 'country' ()
|
87
|
+
# sandy = Client.find(sandy_id)
|
88
|
+
# sandy.reload
|
89
|
+
# sandy.delete_values('hobby' => ['cooking'] )
|
90
|
+
# sandy.delete_attributes('country', 'gender')
|
91
|
+
#
|
92
|
+
# # remove domain
|
93
|
+
# Client.delete_domain
|
94
|
+
#
|
95
|
+
class ActiveSdb
|
96
|
+
|
97
|
+
module ActiveSdbConnect
|
98
|
+
def connection
|
99
|
+
@connection || raise(ActiveSdbError.new('Connection to SDB is not established'))
|
100
|
+
end
|
101
|
+
def close
|
102
|
+
if @connection
|
103
|
+
@connection.fin
|
104
|
+
end
|
105
|
+
end
|
106
|
+
# Create a new handle to an Sdb account. All handles share the same per process or per thread
|
107
|
+
# HTTP connection to Amazon Sdb. Each handle is for a specific account.
|
108
|
+
# The +params+ are passed through as-is to RightAws::SdbInterface.new
|
109
|
+
# Params:
|
110
|
+
# { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
|
111
|
+
# :port => 443 # Amazon service port: 80 or 443(default)
|
112
|
+
# :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
|
113
|
+
# :signature_version => '0' # The signature version : '0' or '1'(default)
|
114
|
+
# DEPRECATED :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
|
115
|
+
# :connection_mode => :default # options are :single, :multi (same as old multi_thread=>true), :pool, :per_request
|
116
|
+
# :logger => Logger Object # Logger instance: logs to STDOUT if omitted
|
117
|
+
# :nil_representation => 'mynil'} # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')
|
118
|
+
|
119
|
+
def establish_connection(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
|
120
|
+
@connection = RightAws::SdbInterface.new(aws_access_key_id, aws_secret_access_key, params)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class ActiveSdbError < RuntimeError ; end
|
125
|
+
|
126
|
+
class << self
|
127
|
+
include ActiveSdbConnect
|
128
|
+
|
129
|
+
# Retreive a list of domains.
|
130
|
+
#
|
131
|
+
# put RightAws::ActiveSdb.domains #=> ['co-workers','family','friends','clients']
|
132
|
+
#
|
133
|
+
def domains
|
134
|
+
connection.list_domains[:domains]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Create new domain.
|
138
|
+
# Raises no errors if the domain already exists.
|
139
|
+
#
|
140
|
+
# RightAws::ActiveSdb.create_domain('alpha') #=> {:request_id=>"6fc652a0-0000-41d5-91f4-3ed390a3d3b2", :box_usage=>"0.0055590278"}
|
141
|
+
#
|
142
|
+
def create_domain(domain_name)
|
143
|
+
connection.create_domain(domain_name)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Remove domain from SDB.
|
147
|
+
# Raises no errors if the domain does not exist.
|
148
|
+
#
|
149
|
+
# RightAws::ActiveSdb.create_domain('alpha') #=> {:request_id=>"6fc652a0-0000-41c6-91f4-3ed390a3d3b2", :box_usage=>"0.0055590001"}
|
150
|
+
#
|
151
|
+
def delete_domain(domain_name)
|
152
|
+
connection.delete_domain(domain_name)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class Base
|
157
|
+
|
158
|
+
class << self
|
159
|
+
include ActiveSdbConnect
|
160
|
+
|
161
|
+
# next_token value returned by last find: is useful to continue finding
|
162
|
+
attr_accessor :next_token
|
163
|
+
|
164
|
+
# Returns a RightAws::SdbInterface object
|
165
|
+
#
|
166
|
+
# class A < RightAws::ActiveSdb::Base
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# class B < RightAws::ActiveSdb::Base
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# class C < RightAws::ActiveSdb::Base
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# RightAws::ActiveSdb.establish_connection 'key_id_1', 'secret_key_1'
|
176
|
+
#
|
177
|
+
# C.establish_connection 'key_id_2', 'secret_key_2'
|
178
|
+
#
|
179
|
+
# # A and B uses the default connection, C - uses its own
|
180
|
+
# puts A.connection #=> #<RightAws::SdbInterface:0xb76d6d7c>
|
181
|
+
# puts B.connection #=> #<RightAws::SdbInterface:0xb76d6d7c>
|
182
|
+
# puts C.connection #=> #<RightAws::SdbInterface:0xb76d6ca0>
|
183
|
+
#
|
184
|
+
def connection
|
185
|
+
@connection || ActiveSdb::connection
|
186
|
+
end
|
187
|
+
|
188
|
+
@domain = nil
|
189
|
+
|
190
|
+
# Current domain name.
|
191
|
+
#
|
192
|
+
# # if 'ActiveSupport' is not loaded then class name converted to downcase
|
193
|
+
# class Client < RightAws::ActiveSdb::Base
|
194
|
+
# end
|
195
|
+
# puts Client.domain #=> 'client'
|
196
|
+
#
|
197
|
+
# # if 'ActiveSupport' is loaded then class name being tableized
|
198
|
+
# require 'activesupport'
|
199
|
+
# class Client < RightAws::ActiveSdb::Base
|
200
|
+
# end
|
201
|
+
# puts Client.domain #=> 'clients'
|
202
|
+
#
|
203
|
+
# # Explicit domain name definition
|
204
|
+
# class Client < RightAws::ActiveSdb::Base
|
205
|
+
# set_domain_name :foreign_clients
|
206
|
+
# end
|
207
|
+
# puts Client.domain #=> 'foreign_clients'
|
208
|
+
#
|
209
|
+
def domain
|
210
|
+
unless @domain
|
211
|
+
if defined? ActiveSupport::CoreExtensions::String::Inflections
|
212
|
+
@domain = name.tableize
|
213
|
+
else
|
214
|
+
@domain = name.downcase
|
215
|
+
end
|
216
|
+
end
|
217
|
+
@domain
|
218
|
+
end
|
219
|
+
|
220
|
+
# Change the default domain name to user defined.
|
221
|
+
#
|
222
|
+
# class Client < RightAws::ActiveSdb::Base
|
223
|
+
# set_domain_name :foreign_clients
|
224
|
+
# end
|
225
|
+
#
|
226
|
+
def set_domain_name(domain)
|
227
|
+
@domain = domain.to_s
|
228
|
+
end
|
229
|
+
|
230
|
+
# Create domain at SDB.
|
231
|
+
# Raises no errors if the domain already exists.
|
232
|
+
#
|
233
|
+
# class Client < RightAws::ActiveSdb::Base
|
234
|
+
# end
|
235
|
+
# Client.create_domain #=> {:request_id=>"6fc652a0-0000-41d5-91f4-3ed390a3d3b2", :box_usage=>"0.0055590278"}
|
236
|
+
#
|
237
|
+
def create_domain
|
238
|
+
connection.create_domain(domain)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Remove domain from SDB.
|
242
|
+
# Raises no errors if the domain does not exist.
|
243
|
+
#
|
244
|
+
# class Client < RightAws::ActiveSdb::Base
|
245
|
+
# end
|
246
|
+
# Client.delete_domain #=> {:request_id=>"e14d90d3-0000-4898-9995-0de28cdda270", :box_usage=>"0.0055590278"}
|
247
|
+
#
|
248
|
+
def delete_domain
|
249
|
+
connection.delete_domain(domain)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Perform a find request.
|
253
|
+
#
|
254
|
+
# Single record:
|
255
|
+
#
|
256
|
+
# Client.find(:first)
|
257
|
+
# Client.find(:first, :conditions=> [ "['name'=?] intersection ['wife'=?]", "Jon", "Sandy"])
|
258
|
+
#
|
259
|
+
# Bunch of records:
|
260
|
+
#
|
261
|
+
# Client.find(:all)
|
262
|
+
# Client.find(:all, :limit => 10)
|
263
|
+
# Client.find(:all, :conditions=> [ "['name'=?] intersection ['girlfriend'=?]", "Jon", "Judy"])
|
264
|
+
# Client.find(:all, :conditions=> [ "['name'=?]", "Sandy"], :limit => 3)
|
265
|
+
#
|
266
|
+
# Records by ids:
|
267
|
+
#
|
268
|
+
# Client.find('1')
|
269
|
+
# Client.find('1234987b4583475347523948')
|
270
|
+
# Client.find('1','2','3','4', :conditions=> [ "['toys'=?]", "beer"])
|
271
|
+
#
|
272
|
+
# Find helpers: RightAws::ActiveSdb::Base.find_by_... and RightAws::ActiveSdb::Base.find_all_by_...
|
273
|
+
#
|
274
|
+
# Client.find_by_name('Matias Rust')
|
275
|
+
# Client.find_by_name_and_city('Putin','Moscow')
|
276
|
+
# Client.find_by_name_and_city_and_post('Medvedev','Moscow','president')
|
277
|
+
#
|
278
|
+
# Client.find_all_by_author('G.Bush jr')
|
279
|
+
# Client.find_all_by_age_and_gender_and_ethnicity('34','male','russian')
|
280
|
+
# Client.find_all_by_gender_and_country('male', 'Russia', :auto_load => true, :order => 'name desc')
|
281
|
+
#
|
282
|
+
# Returned records have to be +reloaded+ to access their attributes.
|
283
|
+
#
|
284
|
+
# item = Client.find_by_name('Cat') #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7"}, @new_record=false>
|
285
|
+
# item.reload #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}, @new_record=false>
|
286
|
+
#
|
287
|
+
# Continue listing:
|
288
|
+
# # initial listing
|
289
|
+
# Client.find(:all, :limit => 10)
|
290
|
+
# # continue listing
|
291
|
+
# begin
|
292
|
+
# Client.find(:all, :limit => 10, :next_token => Client.next_token)
|
293
|
+
# end while Client.next_token
|
294
|
+
#
|
295
|
+
# Sort oder:
|
296
|
+
# Client.find(:all, :order => 'gender')
|
297
|
+
# Client.find(:all, :order => 'name desc')
|
298
|
+
#
|
299
|
+
# Attributes auto load (be carefull - this may take lot of time for a huge bunch of records):
|
300
|
+
# Client.find(:first) #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7"}, @new_record=false>
|
301
|
+
# Client.find(:first, :auto_load => true) #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}, @new_record=false>
|
302
|
+
#
|
303
|
+
# see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingQuery.html
|
304
|
+
#
|
305
|
+
def find(*args)
|
306
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
307
|
+
case args.first
|
308
|
+
when :all then find_every options
|
309
|
+
when :first then find_initial options
|
310
|
+
else find_from_ids args, options
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Perform a SQL-like select request.
|
315
|
+
#
|
316
|
+
# Single record:
|
317
|
+
#
|
318
|
+
# Client.select(:first)
|
319
|
+
# Client.select(:first, :conditions=> [ "name=? AND wife=?", "Jon", "Sandy"])
|
320
|
+
# Client.select(:first, :conditions=> { :name=>"Jon", :wife=>"Sandy" }, :select => :girfriends)
|
321
|
+
#
|
322
|
+
# Bunch of records:
|
323
|
+
#
|
324
|
+
# Client.select(:all)
|
325
|
+
# Client.select(:all, :limit => 10)
|
326
|
+
# Client.select(:all, :conditions=> [ "name=? AND 'girlfriend'=?", "Jon", "Judy"])
|
327
|
+
# Client.select(:all, :conditions=> { :name=>"Sandy" }, :limit => 3)
|
328
|
+
#
|
329
|
+
# Records by ids:
|
330
|
+
#
|
331
|
+
# Client.select('1')
|
332
|
+
# Client.select('1234987b4583475347523948')
|
333
|
+
# Client.select('1','2','3','4', :conditions=> ["toys=?", "beer"])
|
334
|
+
#
|
335
|
+
# Find helpers: RightAws::ActiveSdb::Base.select_by_... and RightAws::ActiveSdb::Base.select_all_by_...
|
336
|
+
#
|
337
|
+
# Client.select_by_name('Matias Rust')
|
338
|
+
# Client.select_by_name_and_city('Putin','Moscow')
|
339
|
+
# Client.select_by_name_and_city_and_post('Medvedev','Moscow','president')
|
340
|
+
#
|
341
|
+
# Client.select_all_by_author('G.Bush jr')
|
342
|
+
# Client.select_all_by_age_and_gender_and_ethnicity('34','male','russian')
|
343
|
+
# Client.select_all_by_gender_and_country('male', 'Russia', :order => 'name')
|
344
|
+
#
|
345
|
+
# Continue listing:
|
346
|
+
#
|
347
|
+
# # initial listing
|
348
|
+
# Client.select(:all, :limit => 10)
|
349
|
+
# # continue listing
|
350
|
+
# begin
|
351
|
+
# Client.select(:all, :limit => 10, :next_token => Client.next_token)
|
352
|
+
# end while Client.next_token
|
353
|
+
#
|
354
|
+
# Sort oder:
|
355
|
+
# If :order=>'attribute' option is specified then result response (ordered by 'attribute') will contain only items where attribute is defined (is not null).
|
356
|
+
#
|
357
|
+
# Client.select(:all) # returns all records
|
358
|
+
# Client.select(:all, :order => 'gender') # returns all records ordered by gender where gender attribute exists
|
359
|
+
# Client.select(:all, :order => 'name desc') # returns all records ordered by name in desc order where name attribute exists
|
360
|
+
#
|
361
|
+
# see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
|
362
|
+
#
|
363
|
+
def select(*args)
|
364
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
365
|
+
case args.first
|
366
|
+
when :all then sql_select(options)
|
367
|
+
when :first then sql_select(options.merge(:limit => 1)).first
|
368
|
+
else select_from_ids args, options
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def generate_id # :nodoc:
|
373
|
+
UUID.timestamp_create().to_s
|
374
|
+
end
|
375
|
+
|
376
|
+
protected
|
377
|
+
|
378
|
+
# Select
|
379
|
+
|
380
|
+
def select_from_ids(args, options) # :nodoc:
|
381
|
+
cond = []
|
382
|
+
# detect amount of records requested
|
383
|
+
bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
|
384
|
+
# flatten ids
|
385
|
+
args = args.to_a.flatten
|
386
|
+
args.each { |id| cond << "id=#{self.connection.escape(id)}" }
|
387
|
+
ids_cond = "(#{cond.join(' OR ')})"
|
388
|
+
# user defined :conditions to string (if it was defined)
|
389
|
+
options[:conditions] = build_conditions(options[:conditions])
|
390
|
+
# join ids condition and user defined conditions
|
391
|
+
options[:conditions] = options[:conditions].blank? ? ids_cond : "(#{options[:conditions]}) AND #{ids_cond}"
|
392
|
+
result = sql_select(options)
|
393
|
+
# if one record was requested then return it
|
394
|
+
unless bunch_of_records_requested
|
395
|
+
record = result.first
|
396
|
+
# railse if nothing was found
|
397
|
+
raise ActiveSdbError.new("Couldn't find #{name} with ID #{args}") unless record
|
398
|
+
record
|
399
|
+
else
|
400
|
+
# if a bunch of records was requested then return check that we found all of them
|
401
|
+
# and return as an array
|
402
|
+
unless args.size == result.size
|
403
|
+
id_list = args.map{|i| "'#{i}'"}.join(',')
|
404
|
+
raise ActiveSdbError.new("Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})")
|
405
|
+
else
|
406
|
+
result
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def sql_select(options) # :nodoc:
|
412
|
+
@next_token = options[:next_token]
|
413
|
+
select_expression = build_select(options)
|
414
|
+
# request items
|
415
|
+
query_result = self.connection.select(select_expression, @next_token)
|
416
|
+
@next_token = query_result[:next_token]
|
417
|
+
items = query_result[:items].map do |hash|
|
418
|
+
id, attributes = hash.shift
|
419
|
+
new_item = self.new( attributes.merge({ 'id' => id }))
|
420
|
+
new_item.mark_as_old
|
421
|
+
new_item
|
422
|
+
end
|
423
|
+
items
|
424
|
+
end
|
425
|
+
|
426
|
+
# select_by helpers
|
427
|
+
def select_all_by_(format_str, args, options) # :nodoc:
|
428
|
+
fields = format_str.to_s.sub(/^select_(all_)?by_/,'').split('_and_')
|
429
|
+
conditions = fields.map { |field| "#{field}=?" }.join(' AND ')
|
430
|
+
options[:conditions] = [conditions, *args]
|
431
|
+
select(:all, options)
|
432
|
+
end
|
433
|
+
|
434
|
+
def select_by_(format_str, args, options) # :nodoc:
|
435
|
+
options[:limit] = 1
|
436
|
+
select_all_by_(format_str, args, options).first
|
437
|
+
end
|
438
|
+
|
439
|
+
# Query
|
440
|
+
|
441
|
+
# Returns an array of query attributes.
|
442
|
+
# Query_expression must be a well formated SDB query string:
|
443
|
+
# query_attributes("['title' starts-with 'O\\'Reily'] intersection ['year' = '2007']") #=> ["title", "year"]
|
444
|
+
def query_attributes(query_expression) # :nodoc:
|
445
|
+
attrs = []
|
446
|
+
array = query_expression.scan(/['"](.*?[^\\])['"]/).flatten
|
447
|
+
until array.empty? do
|
448
|
+
attrs << array.shift # skip it's value
|
449
|
+
array.shift #
|
450
|
+
end
|
451
|
+
attrs
|
452
|
+
end
|
453
|
+
|
454
|
+
# Returns an array of [attribute_name, 'asc'|'desc']
|
455
|
+
def sort_options(sort_string)
|
456
|
+
sort_string[/['"]?(\w+)['"]? *(asc|desc)?/i]
|
457
|
+
[$1, ($2 || 'asc')]
|
458
|
+
end
|
459
|
+
|
460
|
+
# Perform a query request.
|
461
|
+
#
|
462
|
+
# Options
|
463
|
+
# :query_expression nil | string | array
|
464
|
+
# :max_number_of_items nil | integer
|
465
|
+
# :next_token nil | string
|
466
|
+
# :sort_option nil | string "name desc|asc"
|
467
|
+
#
|
468
|
+
def query(options) # :nodoc:
|
469
|
+
@next_token = options[:next_token]
|
470
|
+
query_expression = build_conditions(options[:query_expression])
|
471
|
+
# add sort_options to the query_expression
|
472
|
+
if options[:sort_option]
|
473
|
+
sort_by, sort_order = sort_options(options[:sort_option])
|
474
|
+
sort_query_expression = "['#{sort_by}' starts-with '']"
|
475
|
+
sort_by_expression = " sort '#{sort_by}' #{sort_order}"
|
476
|
+
# make query_expression to be a string (it may be null)
|
477
|
+
query_expression = query_expression.to_s
|
478
|
+
# quote from Amazon:
|
479
|
+
# The sort attribute must be present in at least one of the predicates of the query expression.
|
480
|
+
if query_expression.blank?
|
481
|
+
query_expression = sort_query_expression
|
482
|
+
elsif !query_attributes(query_expression).include?(sort_by)
|
483
|
+
query_expression += " intersection #{sort_query_expression}"
|
484
|
+
end
|
485
|
+
query_expression += sort_by_expression
|
486
|
+
end
|
487
|
+
# request items
|
488
|
+
query_result = self.connection.query(domain, query_expression, options[:max_number_of_items], @next_token)
|
489
|
+
@next_token = query_result[:next_token]
|
490
|
+
items = query_result[:items].map do |name|
|
491
|
+
new_item = self.new('id' => name)
|
492
|
+
new_item.mark_as_old
|
493
|
+
reload_if_exists(record) if options[:auto_load]
|
494
|
+
new_item
|
495
|
+
end
|
496
|
+
items
|
497
|
+
end
|
498
|
+
|
499
|
+
# reload a record unless it is nil
|
500
|
+
def reload_if_exists(record) # :nodoc:
|
501
|
+
record && record.reload
|
502
|
+
end
|
503
|
+
|
504
|
+
def reload_all_records(*list) # :nodoc:
|
505
|
+
list.flatten.each { |record| reload_if_exists(record) }
|
506
|
+
end
|
507
|
+
|
508
|
+
def find_every(options) # :nodoc:
|
509
|
+
records = query( :query_expression => options[:conditions],
|
510
|
+
:max_number_of_items => options[:limit],
|
511
|
+
:next_token => options[:next_token],
|
512
|
+
:sort_option => options[:sort] || options[:order] )
|
513
|
+
options[:auto_load] ? reload_all_records(records) : records
|
514
|
+
end
|
515
|
+
|
516
|
+
def find_initial(options) # :nodoc:
|
517
|
+
options[:limit] = 1
|
518
|
+
record = find_every(options).first
|
519
|
+
options[:auto_load] ? reload_all_records(record).first : record
|
520
|
+
end
|
521
|
+
|
522
|
+
def find_from_ids(args, options) # :nodoc:
|
523
|
+
cond = []
|
524
|
+
# detect amount of records requested
|
525
|
+
bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
|
526
|
+
# flatten ids
|
527
|
+
args = args.to_a.flatten
|
528
|
+
args.each { |id| cond << "'id'=#{self.connection.escape(id)}" }
|
529
|
+
ids_cond = "[#{cond.join(' OR ')}]"
|
530
|
+
# user defined :conditions to string (if it was defined)
|
531
|
+
options[:conditions] = build_conditions(options[:conditions])
|
532
|
+
# join ids condition and user defined conditions
|
533
|
+
options[:conditions] = options[:conditions].blank? ? ids_cond : "#{options[:conditions]} intersection #{ids_cond}"
|
534
|
+
result = find_every(options)
|
535
|
+
# if one record was requested then return it
|
536
|
+
unless bunch_of_records_requested
|
537
|
+
record = result.first
|
538
|
+
# railse if nothing was found
|
539
|
+
raise ActiveSdbError.new("Couldn't find #{name} with ID #{args}") unless record
|
540
|
+
options[:auto_load] ? reload_all_records(record).first : record
|
541
|
+
else
|
542
|
+
# if a bunch of records was requested then return check that we found all of them
|
543
|
+
# and return as an array
|
544
|
+
unless args.size == result.size
|
545
|
+
id_list = args.map{|i| "'#{i}'"}.join(',')
|
546
|
+
raise ActiveSdbError.new("Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})")
|
547
|
+
else
|
548
|
+
options[:auto_load] ? reload_all_records(result) : result
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# find_by helpers
|
554
|
+
def find_all_by_(format_str, args, options) # :nodoc:
|
555
|
+
fields = format_str.to_s.sub(/^find_(all_)?by_/,'').split('_and_')
|
556
|
+
conditions = fields.map { |field| "['#{field}'=?]" }.join(' intersection ')
|
557
|
+
options[:conditions] = [conditions, *args]
|
558
|
+
find(:all, options)
|
559
|
+
end
|
560
|
+
|
561
|
+
def find_by_(format_str, args, options) # :nodoc:
|
562
|
+
options[:limit] = 1
|
563
|
+
find_all_by_(format_str, args, options).first
|
564
|
+
end
|
565
|
+
|
566
|
+
# Misc
|
567
|
+
|
568
|
+
def method_missing(method, *args) # :nodoc:
|
569
|
+
if method.to_s[/^(find_all_by_|find_by_|select_all_by_|select_by_)/]
|
570
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
571
|
+
__send__($1, method, args, options)
|
572
|
+
else
|
573
|
+
super(method, *args)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def build_select(options) # :nodoc:
|
578
|
+
select = options[:select] || '*'
|
579
|
+
from = options[:from] || domain
|
580
|
+
conditions = options[:conditions] ? " WHERE #{build_conditions(options[:conditions])}" : ''
|
581
|
+
order = options[:order] ? " ORDER BY #{options[:order]}" : ''
|
582
|
+
limit = options[:limit] ? " LIMIT #{options[:limit]}" : ''
|
583
|
+
# mix sort by argument (it must present in response)
|
584
|
+
unless order.blank?
|
585
|
+
sort_by, sort_order = sort_options(options[:order])
|
586
|
+
conditions << (conditions.blank? ? " WHERE " : " AND ") << "(#{sort_by} IS NOT NULL)"
|
587
|
+
end
|
588
|
+
"SELECT #{select} FROM #{from}#{conditions}#{order}#{limit}"
|
589
|
+
end
|
590
|
+
|
591
|
+
def build_conditions(conditions) # :nodoc:
|
592
|
+
case
|
593
|
+
when conditions.is_a?(Array) then connection.query_expression_from_array(conditions)
|
594
|
+
when conditions.is_a?(Hash) then connection.query_expression_from_hash(conditions)
|
595
|
+
else conditions
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
end
|
600
|
+
|
601
|
+
public
|
602
|
+
|
603
|
+
# instance attributes
|
604
|
+
attr_accessor :attributes
|
605
|
+
|
606
|
+
# item name
|
607
|
+
attr_accessor :id
|
608
|
+
|
609
|
+
# Create new Item instance.
|
610
|
+
# +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
|
611
|
+
#
|
612
|
+
# item = Client.new('name' => 'Jon', 'toys' => ['girls', 'beer', 'pub'])
|
613
|
+
# puts item.inspect #=> #<Client:0xb77a2698 @new_record=true, @attributes={"name"=>["Jon"], "toys"=>["girls", "beer", "pub"]}>
|
614
|
+
# item.save #=> {"name"=>["Jon"], "id"=>"c03edb7e-e45c-11dc-bede-001bfc466dd7", "toys"=>["girls", "beer", "pub"]}
|
615
|
+
# puts item.inspect #=> #<Client:0xb77a2698 @new_record=false, @attributes={"name"=>["Jon"], "id"=>"c03edb7e-e45c-11dc-bede-001bfc466dd7", "toys"=>["girls", "beer", "pub"]}>
|
616
|
+
#
|
617
|
+
def initialize(attrs={})
|
618
|
+
@attributes = uniq_values(attrs)
|
619
|
+
@new_record = true
|
620
|
+
end
|
621
|
+
|
622
|
+
# Create and save new Item instance.
|
623
|
+
# +Attributes+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
|
624
|
+
#
|
625
|
+
# item = Client.create('name' => 'Cat', 'toys' => ['Jons socks', 'mice', 'clew'])
|
626
|
+
# puts item.inspect #=> #<Client:0xb77a0a78 @new_record=false, @attributes={"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "mice", "clew"]}>
|
627
|
+
#
|
628
|
+
def self.create(attributes={})
|
629
|
+
item = self.new(attributes)
|
630
|
+
item.save
|
631
|
+
item
|
632
|
+
end
|
633
|
+
|
634
|
+
# Returns an item id. Same as: item['id'] or item.attributes['id']
|
635
|
+
def id
|
636
|
+
@attributes['id']
|
637
|
+
end
|
638
|
+
|
639
|
+
# Sets an item id.
|
640
|
+
def id=(id)
|
641
|
+
@attributes['id'] = id.to_s
|
642
|
+
end
|
643
|
+
|
644
|
+
# Returns a hash of all the attributes.
|
645
|
+
#
|
646
|
+
# puts item.attributes.inspect #=> {"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "clew", "mice"]}
|
647
|
+
#
|
648
|
+
def attributes
|
649
|
+
@attributes.dup
|
650
|
+
end
|
651
|
+
|
652
|
+
# Allows one to set all the attributes at once by passing in a hash with keys matching the attribute names.
|
653
|
+
# if '+id+' attribute is not set in new attributes has then it being derived from old attributes.
|
654
|
+
#
|
655
|
+
# puts item.attributes.inspect #=> {"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "clew", "mice"]}
|
656
|
+
# # set new attributes ('id' is missed)
|
657
|
+
# item.attributes = { 'name'=>'Dog', 'toys'=>['bones','cats'] }
|
658
|
+
# puts item.attributes.inspect #=> {"name"=>["Dog"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["bones", "cats"]}
|
659
|
+
# # set new attributes ('id' is set)
|
660
|
+
# item.attributes = { 'id' => 'blah-blah', 'name'=>'Birds', 'toys'=>['seeds','dogs tail'] }
|
661
|
+
# puts item.attributes.inspect #=> {"name"=>["Birds"], "id"=>"blah-blah", "toys"=>["seeds", "dogs tail"]}
|
662
|
+
#
|
663
|
+
def attributes=(attrs)
|
664
|
+
old_id = @attributes['id']
|
665
|
+
@attributes = uniq_values(attrs)
|
666
|
+
@attributes['id'] = old_id if @attributes['id'].blank? && !old_id.blank?
|
667
|
+
self.attributes
|
668
|
+
end
|
669
|
+
|
670
|
+
def connection
|
671
|
+
self.class.connection
|
672
|
+
end
|
673
|
+
|
674
|
+
# Item domain name.
|
675
|
+
def domain
|
676
|
+
self.class.domain
|
677
|
+
end
|
678
|
+
|
679
|
+
# Returns the values of the attribute identified by +attribute+.
|
680
|
+
#
|
681
|
+
# puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
|
682
|
+
#
|
683
|
+
def [](attribute)
|
684
|
+
@attributes[attribute.to_s]
|
685
|
+
end
|
686
|
+
|
687
|
+
# Updates the attribute identified by +attribute+ with the specified +values+.
|
688
|
+
#
|
689
|
+
# puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
|
690
|
+
# item['Cat'] = ["Whiskas", "chicken"]
|
691
|
+
# puts item['Cat'].inspect #=> ["Whiskas", "chicken"]
|
692
|
+
#
|
693
|
+
def []=(attribute, values)
|
694
|
+
attribute = attribute.to_s
|
695
|
+
@attributes[attribute] = attribute == 'id' ? values.to_s : [*values].uniq
|
696
|
+
end
|
697
|
+
|
698
|
+
# Reload attributes from SDB. Replaces in-memory attributes.
|
699
|
+
#
|
700
|
+
# item = Client.find_by_name('Cat') #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7"}, @new_record=false>
|
701
|
+
# item.reload #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}, @new_record=false>
|
702
|
+
#
|
703
|
+
def reload
|
704
|
+
raise_on_id_absence
|
705
|
+
old_id = id
|
706
|
+
attrs = connection.get_attributes(domain, id)[:attributes]
|
707
|
+
@attributes = {}
|
708
|
+
unless attrs.blank?
|
709
|
+
attrs.each { |attribute, values| @attributes[attribute] = values }
|
710
|
+
@attributes['id'] = old_id
|
711
|
+
end
|
712
|
+
mark_as_old
|
713
|
+
@attributes
|
714
|
+
end
|
715
|
+
|
716
|
+
# Reload a set of attributes from SDB. Adds the loaded list to in-memory data.
|
717
|
+
# +attrs_list+ is an array or comma separated list of attributes names.
|
718
|
+
# Returns a hash of loaded attributes.
|
719
|
+
#
|
720
|
+
# This is not the best method to get a bunch of attributes because
|
721
|
+
# a web service call is being performed for every attribute.
|
722
|
+
#
|
723
|
+
# item = Client.find_by_name('Cat')
|
724
|
+
# item.reload_attributes('toys', 'name') #=> {"name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}
|
725
|
+
#
|
726
|
+
def reload_attributes(*attrs_list)
|
727
|
+
raise_on_id_absence
|
728
|
+
attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
|
729
|
+
attrs_list.delete('id')
|
730
|
+
result = {}
|
731
|
+
attrs_list.flatten.uniq.each do |attribute|
|
732
|
+
attribute = attribute.to_s
|
733
|
+
values = connection.get_attributes(domain, id, attribute)[:attributes][attribute]
|
734
|
+
unless values.blank?
|
735
|
+
@attributes[attribute] = result[attribute] = values
|
736
|
+
else
|
737
|
+
@attributes.delete(attribute)
|
738
|
+
end
|
739
|
+
end
|
740
|
+
mark_as_old
|
741
|
+
result
|
742
|
+
end
|
743
|
+
|
744
|
+
# Stores in-memory attributes to SDB.
|
745
|
+
# Adds the attributes values to already stored at SDB.
|
746
|
+
# Returns a hash of stored attributes.
|
747
|
+
#
|
748
|
+
# sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={"name"=>["Sandy"]}, @new_record=true>
|
749
|
+
# sandy['toys'] = 'boys'
|
750
|
+
# sandy.put
|
751
|
+
# sandy['toys'] = 'patchwork'
|
752
|
+
# sandy.put
|
753
|
+
# sandy['toys'] = 'kids'
|
754
|
+
# sandy.put
|
755
|
+
# puts sandy.attributes.inspect #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
|
756
|
+
# sandy.reload #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"]}
|
757
|
+
#
|
758
|
+
# compare to +save+ method
|
759
|
+
def put
|
760
|
+
@attributes = uniq_values(@attributes)
|
761
|
+
prepare_for_update
|
762
|
+
attrs = @attributes.dup
|
763
|
+
attrs.delete('id')
|
764
|
+
connection.put_attributes(domain, id, attrs) unless attrs.blank?
|
765
|
+
connection.put_attributes(domain, id, { 'id' => id }, :replace)
|
766
|
+
mark_as_old
|
767
|
+
@attributes
|
768
|
+
end
|
769
|
+
|
770
|
+
# Stores specified attributes.
|
771
|
+
# +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
|
772
|
+
# Returns a hash of saved attributes.
|
773
|
+
#
|
774
|
+
# see to +put+ method
|
775
|
+
def put_attributes(attrs)
|
776
|
+
attrs = uniq_values(attrs)
|
777
|
+
prepare_for_update
|
778
|
+
# if 'id' is present in attrs hash:
|
779
|
+
# replace internal 'id' attribute and remove it from the attributes to be sent
|
780
|
+
@attributes['id'] = attrs['id'] unless attrs['id'].blank?
|
781
|
+
attrs.delete('id')
|
782
|
+
# add new values to all attributes from list
|
783
|
+
connection.put_attributes(domain, id, attrs) unless attrs.blank?
|
784
|
+
connection.put_attributes(domain, id, { 'id' => id }, :replace)
|
785
|
+
attrs.each do |attribute, values|
|
786
|
+
@attributes[attribute] ||= []
|
787
|
+
@attributes[attribute] += values
|
788
|
+
@attributes[attribute].uniq!
|
789
|
+
end
|
790
|
+
mark_as_old
|
791
|
+
attributes
|
792
|
+
end
|
793
|
+
|
794
|
+
# Store in-memory attributes to SDB.
|
795
|
+
# Replaces the attributes values already stored at SDB by in-memory data.
|
796
|
+
# Returns a hash of stored attributes.
|
797
|
+
#
|
798
|
+
# sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={"name"=>["Sandy"]}, @new_record=true>
|
799
|
+
# sandy['toys'] = 'boys'
|
800
|
+
# sandy.put
|
801
|
+
# sandy['toys'] = 'patchwork'
|
802
|
+
# sandy.put
|
803
|
+
# sandy['toys'] = 'kids'
|
804
|
+
# sandy.put
|
805
|
+
# puts sandy.attributes.inspect #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
|
806
|
+
# sandy.reload #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
|
807
|
+
#
|
808
|
+
# compare to +put+ method
|
809
|
+
def save
|
810
|
+
@attributes = uniq_values(@attributes)
|
811
|
+
prepare_for_update
|
812
|
+
connection.put_attributes(domain, id, @attributes, :replace)
|
813
|
+
mark_as_old
|
814
|
+
@attributes
|
815
|
+
end
|
816
|
+
|
817
|
+
# Replaces the attributes at SDB by the given values.
|
818
|
+
# +Attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
|
819
|
+
# The other in-memory attributes are not being saved.
|
820
|
+
# Returns a hash of stored attributes.
|
821
|
+
#
|
822
|
+
# see +save+ method
|
823
|
+
def save_attributes(attrs)
|
824
|
+
prepare_for_update
|
825
|
+
attrs = uniq_values(attrs)
|
826
|
+
# if 'id' is present in attrs hash then replace internal 'id' attribute
|
827
|
+
unless attrs['id'].blank?
|
828
|
+
@attributes['id'] = attrs['id']
|
829
|
+
else
|
830
|
+
attrs['id'] = id
|
831
|
+
end
|
832
|
+
connection.put_attributes(domain, id, attrs, :replace) unless attrs.blank?
|
833
|
+
attrs.each { |attribute, values| attrs[attribute] = values }
|
834
|
+
mark_as_old
|
835
|
+
attrs
|
836
|
+
end
|
837
|
+
|
838
|
+
# Remove specified values from corresponding attributes.
|
839
|
+
# +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
|
840
|
+
#
|
841
|
+
# sandy = Client.find_by_name 'Sandy'
|
842
|
+
# sandy.reload
|
843
|
+
# puts sandy.inspect #=> #<Client:0xb77b48fc @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"]}>
|
844
|
+
# puts sandy.delete_values('toys' => 'patchwork') #=> { 'toys' => ['patchwork'] }
|
845
|
+
# puts sandy.inspect #=> #<Client:0xb77b48fc @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids"]}>
|
846
|
+
#
|
847
|
+
def delete_values(attrs)
|
848
|
+
raise_on_id_absence
|
849
|
+
attrs = uniq_values(attrs)
|
850
|
+
attrs.delete('id')
|
851
|
+
unless attrs.blank?
|
852
|
+
connection.delete_attributes(domain, id, attrs)
|
853
|
+
attrs.each do |attribute, values|
|
854
|
+
# remove the values from the attribute
|
855
|
+
if @attributes[attribute]
|
856
|
+
@attributes[attribute] -= values
|
857
|
+
else
|
858
|
+
# if the attribute is unknown remove it from a resulting list of fixed attributes
|
859
|
+
attrs.delete(attribute)
|
860
|
+
end
|
861
|
+
end
|
862
|
+
end
|
863
|
+
attrs
|
864
|
+
end
|
865
|
+
|
866
|
+
# Removes specified attributes from the item.
|
867
|
+
# +attrs_list+ is an array or comma separated list of attributes names.
|
868
|
+
# Returns the list of deleted attributes.
|
869
|
+
#
|
870
|
+
# sandy = Client.find_by_name 'Sandy'
|
871
|
+
# sandy.reload
|
872
|
+
# puts sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"}>
|
873
|
+
# puts sandy.delete_attributes('toys') #=> ['toys']
|
874
|
+
# puts sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7"}>
|
875
|
+
#
|
876
|
+
def delete_attributes(*attrs_list)
|
877
|
+
raise_on_id_absence
|
878
|
+
attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
|
879
|
+
attrs_list.delete('id')
|
880
|
+
unless attrs_list.blank?
|
881
|
+
connection.delete_attributes(domain, id, attrs_list)
|
882
|
+
attrs_list.each { |attribute| @attributes.delete(attribute) }
|
883
|
+
end
|
884
|
+
attrs_list
|
885
|
+
end
|
886
|
+
|
887
|
+
# Delete the Item entirely from SDB.
|
888
|
+
#
|
889
|
+
# sandy = Client.find_by_name 'Sandy'
|
890
|
+
# sandy.reload
|
891
|
+
# sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"}>
|
892
|
+
# puts sandy.delete
|
893
|
+
# sandy.reload
|
894
|
+
# puts sandy.inspect #=> #<Client:0xb7761d28 @attributes={}, @new_record=false>
|
895
|
+
#
|
896
|
+
def delete
|
897
|
+
raise_on_id_absence
|
898
|
+
connection.delete_attributes(domain, id)
|
899
|
+
end
|
900
|
+
|
901
|
+
# Item ID
|
902
|
+
def to_s
|
903
|
+
@id
|
904
|
+
end
|
905
|
+
|
906
|
+
# Returns true if this object hasn‘t been saved yet.
|
907
|
+
def new_record?
|
908
|
+
@new_record
|
909
|
+
end
|
910
|
+
|
911
|
+
def mark_as_old # :nodoc:
|
912
|
+
@new_record = false
|
913
|
+
end
|
914
|
+
|
915
|
+
private
|
916
|
+
|
917
|
+
def raise_on_id_absence
|
918
|
+
raise ActiveSdbError.new('Unknown record id') unless id
|
919
|
+
end
|
920
|
+
|
921
|
+
def prepare_for_update
|
922
|
+
@attributes['id'] = self.class.generate_id if @attributes['id'].blank?
|
923
|
+
end
|
924
|
+
|
925
|
+
def uniq_values(attributes=nil) # :nodoc:
|
926
|
+
attrs = {}
|
927
|
+
attributes.each do |attribute, values|
|
928
|
+
attribute = attribute.to_s
|
929
|
+
attrs[attribute] = attribute == 'id' ? values.to_s : values.to_a.uniq
|
930
|
+
attrs.delete(attribute) if values.blank?
|
931
|
+
end
|
932
|
+
attrs
|
933
|
+
end
|
934
|
+
end
|
935
|
+
end
|
936
|
+
end
|