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