simple_record 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.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 = nil
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
- attr = Attribute.new(:string, arg_options)
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
@@ -1,17 +1,21 @@
1
1
  module SimpleRecord
2
2
  class Stats
3
- attr_accessor :selects, :puts, :deletes
3
+ attr_accessor :selects, :saves, :deletes, :s3_puts, :s3_gets
4
4
 
5
5
  def initialize
6
6
  @selects = 0
7
- @puts = 0
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.puts = 0
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 = defined_attributes_local[name.to_sym]
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
- # puts "ENCRYPTING #{name} value #{value}"
32
+ puts "ENCRYPTING #{name} value #{value}"
33
33
  ret = Translations.encrypt(ret, att_meta.options[:encrypted])
34
- # puts 'encrypted value=' + ret.to_s
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 = defined_attributes_local[name.to_sym]
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 $!.message.include? "Couldn't find"
87
- ret = nil
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
- #puts 'local defined_attributes'
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 sdb_att_name(name)
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
- if att_meta.type == :belongs_to
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 []( attribute)
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 # todo: this should use the @dirty hash now
364
- SimpleRecord.stats.puts += 1
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
- @attributes.each do |key, value|
462
- # puts 'key=' + key.inspect + ' value=' + value.inspect
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(arg)
626
+ def get_attribute(name)
534
627
  # puts "GET #{arg}"
535
628
  # Check if this arg is already converted
536
- arg_s = arg.to_s
537
- # instance_var = ("@" + arg_s)
538
- # puts "defined?(#{instance_var.to_sym}) " + (defined?(instance_var.to_sym)).inspect
539
- # if defined?(instance_var.to_sym) # this returns "method" for some reason??
540
- # puts "attribute #{instance_var} is defined"
541
- @attributes_rb = {} unless @attributes_rb # was getting errors after upgrade.
542
- ret = @attributes_rb[arg_s] # instance_variable_get(instance_var)
543
- # puts 'ret from rb=' + ret.inspect
544
- return ret if !ret.nil?
545
- # end
546
- ret = get_attribute_sdb(arg)
547
- # puts 'ret from atts=' + ret.inspect
548
- ret = sdb_to_ruby(arg, ret)
549
- # puts 'ret from atts to rb=' + ret.inspect
550
- # puts "Setting instance var #{arg_s} to #{ret}"
551
- # instance_variable_set(instance_var, ret)
552
- @attributes_rb[arg_s] = ret
553
- return ret
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
- att_meta = defined_attributes_local[name.to_sym]
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
- attname = name.to_s + '_id'
577
- attvalue = value.nil? ? nil : value.id
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
- @attributes_rb.delete(name.to_s) # todo: we should set the value here so it doesn't reget anything
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 mark_as_old
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
@@ -8,6 +8,7 @@ class MyModel < MyBaseModel
8
8
  has_booleans :cool
9
9
  has_dates :birthday, :date1, :date2, :date3
10
10
 
11
+ has_clobs :clob1
11
12
 
12
13
  #callbacks
13
14
  before_create :set_nickname
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
@@ -12,7 +12,7 @@ class Person < SimpleRecord::Base
12
12
  has_strings :name, :i_as_s
13
13
  has_ints :age, :i2
14
14
  end
15
- class DirtyTest < TestBase
15
+ class MarshalTest < TestBase
16
16
 
17
17
  def setup
18
18
  super
@@ -124,7 +124,7 @@ class TestSimpleRecord < TestBase
124
124
  mm.cool = false
125
125
  items << mm
126
126
  MyModel.batch_save(items)
127
- sleep 1
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.puts.to_s
211
+ puts SimpleRecord.stats.saves.to_s
212
212
  SimpleRecord.stats.clear
213
213
  mm2.save(:dirty=>true)
214
- puts SimpleRecord.stats.puts.to_s
215
- assert SimpleRecord.stats.puts == 0
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
- puts SimpleRecord.stats.puts.to_s
233
- assert SimpleRecord.stats.puts == 1 # 1 put only for updated, should have a count of attributes saved in 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.eql?(now), "#{mm.date2.class.name} #{mm.date2.to_time.inspect} != #{now.inspect}"
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
- - 2
8
- - 1
9
- version: 1.2.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-08 00:00:00 -07:00
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