appoxy-aws 1.11.24 → 1.11.25

Sign up to get free protection for your applications and to get access to all the features.
@@ -698,8 +698,11 @@ module RightAws
698
698
  # xml_text - xml message text(String) or Net:HTTPxxx instance (response)
699
699
  # params[:xml_lib] - library name: 'rexml' | 'libxml'
700
700
  def parse(xml_text, params={})
701
- # Get response body
702
- xml_text = xml_text.body.force_encoding("UTF-8") unless xml_text.is_a?(String)
701
+ # Get response body
702
+ unless xml_text.is_a?(String)
703
+ xml_text = xml_text.body.respond_to?(:force_encoding) ? xml_text.body.force_encoding("UTF-8") : xml_text.body
704
+ end
705
+
703
706
  @xml_lib = params[:xml_lib] || @xml_lib
704
707
  # check that we had no problems with this library otherwise use default
705
708
  @xml_lib = DEFAULT_XML_LIBRARY unless @@supported_xml_libs.include?(@xml_lib)
@@ -22,242 +22,243 @@
22
22
  #
23
23
 
24
24
  begin
25
- require 'uuidtools'
25
+ require 'uuidtools'
26
26
  rescue LoadError => e
27
- STDERR.puts("RightSDB requires the uuidtools gem. Run \'gem install uuidtools\' and try again.")
28
- exit
27
+ STDERR.puts("RightSDB requires the uuidtools gem. Run \'gem install uuidtools\' and try again.")
28
+ exit
29
29
  end
30
30
 
31
31
  module RightAws
32
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
-
102
- # Create a new handle to an Sdb account. All handles share the same per process or per thread
103
- # HTTP connection to Amazon Sdb. Each handle is for a specific account.
104
- # The +params+ are passed through as-is to RightAws::SdbInterface.new
105
- # Params:
106
- # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
107
- # :port => 443 # Amazon service port: 80 or 443(default)
108
- # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
109
- # :signature_version => '0' # The signature version : '0' or '1'(default)
110
- # DEPRECATED :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
111
- # :connection_mode => :default # options are :default (will use best known option, may change in the future)
112
- # :per_request (opens and closes a connection on every request to SDB)
113
- # :single (same as old multi_thread=>false)
114
- # :per_thread (same as old multi_thread=>true)
115
- # :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
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
-
123
- def close_connection
124
- @connection.close_connection unless @connection.nil?
125
- end
126
- end
127
-
128
- class ActiveSdbError < RuntimeError ; end
129
-
130
- class << self
131
- include ActiveSdbConnect
132
-
133
- # Retreive a list of domains.
134
- #
135
- # put RightAws::ActiveSdb.domains #=> ['co-workers','family','friends','clients']
136
- #
137
- def domains
138
- connection.list_domains[:domains]
139
- end
140
-
141
- # Create new domain.
142
- # Raises no errors if the domain already exists.
143
- #
144
- # RightAws::ActiveSdb.create_domain('alpha') #=> {:request_id=>"6fc652a0-0000-41d5-91f4-3ed390a3d3b2", :box_usage=>"0.0055590278"}
145
- #
146
- def create_domain(domain_name)
147
- connection.create_domain(domain_name)
148
- end
149
-
150
- # Remove domain from SDB.
151
- # Raises no errors if the domain does not exist.
152
- #
153
- # RightAws::ActiveSdb.create_domain('alpha') #=> {:request_id=>"6fc652a0-0000-41c6-91f4-3ed390a3d3b2", :box_usage=>"0.0055590001"}
154
- #
155
- def delete_domain(domain_name)
156
- connection.delete_domain(domain_name)
157
- end
158
- end
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
159
101
 
160
- class Base
161
-
162
- class << self
163
- include ActiveSdbConnect
164
-
165
- # next_token value returned by last find: is useful to continue finding
166
- attr_accessor :next_token
167
-
168
- # Returns a RightAws::SdbInterface object
169
- #
170
- # class A < RightAws::ActiveSdb::Base
171
- # end
172
- #
173
- # class B < RightAws::ActiveSdb::Base
174
- # end
175
- #
176
- # class C < RightAws::ActiveSdb::Base
177
- # end
178
- #
179
- # RightAws::ActiveSdb.establish_connection 'key_id_1', 'secret_key_1'
180
- #
181
- # C.establish_connection 'key_id_2', 'secret_key_2'
182
- #
183
- # # A and B uses the default connection, C - uses its own
184
- # puts A.connection #=> #<RightAws::SdbInterface:0xb76d6d7c>
185
- # puts B.connection #=> #<RightAws::SdbInterface:0xb76d6d7c>
186
- # puts C.connection #=> #<RightAws::SdbInterface:0xb76d6ca0>
187
- #
188
- def connection
189
- @connection || ActiveSdb::connection
190
- end
102
+ # Create a new handle to an Sdb account. All handles share the same per process or per thread
103
+ # HTTP connection to Amazon Sdb. Each handle is for a specific account.
104
+ # The +params+ are passed through as-is to RightAws::SdbInterface.new
105
+ # Params:
106
+ # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
107
+ # :port => 443 # Amazon service port: 80 or 443(default)
108
+ # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
109
+ # :signature_version => '0' # The signature version : '0' or '1'(default)
110
+ # DEPRECATED :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
111
+ # :connection_mode => :default # options are :default (will use best known option, may change in the future)
112
+ # :per_request (opens and closes a connection on every request to SDB)
113
+ # :single (same as old multi_thread=>false)
114
+ # :per_thread (same as old multi_thread=>true)
115
+ # :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
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
191
122
 
192
- @domain = nil
193
-
194
- # Current domain name.
195
- #
196
- # # if 'ActiveSupport' is not loaded then class name converted to downcase
197
- # class Client < RightAws::ActiveSdb::Base
198
- # end
199
- # puts Client.domain #=> 'client'
200
- #
201
- # # if 'ActiveSupport' is loaded then class name being tableized
202
- # require 'activesupport'
203
- # class Client < RightAws::ActiveSdb::Base
204
- # end
205
- # puts Client.domain #=> 'clients'
206
- #
207
- # # Explicit domain name definition
208
- # class Client < RightAws::ActiveSdb::Base
209
- # set_domain_name :foreign_clients
210
- # end
211
- # puts Client.domain #=> 'foreign_clients'
212
- #
213
- def domain
214
- unless @domain
215
- if defined? ActiveSupport::CoreExtensions::String::Inflections
216
- @domain = name.tableize
217
- else
218
- @domain = name.downcase
123
+ def close_connection
124
+ @connection.close_connection unless @connection.nil?
219
125
  end
220
- end
221
- @domain
222
126
  end
223
127
 
224
- # Change the default domain name to user defined.
225
- #
226
- # class Client < RightAws::ActiveSdb::Base
227
- # set_domain_name :foreign_clients
228
- # end
229
- #
230
- def set_domain_name(domain)
231
- @domain = domain.to_s
128
+ class ActiveSdbError < RuntimeError
232
129
  end
233
130
 
234
- # Create domain at SDB.
235
- # Raises no errors if the domain already exists.
236
- #
237
- # class Client < RightAws::ActiveSdb::Base
238
- # end
239
- # Client.create_domain #=> {:request_id=>"6fc652a0-0000-41d5-91f4-3ed390a3d3b2", :box_usage=>"0.0055590278"}
240
- #
241
- def create_domain
242
- connection.create_domain(domain)
243
- end
131
+ class << self
132
+ include ActiveSdbConnect
133
+
134
+ # Retreive a list of domains.
135
+ #
136
+ # put RightAws::ActiveSdb.domains #=> ['co-workers','family','friends','clients']
137
+ #
138
+ def domains
139
+ connection.list_domains[:domains]
140
+ end
141
+
142
+ # Create new domain.
143
+ # Raises no errors if the domain already exists.
144
+ #
145
+ # RightAws::ActiveSdb.create_domain('alpha') #=> {:request_id=>"6fc652a0-0000-41d5-91f4-3ed390a3d3b2", :box_usage=>"0.0055590278"}
146
+ #
147
+ def create_domain(domain_name)
148
+ connection.create_domain(domain_name)
149
+ end
244
150
 
