simple_record 2.0.2 → 2.0.4

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/simple_record.rb CHANGED
@@ -46,412 +46,434 @@ require_relative 'simple_record/sharding'
46
46
 
47
47
  module SimpleRecord
48
48
 
49
- @@options = {}
50
- @@stats = SimpleRecord::Stats.new
51
- @@logging = false
52
- @@s3 = nil
53
- @@auto_close_s3 = false
54
- @@logger = Logger.new(STDOUT)
55
- @@logger.level = Logger::INFO
49
+ @@options = {}
50
+ @@stats = SimpleRecord::Stats.new
51
+ @@logging = false
52
+ @@s3 = nil
53
+ @@auto_close_s3 = false
54
+ @@logger = Logger.new(STDOUT)
55
+ @@logger.level = Logger::INFO
56
+
57
+ class << self;
58
+ attr_accessor :aws_access_key, :aws_secret_key
59
+
60
+ # Deprecated
61
+ def enable_logging
62
+ @@logging = true
63
+ @@logger.level = Logger::DEBUG
64
+ end
56
65
 
57
- class << self;
58
- attr_accessor :aws_access_key, :aws_secret_key
66
+ # Deprecated
67
+ def disable_logging
68
+ @@logging = false
69
+ end
59
70
 
60
- # Deprecated
61
- def enable_logging
62
- @@logging = true
63
- @@logger.level = Logger::DEBUG
64
- end
71
+ # Deprecated
72
+ def logging?
73
+ @@logging
74
+ end
65
75
 
66
- # Deprecated
67
- def disable_logging
68
- @@logging = false
69
- end
76
+ def logger
77
+ @@logger
78
+ end
70
79
 
71
- # Deprecated
72
- def logging?
73
- @@logging
74
- end
80
+ # This can be used to log queries and what not to a file.
81
+ # Params:
82
+ # :select=>{:filename=>"file_to_write_to", :format=>"csv"}
83
+ def log_usage(types={})
84
+ @usage_logging_options = {} unless @usage_logging_options
85
+ return if types.nil?
86
+ types.each_pair do |type, options|
87
+ options[:lines_between_flushes] = 100 unless options[:lines_between_flushes]
88
+ @usage_logging_options[type] = options
89
+ end
90
+ #puts 'SimpleRecord.usage_logging_options=' + SimpleRecord.usage_logging_options.inspect
91
+ end
75
92
 
76
- def logger
77
- @@logger
78
- end
93
+ def close_usage_log(type)
94
+ return unless @usage_logging_options[type]
95
+ @usage_logging_options[type][:file].close if @usage_logging_options[type][:file]
96
+ end
79
97
 
80
- # This can be used to log queries and what not to a file.
81
- # Params:
82
- # :select=>{:filename=>"file_to_write_to", :format=>"csv"}
83
- def log_usage(types={})
84
- @usage_logging_options = {} unless @usage_logging_options
85
- return if types.nil?
86
- types.each_pair do |type, options|
87
- options[:lines_between_flushes] = 100 unless options[:lines_between_flushes]
88
- @usage_logging_options[type] = options
89
- end
90
- #puts 'SimpleRecord.usage_logging_options=' + SimpleRecord.usage_logging_options.inspect
91
- end
98
+ def usage_logging_options
99
+ @usage_logging_options
100
+ end
92
101
 
93
- def close_usage_log(type)
94
- return unless @usage_logging_options[type]
95
- @usage_logging_options[type][:file].close if @usage_logging_options[type][:file]
96
- end
102
+ def stats
103
+ @@stats
104
+ end
97
105
 
98
- def usage_logging_options
99
- @usage_logging_options
100
- end
101
106
 
102
- def stats
103
- @@stats
104
- end
107
+ # Create a new handle to an Sdb account. All handles share the same per process or per thread
108
+ # HTTP connection to Amazon Sdb. Each handle is for a specific account.
109
+ # The +params+ are passed through as-is to Aws::SdbInterface.new
110
+ # Params:
111
+ # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
112
+ # :port => 443 # Amazon service port: 80(default) or 443
113
+ # :protocol => 'https' # Amazon service protocol: 'http'(default) or 'https'
114
+ # :signature_version => '0' # The signature version : '0' or '1'(default)
115
+ # :connection_mode => :default # options are
116
+ # :default (will use best known safe (as in won't need explicit close) option, may change in the future)
117
+ # :per_request (opens and closes a connection on every request to SDB)
118
+ # :single (one thread across entire app)
119
+ # :per_thread (one connection per thread)
120
+ # :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
121
+ # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
122
+ def establish_connection(aws_access_key=nil, aws_secret_key=nil, options={})
123
+ @aws_access_key = aws_access_key
124
+ @aws_secret_key = aws_secret_key
125
+ @@options.merge!(options)
126
+ #puts 'SimpleRecord.establish_connection with options: ' + @@options.inspect
127
+ SimpleRecord::ActiveSdb.establish_connection(aws_access_key, aws_secret_key, @@options)
128
+ if options[:connection_mode] == :per_thread
129
+ @@auto_close_s3 = true
130
+ # todo: should we init this only when needed?
131
+ end
132
+ s3_ops = {:connection_mode=>options[:connection_mode] || :default}
133
+ @@s3 = Aws::S3.new(SimpleRecord.aws_access_key, SimpleRecord.aws_secret_key, s3_ops)
134
+
135
+ if options[:created_col]
136
+ SimpleRecord::Base.has_dates options[:created_col]
137
+ end
138
+ if options[:updated_col]
139
+ SimpleRecord::Base.has_dates options[:updated_col]
140
+ end
105
141
 
106
142
 
107
- # Create a new handle to an Sdb account. All handles share the same per process or per thread
108
- # HTTP connection to Amazon Sdb. Each handle is for a specific account.
109
- # The +params+ are passed through as-is to Aws::SdbInterface.new
110
- # Params:
111
- # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
112
- # :port => 443 # Amazon service port: 80(default) or 443
113
- # :protocol => 'https' # Amazon service protocol: 'http'(default) or 'https'
114
- # :signature_version => '0' # The signature version : '0' or '1'(default)
115
- # :connection_mode => :default # options are
116
- # :default (will use best known safe (as in won't need explicit close) option, may change in the future)
117
- # :per_request (opens and closes a connection on every request to SDB)
118
- # :single (one thread across entire app)
119
- # :per_thread (one connection per thread)
120
- # :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
121
- # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
122
- def establish_connection(aws_access_key=nil, aws_secret_key=nil, options={})
123
- @aws_access_key = aws_access_key
124
- @aws_secret_key = aws_secret_key
125
- @@options.merge!(options)
126
- #puts 'SimpleRecord.establish_connection with options: ' + @@options.inspect
127
- SimpleRecord::ActiveSdb.establish_connection(aws_access_key, aws_secret_key, @@options)
128
- if options[:connection_mode] == :per_thread
129
- @@auto_close_s3 = true
130
- # todo: should we init this only when needed?
131
- end
132
- s3_ops = {:connection_mode=>options[:connection_mode] || :default}
133
- @@s3 = Aws::S3.new(SimpleRecord.aws_access_key, SimpleRecord.aws_secret_key, s3_ops)
134
- end
135
-
136
- # Call this to close the connection to SimpleDB.
137
- # If you're using this in Rails with per_thread connection mode, you should do this in
138
- # an after_filter for each request.
139
- def close_connection()
140
- SimpleRecord::ActiveSdb.close_connection
141
- @@s3.close_connection if @@auto_close_s3
142
- end
143
+ end
143
144
 
144
- # If you'd like to specify the s3 connection to use for LOBs, you can pass it in here.
145
- # We recommend that this connection matches the type of connection you're using for SimpleDB,
146
- # at least if you're using per_thread connection mode.
147
- def s3=(s3)
148
- @@s3 = s3
149
- end
145
+ # Call this to close the connection to SimpleDB.
146
+ # If you're using this in Rails with per_thread connection mode, you should do this in
147
+ # an after_filter for each request.
148
+ def close_connection()
149
+ SimpleRecord::ActiveSdb.close_connection
150
+ @@s3.close_connection if @@auto_close_s3
151
+ end
150
152
 
