appoxy-aws 1.11.24 → 1.11.25

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