245
- # Remove domain from SDB.
246
- # Raises no errors if the domain does not exist.
247
- #
248
- # class Client < RightAws::ActiveSdb::Base
249
- # end
250
- # Client.delete_domain #=> {:request_id=>"e14d90d3-0000-4898-9995-0de28cdda270", :box_usage=>"0.0055590278"}
251
- #
252
- def delete_domain
253
- connection.delete_domain(domain)
151
+ # Remove domain from SDB.
152
+ # Raises no errors if the domain does not exist.
153
+ #
154
+ # RightAws::ActiveSdb.create_domain('alpha') #=> {:request_id=>"6fc652a0-0000-41c6-91f4-3ed390a3d3b2", :box_usage=>"0.0055590001"}
155
+ #
156
+ def delete_domain(domain_name)
157
+ connection.delete_domain(domain_name)
158
+ end
254
159
  end
255
160
 
256
- #
257
- # See select(), original find with QUERY syntax is deprecated so now find and select are synonyms.
258
- #
259
- def find(*args)
260
- select(*args)
161
+ class Base
162
+
163
+ class << self
164
+ include ActiveSdbConnect
165
+
166
+ # next_token value returned by last find: is useful to continue finding
167
+ attr_accessor :next_token
168
+
169
+ # Returns a RightAws::SdbInterface object
170
+ #
171
+ # class A < RightAws::ActiveSdb::Base
172
+ # end
173
+ #
174
+ # class B < RightAws::ActiveSdb::Base
175
+ # end
176
+ #
177
+ # class C < RightAws::ActiveSdb::Base
178
+ # end
179
+ #
180
+ # RightAws::ActiveSdb.establish_connection 'key_id_1', 'secret_key_1'
181
+ #
182
+ # C.establish_connection 'key_id_2', 'secret_key_2'
183
+ #
184
+ # # A and B uses the default connection, C - uses its own
185
+ # puts A.connection #=> #<RightAws::SdbInterface:0xb76d6d7c>
186
+ # puts B.connection #=> #<RightAws::SdbInterface:0xb76d6d7c>
187
+ # puts C.connection #=> #<RightAws::SdbInterface:0xb76d6ca0>
188
+ #
189
+ def connection
190
+ @connection || ActiveSdb::connection
191
+ end
192
+
193
+ @domain = nil
194
+
195
+ # Current domain name.
196
+ #
197
+ # # if 'ActiveSupport' is not loaded then class name converted to downcase
198
+ # class Client < RightAws::ActiveSdb::Base
199
+ # end
200
+ # puts Client.domain #=> 'client'
201
+ #
202
+ # # if 'ActiveSupport' is loaded then class name being tableized
203
+ # require 'activesupport'
204
+ # class Client < RightAws::ActiveSdb::Base
205
+ # end
206
+ # puts Client.domain #=> 'clients'
207
+ #
208
+ # # Explicit domain name definition
209
+ # class Client < RightAws::ActiveSdb::Base
210
+ # set_domain_name :foreign_clients
211
+ # end
212
+ # puts Client.domain #=> 'foreign_clients'
213
+ #
214
+ def domain
215
+ unless @domain
216
+ if defined? ActiveSupport::CoreExtensions::String::Inflections
217
+ @domain = name.tableize
218
+ else
219
+ @domain = name.downcase
220
+ end
221
+ end
222
+ @domain
223
+ end
224
+
225
+ # Change the default domain name to user defined.
226
+ #
227
+ # class Client < RightAws::ActiveSdb::Base
228
+ # set_domain_name :foreign_clients
229
+ # end
230
+ #
231
+ def set_domain_name(domain)
232
+ @domain = domain.to_s
233
+ end
234
+
235
+ # Create domain at SDB.
236
+ # Raises no errors if the domain already exists.
237
+ #
238
+ # class Client < RightAws::ActiveSdb::Base
239
+ # end
240
+ # Client.create_domain #=> {:request_id=>"6fc652a0-0000-41d5-91f4-3ed390a3d3b2", :box_usage=>"0.0055590278"}
241
+ #
242
+ def create_domain
243
+ connection.create_domain(domain)
244
+ end
245
+
246
+ # Remove domain from SDB.
247
+ # Raises no errors if the domain does not exist.
248
+ #
249
+ # class Client < RightAws::ActiveSdb::Base
250
+ # end
251
+ # Client.delete_domain #=> {:request_id=>"e14d90d3-0000-4898-9995-0de28cdda270", :box_usage=>"0.0055590278"}
252
+ #
253
+ def delete_domain
254
+ connection.delete_domain(domain)
255
+ end
256
+
257
+ #
258
+ # See select(), original find with QUERY syntax is deprecated so now find and select are synonyms.
259
+ #
260
+ def find(*args)
261
+ select(*args)
261
262
 
262
263
  =begin
263
264
 
@@ -270,653 +271,668 @@ module RightAws
270
271
 
271
272
  =end
272
273
 
273
- end
274
-
275
- # Perform a SQL-like select request.
276
- #
277
- # Single record:
278
- #
279
- # Client.select(:first)
280
- # Client.select(:first, :conditions=> [ "name=? AND wife=?", "Jon", "Sandy"])
281
- # Client.select(:first, :conditions=> { :name=>"Jon", :wife=>"Sandy" }, :select => :girfriends)
282
- #
283
- # Bunch of records:
284
- #
285
- # Client.select(:all)
286
- # Client.select(:all, :limit => 10)
287
- # Client.select(:all, :conditions=> [ "name=? AND 'girlfriend'=?", "Jon", "Judy"])
288
- # Client.select(:all, :conditions=> { :name=>"Sandy" }, :limit => 3)
289
- #
290
- # Records by ids:
291
- #
292
- # Client.select('1')
293
- # Client.select('1234987b4583475347523948')
294
- # Client.select('1','2','3','4', :conditions=> ["toys=?", "beer"])
295
- #
296
- # Find helpers: RightAws::ActiveSdb::Base.select_by_... and RightAws::ActiveSdb::Base.select_all_by_...
297
- #
298
- # Client.select_by_name('Matias Rust')
299
- # Client.select_by_name_and_city('Putin','Moscow')
300
- # Client.select_by_name_and_city_and_post('Medvedev','Moscow','president')
301
- #
302
- # Client.select_all_by_author('G.Bush jr')
303
- # Client.select_all_by_age_and_gender_and_ethnicity('34','male','russian')
304
- # Client.select_all_by_gender_and_country('male', 'Russia', :order => 'name')
305
- #
306
- # Continue listing:
307
- #
308
- # # initial listing
309
- # Client.select(:all, :limit => 10)
310
- # # continue listing
311
- # begin
312
- # Client.select(:all, :limit => 10, :next_token => Client.next_token)
313
- # end while Client.next_token
314
- #
315
- # Sort oder:
316
- # If :order=>'attribute' option is specified then result response (ordered by 'attribute') will contain only items where attribute is defined (is not null).
317
- #
318
- # Client.select(:all) # returns all records
319
- # Client.select(:all, :order => 'gender') # returns all records ordered by gender where gender attribute exists
320
- # Client.select(:all, :order => 'name desc') # returns all records ordered by name in desc order where name attribute exists
321
- #
322
- # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
323
- #
324
- def select(*args)
325
- options = args.last.is_a?(Hash) ? args.pop : {}
274
+ end
275
+
276
+ # Perform a SQL-like select request.
277
+ #
278
+ # Single record:
279
+ #
280
+ # Client.select(:first)
281
+ # Client.select(:first, :conditions=> [ "name=? AND wife=?", "Jon", "Sandy"])
282
+ # Client.select(:first, :conditions=> { :name=>"Jon", :wife=>"Sandy" }, :select => :girfriends)
283
+ #
284
+ # Bunch of records:
285
+ #
286
+ # Client.select(:all)
287
+ # Client.select(:all, :limit => 10)
288
+ # Client.select(:all, :conditions=> [ "name=? AND 'girlfriend'=?", "Jon", "Judy"])
289
+ # Client.select(:all, :conditions=> { :name=>"Sandy" }, :limit => 3)
290
+ #
291
+ # Records by ids:
292
+ #
293
+ # Client.select('1')
294
+ # Client.select('1234987b4583475347523948')
295
+ # Client.select('1','2','3','4', :conditions=> ["toys=?", "beer"])
296
+ #
297
+ # Find helpers: RightAws::ActiveSdb::Base.select_by_... and RightAws::ActiveSdb::Base.select_all_by_...
298
+ #
299
+ # Client.select_by_name('Matias Rust')
300
+ # Client.select_by_name_and_city('Putin','Moscow')
301
+ # Client.select_by_name_and_city_and_post('Medvedev','Moscow','president')
302
+ #
303
+ # Client.select_all_by_author('G.Bush jr')
304
+ # Client.select_all_by_age_and_gender_and_ethnicity('34','male','russian')
305
+ # Client.select_all_by_gender_and_country('male', 'Russia', :order => 'name')
306
+ #
307
+ # Continue listing:
308
+ #
309
+ # # initial listing
310
+ # Client.select(:all, :limit => 10)
311
+ # # continue listing
312
+ # begin
313
+ # Client.select(:all, :limit => 10, :next_token => Client.next_token)
314
+ # end while Client.next_token
315
+ #
316
+ # Sort oder:
317
+ # If :order=>'attribute' option is specified then result response (ordered by 'attribute') will contain only items where attribute is defined (is not null).
318
+ #
319
+ # Client.select(:all) # returns all records
320
+ # Client.select(:all, :order => 'gender') # returns all records ordered by gender where gender attribute exists
321
+ # Client.select(:all, :order => 'name desc') # returns all records ordered by name in desc order where name attribute exists
322
+ #
323
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
324
+ #
325
+ def select(*args)
326
+ options = args.last.is_a?(Hash) ? args.pop : {}
326
327
  # puts 'first=' + args.first.to_s