151
- def s3
152
- @@s3
153
- end
153
+ # If you'd like to specify the s3 connection to use for LOBs, you can pass it in here.
154
+ # We recommend that this connection matches the type of connection you're using for SimpleDB,
155
+ # at least if you're using per_thread connection mode.
156
+ def s3=(s3)
157
+ @@s3 = s3
158
+ end
154
159
 
155
- def options
156
- @@options
157
- end
160
+ def s3
161
+ @@s3
162
+ end
158
163
 
164
+ def options
165
+ @@options
159
166
  end
160
167
 
161
- class Base < SimpleRecord::ActiveSdb::Base
168
+ end
169
+
170
+ class Base < SimpleRecord::ActiveSdb::Base
162
171
 
163
172
 
164
173
  # puts 'Is ActiveModel defined? ' + defined?(ActiveModel).inspect
165
- if defined?(ActiveModel)
166
- extend ActiveModel::Naming
167
- include ActiveModel::Conversion
168
- include ActiveModel::Validations
169
- else
170
- attr_accessor :errors
171
- include SimpleRecord::Rails2
172
- end
174
+ if defined?(ActiveModel)
175
+ extend ActiveModel::Naming
176
+ include ActiveModel::Conversion
177
+ include ActiveModel::Validations
178
+ else
179
+ attr_accessor :errors
180
+ include SimpleRecord::Rails2
181
+ end
173
182
 
174
- include SimpleRecord::Translations
183
+ include SimpleRecord::Translations
175
184
  # include SimpleRecord::Attributes
176
- extend SimpleRecord::Attributes::ClassMethods
177
- include SimpleRecord::Attributes
178
- extend SimpleRecord::Sharding::ClassMethods
179
- include SimpleRecord::Sharding
180
- include SimpleRecord::Callbacks
181
- include SimpleRecord::Json
182
- include SimpleRecord::Logging
183
- extend SimpleRecord::Logging::ClassMethods
185
+ extend SimpleRecord::Attributes::ClassMethods
186
+ include SimpleRecord::Attributes
187
+ extend SimpleRecord::Sharding::ClassMethods
188
+ include SimpleRecord::Sharding
189
+ include SimpleRecord::Callbacks
190
+ include SimpleRecord::Json
191
+ include SimpleRecord::Logging
192
+ extend SimpleRecord::Logging::ClassMethods
184
193
 
185
- def self.extended(base)
194
+ def self.extended(base)
186
195
 
187
- end
196
+ end
188
197
 
189
- def initialize(attrs={})
190
- # todo: Need to deal with objects passed in. iterate through belongs_to perhaps and if in attrs, set the objects id rather than the object itself
198
+ def initialize(attrs={})
199
+ # todo: Need to deal with objects passed in. iterate through belongs_to perhaps and if in attrs, set the objects id rather than the object itself
191
200
 
192
- initialize_base(attrs)
201
+ initialize_base(attrs)
193
202
 
194
- # Convert attributes to sdb values
195
- attrs.each_pair do |name, value|
196
- set(name, value, true)
197
- end
198
- end
203
+ # Convert attributes to sdb values
204
+ attrs.each_pair do |name, value|
205
+ set(name, value, true)
206
+ end
207
+ end
199
208
 
200
- def initialize_base(attrs={})
209
+ def initialize_base(attrs={})
201
210
 
202
- #we have to handle the virtuals.
203
- Attributes.handle_virtuals(attrs)
211
+ #we have to handle the virtuals.
212
+ Attributes.handle_virtuals(attrs)
204
213
 
205
- @errors=SimpleRecord_errors.new if not (defined?(ActiveModel))
206
- @dirty = {}
214
+ clear_errors
207
215
 
208
- @attributes = {} # sdb values
209
- @attributes_rb = {} # ruby values
210
- @lobs = {}
211
- @new_record = true
216
+ @dirty = {}
212
217
 
213
- end
218
+ @attributes = {} # sdb values
219
+ @attributes_rb = {} # ruby values
220
+ @lobs = {}
221
+ @new_record = true
214
222
 
215
- def initialize_from_db(attrs={})
216
- initialize_base(attrs)
217
- attrs.each_pair do |k, v|
218
- @attributes[k.to_s] = v
219
- end
220
- end
223
+ end
224
+
225
+ def initialize_from_db(attrs={})
226
+ initialize_base(attrs)
227
+ attrs.each_pair do |k, v|
228
+ @attributes[k.to_s] = v
229
+ end
230
+ end
221
231
 
222
232
 
223
- def self.inherited(base)
224
- #puts 'SimpleRecord::Base is inherited by ' + base.inspect
225
- Callbacks.setup_callbacks(base)
233
+ def self.inherited(base)
234
+ # puts 'SimpleRecord::Base is inherited by ' + base.inspect
235
+ Callbacks.setup_callbacks(base)
226
236
 
227
237
  # base.has_strings :id
228
- base.has_dates :created, :updated
229
- base.before_create :set_created, :set_updated
230
- base.before_update :set_updated
238
+ base.has_dates :created, :updated
239
+ base.before_create :set_created, :set_updated
240
+ base.before_update :set_updated
231
241
 
232
- end
242
+ end
233
243
 
234
244
 
235
- def persisted?
236
- true
237
- end
245
+ def persisted?
246
+ !@new_record && !destroyed?
247
+ end
238
248
 
249
+ def destroyed?
250
+ @deleted
251
+ end
239
252
 
240
- def defined_attributes_local
241
- # todo: store this somewhere so it doesn't keep going through this
242
- ret = self.class.defined_attributes
243
- ret.merge!(self.class.superclass.defined_attributes) if self.class.superclass.respond_to?(:defined_attributes)
244
- end
253
+ def defined_attributes_local
254
+ # todo: store this somewhere so it doesn't keep going through this
255
+ ret = self.class.defined_attributes
256
+ ret.merge!(self.class.superclass.defined_attributes) if self.class.superclass.respond_to?(:defined_attributes)
257
+ end
245
258
 
246
259
 
247
- class << self;
248
- attr_accessor :domain_prefix
249
- end
260
+ class << self;
261
+ attr_accessor :domain_prefix
262
+ end
250
263
 
251
- #@domain_name_for_class = nil
264
+ #@domain_name_for_class = nil
252
265
 
253
- @@cache_store = nil
254
- # Set the cache to use
255
- def self.cache_store=(cache)
256
- @@cache_store = cache
257
- end
266
+ @@cache_store = nil
267
+ # Set the cache to use
268
+ def self.cache_store=(cache)
269
+ @@cache_store = cache
270
+ end
258
271
 
259
- def self.cache_store
260
- return @@cache_store
261
- end
272
+ def self.cache_store
273
+ return @@cache_store
274
+ end
262
275
 
263
- # If you want a domain prefix for all your models, set it here.
264
- def self.set_domain_prefix(prefix)
265
- #puts 'set_domain_prefix=' + prefix
266
- self.domain_prefix = prefix
267
- end
276
+ # If you want a domain prefix for all your models, set it here.
277
+ def self.set_domain_prefix(prefix)
278
+ #puts 'set_domain_prefix=' + prefix
279
+ self.domain_prefix = prefix
280
+ end
268
281
 
269
- # Same as set_table_name
270
- def self.set_table_name(table_name)
271
- set_domain_name table_name
272
- end
282
+ # Same as set_table_name
283
+ def self.set_table_name(table_name)
284
+ set_domain_name table_name
285
+ end
273
286
 
274
- # Sets the domain name for this class
275
- def self.set_domain_name(table_name)
276
- super
277
- end
287
+ # Sets the domain name for this class
288
+ def self.set_domain_name(table_name)
289
+ super
290
+ end
278
291
 
279
292
 
280
- def domain
281
- self.class.domain
282
- end
293
+ def domain
294
+ self.class.domain
295
+ end
283
296
 
284
- def self.domain
285
- unless @domain
286
- # This strips off the module if there is one.
287
- n2 = name.split('::').last || name
297
+ def self.domain
298
+ unless @domain
299
+ # This strips off the module if there is one.
300
+ n2 = name.split('::').last || name
288
301
  # puts 'n2=' + n2
289
- if n2.respond_to?(:tableize)
290
- @domain = n2.tableize
291
- else
292
- @domain = n2.downcase
293
- end
294
- set_domain_name @domain
295
- end
296
- domain_name_for_class = (SimpleRecord::Base.domain_prefix || "") + @domain.to_s
297
- domain_name_for_class
302
+ if n2.respond_to?(:tableize)
303
+ @domain = n2.tableize
304
+ else
305
+ @domain = n2.downcase
298
306
  end
