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.
- data/lib/awsbase/right_awsbase.rb +5 -2
- data/lib/sdb/active_sdb.rb +857 -841
- metadata +4 -3
|
@@ -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
|
-
|
|
702
|
-
|
|
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)
|
data/lib/sdb/active_sdb.rb
CHANGED
|
@@ -22,242 +22,243 @@
|
|
|
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
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
335
|
-
UUIDTools::UUID.timestamp_create().to_s
|
|
336
|
-
end
|
|
577
|
+
end
|
|
337
578
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
617
|
+
# Sets an item id.
|
|
618
|
+
def id=(id)
|
|
619
|
+
@attributes['id'] = id.to_s
|
|
620
|
+
end
|
|
400
621
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
652
|
+
# Item domain name.
|
|
653
|
+
def domain
|
|
654
|
+
self.class.domain
|
|
655
|
+
end
|
|
465
656
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
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
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
#
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
#
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
#
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
end
|
|
546
|
-
end
|
|
810
|
+
def pre_save2
|
|
811
|
+
@attributes = uniq_values(@attributes)
|
|
812
|
+
prepare_for_update
|
|
813
|
+
end
|
|
547
814
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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
|