327
- case args.first
328
- when :all then sql_select(options)
329
- when :first then sql_select(options.merge(:limit => 1)).first
330
- else select_from_ids args, options
331
- end
332
- end
328
+ case args.first
329
+ when :all then
330
+ sql_select(options)
331
+ when :first then
332
+ sql_select(options.merge(:limit => 1)).first
333
+ else
334
+ select_from_ids args, options
335
+ end
336
+ end
337
+
338
+ def generate_id # :nodoc:
339
+ UUIDTools::UUID.timestamp_create().to_s
340
+ end
341
+
342
+ protected
343
+
344
+ # Select
345
+
346
+ def select_from_ids(args, options) # :nodoc:
347
+ cond = []
348
+ # detect amount of records requested
349
+ bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
350
+ # flatten ids
351
+ args = args.to_a.flatten
352
+ args.each { |id| cond << "id=#{self.connection.escape(id)}" }
353
+ ids_cond = "(#{cond.join(' OR ')})"
354
+ # user defined :conditions to string (if it was defined)
355
+ options[:conditions] = build_conditions(options[:conditions])
356
+ # join ids condition and user defined conditions
357
+ options[:conditions] = options[:conditions].blank? ? ids_cond : "(#{options[:conditions]}) AND #{ids_cond}"
358
+ result = sql_select(options)
359
+ # if one record was requested then return it
360
+ unless bunch_of_records_requested
361
+ record = result.first
362
+ # railse if nothing was found
363
+ raise ActiveSdbError.new("Couldn't find #{name} with ID #{args}") unless record
364
+ record
365
+ else
366
+ # if a bunch of records was requested then return check that we found all of them
367
+ # and return as an array
368
+ unless args.size == result.size
369
+ id_list = args.map{|i| "'#{i}'"}.join(',')
370
+ raise ActiveSdbError.new("Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})")
371
+ else
372
+ result
373
+ end
374
+ end
375
+ end
376
+
377
+ def sql_select(options) # :nodoc:
378
+ @next_token = options[:next_token]
379
+ select_expression = build_select(options)
380
+ # request items
381
+ query_result = self.connection.select(select_expression, @next_token)
382
+ @next_token = query_result[:next_token]
383
+ items = query_result[:items].map do |hash|
384
+ id, attributes = hash.shift
385
+ new_item = self.new( attributes.merge({ 'id' => id }))
386
+ new_item.mark_as_old
387
+ new_item
388
+ end
389
+ items
390
+ end
391
+
392
+ # select_by helpers
393
+ def select_all_by_(format_str, args, options) # :nodoc:
394
+ fields = format_str.to_s.sub(/^select_(all_)?by_/, '').split('_and_')
395
+ conditions = fields.map { |field| "#{field}=?" }.join(' AND ')
396
+ options[:conditions] = [conditions, *args]
397
+ select(:all, options)
398
+ end
399
+
400
+ def select_by_(format_str, args, options) # :nodoc:
401
+ options[:limit] = 1
402
+ select_all_by_(format_str, args, options).first
403
+ end
404
+
405
+ # Query
406
+
407
+ # Returns an array of query attributes.
408
+ # Query_expression must be a well formated SDB query string:
409
+ # query_attributes("['title' starts-with 'O\\'Reily'] intersection ['year' = '2007']") #=> ["title", "year"]
410
+ def query_attributes(query_expression) # :nodoc:
411
+ attrs = []
412
+ array = query_expression.scan(/['"](.*?[^\\])['"]/).flatten
413
+ until array.empty? do
414
+ attrs << array.shift # skip it's value
415
+ array.shift #
416
+ end
417
+ attrs
418
+ end
419
+
420
+ # Returns an array of [attribute_name, 'asc'|'desc']
421
+ def sort_options(sort_string)
422
+ sort_string[/['"]?(\w+)['"]? *(asc|desc)?/i]
423
+ [$1, ($2 || 'asc')]
424
+ end
425
+
426
+ # Perform a query request.
427
+ #
428
+ # Options
429
+ # :query_expression nil | string | array
430
+ # :max_number_of_items nil | integer
431
+ # :next_token nil | string
432
+ # :sort_option nil | string "name desc|asc"
433
+ #
434
+ def query(options) # :nodoc:
435
+ @next_token = options[:next_token]
436
+ query_expression = build_conditions(options[:query_expression])
437
+ # add sort_options to the query_expression
438
+ if options[:sort_option]
439
+ sort_by, sort_order = sort_options(options[:sort_option])
440
+ sort_query_expression = "['#{sort_by}' starts-with '']"
441
+ sort_by_expression = " sort '#{sort_by}' #{sort_order}"
442
+ # make query_expression to be a string (it may be null)
443
+ query_expression = query_expression.to_s
444
+ # quote from Amazon:
445
+ # The sort attribute must be present in at least one of the predicates of the query expression.
446
+ if query_expression.blank?
447
+ query_expression = sort_query_expression
448
+ elsif !query_attributes(query_expression).include?(sort_by)
449
+ query_expression += " intersection #{sort_query_expression}"
450
+ end
451
+ query_expression += sort_by_expression
452
+ end
453
+ # request items
454
+ query_result = self.connection.query(domain, query_expression, options[:max_number_of_items], @next_token)
455
+ @next_token = query_result[:next_token]
456
+ items = query_result[:items].map do |name|
457
+ new_item = self.new('id' => name)
458
+ new_item.mark_as_old
459
+ reload_if_exists(record) if options[:auto_load]
460
+ new_item
461
+ end
462
+ items
463
+ end
464
+
465
+ # reload a record unless it is nil
466
+ def reload_if_exists(record) # :nodoc:
467
+ record && record.reload
468
+ end
469
+
470
+ def reload_all_records(*list) # :nodoc:
471
+ list.flatten.each { |record| reload_if_exists(record) }
472
+ end
473
+
474
+ def find_every(options) # :nodoc:
475
+ records = query( :query_expression => options[:conditions],
476
+ :max_number_of_items => options[:limit],
477
+ :next_token => options[:next_token],
478
+ :sort_option => options[:sort] || options[:order] )
479
+ options[:auto_load] ? reload_all_records(records) : records
480
+ end
481
+
482
+ def find_initial(options) # :nodoc:
483
+ options[:limit] = 1
484
+ record = find_every(options).first
485
+ options[:auto_load] ? reload_all_records(record).first : record
486
+ end
487
+
488
+ def find_from_ids(args, options) # :nodoc:
489
+ cond = []
490
+ # detect amount of records requested
491
+ bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
492
+ # flatten ids
493
+ args = args.to_a.flatten
494
+ args.each { |id| cond << "'id'=#{self.connection.escape(id)}" }
495
+ ids_cond = "[#{cond.join(' OR ')}]"
496
+ # user defined :conditions to string (if it was defined)
497
+ options[:conditions] = build_conditions(options[:conditions])
498
+ # join ids condition and user defined conditions
499
+ options[:conditions] = options[:conditions].blank? ? ids_cond : "#{options[:conditions]} intersection #{ids_cond}"
500
+ result = find_every(options)
501
+ # if one record was requested then return it
502
+ unless bunch_of_records_requested
503
+ record = result.first
504
+ # railse if nothing was found
505
+ raise ActiveSdbError.new("Couldn't find #{name} with ID #{args}") unless record
506
+ options[:auto_load] ? reload_all_records(record).first : record
507
+ else
508
+ # if a bunch of records was requested then return check that we found all of them
509
+ # and return as an array
510
+ unless args.size == result.size
511
+ id_list = args.map{|i| "'#{i}'"}.join(',')
512
+ raise ActiveSdbError.new("Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})")
513
+ else
514
+ options[:auto_load] ? reload_all_records(result) : result
515
+ end
516
+ end
517
+ end
518
+
519
+ # find_by helpers
520
+ def find_all_by_(format_str, args, options) # :nodoc:
521
+ fields = format_str.to_s.sub(/^find_(all_)?by_/, '').split('_and_')
522
+ conditions = fields.map { |field| "['#{field}'=?]" }.join(' intersection ')
523
+ options[:conditions] = [conditions, *args]
524
+ find(:all, options)
525
+ end
526
+
527
+ def find_by_(format_str, args, options) # :nodoc:
528
+ options[:limit] = 1
529
+ find_all_by_(format_str, args, options).first
530
+ end
531
+
532
+ # Misc
533
+
534
+ def method_missing(method, *args) # :nodoc:
535
+ if method.to_s[/^(find_all_by_|find_by_|select_all_by_|select_by_)/]
536
+ # get rid of the find ones, only select now
537
+ to_send_to = $1
538
+ attributes = method.to_s[$1.length..method.to_s.length]
539
+ # puts 'attributes=' + attributes
540
+ if to_send_to[0...4] == "find"
541
+ to_send_to = "select" + to_send_to[4..to_send_to.length]
542
+ # puts 'CONVERTED ' + $1 + " to " + to_send_to
543
+ end
544
+
545
+ options = args.last.is_a?(Hash) ? args.pop : {}
546
+ __send__(to_send_to, attributes, args, options)
547
+ else
548
+ super(method, *args)
549
+ end
550
+ end
551
+
552
+ def build_select(options) # :nodoc:
553
+ select = options[:select] || '*'
554
+ from = options[:from] || domain
555
+ conditions = options[:conditions] ? " WHERE #{build_conditions(options[:conditions])}" : ''
556
+ order = options[:order] ? " ORDER BY #{options[:order]}" : ''
557
+ limit = options[:limit] ? " LIMIT #{options[:limit]}" : ''
558
+ # mix sort by argument (it must present in response)
559
+ unless order.blank?
560
+ sort_by, sort_order = sort_options(options[:order])
561
+ conditions << (conditions.blank? ? " WHERE " : " AND ") << "(#{sort_by} IS NOT NULL)"
562
+ end
563
+ "SELECT #{select} FROM #{from}#{conditions}#{order}#{limit}"
564
+ end
565
+
566
+ def build_conditions(conditions) # :nodoc:
567
+ case
568
+ when conditions.is_a?(Array) then
569
+ connection.query_expression_from_array(conditions)
570
+ when conditions.is_a?(Hash) then
571
+ connection.query_expression_from_hash(conditions)
572
+ else
573
+ conditions
574
+ end
575
+ end
333
576
 
334
- def generate_id # :nodoc:
335
- UUIDTools::UUID.timestamp_create().to_s
336
- end
577
+ end
337
578
 
338
- protected
339
-
340
- # Select
341
-
342
- def select_from_ids(args, options) # :nodoc:
343
- cond = []
344
- # detect amount of records requested
345
- bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
346
- # flatten ids
347
- args = args.to_a.flatten
348
- args.each { |id| cond << "id=#{self.connection.escape(id)}" }
349
- ids_cond = "(#{cond.join(' OR ')})"
350
- # user defined :conditions to string (if it was defined)
351
- options[:conditions] = build_conditions(options[:conditions])
352
- # join ids condition and user defined conditions
353
- options[:conditions] = options[:conditions].blank? ? ids_cond : "(#{options[:conditions]}) AND #{ids_cond}"
354
- result = sql_select(options)
355
- # if one record was requested then return it
356
- unless bunch_of_records_requested
357
- record = result.first
358
- # railse if nothing was found
359
- raise ActiveSdbError.new("Couldn't find #{name} with ID #{args}") unless record
360
- record
361
- else
362
- # if a bunch of records was requested then return check that we found all of them
363
- # and return as an array
364
- unless args.size == result.size
365
- id_list = args.map{|i| "'#{i}'"}.join(',')
366
- raise ActiveSdbError.new("Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})")
367
- else
368
- result
579
+ public
580
+
581
+ # instance attributes
582
+ attr_accessor :attributes
583
+
584
+ # item name
585
+ attr_accessor :id
586
+
587
+ # Create new Item instance.
588
+ # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
589
+ #
590
+ # item = Client.new('name' => 'Jon', 'toys' => ['girls', 'beer', 'pub'])
591
+ # puts item.inspect #=> #<Client:0xb77a2698 @new_record=true, @attributes={"name"=>["Jon"], "toys"=>["girls", "beer", "pub"]}>
592
+ # item.save #=> {"name"=>["Jon"], "id"=>"c03edb7e-e45c-11dc-bede-001bfc466dd7", "toys"=>["girls", "beer", "pub"]}
593
+ # puts item.inspect #=> #<Client:0xb77a2698 @new_record=false, @attributes={"name"=>["Jon"], "id"=>"c03edb7e-e45c-11dc-bede-001bfc466dd7", "toys"=>["girls", "beer", "pub"]}>
594
+ #
595
+ def initialize(attrs={})
596
+ @attributes = uniq_values(attrs)
597
+ @new_record = true
369
598
  end
370
- end
371
- end
372
599
 
373
- def sql_select(options) # :nodoc:
374
- @next_token = options[:next_token]
375
- select_expression = build_select(options)
376
- # request items
377
- query_result = self.connection.select(select_expression, @next_token)
378
- @next_token = query_result[:next_token]
379
- items = query_result[:items].map do |hash|
380
- id, attributes = hash.shift
381
- new_item = self.new( attributes.merge({ 'id' => id }))
382
- new_item.mark_as_old
383
- new_item
384
- end
385
- items
386
- end
600
+ # Create and save new Item instance.
601
+ # +Attributes+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
602
+ #
603
+ # item = Client.create('name' => 'Cat', 'toys' => ['Jons socks', 'mice', 'clew'])
604
+ # puts item.inspect #=> #<Client:0xb77a0a78 @new_record=false, @attributes={"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "mice", "clew"]}>
605
+ #
606
+ def self.create(attributes={})
607
+ item = self.new(attributes)
608
+ item.save
609
+ item
610
+ end
387
611
 
388
- # select_by helpers
389
- def select_all_by_(format_str, args, options) # :nodoc:
390
- fields = format_str.to_s.sub(/^select_(all_)?by_/,'').split('_and_')
391
- conditions = fields.map { |field| "#{field}=?" }.join(' AND ')
392
- options[:conditions] = [conditions, *args]
393
- select(:all, options)
394
- end
612
+ # Returns an item id. Same as: item['id'] or item.attributes['id']
613
+ def id
614
+ @attributes['id']
615
+ end
395
616
 
396
- def select_by_(format_str, args, options) # :nodoc:
397
- options[:limit] = 1
398
- select_all_by_(format_str, args, options).first
399
- end
617
+ # Sets an item id.
618
+ def id=(id)
619
+ @attributes['id'] = id.to_s
620
+ end
400
621
 
401
- # Query
402
-
403
- # Returns an array of query attributes.
404
- # Query_expression must be a well formated SDB query string:
405
- # query_attributes("['title' starts-with 'O\\'Reily'] intersection ['year' = '2007']") #=> ["title", "year"]
406
- def query_attributes(query_expression) # :nodoc:
407
- attrs = []
408
- array = query_expression.scan(/['"](.*?[^\\])['"]/).flatten
409
- until array.empty? do
410
- attrs << array.shift # skip it's value
411
- array.shift #
412
- end
413
- attrs
414
- end
622
+ # Returns a hash of all the attributes.
623
+ #
624
+ # puts item.attributes.inspect #=> {"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "clew", "mice"]}
625
+ #
626
+ def attributes
627
+ @attributes.dup
628
+ end
415
629
 
416
- # Returns an array of [attribute_name, 'asc'|'desc']
417
- def sort_options(sort_string)
418
- sort_string[/['"]?(\w+)['"]? *(asc|desc)?/i]
419
- [$1, ($2 || 'asc')]
420
- end
630
+ # Allows one to set all the attributes at once by passing in a hash with keys matching the attribute names.
631
+ # if '+id+' attribute is not set in new attributes has then it being derived from old attributes.
632
+ #
633
+ # puts item.attributes.inspect #=> {"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "clew", "mice"]}
634
+ # # set new attributes ('id' is missed)
635
+ # item.attributes = { 'name'=>'Dog', 'toys'=>['bones','cats'] }
636
+ # puts item.attributes.inspect #=> {"name"=>["Dog"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["bones", "cats"]}
637
+ # # set new attributes ('id' is set)
638
+ # item.attributes = { 'id' => 'blah-blah', 'name'=>'Birds', 'toys'=>['seeds','dogs tail'] }
639
+ # puts item.attributes.inspect #=> {"name"=>["Birds"], "id"=>"blah-blah", "toys"=>["seeds", "dogs tail"]}
640
+ #
641
+ def attributes=(attrs)
642
+ old_id = @attributes['id']
643
+ @attributes = uniq_values(attrs)
644
+ @attributes['id'] = old_id if @attributes['id'].blank? && !old_id.blank?
645
+ self.attributes
646
+ end
421
647
 
422
- # Perform a query request.
423
- #
424
- # Options
425
- # :query_expression nil | string | array
426
- # :max_number_of_items nil | integer
427
- # :next_token nil | string
428
- # :sort_option nil | string "name desc|asc"
429
- #
430
- def query(options) # :nodoc:
431
- @next_token = options[:next_token]
432
- query_expression = build_conditions(options[:query_expression])
433
- # add sort_options to the query_expression
434
- if options[:sort_option]
435
- sort_by, sort_order = sort_options(options[:sort_option])
436
- sort_query_expression = "['#{sort_by}' starts-with '']"
437
- sort_by_expression = " sort '#{sort_by}' #{sort_order}"
438
- # make query_expression to be a string (it may be null)
439
- query_expression = query_expression.to_s
440
- # quote from Amazon:
441
- # The sort attribute must be present in at least one of the predicates of the query expression.
442
- if query_expression.blank?
443
- query_expression = sort_query_expression
444
- elsif !query_attributes(query_expression).include?(sort_by)
445
- query_expression += " intersection #{sort_query_expression}"
446
- end
447
- query_expression += sort_by_expression
448
- end
449
- # request items
450
- query_result = self.connection.query(domain, query_expression, options[:max_number_of_items], @next_token)
451
- @next_token = query_result[:next_token]
452
- items = query_result[:items].map do |name|
453
- new_item = self.new('id' => name)
454
- new_item.mark_as_old
455
- reload_if_exists(record) if options[:auto_load]
456
- new_item
457
- end
458
- items
459
- end
648
+ def connection
649
+ self.class.connection
650
+ end
460
651
 
461
- # reload a record unless it is nil
462
- def reload_if_exists(record) # :nodoc:
463
- record && record.reload
464
- end
652
+ # Item domain name.
653
+ def domain
654
+ self.class.domain
655
+ end
465
656
 
466
- def reload_all_records(*list) # :nodoc:
467
- list.flatten.each { |record| reload_if_exists(record) }
468
- end
657
+ # Returns the values of the attribute identified by +attribute+.
658
+ #
659
+ # puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
660
+ #
661
+ def [](attribute)
662
+ @attributes[attribute.to_s]
663
+ end
469
664
 
470
- def find_every(options) # :nodoc:
471
- records = query( :query_expression => options[:conditions],
472
- :max_number_of_items => options[:limit],
473
- :next_token => options[:next_token],
474
- :sort_option => options[:sort] || options[:order] )
475
- options[:auto_load] ? reload_all_records(records) : records
476
- end
665
+ # Updates the attribute identified by +attribute+ with the specified +values+.
666
+ #
667
+ # puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
668
+ # item['Cat'] = ["Whiskas", "chicken"]
669
+ # puts item['Cat'].inspect #=> ["Whiskas", "chicken"]
670
+ #
671
+ def []=(attribute, values)
672
+ attribute = attribute.to_s
673
+ @attributes[attribute] = attribute == 'id' ? values.to_s : values.is_a?(Array) ? values.uniq : [values]
477
674
 
478
- def find_initial(options) # :nodoc:
479
- options[:limit] = 1
480
- record = find_every(options).first
481
- options[:auto_load] ? reload_all_records(record).first : record
482
- end
675
+ end
483
676
 
484
- def find_from_ids(args, options) # :nodoc:
485
- cond = []
486
- # detect amount of records requested
487
- bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
488
- # flatten ids
489
- args = args.to_a.flatten
490
- args.each { |id| cond << "'id'=#{self.connection.escape(id)}" }
491
- ids_cond = "[#{cond.join(' OR ')}]"
492
- # user defined :conditions to string (if it was defined)
493
- options[:conditions] = build_conditions(options[:conditions])
494
- # join ids condition and user defined conditions
495
- options[:conditions] = options[:conditions].blank? ? ids_cond : "#{options[:conditions]} intersection #{ids_cond}"
496
- result = find_every(options)
497
- # if one record was requested then return it
498
- unless bunch_of_records_requested
499
- record = result.first
500
- # railse if nothing was found
501
- raise ActiveSdbError.new("Couldn't find #{name} with ID #{args}") unless record
502
- options[:auto_load] ? reload_all_records(record).first : record
503
- else
504
- # if a bunch of records was requested then return check that we found all of them
505
- # and return as an array
506
- unless args.size == result.size
507
- id_list = args.map{|i| "'#{i}'"}.join(',')
508
- raise ActiveSdbError.new("Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})")
509
- else
510
- options[:auto_load] ? reload_all_records(result) : result
677
+ # Reload attributes from SDB. Replaces in-memory attributes.
678
+ #
679
+ # item = Client.find_by_name('Cat') #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7"}, @new_record=false>
680
+ # item.reload #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}, @new_record=false>
681
+ #
682
+ def reload
683
+ raise_on_id_absence
684
+ old_id = id
685
+ attrs = connection.get_attributes(domain, id)[:attributes]
686
+ @attributes = {}
687
+ unless attrs.blank?
688
+ attrs.each { |attribute, values| @attributes[attribute] = values }
689
+ @attributes['id'] = old_id
690
+ end
691
+ mark_as_old
692
+ @attributes
511
693
  end
512
- end
513
- end
514
694
 
515
- # find_by helpers
516
- def find_all_by_(format_str, args, options) # :nodoc:
517
- fields = format_str.to_s.sub(/^find_(all_)?by_/,'').split('_and_')
518
- conditions = fields.map { |field| "['#{field}'=?]" }.join(' intersection ')
519
- options[:conditions] = [conditions, *args]
520
- find(:all, options)
521
- end
695
+ # Reload a set of attributes from SDB. Adds the loaded list to in-memory data.
696
+ # +attrs_list+ is an array or comma separated list of attributes names.
697
+ # Returns a hash of loaded attributes.
698
+ #
699
+ # This is not the best method to get a bunch of attributes because
700
+ # a web service call is being performed for every attribute.
701
+ #
702
+ # item = Client.find_by_name('Cat')
703
+ # item.reload_attributes('toys', 'name') #=> {"name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}
704
+ #
705
+ def reload_attributes(*attrs_list)
706
+ raise_on_id_absence
707
+ attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
708
+ attrs_list.delete('id')
709
+ result = {}
710
+ attrs_list.flatten.uniq.each do |attribute|
711
+ attribute = attribute.to_s
712
+ values = connection.get_attributes(domain, id, attribute)[:attributes][attribute]
713
+ unless values.blank?
714
+ @attributes[attribute] = result[attribute] = values
715
+ else
716
+ @attributes.delete(attribute)
717
+ end
718
+ end
719
+ mark_as_old
720
+ result
721
+ end
522
722
 
523
- def find_by_(format_str, args, options) # :nodoc:
524
- options[:limit] = 1
525
- find_all_by_(format_str, args, options).first
526
- end
723
+ # Stores in-memory attributes to SDB.
724
+ # Adds the attributes values to already stored at SDB.
725
+ # Returns a hash of stored attributes.
726
+ #
727
+ # sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={"name"=>["Sandy"]}, @new_record=true>
728
+ # sandy['toys'] = 'boys'
729
+ # sandy.put
730
+ # sandy['toys'] = 'patchwork'
731
+ # sandy.put
732
+ # sandy['toys'] = 'kids'
733
+ # sandy.put
734
+ # puts sandy.attributes.inspect #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
735
+ # sandy.reload #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"]}
736
+ #
737
+ # compare to +save+ method
738
+ def put
739
+ @attributes = uniq_values(@attributes)
740
+ prepare_for_update
741
+ attrs = @attributes.dup
742
+ attrs.delete('id')
743
+ connection.put_attributes(domain, id, attrs) unless attrs.blank?
744
+ connection.put_attributes(domain, id, { 'id' => id }, :replace)
745
+ mark_as_old
746
+ @attributes
747
+ end
527
748
 
528
- # Misc
749
+ # Stores specified attributes.
750
+ # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
751
+ # Returns a hash of saved attributes.
752
+ #
753
+ # see to +put+ method
754
+ def put_attributes(attrs)
755
+ attrs = uniq_values(attrs)
756
+ prepare_for_update
757
+ # if 'id' is present in attrs hash:
758
+ # replace internal 'id' attribute and remove it from the attributes to be sent
759
+ @attributes['id'] = attrs['id'] unless attrs['id'].blank?
760
+ attrs.delete('id')
761
+ # add new values to all attributes from list
762
+ connection.put_attributes(domain, id, attrs) unless attrs.blank?
763
+ connection.put_attributes(domain, id, { 'id' => id }, :replace)
764
+ attrs.each do |attribute, values|
765
+ @attributes[attribute] ||= []
766
+ @attributes[attribute] += values
767
+ @attributes[attribute].uniq!
768
+ end
769
+ mark_as_old
770
+ attributes
771
+ end
529
772
 
530
- def method_missing(method, *args) # :nodoc:
531
- if method.to_s[/^(find_all_by_|find_by_|select_all_by_|select_by_)/]
532
- # get rid of the find ones, only select now
533
- to_send_to = $1
534
- attributes = method.to_s[$1.length..method.to_s.length]
535
- # puts 'attributes=' + attributes
536
- if to_send_to[0...4] == "find"
537
- to_send_to = "select" + to_send_to[4..to_send_to.length]
538
- # puts 'CONVERTED ' + $1 + " to " + to_send_to
773
+ # Store in-memory attributes to SDB.
774
+ # Replaces the attributes values already stored at SDB by in-memory data.
775
+ # Returns a hash of stored attributes.
776
+ #
777
+ # sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={"name"=>["Sandy"]}, @new_record=true>
778
+ # sandy['toys'] = 'boys'
779
+ # sandy.save
780
+ # sandy['toys'] = 'patchwork'
781
+ # sandy.save
782
+ # sandy['toys'] = 'kids'
783
+ # sandy.save
784
+ # puts sandy.attributes.inspect #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
785
+ # sandy.reload #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
786
+ #
787
+ # Options:
788
+ # - :except => Array of attributes to NOT save
789
+ #
790
+ # compare to +put+ method
791
+ def save(options={})
792
+ pre_save2
793
+ atts_to_save = @attributes.dup
794
+ #options = params.first.is_a?(Hash) ? params.pop : {}
795
+ if options[:except]
796
+ options[:except].each do |e|
797
+ atts_to_save.delete(e).inspect
798
+ end
799
+ end
800
+ if options[:dirty] # Only used in simple_record right now
801
+ # only save if the attribute is dirty
802
+ dirty_atts = options[:dirty_atts]
803
+ atts_to_save.delete_if { |key, value| !dirty_atts.has_key?(key) }
804
+ end
805
+ connection.put_attributes(domain, id, atts_to_save, :replace)
806
+ apres_save2
807
+ @attributes
539
808
  end
540
809
 
541
- options = args.last.is_a?(Hash) ? args.pop : {}
542
- __send__(to_send_to, attributes, args, options)
543
- else
544
- super(method, *args)
545
- end
546
- end
810
+ def pre_save2
811
+ @attributes = uniq_values(@attributes)
812
+ prepare_for_update
813
+ end
547
814
 
548
- def build_select(options) # :nodoc:
549
- select = options[:select] || '*'
550
- from = options[:from] || domain
551
- conditions = options[:conditions] ? " WHERE #{build_conditions(options[:conditions])}" : ''
552
- order = options[:order] ? " ORDER BY #{options[:order]}" : ''
553
- limit = options[:limit] ? " LIMIT #{options[:limit]}" : ''
554
- # mix sort by argument (it must present in response)
555
- unless order.blank?
556
- sort_by, sort_order = sort_options(options[:order])
557
- conditions << (conditions.blank? ? " WHERE " : " AND ") << "(#{sort_by} IS NOT NULL)"
558
- end
559
- "SELECT #{select} FROM #{from}#{conditions}#{order}#{limit}"
560
- end
815
+ def apres_save2
816
+ mark_as_old
817
+ end
561
818
 
562
- def build_conditions(conditions) # :nodoc:
563
- case
564
- when conditions.is_a?(Array) then connection.query_expression_from_array(conditions)
565
- when conditions.is_a?(Hash) then connection.query_expression_from_hash(conditions)
566
- else conditions
567
- end
568
- end
819
+ # Replaces the attributes at SDB by the given values.
820
+ # +Attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
821
+ # The other in-memory attributes are not being saved.
822
+ # Returns a hash of stored attributes.
823
+ #
824
+ # see +save+ method
825
+ def save_attributes(attrs)
826
+ prepare_for_update
827
+ attrs = uniq_values(attrs)
828
+ # if 'id' is present in attrs hash then replace internal 'id' attribute
829
+ unless attrs['id'].blank?
830
+ @attributes['id'] = attrs['id']
831
+ else
832
+ attrs['id'] = id
833
+ end
834
+ connection.put_attributes(domain, id, attrs, :replace) unless attrs.blank?
835
+ attrs.each { |attribute, values| attrs[attribute] = values }
836
+ mark_as_old
837
+ attrs
838
+ end
569
839
 
570
- end
571
-
572
- public
573
-
574
- # instance attributes
575
- attr_accessor :attributes
576
-
577
- # item name
578
- attr_accessor :id
579
-
580
- # Create new Item instance.
581
- # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
582
- #
583
- # item = Client.new('name' => 'Jon', 'toys' => ['girls', 'beer', 'pub'])
584
- # puts item.inspect #=> #<Client:0xb77a2698 @new_record=true, @attributes={"name"=>["Jon"], "toys"=>["girls", "beer", "pub"]}>
585
- # item.save #=> {"name"=>["Jon"], "id"=>"c03edb7e-e45c-11dc-bede-001bfc466dd7", "toys"=>["girls", "beer", "pub"]}
586
- # puts item.inspect #=> #<Client:0xb77a2698 @new_record=false, @attributes={"name"=>["Jon"], "id"=>"c03edb7e-e45c-11dc-bede-001bfc466dd7", "toys"=>["girls", "beer", "pub"]}>
587
- #
588
- def initialize(attrs={})
589
- @attributes = uniq_values(attrs)
590
- @new_record = true
591
- end
592
-
593
- # Create and save new Item instance.
594
- # +Attributes+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
595
- #
596
- # item = Client.create('name' => 'Cat', 'toys' => ['Jons socks', 'mice', 'clew'])
597
- # puts item.inspect #=> #<Client:0xb77a0a78 @new_record=false, @attributes={"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "mice", "clew"]}>
598
- #
599
- def self.create(attributes={})
600
- item = self.new(attributes)
601
- item.save
602
- item
603
- end
604
-
605
- # Returns an item id. Same as: item['id'] or item.attributes['id']
606
- def id
607
- @attributes['id']
608
- end
609
-
610
- # Sets an item id.
611
- def id=(id)
612
- @attributes['id'] = id.to_s
613
- end
614
-
615
- # Returns a hash of all the attributes.
616
- #
617
- # puts item.attributes.inspect #=> {"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "clew", "mice"]}
618
- #
619
- def attributes
620
- @attributes.dup
621
- end
622
-
623
- # Allows one to set all the attributes at once by passing in a hash with keys matching the attribute names.
624
- # if '+id+' attribute is not set in new attributes has then it being derived from old attributes.
625
- #
626
- # puts item.attributes.inspect #=> {"name"=>["Cat"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["Jons socks", "clew", "mice"]}
627
- # # set new attributes ('id' is missed)
628
- # item.attributes = { 'name'=>'Dog', 'toys'=>['bones','cats'] }
629
- # puts item.attributes.inspect #=> {"name"=>["Dog"], "id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "toys"=>["bones", "cats"]}
630
- # # set new attributes ('id' is set)
631
- # item.attributes = { 'id' => 'blah-blah', 'name'=>'Birds', 'toys'=>['seeds','dogs tail'] }
632
- # puts item.attributes.inspect #=> {"name"=>["Birds"], "id"=>"blah-blah", "toys"=>["seeds", "dogs tail"]}
633
- #
634
- def attributes=(attrs)
635
- old_id = @attributes['id']
636
- @attributes = uniq_values(attrs)
637
- @attributes['id'] = old_id if @attributes['id'].blank? && !old_id.blank?
638
- self.attributes
639
- end
640
-
641
- def connection
642
- self.class.connection
643
- end
644
-
645
- # Item domain name.
646
- def domain
647
- self.class.domain
648
- end
649
-
650
- # Returns the values of the attribute identified by +attribute+.
651
- #
652
- # puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
653
- #
654
- def [](attribute)
655
- @attributes[attribute.to_s]
656
- end
657
-
658
- # Updates the attribute identified by +attribute+ with the specified +values+.
659
- #
660
- # puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
661
- # item['Cat'] = ["Whiskas", "chicken"]
662
- # puts item['Cat'].inspect #=> ["Whiskas", "chicken"]
663
- #
664
- def []=(attribute, values)
665
- attribute = attribute.to_s
666
- @attributes[attribute] = attribute == 'id' ? values.to_s : values.is_a?(Array) ? values.uniq : [values]
667
- end
668
-
669
- # Reload attributes from SDB. Replaces in-memory attributes.
670
- #
671
- # item = Client.find_by_name('Cat') #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7"}, @new_record=false>
672
- # item.reload #=> #<Client:0xb77d0d40 @attributes={"id"=>"2937601a-e45d-11dc-a75f-001bfc466dd7", "name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}, @new_record=false>
673
- #
674
- def reload
675
- raise_on_id_absence
676
- old_id = id
677
- attrs = connection.get_attributes(domain, id)[:attributes]
678
- @attributes = {}
679
- unless attrs.blank?
680
- attrs.each { |attribute, values| @attributes[attribute] = values }
681
- @attributes['id'] = old_id
682
- end
683
- mark_as_old
684
- @attributes
685
- end
686
-
687
- # Reload a set of attributes from SDB. Adds the loaded list to in-memory data.
688
- # +attrs_list+ is an array or comma separated list of attributes names.
689
- # Returns a hash of loaded attributes.
690
- #
691
- # This is not the best method to get a bunch of attributes because
692
- # a web service call is being performed for every attribute.
693
- #
694
- # item = Client.find_by_name('Cat')
695
- # item.reload_attributes('toys', 'name') #=> {"name"=>["Cat"], "toys"=>["Jons socks", "clew", "mice"]}
696
- #
697
- def reload_attributes(*attrs_list)
698
- raise_on_id_absence
699
- attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
700
- attrs_list.delete('id')
701
- result = {}
702
- attrs_list.flatten.uniq.each do |attribute|
703
- attribute = attribute.to_s
704
- values = connection.get_attributes(domain, id, attribute)[:attributes][attribute]
705
- unless values.blank?
706
- @attributes[attribute] = result[attribute] = values
707
- else
708
- @attributes.delete(attribute)
709
- end
710
- end
711
- mark_as_old
712
- result
713
- end
714
-
715
- # Stores in-memory attributes to SDB.
716
- # Adds the attributes values to already stored at SDB.
717
- # Returns a hash of stored attributes.
718
- #
719
- # sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={"name"=>["Sandy"]}, @new_record=true>
720
- # sandy['toys'] = 'boys'
721
- # sandy.put
722
- # sandy['toys'] = 'patchwork'
723
- # sandy.put
724
- # sandy['toys'] = 'kids'
725
- # sandy.put
726
- # puts sandy.attributes.inspect #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
727
- # sandy.reload #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"]}
728
- #
729
- # compare to +save+ method
730
- def put
731
- @attributes = uniq_values(@attributes)
732
- prepare_for_update
733
- attrs = @attributes.dup
734
- attrs.delete('id')
735
- connection.put_attributes(domain, id, attrs) unless attrs.blank?
736
- connection.put_attributes(domain, id, { 'id' => id }, :replace)
737
- mark_as_old
738
- @attributes
739
- end
740
-
741
- # Stores specified attributes.
742
- # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
743
- # Returns a hash of saved attributes.
744
- #
745
- # see to +put+ method
746
- def put_attributes(attrs)
747
- attrs = uniq_values(attrs)
748
- prepare_for_update
749
- # if 'id' is present in attrs hash:
750
- # replace internal 'id' attribute and remove it from the attributes to be sent
751
- @attributes['id'] = attrs['id'] unless attrs['id'].blank?
752
- attrs.delete('id')
753
- # add new values to all attributes from list
754
- connection.put_attributes(domain, id, attrs) unless attrs.blank?
755
- connection.put_attributes(domain, id, { 'id' => id }, :replace)
756
- attrs.each do |attribute, values|
757
- @attributes[attribute] ||= []
758
- @attributes[attribute] += values
759
- @attributes[attribute].uniq!
760
- end
761
- mark_as_old
762
- attributes
763
- end
764
-
765
- # Store in-memory attributes to SDB.
766
- # Replaces the attributes values already stored at SDB by in-memory data.
767
- # Returns a hash of stored attributes.
768
- #
769
- # sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={"name"=>["Sandy"]}, @new_record=true>
770
- # sandy['toys'] = 'boys'
771
- # sandy.put
772
- # sandy['toys'] = 'patchwork'
773
- # sandy.put
774
- # sandy['toys'] = 'kids'
775
- # sandy.put
776
- # puts sandy.attributes.inspect #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
777
- # sandy.reload #=> {"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["kids"]}
778
- #
779
- # compare to +put+ method
780
- def save(*params)
781
- pre_save2
782
- atts_to_save = @attributes.dup
783
- options = params.first.is_a?(Hash) ? params.pop : {}
784
- if options[:except]
785
- options[:except].each do |e|
786
- atts_to_save.delete(e).inspect
840
+ # Remove specified values from corresponding attributes.
841
+ # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
842
+ #
843
+ # sandy = Client.find_by_name 'Sandy'
844
+ # sandy.reload
845
+ # puts sandy.inspect #=> #<Client:0xb77b48fc @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"]}>
846
+ # puts sandy.delete_values('toys' => 'patchwork') #=> { 'toys' => ['patchwork'] }
847
+ # puts sandy.inspect #=> #<Client:0xb77b48fc @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids"]}>
848
+ #
849
+ def delete_values(attrs)
850
+ raise_on_id_absence
851
+ attrs = uniq_values(attrs)
852
+ attrs.delete('id')
853
+ unless attrs.blank?
854
+ connection.delete_attributes(domain, id, attrs)
855
+ attrs.each do |attribute, values|
856
+ # remove the values from the attribute
857
+ if @attributes[attribute]
858
+ @attributes[attribute] -= values
859
+ else
860
+ # if the attribute is unknown remove it from a resulting list of fixed attributes
861
+ attrs.delete(attribute)
862
+ end
863
+ end
864
+ end
865
+ attrs
787
866
  end
788
- end
789
- connection.put_attributes(domain, id, atts_to_save, :replace)
790
- apres_save2
791
- @attributes
792
- end
793
-
794
- def pre_save2
795
- @attributes = uniq_values(@attributes)
796
- prepare_for_update
797
- end
798
-
799
- def apres_save2
800
- mark_as_old
801
- end
802
-
803
- # Replaces the attributes at SDB by the given values.
804
- # +Attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
805
- # The other in-memory attributes are not being saved.
806
- # Returns a hash of stored attributes.
807
- #
808
- # see +save+ method
809
- def save_attributes(attrs)
810
- prepare_for_update
811
- attrs = uniq_values(attrs)
812
- # if 'id' is present in attrs hash then replace internal 'id' attribute
813
- unless attrs['id'].blank?
814
- @attributes['id'] = attrs['id']
815
- else
816
- attrs['id'] = id
817
- end
818
- connection.put_attributes(domain, id, attrs, :replace) unless attrs.blank?
819
- attrs.each { |attribute, values| attrs[attribute] = values }
820
- mark_as_old
821
- attrs
822
- end
823
-
824
- # Remove specified values from corresponding attributes.
825
- # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.
826
- #
827
- # sandy = Client.find_by_name 'Sandy'
828
- # sandy.reload
829
- # puts sandy.inspect #=> #<Client:0xb77b48fc @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"]}>
830
- # puts sandy.delete_values('toys' => 'patchwork') #=> { 'toys' => ['patchwork'] }
831
- # puts sandy.inspect #=> #<Client:0xb77b48fc @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids"]}>
832
- #
833
- def delete_values(attrs)
834
- raise_on_id_absence
835
- attrs = uniq_values(attrs)
836
- attrs.delete('id')
837
- unless attrs.blank?
838
- connection.delete_attributes(domain, id, attrs)
839
- attrs.each do |attribute, values|
840
- # remove the values from the attribute
841
- if @attributes[attribute]
842
- @attributes[attribute] -= values
843
- else
844
- # if the attribute is unknown remove it from a resulting list of fixed attributes
845
- attrs.delete(attribute)
867
+
868
+ # Removes specified attributes from the item.
869
+ # +attrs_list+ is an array or comma separated list of attributes names.
870
+ # Returns the list of deleted attributes.
871
+ #
872
+ # sandy = Client.find_by_name 'Sandy'
873
+ # sandy.reload
874
+ # puts sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"}>
875
+ # puts sandy.delete_attributes('toys') #=> ['toys']
876
+ # puts sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7"}>
877
+ #
878
+ def delete_attributes(*attrs_list)
879
+ raise_on_id_absence
880
+ attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
881
+ attrs_list.delete('id')
882
+ unless attrs_list.blank?
883
+ connection.delete_attributes(domain, id, attrs_list)
884
+ attrs_list.each { |attribute| @attributes.delete(attribute) }
885
+ end
886
+ attrs_list
887
+ end
888
+
889
+ # Delete the Item entirely from SDB.
890
+ #
891
+ # sandy = Client.find_by_name 'Sandy'
892
+ # sandy.reload
893
+ # sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"}>
894
+ # puts sandy.delete
895
+ # sandy.reload
896
+ # puts sandy.inspect #=> #<Client:0xb7761d28 @attributes={}, @new_record=false>
897
+ #
898
+ def delete
899
+ raise_on_id_absence
900
+ connection.delete_attributes(domain, id)
901
+ end
902
+
903
+ # Item ID
904
+ def to_s
905
+ @id
906
+ end
907
+
908
+ # Returns true if this object hasn‘t been saved yet.
909
+ def new_record?
910
+ @new_record
911
+ end
912
+
913
+ def mark_as_old # :nodoc:
914
+ @new_record = false
915
+ end
916
+
917
+ private
918
+
919
+ def raise_on_id_absence
920
+ raise ActiveSdbError.new('Unknown record id') unless id
921
+ end
922
+
923
+ def prepare_for_update
924
+ @attributes['id'] = self.class.generate_id if @attributes['id'].blank?
925
+ end
926
+
927
+ def uniq_values(attributes=nil) # :nodoc:
928
+ attrs = {}
929
+ attributes.each do |attribute, values|
930
+ attribute = attribute.to_s
931
+ attrs[attribute] = attribute == 'id' ? values.to_s : values.is_a?(Array) ? values.uniq : [values]
932
+ attrs.delete(attribute) if values.blank?
933
+ end
934
+ attrs
846
935
  end
847
- end
848
- end
849
- attrs
850
- end
851
-
852
- # Removes specified attributes from the item.
853
- # +attrs_list+ is an array or comma separated list of attributes names.
854
- # Returns the list of deleted attributes.
855
- #
856
- # sandy = Client.find_by_name 'Sandy'
857
- # sandy.reload
858
- # puts sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"}>
859
- # puts sandy.delete_attributes('toys') #=> ['toys']
860
- # puts sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7"}>
861
- #
862
- def delete_attributes(*attrs_list)
863
- raise_on_id_absence
864
- attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
865
- attrs_list.delete('id')
866
- unless attrs_list.blank?
867
- connection.delete_attributes(domain, id, attrs_list)
868
- attrs_list.each { |attribute| @attributes.delete(attribute) }
869
- end
870
- attrs_list
871
- end
872
-
873
- # Delete the Item entirely from SDB.
874
- #
875
- # sandy = Client.find_by_name 'Sandy'
876
- # sandy.reload
877
- # sandy.inspect #=> #<Client:0xb7761d28 @new_record=false, @attributes={"name"=>["Sandy"], "id"=>"b2832ce2-e461-11dc-b13c-001bfc466dd7", "toys"=>["boys", "kids", "patchwork"}>
878
- # puts sandy.delete
879
- # sandy.reload
880
- # puts sandy.inspect #=> #<Client:0xb7761d28 @attributes={}, @new_record=false>
881
- #
882
- def delete
883
- raise_on_id_absence
884
- connection.delete_attributes(domain, id)
885
- end
886
-
887
- # Item ID
888
- def to_s
889
- @id
890
- end
891
-
892
- # Returns true if this object hasn‘t been saved yet.
893
- def new_record?
894
- @new_record
895
- end
896
-
897
- def mark_as_old # :nodoc:
898
- @new_record = false
899
- end
900
-
901
- private
902
-
903
- def raise_on_id_absence
904
- raise ActiveSdbError.new('Unknown record id') unless id
905
- end
906
-
907
- def prepare_for_update
908
- @attributes['id'] = self.class.generate_id if @attributes['id'].blank?
909
- end
910
-
911
- def uniq_values(attributes=nil) # :nodoc:
912
- attrs = {}
913
- attributes.each do |attribute, values|
914
- attribute = attribute.to_s
915
- attrs[attribute] = attribute == 'id' ? values.to_s : values.is_a?(Array) ? values.uniq : [values]
916
- attrs.delete(attribute) if values.blank?
917
936
  end
918
- attrs
919
- end
920
937
  end
921
- end
922
938
  end