307
+ set_domain_name @domain
308
+ end
309
+ domain_name_for_class = (SimpleRecord::Base.domain_prefix || "") + @domain.to_s
310
+ domain_name_for_class
311
+ end
299
312
 
300
- def has_id_on_end(name_s)
301
- name_s = name_s.to_s
302
- name_s.length > 3 && name_s[-3..-1] == "_id"
303
- end
313
+ def has_id_on_end(name_s)
314
+ name_s = name_s.to_s
315
+ name_s.length > 3 && name_s[-3..-1] == "_id"
316
+ end
304
317
 
305
- def get_att_meta(name)
306
- name_s = name.to_s
307
- att_meta = defined_attributes_local[name.to_sym]
308
- if att_meta.nil? && has_id_on_end(name_s)
309
- att_meta = defined_attributes_local[name_s[0..-4].to_sym]
310
- end
311
- return att_meta
312
- end
318
+ def get_att_meta(name)
319
+ name_s = name.to_s
320
+ att_meta = defined_attributes_local[name.to_sym]
321
+ if att_meta.nil? && has_id_on_end(name_s)
322
+ att_meta = defined_attributes_local[name_s[0..-4].to_sym]
323
+ end
324
+ return att_meta
325
+ end
313
326
 
314
- def sdb_att_name(name)
315
- att_meta = get_att_meta(name)
316
- if att_meta.type == :belongs_to && !has_id_on_end(name.to_s)
317
- return "#{name}_id"
318
- end
319
- name.to_s
320
- end
327
+ def sdb_att_name(name)
328
+ att_meta = get_att_meta(name)
329
+ if att_meta.type == :belongs_to && !has_id_on_end(name.to_s)
330
+ return "#{name}_id"
331
+ end
332
+ name.to_s
333
+ end
321
334
 
322
- def strip_array(arg)
323
- if arg.is_a? Array
324
- if arg.length==1
325
- ret = arg[0]
326
- else
327
- ret = arg
328
- end
329
- else
330
- ret = arg
331
- end
332
- return ret
335
+ def strip_array(arg)
336
+ if arg.is_a? Array
337
+ if arg.length==1
338
+ ret = arg[0]
339
+ else
340
+ ret = arg
333
341
  end
342
+ else
343
+ ret = arg
344
+ end
345
+ return ret
346
+ end
334
347
 
335
348
 
336
- def make_dirty(arg, value)
337
- sdb_att_name = sdb_att_name(arg)
338
- arg = arg.to_s
349
+ def make_dirty(arg, value)
350
+ sdb_att_name = sdb_att_name(arg)
351
+ arg = arg.to_s
339
352
 
340
353
  # puts "Marking #{arg} dirty with #{value}" if SimpleRecord.logging?
341
- if @dirty.include?(sdb_att_name)
342
- old = @dirty[sdb_att_name]
354
+ if @dirty.include?(sdb_att_name)
355
+ old = @dirty[sdb_att_name]
343
356
  # puts "#{sdb_att_name} was already dirty #{old}"
344
- @dirty.delete(sdb_att_name) if value == old
345
- else
346
- old = get_attribute(arg)
357
+ @dirty.delete(sdb_att_name) if value == old
358
+ else
359
+ old = get_attribute(arg)
347
360
  # puts "dirtifying #{sdb_att_name} old=#{old.inspect} to new=#{value.inspect}" if SimpleRecord.logging?
348
- @dirty[sdb_att_name] = old if value != old
349
- end
350
- end
361
+ @dirty[sdb_att_name] = old if value != old
362
+ end
363
+ end
351
364
 
352
- def clear_errors
353
- @errors=SimpleRecord_errors.new
354
- end
365
+ def clear_errors
366
+ # @errors=SimpleRecord_errors.new
367
+ if not (defined?(ActiveModel))
368
+ @errors=SimpleRecord_errors.new
369
+ else
370
+ @errors = ActiveModel::Errors.new(self)
371
+ end
372
+ end
355
373
 
356
- def []=(attribute, values)
357
- make_dirty(attribute, values)
358
- super
359
- end
374
+ def []=(attribute, values)
375
+ make_dirty(attribute, values)
376
+ super
377
+ end
360
378
 
361
- def [](attribute)
362
- super
363
- end
379
+ def [](attribute)
380
+ super
381
+ end
364
382
 
365
383
 
366
- def set_created
367
- set(:created, Time.now)
368
- end
384
+ def set_created
385
+ set(SimpleRecord.options[:created_col] || :created, Time.now)
386
+ end
369
387
 
370
- def set_updated
371
- set(:updated, Time.now)
372
- end
388
+ def set_updated
389
+ set(SimpleRecord.options[:updated_col] || :updated, Time.now)
390
+ end
373
391
 
374
- # an aliased method since many people use created_at/updated_at naming convention
375
- def created_at
376
- self.created
377
- end
392
+ # an aliased method since many people use created_at/updated_at naming convention
393
+ def created_at
394
+ self.created
395
+ end
378
396
 
379
- # an aliased method since many people use created_at/updated_at naming convention
380
- def updated_at
381
- self.updated
382
- end
397
+ # an aliased method since many people use created_at/updated_at naming convention
398
+ def updated_at
399
+ self.updated
400
+ end
383
401
 
384
- def cache_store
385
- @@cache_store
386
- end
402
+ def read_attribute_for_validation(key)
403
+ @attributes[key.to_s]
404
+ end
387
405
 
388
- def domain_ok(ex, options={})
389
- if (ex.message().index("NoSuchDomain") != nil)
390
- dom = options[:domain] || domain
391
- self.class.create_domain(dom)
392
- return true
393
- end
394
- return false
395
- end
406
+ def cache_store
407
+ @@cache_store
408
+ end
396
409
 
410
+ def domain_ok(ex, options={})
411
+ if (ex.message().index("NoSuchDomain") != nil)
412
+ dom = options[:domain] || domain
413
+ self.class.create_domain(dom)
414
+ return true
415
+ end
416
+ return false
417
+ end
397
418
 
398
- def new_record?
399
- # todo: new_record in activesdb should align with how we're defining a new record here, ie: if id is nil
400
- super
401
- end
419
+
420
+ def new_record?
421
+ # todo: new_record in activesdb should align with how we're defining a new record here, ie: if id is nil
422
+ super
423
+ end
402
424
 
403
425
 
404
- @create_domain_called = false
426
+ @create_domain_called = false
405
427
 
406
- # Options:
407
- # - :except => Array of attributes to NOT save
408
- # - :dirty => true - Will only store attributes that were modified. To make it save regardless and have it update the :updated value, include this and set it to false.
409
- # - :domain => Explicitly define domain to use.
410
- #
411
- def save(options={})
428
+ # Options:
429
+ # - :except => Array of attributes to NOT save
430
+ # - :dirty => true - Will only store attributes that were modified. To make it save regardless and have it update the :updated value, include this and set it to false.
431
+ # - :domain => Explicitly define domain to use.
432
+ #
433
+ def save(options={})
412
434
  # puts 'SAVING: ' + self.inspect if SimpleRecord.logging?
413
- # todo: Clean out undefined values in @attributes (in case someone set the attributes hash with values that they hadn't defined)
414
- clear_errors
415
- # todo: decide whether this should go before pre_save or after pre_save? pre_save dirties "updated" and perhaps other items due to callbacks
416
- if options[:dirty]
435
+ # todo: Clean out undefined values in @attributes (in case someone set the attributes hash with values that they hadn't defined)
436
+ clear_errors
437
+ # todo: decide whether this should go before pre_save or after pre_save? pre_save dirties "updated" and perhaps other items due to callbacks
438
+ if options[:dirty]
417
439
  # puts '@dirtyA=' + @dirty.inspect
418
- return true if @dirty.size == 0 # Nothing to save so skip it
419
- end
420
- is_create = self[:id].nil?
421
- ok = pre_save(options) # Validates and sets ID
422
- if ok
423
- begin
424
- dirty = @dirty
440
+ return true if @dirty.size == 0 # Nothing to save so skip it
441
+ end
442
+ is_create = self[:id].nil?
443
+ ok = pre_save(options) # Validates and sets ID
444
+ if ok
445
+ begin
446
+ dirty = @dirty
425
447
  # puts 'dirty before=' + @dirty.inspect
