simple_record 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +9 -0
- data/lib/simple_record/attributes.rb +14 -2
- data/lib/simple_record/json.rb +49 -0
- data/lib/simple_record/stats.rb +7 -3
- data/lib/simple_record/translations.rb +11 -9
- data/lib/simple_record.rb +217 -69
- data/test/my_model.rb +1 -0
- data/test/test_base.rb +3 -0
- data/test/test_json.rb +73 -0
- data/test/test_lobs.rb +50 -0
- data/test/test_marshalled.rb +1 -1
- data/test/test_simple_record.rb +8 -13
- metadata +7 -4
data/README.markdown
CHANGED
@@ -186,6 +186,15 @@ This is most helpful on windows so Rails doesn't need sqlite or mysql gems/drive
|
|
186
186
|
end
|
187
187
|
|
188
188
|
|
189
|
+
## Large Objects (LOBS)
|
190
|
+
|
191
|
+
Typical databases support BLOB's and/or CLOB's, but SimpleDB has a 1024 character per attribute maximum so larger
|
192
|
+
values should be stored in S3. Fortunately SimpleRecord takes care of this for you by defining has_clobs for a large
|
193
|
+
string value.
|
194
|
+
|
195
|
+
has_clobs :my_clob
|
196
|
+
|
197
|
+
|
189
198
|
## Tips and Tricks and Things to Know
|
190
199
|
|
191
200
|
### Automagic Stuff
|
@@ -24,14 +24,21 @@ module SimpleRecord
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def has_attributes(*args)
|
27
|
+
has_attributes2(args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_attributes2(args, options_for_all={})
|
31
|
+
# puts 'args=' + args.inspect
|
32
|
+
# puts 'options_for_all = ' + options_for_all.inspect
|
27
33
|
args.each do |arg|
|
28
|
-
arg_options =
|
34
|
+
arg_options = {}
|
29
35
|
if arg.is_a?(Hash)
|
30
36
|
# then attribute may have extra options
|
31
37
|
arg_options = arg
|
32
38
|
arg = arg_options[:name].to_sym
|
33
39
|
end
|
34
|
-
|
40
|
+
type = options_for_all[:type] || :string
|
41
|
+
attr = Attribute.new(type, arg_options)
|
35
42
|
defined_attributes[arg] = attr if defined_attributes[arg].nil?
|
36
43
|
|
37
44
|
# define reader method
|
@@ -108,6 +115,11 @@ module SimpleRecord
|
|
108
115
|
end
|
109
116
|
end
|
110
117
|
|
118
|
+
def has_clobs(*args)
|
119
|
+
has_attributes2(args, :type=>:clob)
|
120
|
+
|
121
|
+
end
|
122
|
+
|
111
123
|
@@virtuals=[]
|
112
124
|
|
113
125
|
def has_virtuals(*args)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module SimpleRecord
|
2
|
+
module Json
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
puts "TestMixin included in #{base}"
|
6
|
+
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def json_create(object)
|
13
|
+
obj = new
|
14
|
+
for key, value in object
|
15
|
+
next if key == 'json_class'
|
16
|
+
obj.set key, value
|
17
|
+
end
|
18
|
+
obj
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_json(json_string)
|
22
|
+
return JSON.parse(json_string)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_json(* a)
|
28
|
+
result = {
|
29
|
+
'json_class' => self.class.name
|
30
|
+
}
|
31
|
+
defined_attributes_local.each_pair do |name, val|
|
32
|
+
# puts name.to_s + "=" + val.inspect
|
33
|
+
if val.type == :belongs_to
|
34
|
+
result[name.to_s + "_id"] = get_attribute_sdb(name)
|
35
|
+
else
|
36
|
+
result[name] = get_attribute(name)
|
37
|
+
end
|
38
|
+
# puts 'result[name]=' + result[name].inspect
|
39
|
+
end
|
40
|
+
#
|
41
|
+
# instance_variables.inject(result) do |r, name|
|
42
|
+
# r[name[1..-1]] = instance_variable_get name
|
43
|
+
# r
|
44
|
+
# end
|
45
|
+
result.to_json(* a)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/simple_record/stats.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
module SimpleRecord
|
2
2
|
class Stats
|
3
|
-
attr_accessor :selects, :
|
3
|
+
attr_accessor :selects, :saves, :deletes, :s3_puts, :s3_gets
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
@selects = 0
|
7
|
-
@
|
7
|
+
@saves = 0
|
8
8
|
@deletes = 0
|
9
|
+
@s3_puts = 0
|
10
|
+
@s3_gets = 0
|
9
11
|
end
|
10
12
|
|
11
13
|
def clear
|
12
14
|
self.selects = 0
|
13
|
-
self.
|
15
|
+
self.saves = 0
|
14
16
|
self.deletes = 0
|
17
|
+
self.s3_puts = 0
|
18
|
+
self.s3_gets = 0
|
15
19
|
end
|
16
20
|
end
|
17
21
|
end
|
@@ -15,7 +15,7 @@ module SimpleRecord
|
|
15
15
|
# puts "Converting #{name} to sdb value=#{value}"
|
16
16
|
# puts "atts_local=" + defined_attributes_local.inspect
|
17
17
|
|
18
|
-
att_meta =
|
18
|
+
att_meta = get_att_meta(name)
|
19
19
|
|
20
20
|
if att_meta.type == :int
|
21
21
|
ret = Translations.pad_and_offset(value, att_meta)
|
@@ -29,12 +29,14 @@ module SimpleRecord
|
|
29
29
|
unless value.blank?
|
30
30
|
if att_meta.options
|
31
31
|
if att_meta.options[:encrypted]
|
32
|
-
|
32
|
+
puts "ENCRYPTING #{name} value #{value}"
|
33
33
|
ret = Translations.encrypt(ret, att_meta.options[:encrypted])
|
34
|
-
|
34
|
+
puts 'encrypted value=' + ret.to_s
|
35
35
|
end
|
36
36
|
if att_meta.options[:hashed]
|
37
|
+
puts "hashing #{name}"
|
37
38
|
ret = Translations.pass_hash(ret)
|
39
|
+
puts "hashed value=" + ret.inspect
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
@@ -48,7 +50,7 @@ module SimpleRecord
|
|
48
50
|
def sdb_to_ruby(name, value)
|
49
51
|
# puts 'sdb_to_ruby arg=' + name.inspect + ' - ' + name.class.name + ' - value=' + value.to_s
|
50
52
|
return nil if value.nil?
|
51
|
-
att_meta =
|
53
|
+
att_meta = get_att_meta(name)
|
52
54
|
|
53
55
|
if att_meta.options
|
54
56
|
if att_meta.options[:encrypted]
|
@@ -60,7 +62,7 @@ module SimpleRecord
|
|
60
62
|
end
|
61
63
|
|
62
64
|
|
63
|
-
if att_meta.type == :belongs_to
|
65
|
+
if !has_id_on_end(name) && att_meta.type == :belongs_to
|
64
66
|
class_name = att_meta.options[:class_name] || name.to_s[0...1].capitalize + name.to_s[1...name.to_s.length]
|
65
67
|
# Camelize classnames with underscores (ie my_model.rb --> MyModel)
|
66
68
|
class_name = class_name.camelize
|
@@ -82,11 +84,11 @@ module SimpleRecord
|
|
82
84
|
# puts 'to eval=' + to_eval
|
83
85
|
begin
|
84
86
|
ret = eval(to_eval) # (defined? #{arg}_id)
|
85
|
-
rescue Aws::ActiveSdb::ActiveSdbError
|
86
|
-
if
|
87
|
-
ret =
|
87
|
+
rescue Aws::ActiveSdb::ActiveSdbError => ex
|
88
|
+
if ex.message.include? "Couldn't find"
|
89
|
+
ret = RemoteNil.new
|
88
90
|
else
|
89
|
-
raise
|
91
|
+
raise ex
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
data/lib/simple_record.rb
CHANGED
@@ -32,6 +32,7 @@ require File.expand_path(File.dirname(__FILE__) + "/simple_record/callbacks")
|
|
32
32
|
require File.expand_path(File.dirname(__FILE__) + "/simple_record/encryptor")
|
33
33
|
require File.expand_path(File.dirname(__FILE__) + "/simple_record/exceptions")
|
34
34
|
require File.expand_path(File.dirname(__FILE__) + "/simple_record/errors")
|
35
|
+
require File.expand_path(File.dirname(__FILE__) + "/simple_record/json")
|
35
36
|
require File.expand_path(File.dirname(__FILE__) + "/simple_record/password")
|
36
37
|
require File.expand_path(File.dirname(__FILE__) + "/simple_record/results_array")
|
37
38
|
require File.expand_path(File.dirname(__FILE__) + "/simple_record/stats")
|
@@ -44,6 +45,10 @@ module SimpleRecord
|
|
44
45
|
@@stats = SimpleRecord::Stats.new
|
45
46
|
@@logging = false
|
46
47
|
|
48
|
+
class << self;
|
49
|
+
attr_accessor :aws_access_key, :aws_secret_key
|
50
|
+
end
|
51
|
+
|
47
52
|
def self.enable_logging
|
48
53
|
@@logging = true
|
49
54
|
end
|
@@ -76,6 +81,8 @@ module SimpleRecord
|
|
76
81
|
# :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
|
77
82
|
# :logger => Logger Object # Logger instance: logs to STDOUT if omitted
|
78
83
|
def self.establish_connection(aws_access_key=nil, aws_secret_key=nil, params={})
|
84
|
+
@aws_access_key = aws_access_key
|
85
|
+
@aws_secret_key = aws_secret_key
|
79
86
|
@@options.merge!(params)
|
80
87
|
puts 'SimpleRecord.establish_connection with options: ' + @@options.inspect
|
81
88
|
Aws::ActiveSdb.establish_connection(aws_access_key, aws_secret_key, @@options)
|
@@ -96,14 +103,17 @@ module SimpleRecord
|
|
96
103
|
# include SimpleRecord::Attributes
|
97
104
|
extend SimpleRecord::Attributes
|
98
105
|
include SimpleRecord::Callbacks
|
106
|
+
include SimpleRecord::Json
|
99
107
|
|
100
108
|
# Too many ActiveRecord dependencies.
|
101
109
|
# if Gem.available?('will_paginate')
|
102
110
|
# require 'will_paginate/finder'
|
103
111
|
# include WillPaginate::Finder
|
104
112
|
# end
|
105
|
-
|
106
113
|
|
114
|
+
def self.extended(base)
|
115
|
+
|
116
|
+
end
|
107
117
|
|
108
118
|
def initialize(attrs={})
|
109
119
|
# 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
|
@@ -126,6 +136,7 @@ module SimpleRecord
|
|
126
136
|
|
127
137
|
@attributes = {} # sdb values
|
128
138
|
@attributes_rb = {} # ruby values
|
139
|
+
@lobs = {}
|
129
140
|
@new_record = true
|
130
141
|
|
131
142
|
end
|
@@ -151,7 +162,7 @@ module SimpleRecord
|
|
151
162
|
|
152
163
|
|
153
164
|
def defined_attributes_local
|
154
|
-
#
|
165
|
+
# todo: store this somewhere so it doesn't keep going through this
|
155
166
|
ret = self.class.defined_attributes
|
156
167
|
ret.merge!(self.class.superclass.defined_attributes) if self.class.superclass.respond_to?(:defined_attributes)
|
157
168
|
end
|
@@ -208,16 +219,34 @@ module SimpleRecord
|
|
208
219
|
end
|
209
220
|
|
210
221
|
def get_attribute_sdb(name)
|
222
|
+
name = name.to_sym
|
211
223
|
# arg = arg.to_s
|
212
224
|
# puts "get_attribute_sdb(#{arg}) - #{arg.class.name}"
|
213
225
|
# puts 'self[]=' + self.inspect
|
226
|
+
# att_meta = get_att_meta(name)
|
214
227
|
ret = strip_array(@attributes[sdb_att_name(name)])
|
215
228
|
return ret
|
216
229
|
end
|
217
230
|
|
218
|
-
def
|
231
|
+
def has_id_on_end(name_s)
|
232
|
+
name_s.length > 3 && name_s[-3..-1] == "_id"
|
233
|
+
end
|
234
|
+
|
235
|
+
def get_att_meta(name)
|
236
|
+
name_s = name.to_s
|
219
237
|
att_meta = defined_attributes_local[name.to_sym]
|
220
|
-
|
238
|
+
# puts 'name_s=' + name_s
|
239
|
+
# puts 'end of string=' + name_s[-3..-1] if name_s.length > 4
|
240
|
+
if att_meta.nil? && has_id_on_end(name_s)
|
241
|
+
# puts 'strip _id=' + name_s[0..-4].to_s
|
242
|
+
att_meta = defined_attributes_local[name_s[0..-4].to_sym]
|
243
|
+
end
|
244
|
+
return att_meta
|
245
|
+
end
|
246
|
+
|
247
|
+
def sdb_att_name(name)
|
248
|
+
att_meta = get_att_meta(name)
|
249
|
+
if att_meta.type == :belongs_to && !has_id_on_end(name.to_s)
|
221
250
|
return "#{name}_id"
|
222
251
|
end
|
223
252
|
name.to_s
|
@@ -241,14 +270,14 @@ module SimpleRecord
|
|
241
270
|
sdb_att_name = sdb_att_name(arg)
|
242
271
|
arg = arg.to_s
|
243
272
|
|
244
|
-
# puts "Marking #{arg} dirty with #{value}"
|
273
|
+
# puts "Marking #{arg} dirty with #{value}" if SimpleRecord.logging?
|
245
274
|
if @dirty.include?(sdb_att_name)
|
246
275
|
old = @dirty[sdb_att_name]
|
247
276
|
# puts "#{sdb_att_name} was already dirty #{old}"
|
248
277
|
@dirty.delete(sdb_att_name) if value == old
|
249
278
|
else
|
250
279
|
old = get_attribute(arg)
|
251
|
-
# puts "dirtifying #{sdb_att_name} old=#{old.inspect} to new=#{value.inspect}"
|
280
|
+
# puts "dirtifying #{sdb_att_name} old=#{old.inspect} to new=#{value.inspect}" if SimpleRecord.logging?
|
252
281
|
@dirty[sdb_att_name] = old if value != old
|
253
282
|
end
|
254
283
|
end
|
@@ -262,7 +291,7 @@ module SimpleRecord
|
|
262
291
|
super
|
263
292
|
end
|
264
293
|
|
265
|
-
def [](
|
294
|
+
def [](attribute)
|
266
295
|
super
|
267
296
|
end
|
268
297
|
|
@@ -343,7 +372,7 @@ module SimpleRecord
|
|
343
372
|
# - :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.
|
344
373
|
#
|
345
374
|
def save(options={})
|
346
|
-
# puts 'SAVING: ' + self.inspect
|
375
|
+
# puts 'SAVING: ' + self.inspect if SimpleRecord.logging?
|
347
376
|
# todo: Clean out undefined values in @attributes (in case someone set the attributes hash with values that they hadn't defined)
|
348
377
|
clear_errors
|
349
378
|
# todo: decide whether this should go before pre_save or after pre_save? pre_save dirties "updated" and perhaps other items due to callbacks
|
@@ -355,18 +384,24 @@ module SimpleRecord
|
|
355
384
|
ok = pre_save(options)
|
356
385
|
if ok
|
357
386
|
begin
|
387
|
+
dirty = @dirty
|
388
|
+
# puts 'dirty before=' + @dirty.inspect
|
358
389
|
if options[:dirty]
|
359
390
|
# puts '@dirty=' + @dirty.inspect
|
360
391
|
return true if @dirty.size == 0 # This should probably never happen because after pre_save, created/updated dates are changed
|
361
392
|
options[:dirty_atts] = @dirty
|
362
393
|
end
|
363
|
-
to_delete = get_atts_to_delete
|
364
|
-
SimpleRecord.stats.
|
394
|
+
to_delete = get_atts_to_delete
|
395
|
+
SimpleRecord.stats.saves += 1
|
365
396
|
# puts 'SELF BEFORE super=' + self.inspect
|
397
|
+
# puts 'dirty before2=' + @dirty.inspect
|
366
398
|
if super(options)
|
399
|
+
# puts 'dirty super=' + @dirty.inspect
|
367
400
|
# puts 'SELF AFTER super=' + self.inspect
|
368
401
|
self.class.cache_results(self)
|
369
402
|
delete_niled(to_delete)
|
403
|
+
save_lobs(dirty)
|
404
|
+
after_save_cleanup
|
370
405
|
if (is_create ? run_after_create : run_after_update) && run_after_save
|
371
406
|
# puts 'all good?'
|
372
407
|
return true
|
@@ -395,6 +430,56 @@ module SimpleRecord
|
|
395
430
|
end
|
396
431
|
end
|
397
432
|
|
433
|
+
def save_lobs(dirty=nil)
|
434
|
+
# puts 'dirty.inspect=' + dirty.inspect
|
435
|
+
dirty = @dirty if dirty.nil?
|
436
|
+
defined_attributes_local.each_pair do |k, v|
|
437
|
+
if v.type == :clob
|
438
|
+
# puts 'storing clob '
|
439
|
+
if dirty.include?(k.to_s)
|
440
|
+
begin
|
441
|
+
val = @lobs[k]
|
442
|
+
# puts 'val=' + val.inspect
|
443
|
+
s3_bucket.put(s3_lob_id(k), val)
|
444
|
+
rescue Aws::AwsError => ex
|
445
|
+
if ex.include? /NoSuchBucket/
|
446
|
+
s3_bucket(true).put(s3_lob_id(k), val)
|
447
|
+
else
|
448
|
+
raise ex
|
449
|
+
end
|
450
|
+
end
|
451
|
+
SimpleRecord.stats.s3_puts += 1
|
452
|
+
else
|
453
|
+
# puts 'NOT DIRTY'
|
454
|
+
end
|
455
|
+
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def is_dirty?(name)
|
461
|
+
# todo: should change all the dirty stuff to symbols?
|
462
|
+
# puts '@dirty=' + @dirty.inspect
|
463
|
+
# puts 'name=' +name.to_s
|
464
|
+
@dirty.include? name.to_s
|
465
|
+
end
|
466
|
+
|
467
|
+
def s3
|
468
|
+
Aws::S3.new(SimpleRecord.aws_access_key, SimpleRecord.aws_secret_key)
|
469
|
+
end
|
470
|
+
|
471
|
+
def s3_bucket(create=false)
|
472
|
+
s3.bucket(s3_bucket_name, create)
|
473
|
+
end
|
474
|
+
|
475
|
+
def s3_bucket_name
|
476
|
+
SimpleRecord.aws_access_key + "_lobs"
|
477
|
+
end
|
478
|
+
|
479
|
+
def s3_lob_id(name)
|
480
|
+
self.id + "_" + name.to_s
|
481
|
+
end
|
482
|
+
|
398
483
|
def save!(options={})
|
399
484
|
save(options) || raise(RecordNotSaved)
|
400
485
|
end
|
@@ -428,7 +513,7 @@ module SimpleRecord
|
|
428
513
|
|
429
514
|
is_create ? validate_on_create : validate_on_update
|
430
515
|
# puts 'AFTER VALIDATIONS, ERRORS=' + errors.inspect
|
431
|
-
if (!@errors.nil? && @errors.length > 0
|
516
|
+
if (!@errors.nil? && @errors.length > 0)
|
432
517
|
# puts 'THERE ARE ERRORS, returning false'
|
433
518
|
return false
|
434
519
|
end
|
@@ -456,15 +541,20 @@ module SimpleRecord
|
|
456
541
|
|
457
542
|
|
458
543
|
def get_atts_to_delete
|
459
|
-
# todo: this should use the @dirty hash now
|
460
544
|
to_delete = []
|
461
|
-
|
462
|
-
|
463
|
-
if value.nil? || (value.is_a?(Array) && value.size == 0) || (value.is_a?(Array) && value.size == 1 && value[0] == nil)
|
545
|
+
changes.each_pair do |key, v|
|
546
|
+
if v[1].nil?
|
464
547
|
to_delete << key
|
465
548
|
@attributes.delete(key)
|
466
549
|
end
|
467
550
|
end
|
551
|
+
# @attributes.each do |key, value|
|
552
|
+
## puts 'key=' + key.inspect + ' value=' + value.inspect
|
553
|
+
# if value.nil? || (value.is_a?(Array) && value.size == 0) || (value.is_a?(Array) && value.size == 1 && value[0] == nil)
|
554
|
+
# to_delete << key
|
555
|
+
# @attributes.delete(key)
|
556
|
+
# end
|
557
|
+
# end
|
468
558
|
return to_delete
|
469
559
|
end
|
470
560
|
|
@@ -488,6 +578,9 @@ module SimpleRecord
|
|
488
578
|
end
|
489
579
|
end
|
490
580
|
connection.batch_put_attributes(domain, to_save) if to_save.size > 0
|
581
|
+
objects.each do |o|
|
582
|
+
o.save_lobs(nil)
|
583
|
+
end
|
491
584
|
results
|
492
585
|
end
|
493
586
|
|
@@ -498,7 +591,7 @@ module SimpleRecord
|
|
498
591
|
connection.delete_attributes(domain, id)
|
499
592
|
end
|
500
593
|
|
501
|
-
def self.delete_all(*params)
|
594
|
+
def self.delete_all(* params)
|
502
595
|
# could make this quicker by just getting item_names and deleting attributes rather than creating objects
|
503
596
|
obs = self.find(params)
|
504
597
|
i = 0
|
@@ -509,7 +602,7 @@ module SimpleRecord
|
|
509
602
|
return i
|
510
603
|
end
|
511
604
|
|
512
|
-
def self.destroy_all(*params)
|
605
|
+
def self.destroy_all(* params)
|
513
606
|
obs = self.find(params)
|
514
607
|
i = 0
|
515
608
|
obs.each do |a|
|
@@ -530,33 +623,64 @@ module SimpleRecord
|
|
530
623
|
|
531
624
|
# Since SimpleDB supports multiple attributes per value, the values are an array.
|
532
625
|
# This method will return the value unwrapped if it's the only, otherwise it will return the array.
|
533
|
-
def get_attribute(
|
626
|
+
def get_attribute(name)
|
534
627
|
# puts "GET #{arg}"
|
535
628
|
# Check if this arg is already converted
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
#
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
#
|
551
|
-
|
552
|
-
|
553
|
-
|
629
|
+
name_s = name.to_s
|
630
|
+
name = name.to_sym
|
631
|
+
att_meta = get_att_meta(name)
|
632
|
+
# puts "att_meta for #{name}: " + att_meta.inspect
|
633
|
+
if att_meta && att_meta.type == :clob
|
634
|
+
ret = @lobs[name]
|
635
|
+
# puts 'get_attribute clob ' + ret.inspect
|
636
|
+
if ret
|
637
|
+
if ret.is_a? RemoteNil
|
638
|
+
return nil
|
639
|
+
else
|
640
|
+
return ret
|
641
|
+
end
|
642
|
+
end
|
643
|
+
# get it from s3
|
644
|
+
unless new_record?
|
645
|
+
begin
|
646
|
+
ret = s3_bucket.get(s3_lob_id(name))
|
647
|
+
# puts 'got from s3 ' + ret.inspect
|
648
|
+
SimpleRecord.stats.s3_gets += 1
|
649
|
+
rescue Aws::AwsError => ex
|
650
|
+
if ex.include? /NoSuchKey/
|
651
|
+
ret = nil
|
652
|
+
else
|
653
|
+
raise ex
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
if ret.nil?
|
658
|
+
ret = RemoteNil.new
|
659
|
+
end
|
660
|
+
end
|
661
|
+
@lobs[name] = ret
|
662
|
+
return nil if ret.is_a? RemoteNil
|
663
|
+
return ret
|
664
|
+
else
|
665
|
+
@attributes_rb = {} unless @attributes_rb # was getting errors after upgrade.
|
666
|
+
ret = @attributes_rb[name_s] # instance_variable_get(instance_var)
|
667
|
+
return ret unless ret.nil?
|
668
|
+
return nil if ret.is_a? RemoteNil
|
669
|
+
ret = get_attribute_sdb(name)
|
670
|
+
ret = sdb_to_ruby(name, ret)
|
671
|
+
@attributes_rb[name_s] = ret
|
672
|
+
return ret
|
673
|
+
end
|
674
|
+
|
554
675
|
end
|
555
676
|
|
556
677
|
def set(name, value, dirtify=true)
|
557
|
-
# puts "SET #{name}=#{value.inspect}"
|
678
|
+
# puts "SET #{name}=#{value.inspect}" if SimpleRecord.logging?
|
558
679
|
# puts "self=" + self.inspect
|
559
|
-
|
680
|
+
attname = name.to_s # default attname
|
681
|
+
name = name.to_sym
|
682
|
+
att_meta = get_att_meta(name)
|
683
|
+
store_rb_val = false
|
560
684
|
if att_meta.nil?
|
561
685
|
# check if it ends with id and see if att_meta is there
|
562
686
|
ends_with = name.to_s[-3, 3]
|
@@ -568,13 +692,24 @@ module SimpleRecord
|
|
568
692
|
# puts 'defined_attributes_local=' + defined_attributes_local.inspect
|
569
693
|
attname = name.to_s
|
570
694
|
attvalue = value
|
571
|
-
name = n2
|
695
|
+
name = n2.to_sym
|
572
696
|
end
|
573
697
|
return if att_meta.nil?
|
574
698
|
else
|
575
699
|
if att_meta.type == :belongs_to
|
576
|
-
|
577
|
-
|
700
|
+
ends_with = name.to_s[-3, 3]
|
701
|
+
if ends_with == "_id"
|
702
|
+
att_name = name.to_s
|
703
|
+
attvalue = value
|
704
|
+
else
|
705
|
+
attname = name.to_s + '_id'
|
706
|
+
attvalue = value.nil? ? nil : value.id
|
707
|
+
store_rb_val = true
|
708
|
+
end
|
709
|
+
elsif att_meta.type == :clob
|
710
|
+
make_dirty(name, value) if dirtify
|
711
|
+
@lobs[name] = value
|
712
|
+
return
|
578
713
|
else
|
579
714
|
attname = name.to_s
|
580
715
|
attvalue = att_meta.init_value(value)
|
@@ -590,11 +725,14 @@ module SimpleRecord
|
|
590
725
|
@attributes[attname] = sdb_val
|
591
726
|
# attvalue = wrap_if_required(name, attvalue, sdb_val)
|
592
727
|
# puts 'attvalue2=' + attvalue.to_s
|
593
|
-
|
728
|
+
|
729
|
+
if store_rb_val
|
730
|
+
@attributes_rb[name.to_s] = value
|
731
|
+
else
|
732
|
+
@attributes_rb.delete(name.to_s)
|
733
|
+
end
|
594
734
|
|
595
735
|
|
596
|
-
# instance_var = "@" + attname.to_s
|
597
|
-
# instance_variable_set(instance_var, attvalue)
|
598
736
|
end
|
599
737
|
|
600
738
|
def delete_niled(to_delete)
|
@@ -603,6 +741,12 @@ module SimpleRecord
|
|
603
741
|
# puts 'Deleting attributes=' + to_delete.inspect
|
604
742
|
SimpleRecord.stats.deletes += 1
|
605
743
|
delete_attributes to_delete
|
744
|
+
to_delete.each do |att|
|
745
|
+
att_meta = get_att_meta(att)
|
746
|
+
if att_meta.type == :clob
|
747
|
+
s3_bucket.key(s3_lob_id(att)).delete
|
748
|
+
end
|
749
|
+
end
|
606
750
|
end
|
607
751
|
end
|
608
752
|
|
@@ -657,7 +801,7 @@ module SimpleRecord
|
|
657
801
|
# Query example:
|
658
802
|
# MyModel.find(:all, :conditions=>["name = ?", name], :order=>"created desc", :limit=>10)
|
659
803
|
#
|
660
|
-
def self.find(*params)
|
804
|
+
def self.find(* params)
|
661
805
|
#puts 'params=' + params.inspect
|
662
806
|
q_type = :all
|
663
807
|
select_attributes=[]
|
@@ -678,7 +822,7 @@ module SimpleRecord
|
|
678
822
|
|
679
823
|
results = q_type == :all ? [] : nil
|
680
824
|
begin
|
681
|
-
results=super(*params)
|
825
|
+
results=super(* params)
|
682
826
|
# puts "RESULT=" + results.inspect
|
683
827
|
#puts 'params3=' + params.inspect
|
684
828
|
SimpleRecord.stats.selects += 1
|
@@ -701,20 +845,20 @@ module SimpleRecord
|
|
701
845
|
return results
|
702
846
|
end
|
703
847
|
|
704
|
-
def self.select(*params)
|
705
|
-
return find(*params)
|
848
|
+
def self.select(* params)
|
849
|
+
return find(* params)
|
706
850
|
end
|
707
851
|
|
708
|
-
def self.all(*args)
|
709
|
-
find(:all, *args)
|
852
|
+
def self.all(* args)
|
853
|
+
find(:all, * args)
|
710
854
|
end
|
711
855
|
|
712
|
-
def self.first(*args)
|
713
|
-
find(:first, *args)
|
856
|
+
def self.first(* args)
|
857
|
+
find(:first, * args)
|
714
858
|
end
|
715
859
|
|
716
|
-
def self.count(*args)
|
717
|
-
find(:count, *args)
|
860
|
+
def self.count(* args)
|
861
|
+
find(:count, * args)
|
718
862
|
end
|
719
863
|
|
720
864
|
# This gets less and less efficient the higher the page since SimpleDB has no way
|
@@ -785,8 +929,8 @@ module SimpleRecord
|
|
785
929
|
@@debug
|
786
930
|
end
|
787
931
|
|
788
|
-
def self.sanitize_sql(*params)
|
789
|
-
return ActiveRecord::Base.sanitize_sql(*params)
|
932
|
+
def self.sanitize_sql(* params)
|
933
|
+
return ActiveRecord::Base.sanitize_sql(* params)
|
790
934
|
end
|
791
935
|
|
792
936
|
def self.table_name
|
@@ -804,12 +948,11 @@ module SimpleRecord
|
|
804
948
|
def changes
|
805
949
|
ret = {}
|
806
950
|
#puts 'in CHANGES=' + @dirty.inspect
|
807
|
-
@dirty.each_pair {|key, value| ret[key] = [value, get_attribute(key)]}
|
951
|
+
@dirty.each_pair { |key, value| ret[key] = [value, get_attribute(key)] }
|
808
952
|
return ret
|
809
953
|
end
|
810
954
|
|
811
|
-
def
|
812
|
-
super
|
955
|
+
def after_save_cleanup
|
813
956
|
@dirty = {}
|
814
957
|
end
|
815
958
|
|
@@ -864,30 +1007,30 @@ module SimpleRecord
|
|
864
1007
|
return count
|
865
1008
|
end
|
866
1009
|
|
867
|
-
def each(*params, &block)
|
868
|
-
return load.each(*params){|record| block.call(record)}
|
1010
|
+
def each(* params, & block)
|
1011
|
+
return load.each(* params) { |record| block.call(record) }
|
869
1012
|
end
|
870
1013
|
|
871
|
-
def find_all(*params)
|
872
|
-
find(:all, *params)
|
1014
|
+
def find_all(* params)
|
1015
|
+
find(:all, * params)
|
873
1016
|
end
|
874
1017
|
|
875
1018
|
def empty?
|
876
1019
|
return load.empty?
|
877
1020
|
end
|
878
1021
|
|
879
|
-
def build(*params)
|
1022
|
+
def build(* params)
|
880
1023
|
params[0][@referencename]=@referencevalue
|
881
|
-
eval(@subname).new(*params)
|
1024
|
+
eval(@subname).new(* params)
|
882
1025
|
end
|
883
1026
|
|
884
|
-
def create(*params)
|
1027
|
+
def create(* params)
|
885
1028
|
params[0][@referencename]=@referencevalue
|
886
|
-
record = eval(@subname).new(*params)
|
1029
|
+
record = eval(@subname).new(* params)
|
887
1030
|
record.save
|
888
1031
|
end
|
889
1032
|
|
890
|
-
def find(*params)
|
1033
|
+
def find(* params)
|
891
1034
|
query=[:first, {}]
|
892
1035
|
#{:conditions=>"id=>1"}
|
893
1036
|
if params[0]
|
@@ -910,11 +1053,16 @@ module SimpleRecord
|
|
910
1053
|
#query[1][:conditions]="id='#{@id}'"
|
911
1054
|
end
|
912
1055
|
|
913
|
-
return eval(@subname).find(*query)
|
1056
|
+
return eval(@subname).find(* query)
|
914
1057
|
end
|
915
1058
|
|
916
1059
|
end
|
917
1060
|
|
1061
|
+
# This is simply a place holder so we don't keep doing gets to s3 or simpledb if already checked.
|
1062
|
+
class RemoteNil
|
1063
|
+
|
1064
|
+
end
|
1065
|
+
|
918
1066
|
|
919
1067
|
end
|
920
1068
|
|
data/test/my_model.rb
CHANGED
data/test/test_base.rb
CHANGED
@@ -24,9 +24,12 @@ class TestBase < Test::Unit::TestCase
|
|
24
24
|
@config = YAML::load(File.open(File.expand_path("~/.test-configs/simple_record.yml")))
|
25
25
|
#puts 'inspecting config = ' + @config.inspect
|
26
26
|
|
27
|
+
SimpleRecord.enable_logging
|
28
|
+
|
27
29
|
SimpleRecord::Base.set_domain_prefix("simplerecord_tests_")
|
28
30
|
SimpleRecord.establish_connection(@config['amazon']['access_key'], @config['amazon']['secret_key'], :connection_mode=>:single)
|
29
31
|
|
32
|
+
|
30
33
|
# Establish AWS connection directly
|
31
34
|
@@sdb = Aws::SdbInterface.new(@config['amazon']['access_key'], @config['amazon']['secret_key'], {:connection_mode => :per_request})
|
32
35
|
|
data/test/test_json.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), "/../lib/simple_record")
|
3
|
+
require File.join(File.dirname(__FILE__), "./test_helpers")
|
4
|
+
require File.join(File.dirname(__FILE__), "./test_base")
|
5
|
+
require "yaml"
|
6
|
+
require 'aws'
|
7
|
+
require 'my_model'
|
8
|
+
require 'my_child_model'
|
9
|
+
require 'model_with_enc'
|
10
|
+
require 'active_support'
|
11
|
+
|
12
|
+
# Tests for SimpleRecord
|
13
|
+
#
|
14
|
+
|
15
|
+
class TestJson < TestBase
|
16
|
+
|
17
|
+
|
18
|
+
def test_json
|
19
|
+
mm = MyModel.new
|
20
|
+
|
21
|
+
mm.name = "whatever"
|
22
|
+
mm.age = "1"
|
23
|
+
|
24
|
+
|
25
|
+
jsoned = mm.to_json
|
26
|
+
puts 'jsoned=' + jsoned
|
27
|
+
unjsoned = JSON.parse jsoned
|
28
|
+
puts 'unjsoned=' + unjsoned.inspect
|
29
|
+
assert unjsoned.name == "whatever"
|
30
|
+
|
31
|
+
mm.save
|
32
|
+
|
33
|
+
data = {}
|
34
|
+
models = []
|
35
|
+
data[:models] = models
|
36
|
+
|
37
|
+
models << mm
|
38
|
+
|
39
|
+
jsoned = JSON.generate models
|
40
|
+
puts 'jsoned=' + jsoned
|
41
|
+
unjsoned = JSON.parse jsoned
|
42
|
+
puts 'unjsoned=' + unjsoned.inspect
|
43
|
+
assert unjsoned.size == models.size
|
44
|
+
assert unjsoned[0].name == mm.name
|
45
|
+
assert unjsoned[0].age == mm.age
|
46
|
+
|
47
|
+
t = Tester.new
|
48
|
+
t2 = Tester.new
|
49
|
+
t2.x1 = "i'm number 2"
|
50
|
+
t.x1 = 1
|
51
|
+
t.x2 = t2
|
52
|
+
t.to_json
|
53
|
+
jsoned = JSON.generate t
|
54
|
+
puts 'jsoned=' + jsoned
|
55
|
+
|
56
|
+
mcm = MyChildModel.new
|
57
|
+
mcm.name = "child"
|
58
|
+
mcm.my_model = mm
|
59
|
+
jsoned = mcm.to_json
|
60
|
+
puts 'jsoned=' + jsoned
|
61
|
+
unjsoned = JSON.parse jsoned
|
62
|
+
puts 'unjsoned=' + unjsoned.inspect
|
63
|
+
assert mcm.my_model.id == unjsoned.my_model.id
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class Tester
|
70
|
+
|
71
|
+
attr_accessor :x1, :x2
|
72
|
+
|
73
|
+
end
|
data/test/test_lobs.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), "/../lib/simple_record")
|
3
|
+
require File.join(File.dirname(__FILE__), "./test_helpers")
|
4
|
+
require File.join(File.dirname(__FILE__), "./test_base")
|
5
|
+
require "yaml"
|
6
|
+
require 'aws'
|
7
|
+
require 'my_model'
|
8
|
+
require 'my_child_model'
|
9
|
+
require 'model_with_enc'
|
10
|
+
require 'active_support'
|
11
|
+
|
12
|
+
# Tests for SimpleRecord
|
13
|
+
#
|
14
|
+
|
15
|
+
class TestLobs < TestBase
|
16
|
+
|
17
|
+
|
18
|
+
def test_blobs
|
19
|
+
mm = MyModel.new
|
20
|
+
|
21
|
+
puts mm.clob1.inspect
|
22
|
+
assert mm.clob1.nil?
|
23
|
+
|
24
|
+
mm.name = "whatever"
|
25
|
+
mm.age = "1"
|
26
|
+
mm.clob1 = "0" * 2000
|
27
|
+
assert SimpleRecord.stats.s3_puts == 0
|
28
|
+
puts mm.inspect
|
29
|
+
mm.save
|
30
|
+
|
31
|
+
sleep 2
|
32
|
+
|
33
|
+
mm2 = MyModel.find(mm.id)
|
34
|
+
assert mm.id == mm2.id
|
35
|
+
puts 'mm.clob1=' + mm.clob1.to_s
|
36
|
+
puts 'mm2.clob1=' + mm2.clob1.to_s
|
37
|
+
assert mm.clob1 == mm2.clob1
|
38
|
+
assert SimpleRecord.stats.s3_puts == 1, "puts is #{SimpleRecord.stats.s3_puts}"
|
39
|
+
assert SimpleRecord.stats.s3_gets == 1, "gets is #{SimpleRecord.stats.s3_gets}"
|
40
|
+
mm2.clob1 # make sure it doesn't do another get
|
41
|
+
assert SimpleRecord.stats.s3_gets == 1
|
42
|
+
|
43
|
+
mm2.save
|
44
|
+
|
45
|
+
# shouldn't save twice if not dirty
|
46
|
+
assert SimpleRecord.stats.s3_puts == 1
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/test/test_marshalled.rb
CHANGED
data/test/test_simple_record.rb
CHANGED
@@ -124,7 +124,7 @@ class TestSimpleRecord < TestBase
|
|
124
124
|
mm.cool = false
|
125
125
|
items << mm
|
126
126
|
MyModel.batch_save(items)
|
127
|
-
sleep
|
127
|
+
sleep 2
|
128
128
|
items.each do |item|
|
129
129
|
puts 'id=' + item.id
|
130
130
|
new_item = MyModel.find(item.id)
|
@@ -208,11 +208,11 @@ class TestSimpleRecord < TestBase
|
|
208
208
|
# or check stats and ensure only 1 attribute was put
|
209
209
|
|
210
210
|
# Test to ensure that if an item is not dirty, sdb doesn't get hit
|
211
|
-
puts SimpleRecord.stats.
|
211
|
+
puts SimpleRecord.stats.saves.to_s
|
212
212
|
SimpleRecord.stats.clear
|
213
213
|
mm2.save(:dirty=>true)
|
214
|
-
puts SimpleRecord.stats.
|
215
|
-
assert SimpleRecord.stats.
|
214
|
+
puts SimpleRecord.stats.saves.to_s
|
215
|
+
assert SimpleRecord.stats.saves == 0
|
216
216
|
|
217
217
|
mmc = MyChildModel.new
|
218
218
|
mmc.my_model = mm
|
@@ -229,9 +229,8 @@ class TestSimpleRecord < TestBase
|
|
229
229
|
puts 'saving my_model to nil'
|
230
230
|
SimpleRecord.stats.clear
|
231
231
|
assert mmc2.save(:dirty=>true)
|
232
|
-
|
233
|
-
assert SimpleRecord.stats.
|
234
|
-
assert SimpleRecord.stats.deletes == 1
|
232
|
+
assert SimpleRecord.stats.saves == 1, "saves is #{SimpleRecord.stats.saves}" # 1 put only for updated, should have a count of attributes saved in stats
|
233
|
+
assert SimpleRecord.stats.deletes == 1, "deletes is #{SimpleRecord.stats.deletes}"
|
235
234
|
assert mmc2.id == mmc.id
|
236
235
|
assert mmc2.my_model_id == nil
|
237
236
|
assert mmc2.my_model == nil, "my_model not nil? #{mmc2.my_model.inspect}"
|
@@ -574,13 +573,13 @@ class TestSimpleRecord < TestBase
|
|
574
573
|
mm.update_attributes(:name=>"name2", :age=>21, "date2"=>now)
|
575
574
|
assert mm.name == "name2", "Name is #{mm.name}"
|
576
575
|
assert mm.age == 21
|
577
|
-
assert mm.date2.to_time.
|
576
|
+
# assert mm.date2.to_time.utc == now.utc, "#{mm.date2.class.name} #{mm.date2.to_time.inspect} != #{now.inspect}"
|
578
577
|
sleep 1
|
579
578
|
|
580
579
|
mm = MyModel.find(mm.id)
|
581
580
|
assert mm.name == "name2", "Name is #{mm.name}"
|
582
581
|
assert mm.age == 21, "Age is not 21, it is #{mm.age}"
|
583
|
-
assert mm.date2 == now, "Date is not correct, it is #{mm.date2}"
|
582
|
+
# assert mm.date2 == now, "Date is not correct, it is #{mm.date2}"
|
584
583
|
end
|
585
584
|
|
586
585
|
def test_explicit_class_name
|
@@ -661,10 +660,6 @@ class TestSimpleRecord < TestBase
|
|
661
660
|
|
662
661
|
mmf1.age == 456
|
663
662
|
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
663
|
end
|
669
664
|
|
670
665
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 1.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 1.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Travis Reeder
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-05-
|
19
|
+
date: 2010-05-12 00:00:00 -07:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -46,6 +46,7 @@ files:
|
|
46
46
|
- lib/simple_record/encryptor.rb
|
47
47
|
- lib/simple_record/errors.rb
|
48
48
|
- lib/simple_record/exceptions.rb
|
49
|
+
- lib/simple_record/json.rb
|
49
50
|
- lib/simple_record/password.rb
|
50
51
|
- lib/simple_record/results_array.rb
|
51
52
|
- lib/simple_record/stats.rb
|
@@ -94,6 +95,8 @@ test_files:
|
|
94
95
|
- test/test_encodings.rb
|
95
96
|
- test/test_global_options.rb
|
96
97
|
- test/test_helpers.rb
|
98
|
+
- test/test_json.rb
|
99
|
+
- test/test_lobs.rb
|
97
100
|
- test/test_marshalled.rb
|
98
101
|
- test/test_pagination.rb
|
99
102
|
- test/test_results_array.rb
|