426
- if options[:dirty]
448
+ if options[:dirty]
427
449
  # puts '@dirty=' + @dirty.inspect
428
- return true if @dirty.size == 0 # This should probably never happen because after pre_save, created/updated dates are changed
429
- options[:dirty_atts] = @dirty
430
- end
431
- to_delete = get_atts_to_delete
432
- SimpleRecord.stats.saves += 1
433
-
434
- if self.class.is_sharded?
435
- options[:domain] = sharded_domain
436
- end
437
-
438
- if super(options)
439
- self.class.cache_results(self)
440
- delete_niled(to_delete)
441
- save_lobs(dirty)
442
- after_save_cleanup
443
- if (is_create ? run_after_create : run_after_update) && run_after_save
450
+ return true if @dirty.size == 0 # This should probably never happen because after pre_save, created/updated dates are changed
451
+ options[:dirty_atts] = @dirty
452
+ end
453
+ to_delete = get_atts_to_delete
454
+ SimpleRecord.stats.saves += 1
455
+
456
+ if self.class.is_sharded?
457
+ options[:domain] = sharded_domain
458
+ end
459
+
460
+ if super(options)
461
+ self.class.cache_results(self)
462
+ delete_niled(to_delete)
463
+ save_lobs(dirty)
464
+ after_save_cleanup
465
+ if (is_create ? run_after_create : run_after_update) && run_after_save
444
466
  # puts 'all good?'
445
- return true
446
- else
447
- return false
448
- end
449
- else
450
- return false
451
- end
452
- rescue Aws::AwsError => ex
453
- # puts "RESCUED in save: " + $!
454
- # Domain is created in aws lib now using :create_domain=>true
467
+ return true
468
+ else
469
+ return false
470
+ end
471
+ else
472
+ return false
473
+ end
474
+ rescue Aws::AwsError => ex
475
+ # puts "RESCUED in save: " + $!
476
+ # Domain is created in aws lib now using :create_domain=>true
455
477
  # if (domain_ok(ex, options))
456
478
  # if !@create_domain_called
457
479
  # @create_domain_called = true
@@ -462,201 +484,218 @@ module SimpleRecord
462
484
  # else
463
485
  # raise $!
464
486
  # end
465
- raise ex
466
- end
467
- else
468
- #@debug = "not saved"
469
- return false
470
- end
487
+ raise ex
471
488
  end
489
+ else
490
+ #@debug = "not saved"
491
+ return false
492
+ end
493
+ end
472
494
 
473
- def save_lobs(dirty=nil)
495
+ def save_lobs(dirty=nil)
474
496
  # puts 'dirty.inspect=' + dirty.inspect
475
- dirty = @dirty if dirty.nil?
476
- all_clobs = {}
477
- dirty_clobs = {}
478
- defined_attributes_local.each_pair do |k, v|
479
- # collect up the clobs in case it's a single put
480
- if v.type == :clob
481
- val = @lobs[k]
482
- all_clobs[k] = val
483
- if dirty.include?(k.to_s)
484
- dirty_clobs[k] = val
485
- else
497
+ dirty = @dirty if dirty.nil?
498
+ all_clobs = {}
499
+ dirty_clobs = {}
500
+ defined_attributes_local.each_pair do |k, v|
501
+ # collect up the clobs in case it's a single put
502
+ if v.type == :clob
503
+ val = @lobs[k]
504
+ all_clobs[k] = val
505
+ if dirty.include?(k.to_s)
506
+ dirty_clobs[k] = val
507
+ else
486
508
  # puts 'NOT DIRTY'
487
- end
509
+ end
488
510
 
489
- end
490
- end
491
- if dirty_clobs.size > 0
492
- if self.class.get_sr_config[:single_clob]
493
- # all clobs in one chunk
494
- # using json for now, could change later
495
- val = all_clobs.to_json
496
- puts 'val=' + val.inspect
497
- put_lob(single_clob_id, val, :new_bucket=>true)
498
- else
499
- dirty_clobs.each_pair do |k, val|
500
- put_lob(s3_lob_id(k), val)
501
- end
502
- end
503
- end
504
511
  end
505
-
506
- def delete_lobs
507
- defined_attributes_local.each_pair do |k, v|
508
- if v.type == :clob
509
- if self.class.get_sr_config[:single_clob]
510
- s3_bucket(false, :new_bucket=>true).delete_key(single_clob_id)
511
- SimpleRecord.stats.s3_deletes += 1
512
- return
513
- else
514
- s3_bucket.delete_key(s3_lob_id(k))
515
- SimpleRecord.stats.s3_deletes += 1
516
- end
517
- end
518
- end
512
+ end
513
+ if dirty_clobs.size > 0
514
+ if self.class.get_sr_config[:single_clob]
515
+ # all clobs in one chunk
516
+ # using json for now, could change later
517
+ val = all_clobs.to_json
518
+ puts 'val=' + val.inspect
519
+ put_lob(single_clob_id, val, :s3_bucket=>:new)
520
+ else
521
+ dirty_clobs.each_pair do |k, val|
522
+ put_lob(s3_lob_id(k), val)
523
+ end
519
524
  end
525
+ end
526
+ end
520
527
 
528
+ def delete_lobs
529
+ defined_attributes_local.each_pair do |k, v|
530
+ if v.type == :clob
531
+ if self.class.get_sr_config[:single_clob]
532
+ s3_bucket(false, :s3_bucket=>:new).delete_key(single_clob_id)
533
+ SimpleRecord.stats.s3_deletes += 1
534
+ return
535
+ else
536
+ s3_bucket.delete_key(s3_lob_id(k))
537
+ SimpleRecord.stats.s3_deletes += 1
538
+ end
539
+ end
540
+ end
541
+ end
521
542
 
522
- def put_lob(k, val, options={})
523
- begin
524
- s3_bucket(false, options).put(k, val)
525
- rescue Aws::AwsError => ex
526
- if ex.include? /NoSuchBucket/
527
- s3_bucket(true, options).put(k, val)
528
- else
529
- raise ex
530
- end
531
- end
532
- SimpleRecord.stats.s3_puts += 1
543
+
544
+ def put_lob(k, val, options={})
545
+ begin
546
+ s3_bucket(false, options).put(k, val)
547
+ rescue Aws::AwsError => ex
548
+ if ex.include? /NoSuchBucket/
549
+ s3_bucket(true, options).put(k, val)
550
+ else
551
+ raise ex
533
552
  end
553
+ end
554
+ SimpleRecord.stats.s3_puts += 1
555
+ end
534
556
 
535
557
 
536
- def is_dirty?(name)
537
- # todo: should change all the dirty stuff to symbols?
558
+ def is_dirty?(name)
559
+ # todo: should change all the dirty stuff to symbols?
538
560
  # puts '@dirty=' + @dirty.inspect
539
561
  # puts 'name=' +name.to_s
540
- @dirty.include? name.to_s
541
- end
562
+ @dirty.include? name.to_s
563
+ end
542
564
 
543
- def s3
565
+ def s3
544
566
 
545
- return SimpleRecord.s3 if SimpleRecord.s3
546
- # todo: should optimize this somehow, like use the same connection_mode as used in SR
547
- # or keep open while looping in ResultsArray.
548
- Aws::S3.new(SimpleRecord.aws_access_key, SimpleRecord.aws_secret_key)
549
- end
567
+ return SimpleRecord.s3 if SimpleRecord.s3
568
+ # todo: should optimize this somehow, like use the same connection_mode as used in SR
569
+ # or keep open while looping in ResultsArray.
570
+ Aws::S3.new(SimpleRecord.aws_access_key, SimpleRecord.aws_secret_key)
571
+ end
550
572
 
551
- # options:
552
- # :new_bucket => true/false. True if want to use new bucket. Defaults to false for backwards compatability.
553
- def s3_bucket(create=false, options={})
554
- s3.bucket(options[:new_bucket] || SimpleRecord.options[:new_bucket] ? s3_bucket_name2 : s3_bucket_name, create)
555
- end
573
+ # options:
574
+ # :s3_bucket => :old/:new/"#{any_bucket_name}". :new if want to use new bucket. Defaults to :old for backwards compatability.
575
+ def s3_bucket(create=false, options={})
576
+ s3.bucket(s3_bucket_name(options[:s3_bucket]), create)
577
+ end
556
578
 
579
+ def s3_bucket_name(s3_bucket_option=:old)
580
+ if s3_bucket_option == :new || SimpleRecord.options[:s3_bucket] == :new
557
581
  # this is the bucket that will be used going forward for anything related to s3
558
- def s3_bucket_name2
559
- "simple_record_#{SimpleRecord.aws_access_key}"
560
- end
582
+ ret = "simple_record_#{SimpleRecord.aws_access_key}"
583
+ elsif !SimpleRecord.options[:s3_bucket].nil? && SimpleRecord.options[:s3_bucket] != :old
584
+ ret = SimpleRecord.options[:s3_bucket]
585
+ else
586
+ ret = SimpleRecord.aws_access_key + "_lobs"
587
+ end
588
+ ret
589
+ end
561
590
 
562
- def s3_bucket_name
563
- SimpleRecord.aws_access_key + "_lobs"
564
- end
591
+ def s3_lob_id(name)
592
+ # if s3_bucket is not nil and not :old, then we use the new key.
593
+ if !SimpleRecord.options[:s3_bucket].nil? && SimpleRecord.options[:s3_bucket] != :old
594
+ "lobs/#{self.id}_#{name}"
595
+ else
596
+ self.id + "_" + name.to_s
597
+ end
598
+ end
565
599
 
566
- def s3_lob_id(name)
567
- if SimpleRecord.options[:new_bucket]
568
- "lobs/#{self.id}_#{name}"
569
- else
570
- self.id + "_" + name.to_s
571
- end
572
- end
600
+ def single_clob_id
601
+ "lobs/#{self.id}_single_clob"
602
+ end
573
603
 
574
- def single_clob_id
575
- "lobs/#{self.id}_single_clob"
576
- end
604
+ def save!(options={})
605
+ save(options) || raise(RecordNotSaved)
606
+ end
577
607
 
578
- def save!(options={})
579
- save(options) || raise(RecordNotSaved)
580
- end
608
+ def self.create(attributes={})
609
+ # puts "About to create in domain #{domain}"
610
+ super
611
+ end
581
612
 
582
- def save_with_validation!(options={})
583
- if valid?
584
- save
585
- else
586
- raise RecordInvalid.new(self)
587
- end
588
- end
613
+ def self.create!(attributes={})
614
+ item = self.new(attributes)
615
+ item.save!
616
+ item
617
+ end
618
+
619
+ def save_with_validation!(options={})
620
+ if valid?
621
+ save
622
+ else
623
+ raise RecordInvalid.new(self)
624
+ end
625
+ end
589
626
 
590
627
 
591
- def self.get_encryption_key()
592
- key = SimpleRecord.options[:encryption_key]
628
+ def self.get_encryption_key()
629
+ key = SimpleRecord.options[:encryption_key]
593
630
  # if key.nil?
594
631
  # puts 'WARNING: Encrypting attributes with your AWS Access Key. You should use your own :encryption_key so it doesn\'t change'
595
632
  # key = connection.aws_access_key_id # default to aws access key. NOT recommended in case you start using a new key
596
633
  # end
597
- return key
598
- end
599
-
600
-
601
- def validate
602
- true
603
- end
604
-
605
- def validate_on_create
606
- true
607
- end
608
-
609
- def validate_on_update
610
- true
611
- end
634
+ return key
635
+ end
612
636
 
637
+ def validate
638
+ true
639
+ end
613
640
 
614
- def pre_save(options)
641
+ def validate_on_create
642
+ true
643
+ end
615
644
 
616
- is_create = self[:id].nil?
617
- ok = run_before_validation && (is_create ? run_before_validation_on_create : run_before_validation_on_update)
618
- return false unless ok
645
+ def validate_on_update
646
+ true
647
+ end
619
648
 
620
- validate()
621
649
 
622
- is_create ? validate_on_create : validate_on_update
623
- # puts 'AFTER VALIDATIONS, ERRORS=' + errors.inspect
624
- if (!errors.nil? && errors.size > 0)
625
- # puts 'THERE ARE ERRORS, returning false'
626
- return false
627
- end
650
+ def pre_save(options)
628
651
 
629
- ok = run_after_validation && (is_create ? run_after_validation_on_create : run_after_validation_on_update)
630
- return false unless ok
652
+ is_create = self[:id].nil?
653
+ ok = run_before_validation && (is_create ? run_before_validation_on_create : run_before_validation_on_update)
654
+ return false unless ok
631
655
 
632
- ok = respond_to?('before_save') ? before_save : true
633
- if ok
634
- if is_create && respond_to?('before_create')
635
- ok = before_create
636
- elsif !is_create && respond_to?('before_update')
637
- ok = before_update
638
- end
639
- end
640
- if ok
641
- ok = run_before_save && (is_create ? run_before_create : run_before_update)
642
- end
643
- if ok
644
- # Now translate all fields into SimpleDB friendly strings
656
+ # validate()
657
+ # is_create ? validate_on_create : validate_on_update
658
+ if !valid?
659
+ return false
660
+ end
661
+ #
662
+ ## puts 'AFTER VALIDATIONS, ERRORS=' + errors.inspect
663
+ # if (!errors.nil? && errors.size > 0)
664
+ ## puts 'THERE ARE ERRORS, returning false'
665
+ # return false
666
+ # end
667
+
668
+ ok = run_after_validation && (is_create ? run_after_validation_on_create : run_after_validation_on_update)
669
+ return false unless ok
670
+
671
+ ok = respond_to?('before_save') ? before_save : true
672
+ if ok
673
+ if is_create && respond_to?('before_create')
674
+ ok = before_create
675
+ elsif !is_create && respond_to?('before_update')
676
+ ok = before_update
677
+ end
678
+ end
679
+ if ok
680
+ ok = run_before_save && (is_create ? run_before_create : run_before_update)
681
+ end
682
+ if ok
683
+ # Now translate all fields into SimpleDB friendly strings
645
684
  # convert_all_atts_to_sdb()
646
- end
647
- prepare_for_update
648
- ok
649
- end
685
+ end
686
+ prepare_for_update
687
+ ok
688
+ end
650
689
 
651
690
 
652
- def get_atts_to_delete
653
- to_delete = []
654
- changes.each_pair do |key, v|
655
- if v[1].nil?
656
- to_delete << key
657
- @attributes.delete(key)
658
- end
659
- end
691
+ def get_atts_to_delete
692
+ to_delete = []
693
+ changes.each_pair do |key, v|
694
+ if v[1].nil?
695
+ to_delete << key
696
+ @attributes.delete(key)
697
+ end
698
+ end
660
699
  # @attributes.each do |key, value|
661
700
  ## puts 'key=' + key.inspect + ' value=' + value.inspect
662
701
  # if value.nil? || (value.is_a?(Array) && value.size == 0) || (value.is_a?(Array) && value.size == 1 && value[0] == nil)
@@ -664,437 +703,436 @@ module SimpleRecord
664
703
  # @attributes.delete(key)
665
704
  # end
666
705
  # end
667
- return to_delete
668
- end
706
+ return to_delete
707
+ end
669
708
 
670
- # Run pre_save on each object, then runs batch_put_attributes
671
- # Returns
672
- def self.batch_save(objects, options={})
673
- options[:create_domain] = true if options[:create_domain].nil?
674
- results = []
675
- to_save = []
676
- if objects && objects.size > 0
677
- objects.each do |o|
678
- ok = o.pre_save(options)
679
- raise "Pre save failed on object [" + o.inspect + "]" if !ok
680
- results << ok
681
- next if !ok # todo: this shouldn't be here should it? raises above
682
- o.pre_save2
683
- to_save << Aws::SdbInterface::Item.new(o.id, o.attributes, true)
684
- if to_save.size == 25 # Max amount SDB will accept
685
- connection.batch_put_attributes(domain, to_save, options)
686
- to_save.clear
687
- end
688
- end
689
- end
690
- connection.batch_put_attributes(domain, to_save, options) if to_save.size > 0
691
- objects.each do |o|
692
- o.save_lobs(nil)
693
- end
694
- results
695
- end
709
+ # Run pre_save on each object, then runs batch_put_attributes
710
+ # Returns
711
+ def self.batch_save(objects, options={})
712
+ options[:create_domain] = true if options[:create_domain].nil?
713
+ results = []
714
+ to_save = []
715
+ if objects && objects.size > 0
716
+ objects.each do |o|
717
+ ok = o.pre_save(options)
718
+ raise "Pre save failed on object [" + o.inspect + "]" if !ok
719
+ results << ok
720
+ next if !ok # todo: this shouldn't be here should it? raises above
721
+ o.pre_save2
722
+ to_save << Aws::SdbInterface::Item.new(o.id, o.attributes, true)
723
+ if to_save.size == 25 # Max amount SDB will accept
724
+ connection.batch_put_attributes(domain, to_save, options)
725
+ to_save.clear
726
+ end
727
+ end
728
+ end
729
+ connection.batch_put_attributes(domain, to_save, options) if to_save.size > 0
730
+ objects.each do |o|
731
+ o.save_lobs(nil)
732
+ end
733
+ results
734
+ end
696
735
 
697
- # Pass in an array of objects
698
- def self.batch_delete(objects, options={})
699
- if objects
700
- # 25 item limit, we should maybe handle this limit in here.
701
- connection.batch_delete_attributes @domain, objects.collect { |x| x.id }
702
- end
703
- end
736
+ # Pass in an array of objects
737
+ def self.batch_delete(objects, options={})
738
+ if objects
739
+ # 25 item limit, we should maybe handle this limit in here.
740
+ connection.batch_delete_attributes @domain, objects.collect { |x| x.id }
741
+ end
742
+ end
704
743
 
705
- #
706
- # Usage: ClassName.delete id
707
- #
708
- def self.delete(id)
709
- connection.delete_attributes(domain, id)
710
- end
744
+ #
745
+ # Usage: ClassName.delete id
746
+ #
747
+ def self.delete(id)
748
+ connection.delete_attributes(domain, id)
749
+ @deleted = true
750
+ end
711
751
 
712
- # Pass in the same OPTIONS you'd pass into a find(:all, OPTIONS)
713
- def self.delete_all(options)
714
- # could make this quicker by just getting item_names and deleting attributes rather than creating objects
715
- obs = self.find(:all, options)
716
- i = 0
717
- obs.each do |a|
718
- a.delete
719
- i+=1
720
- end
721
- return i
722
- end
752
+ # Pass in the same OPTIONS you'd pass into a find(:all, OPTIONS)
753
+ def self.delete_all(options)
754
+ # could make this quicker by just getting item_names and deleting attributes rather than creating objects
755
+ obs = self.find(:all, options)
756
+ i = 0
757
+ obs.each do |a|
758
+ a.delete
759
+ i+=1
760
+ end
761
+ return i
762
+ end
723
763
 
724
- # Pass in the same OPTIONS you'd pass into a find(:all, OPTIONS)
725
- def self.destroy_all(options)
726
- obs = self.find(:all, options)
727
- i = 0
728
- obs.each do |a|
729
- a.destroy
730
- i+=1
731
- end
732
- return i
733
- end
764
+ # Pass in the same OPTIONS you'd pass into a find(:all, OPTIONS)
765
+ def self.destroy_all(options)
766
+ obs = self.find(:all, options)
767
+ i = 0
768
+ obs.each do |a|
769
+ a.destroy
770
+ i+=1
771
+ end
772
+ return i
773
+ end
734
774
 
735
- def delete(options={})
736
- if self.class.is_sharded?
737
- options[:domain] = sharded_domain
738
- end
739
- super(options)
775
+ def delete(options={})
776
+ if self.class.is_sharded?
777
+ options[:domain] = sharded_domain
778
+ end
779
+ super(options)
740
780
 
741
- # delete lobs now too
742
- delete_lobs
743
- end
781
+ # delete lobs now too
782
+ delete_lobs
783
+ end
744
784
 
745
- def destroy
746
- return run_before_destroy && delete && run_after_destroy
747
- end
785
+ def destroy
786
+ return run_before_destroy && delete && run_after_destroy
787
+ end
748
788
 
749
789
 
750
- def delete_niled(to_delete)
790
+ def delete_niled(to_delete)
751
791
  # puts 'to_delete=' + to_delete.inspect
752
- if to_delete.size > 0
792
+ if to_delete.size > 0
753
793
  # puts 'Deleting attributes=' + to_delete.inspect
754
- SimpleRecord.stats.deletes += 1
755
- delete_attributes to_delete
756
- to_delete.each do |att|
757
- att_meta = get_att_meta(att)
758
- if att_meta.type == :clob
759
- s3_bucket.key(s3_lob_id(att)).delete
760
- end
761
- end
762
- end
763
- end
794
+ SimpleRecord.stats.deletes += 1
795
+ delete_attributes to_delete
796
+ to_delete.each do |att|
797
+ att_meta = get_att_meta(att)
798
+ if att_meta.type == :clob
799
+ s3_bucket.key(s3_lob_id(att)).delete
800
+ end
801
+ end
802
+ end
803
+ end
764
804
 
765
- def reload
766
- super()
767
- end
805
+ def reload
806
+ super()
807
+ end
768
808
 
769
809
 
770
- def update_attributes(atts)
771
- set_attributes(atts)
772
- save
773
- end
810
+ def update_attributes(atts)
811
+ set_attributes(atts)
812
+ save
813
+ end
774
814
 
775
- def update_attributes!(atts)
776
- set_attributes(atts)
777
- save!
778
- end
815
+ def update_attributes!(atts)
816
+ set_attributes(atts)
817
+ save!
818
+ end
779
819
 
780
820
 
781
- def self.quote_regexp(a, re)
782
- a =~ re
783
- #was there a match?
784
- if $&
785
- before=$`
786
- middle=$&
787
- after =$'
788
-
789
- before =~ /'$/ #is there already a quote immediately before the match?
790
- unless $&
791
- return "#{before}'#{middle}'#{quote_regexp(after, re)}" #if not, put quotes around the match
792
- else
793
- return "#{before}#{middle}#{quote_regexp(after, re)}" #if so, assume it is quoted already and move on
794
- end
795
- else
796
- #no match, just return the string
797
- return a
798
- end
799
- end
821
+ def self.quote_regexp(a, re)
822
+ a =~ re
823
+ #was there a match?
824
+ if $&
825
+ before=$`
826
+ middle=$&
827
+ after =$'
800
828
 
801
- def self.create(attributes={})
802
- # puts "About to create in domain #{domain}"
803
- super
829
+ before =~ /'$/ #is there already a quote immediately before the match?
830
+ unless $&
831
+ return "#{before}'#{middle}'#{quote_regexp(after, re)}" #if not, put quotes around the match
832
+ else
833
+ return "#{before}#{middle}#{quote_regexp(after, re)}" #if so, assume it is quoted already and move on
804
834
  end
835
+ else
836
+ #no match, just return the string
837
+ return a
838
+ end
839
+ end
805
840
 
806
- @@regex_no_id = /.*Couldn't find.*with ID.*/
807
-
808
- #
809
- # Usage:
810
- # Find by ID:
811
- # MyModel.find(ID)
812
- #
813
- # Query example:
814
- # MyModel.find(:all, :conditions=>["name = ?", name], :order=>"created desc", :limit=>10)
815
- #
816
- # Extra options:
817
- # :per_token => the number of results to return per next_token, max is 2500.
818
- # :consistent_read => true/false -- as per http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3572
819
- # :retries => maximum number of times to retry this query on an error response.
820
- # :shard => shard name or array of shard names to use on this query.
821
- def self.find(*params)
822
- #puts 'params=' + params.inspect
823
-
824
- q_type = :all
825
- select_attributes=[]
826
- if params.size > 0
827
- q_type = params[0]
828
- end
829
- options = {}
830
- if params.size > 1
831
- options = params[1]
832
- end
833
-
834
- if !options[:shard_find] && is_sharded?
835
- # then break off and get results across all shards
836
- return find_sharded(*params)
837
- end
838
-
839
- # Pad and Offset number attributes
840
- params_dup = params.dup
841
- if params.size > 1
842
- options = params[1]
843
- #puts 'options=' + options.inspect
844
- #puts 'after collect=' + options.inspect
845
- convert_condition_params(options)
846
- per_token = options[:per_token]
847
- consistent_read = options[:consistent_read]
848
- if per_token || consistent_read then
849
- op_dup = options.dup
850
- op_dup[:limit] = per_token # simpledb uses Limit as a paging thing, not what is normal
851
- op_dup[:consistent_read] = consistent_read
852
- params_dup[1] = op_dup
853
- end
854
- end
841
+ @@regex_no_id = /.*Couldn't find.*with ID.*/
842
+
843
+ #
844
+ # Usage:
845
+ # Find by ID:
846
+ # MyModel.find(ID)
847
+ #
848
+ # Query example:
849
+ # MyModel.find(:all, :conditions=>["name = ?", name], :order=>"created desc", :limit=>10)
850
+ #
851
+ # Extra options:
852
+ # :per_token => the number of results to return per next_token, max is 2500.
853
+ # :consistent_read => true/false -- as per http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3572
854
+ # :retries => maximum number of times to retry this query on an error response.
855
+ # :shard => shard name or array of shard names to use on this query.
856
+ def self.find(*params)
857
+ #puts 'params=' + params.inspect
858
+
859
+ q_type = :all
860
+ select_attributes=[]
861
+ if params.size > 0
862
+ q_type = params[0]
863
+ end
864
+ options = {}
865
+ if params.size > 1
866
+ options = params[1]
867
+ end
868
+
869
+ if !options[:shard_find] && is_sharded?
870
+ # then break off and get results across all shards
871
+ return find_sharded(*params)
872
+ end
873
+
874
+ # Pad and Offset number attributes
875
+ params_dup = params.dup
876
+ if params.size > 1
877
+ options = params[1]
878
+ #puts 'options=' + options.inspect
879
+ #puts 'after collect=' + options.inspect
880
+ convert_condition_params(options)
881
+ per_token = options[:per_token]
882
+ consistent_read = options[:consistent_read]
883
+ if per_token || consistent_read then
884
+ op_dup = options.dup
885
+ op_dup[:limit] = per_token # simpledb uses Limit as a paging thing, not what is normal
886
+ op_dup[:consistent_read] = consistent_read
887
+ params_dup[1] = op_dup
888
+ end
889
+ end
855
890
  # puts 'params2=' + params.inspect
856
891
 
857
- ret = q_type == :all ? [] : nil
858
- begin
859
-
860
- results=find_with_metadata(*params_dup)
892
+ ret = q_type == :all ? [] : nil
893
+ begin
894
+ results=find_with_metadata(*params_dup)
861
895
  # puts "RESULT=" + results.inspect
862
- write_usage(:select, domain, q_type, options, results)
863
- #puts 'params3=' + params.inspect
864
- SimpleRecord.stats.selects += 1
865
- if q_type == :count
866
- ret = results[:count]
867
- elsif q_type == :first
868
- ret = results[:items].first
869
- # todo: we should store request_id and box_usage with the object maybe?
870
- cache_results(ret)
871
- elsif results[:single]
872
- ret = results[:single]
873
- cache_results(ret)
874
- else
875
- if results[:items] #.is_a?(Array)
876
- cache_results(results[:items])
877
- ret = SimpleRecord::ResultsArray.new(self, params, results, next_token)
878
- end
879
- end
880
- rescue Aws::AwsError, SimpleRecord::ActiveSdb::ActiveSdbError => ex
896
+ write_usage(:select, domain, q_type, options, results)
897
+ #puts 'params3=' + params.inspect
898
+ SimpleRecord.stats.selects += 1
899
+ if q_type == :count
900
+ ret = results[:count]
901
+ elsif q_type == :first
902
+ ret = results[:items].first
903
+ # todo: we should store request_id and box_usage with the object maybe?
904
+ cache_results(ret)
905
+ elsif results[:single]
906
+ ret = results[:single]
907
+ cache_results(ret)
908
+ else
909
+ if results[:items] #.is_a?(Array)
910
+ cache_results(results[:items])
911
+ ret = SimpleRecord::ResultsArray.new(self, params, results, next_token)
912
+ end
913
+ end
914
+ rescue Aws::AwsError, SimpleRecord::ActiveSdb::ActiveSdbError => ex
881
915
  # puts "RESCUED: " + ex.message
882
- if (ex.message().index("NoSuchDomain") != nil)
883
- # this is ok
884
- elsif (ex.message() =~ @@regex_no_id)
885
- ret = nil
886
- else
887
- raise ex
888
- end
889
- end
890
- # puts 'single2=' + ret.inspect
891
- return ret
916
+ if (ex.message().index("NoSuchDomain") != nil)
917
+ # this is ok
918
+ elsif (ex.message() =~ @@regex_no_id)
919
+ ret = nil
920
+ else
921
+ raise ex
892
922
  end
923
+ end
924
+ # puts 'single2=' + ret.inspect
925
+ return ret
926
+ end
893
927
 
894
- def self.select(*params)
895
- return find(*params)
896
- end
928
+ def self.select(*params)
929
+ return find(*params)
930
+ end
897
931
 
898
- def self.all(*args)
899
- find(:all, *args)
900
- end
932
+ def self.all(*args)
933
+ find(:all, *args)
934
+ end
901
935
 
902
- def self.first(*args)
903
- find(:first, *args)
904
- end
936
+ def self.first(*args)
937
+ find(:first, *args)
938
+ end
905
939
 
906
- def self.count(*args)
907
- find(:count, *args)
908
- end
940
+ def self.count(*args)
941
+ find(:count, *args)
942
+ end
909
943
 
910
- # This gets less and less efficient the higher the page since SimpleDB has no way
911
- # to start at a specific row. So it will iterate from the first record and pull out the specific pages.
912
- def self.paginate(options={})
944
+ # This gets less and less efficient the higher the page since SimpleDB has no way
945
+ # to start at a specific row. So it will iterate from the first record and pull out the specific pages.
946
+ def self.paginate(options={})
913
947
  # options = args.pop
914
948
  # puts 'paginate options=' + options.inspect if SimpleRecord.logging?
915
- page = options[:page] || 1
916
- per_page = options[:per_page] || 50
949
+ page = options[:page] || 1
950
+ per_page = options[:per_page] || 50
917
951
  # total = options[:total_entries].to_i
918
- options[:page] = page.to_i # makes sure it's to_i
919
- options[:per_page] = per_page.to_i
920
- options[:limit] = options[:page] * options[:per_page]
952
+ options[:page] = page.to_i # makes sure it's to_i
953
+ options[:per_page] = per_page.to_i
954
+ options[:limit] = options[:page] * options[:per_page]
921
955
  # puts 'paging options=' + options.inspect
922
- fr = find(:all, options)
923
- return fr
956
+ fr = find(:all, options)
957
+ return fr
924
958
 
925
- end
959
+ end
926
960
 
927
961
 
928
- def self.convert_condition_params(options)
929
- return if options.nil?
930
- conditions = options[:conditions]
931
- if !conditions.nil? && conditions.size > 1
932
- # all after first are values
933
- conditions.collect! { |x|
934
- Translations.pad_and_offset(x)
935
- }
936
- end
962
+ def self.convert_condition_params(options)
963
+ return if options.nil?
964
+ conditions = options[:conditions]
965
+ if !conditions.nil? && conditions.size > 1
966
+ # all after first are values
967
+ conditions.collect! { |x|
968
+ Translations.pad_and_offset(x)
969
+ }
970
+ end
937
971
 
938
- end
972
+ end
939
973
 
940
- def self.cache_results(results)
941
- if !cache_store.nil? && !results.nil?
942
- if results.is_a?(Array)
943
- # todo: cache each result
944
- results.each do |item|
945
- class_name = item.class.name
946
- id = item.id
947
- cache_key = self.cache_key(class_name, id)
948
- #puts 'caching result at ' + cache_key + ': ' + results.inspect
949
- cache_store.write(cache_key, item, :expires_in =>30)
950
- end
951
- else
952
- class_name = results.class.name
953
- id = results.id
954
- cache_key = self.cache_key(class_name, id)
955
- #puts 'caching result at ' + cache_key + ': ' + results.inspect
956
- cache_store.write(cache_key, results, :expires_in =>30)
957
- end
958
- end
974
+ def self.cache_results(results)
975
+ if !cache_store.nil? && !results.nil?
976
+ if results.is_a?(Array)
977
+ # todo: cache each result
978
+ results.each do |item|
979
+ class_name = item.class.name
980
+ id = item.id
981
+ cache_key = self.cache_key(class_name, id)
982
+ #puts 'caching result at ' + cache_key + ': ' + results.inspect
983
+ cache_store.write(cache_key, item, :expires_in =>30)
984
+ end
985
+ else
986
+ class_name = results.class.name
987
+ id = results.id
988
+ cache_key = self.cache_key(class_name, id)
989
+ #puts 'caching result at ' + cache_key + ': ' + results.inspect
990
+ cache_store.write(cache_key, results, :expires_in =>30)
959
991
  end
992
+ end
993
+ end
960
994
 
961
- def self.cache_key(class_name, id)
962
- return class_name + "/" + id.to_s
963
- end
995
+ def self.cache_key(class_name, id)
996
+ return class_name + "/" + id.to_s
997
+ end
964
998
 
965
- @@debug=""
999
+ @@debug=""
966
1000
 
967
- def self.debug
968
- @@debug
969
- end
1001
+ def self.debug
1002
+ @@debug
1003
+ end
970
1004
 
971
- def self.sanitize_sql(*params)
972
- return ActiveRecord::Base.sanitize_sql(*params)
973
- end
1005
+ def self.sanitize_sql(*params)
1006
+ return ActiveRecord::Base.sanitize_sql(*params)
1007
+ end
974
1008
 
975
- def self.table_name
976
- return domain
977
- end
1009
+ def self.table_name
1010
+ return domain
1011
+ end
978
1012
 
979
- def changed
980
- return @dirty.keys
981
- end
1013
+ def changed
1014
+ return @dirty.keys
1015
+ end
982
1016
 
983
- def changed?
984
- return @dirty.size > 0
985
- end
1017
+ def changed?
1018
+ return @dirty.size > 0
1019
+ end
986
1020
 
987
- def changes
988
- ret = {}
989
- #puts 'in CHANGES=' + @dirty.inspect
990
- @dirty.each_pair { |key, value| ret[key] = [value, get_attribute(key)] }
991
- return ret
992
- end
1021
+ def changes
1022
+ ret = {}
1023
+ #puts 'in CHANGES=' + @dirty.inspect
1024
+ @dirty.each_pair { |key, value| ret[key] = [value, get_attribute(key)] }
1025
+ return ret
1026
+ end
993
1027
 
994
- def after_save_cleanup
995
- @dirty = {}
996
- end
1028
+ def after_save_cleanup
1029
+ @dirty = {}
1030
+ end
997
1031
 
998
- def hash
999
- # same as ActiveRecord
1000
- id.hash
1001
- end
1032
+ def hash
1033
+ # same as ActiveRecord
1034
+ id.hash
1035
+ end
1002
1036
 
1003
1037
 
1004
- end
1038
+ end
1005
1039
 
1006
1040
 
1007
- class Activerecordtosdb_subrecord_array
1008
- def initialize(subname, referencename, referencevalue)
1009
- @subname =subname.classify
1010
- @referencename =referencename.tableize.singularize + "_id"
1011
- @referencevalue=referencevalue
1012
- end
1041
+ class Activerecordtosdb_subrecord_array
1042
+ def initialize(subname, referencename, referencevalue)
1043
+ @subname =subname.classify
1044
+ @referencename =referencename.tableize.singularize + "_id"
1045
+ @referencevalue=referencevalue
1046
+ end
1013
1047
 
1014
- # Performance optimization if you know the array should be empty
1048
+ # Performance optimization if you know the array should be empty
1015
1049
 
1016
- def init_empty
1017
- @records = []
1018
- end
1050
+ def init_empty
1051
+ @records = []
1052
+ end
1019
1053
 
1020
- def load
1021
- if @records.nil?
1022
- @records = find_all
1023
- end
1024
- return @records
1025
- end
1054
+ def load
1055
+ if @records.nil?
1056
+ @records = find_all
1057
+ end
1058
+ return @records
1059
+ end
1026
1060
 
1027
- def [](key)
1028
- return load[key]
1029
- end
1061
+ def [](key)
1062
+ return load[key]
1063
+ end
1030
1064
 
1031
- def <<(ob)
1032
- return load << ob
1033
- end
1065
+ def first
1066
+ load[0]
1067
+ end
1034
1068
 
1035
- def count
1036
- return load.count
1037
- end
1069
+ def <<(ob)
1070
+ return load << ob
1071
+ end
1038
1072
 
1039
- def size
1040
- return count
1041
- end
1073
+ def count
1074
+ return load.count
1075
+ end
1042
1076
 
1043
- def each(*params, &block)
1044
- return load.each(*params) { |record| block.call(record) }
1045
- end
1077
+ def size
1078
+ return count
1079
+ end
1046
1080
 
1047
- def find_all(*params)
1048
- find(:all, *params)
1049
- end
1081
+ def each(*params, &block)
1082
+ return load.each(*params) { |record| block.call(record) }
1083
+ end
1050
1084
 
1051
- def empty?
1052
- return load.empty?
1053
- end
1085
+ def find_all(*params)
1086
+ find(:all, *params)
1087
+ end
1054
1088
 
1055
- def build(*params)
1056
- params[0][@referencename]=@referencevalue
1057
- eval(@subname).new(*params)
1058
- end
1089
+ def empty?
1090
+ return load.empty?
1091
+ end
1059
1092
 
1060
- def create(*params)
1061
- params[0][@referencename]=@referencevalue
1062
- record = eval(@subname).new(*params)
1063
- record.save
1064
- end
1093
+ def build(*params)
1094
+ params[0][@referencename]=@referencevalue
1095
+ eval(@subname).new(*params)
1096
+ end
1065
1097
 
1066
- def find(*params)
1067
- query=[:first, {}]
1068
- #{:conditions=>"id=>1"}
1069
- if params[0]
1070
- if params[0]==:all
1071
- query[0]=:all
1072
- end
1073
- end
1098
+ def create(*params)
1099
+ params[0][@referencename]=@referencevalue
1100
+ record = eval(@subname).new(*params)
1101
+ record.save
1102
+ end
1074
1103
 
1075
- if params[1]
1076
- query[1]=params[1]
1077
- if query[1][:conditions]
1078
- query[1][:conditions]=SimpleRecord::Base.sanitize_sql(query[1][:conditions])+" AND "+ SimpleRecord::Base.sanitize_sql(["#{@referencename} = ?", @referencevalue])
1079
- #query[1][:conditions]=Activerecordtosdb.sanitize_sql(query[1][:conditions])+" AND id='#{@id}'"
1080
- else
1081
- query[1][:conditions]=["#{@referencename} = ?", @referencevalue]
1082
- #query[1][:conditions]="id='#{@id}'"
1083
- end
1084
- else
1085
- query[1][:conditions]=["#{@referencename} = ?", @referencevalue]
1086
- #query[1][:conditions]="id='#{@id}'"
1087
- end
1104
+ def find(*params)
1105
+ query=[:first, {}]
1106
+ #{:conditions=>"id=>1"}
1107
+ if params[0]
1108
+ if params[0]==:all
1109
+ query[0]=:all
1110
+ end
1111
+ end
1088
1112
 
1089
- return eval(@subname).find(*query)
1113
+ if params[1]
1114
+ query[1]=params[1]
1115
+ if query[1][:conditions]
1116
+ query[1][:conditions]=SimpleRecord::Base.sanitize_sql(query[1][:conditions])+" AND "+ SimpleRecord::Base.sanitize_sql(["#{@referencename} = ?", @referencevalue])
1117
+ #query[1][:conditions]=Activerecordtosdb.sanitize_sql(query[1][:conditions])+" AND id='#{@id}'"
1118
+ else
1119
+ query[1][:conditions]=["#{@referencename} = ?", @referencevalue]
1120
+ #query[1][:conditions]="id='#{@id}'"
1090
1121
  end
1122
+ else
1123
+ query[1][:conditions]=["#{@referencename} = ?", @referencevalue]
1124
+ #query[1][:conditions]="id='#{@id}'"
1125
+ end
1091
1126
 
1127
+ return eval(@subname).find(*query)
1092
1128
  end
1093
1129
 
1094
- # This is simply a place holder so we don't keep doing gets to s3 or simpledb if already checked.
1095
- class RemoteNil
1130
+ end
1096
1131
 
1097
- end
1132
+ # This is simply a place holder so we don't keep doing gets to s3 or simpledb if already checked.
1133
+ class RemoteNil
1134
+
1135
+ end
1098
1136
 
1099
1137
 
1100
1138
  end