simple_record 1.1.41 → 1.1.42

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
@@ -29,10 +29,12 @@ require 'sdb/active_sdb'
29
29
  require 'base64'
30
30
  require File.expand_path(File.dirname(__FILE__) + "/simple_record/encryptor")
31
31
  require File.expand_path(File.dirname(__FILE__) + "/simple_record/callbacks")
32
+ require File.expand_path(File.dirname(__FILE__) + "/simple_record/attributes")
32
33
  require File.expand_path(File.dirname(__FILE__) + "/simple_record/errors")
33
34
  require File.expand_path(File.dirname(__FILE__) + "/simple_record/password")
34
35
  require File.expand_path(File.dirname(__FILE__) + "/simple_record/results_array")
35
36
  require File.expand_path(File.dirname(__FILE__) + "/simple_record/stats")
37
+ require File.expand_path(File.dirname(__FILE__) + "/simple_record/translations")
36
38
 
37
39
 
38
40
  module SimpleRecord
@@ -73,8 +75,12 @@ module SimpleRecord
73
75
 
74
76
  class Base < Aws::ActiveSdb::Base
75
77
 
78
+ include SimpleRecord::Translations
79
+ # include SimpleRecord::Attributes
80
+ extend SimpleRecord::Attributes
76
81
  include SimpleRecord::Callbacks
77
82
 
83
+
78
84
  def initialize(attrs={})
79
85
  # 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
80
86
 
@@ -89,12 +95,7 @@ module SimpleRecord
89
95
  def initialize_base(attrs={})
90
96
 
91
97
  #we have to handle the virtuals.
92
- @@virtuals.each do |virtual|
93
- #we first copy the information for the virtual to an instance variable of the same name
94
- eval("@#{virtual}=attrs['#{virtual}']")
95
- #and then remove the parameter before it is passed to initialize, so that it is NOT sent to SimpleDB
96
- eval("attrs.delete('#{virtual}')")
97
- end
98
+ Attributes.handle_virtuals(attrs)
98
99
 
99
100
  @errors=SimpleRecord_errors.new
100
101
  @dirty = {}
@@ -112,40 +113,9 @@ module SimpleRecord
112
113
  end
113
114
 
114
115
 
115
- # todo: move into Callbacks module
116
- #this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
117
- #this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
118
- #basically, this is how we recreate the callback functions
119
- @@callbacks.each do |callback|
120
- instance_eval <<-endofeval
121
-
122
- #puts 'doing callback=' + callback + ' for ' + self.inspect
123
- #we first have to make an initialized array for each of the callbacks, to prevent problems if they are not called
124
-
125
- def #{callback}(*args)
126
- #puts 'callback called in ' + self.inspect + ' with ' + args.inspect
127
-
128
- #make_dirty(arg_s, value)
129
- #self[arg.to_s]=value
130
- #puts 'value in callback #{callback}=' + value.to_s
131
- args.each do |arg|
132
- cnames = callbacks['#{callback}']
133
- #puts '\tcnames1=' + cnames.inspect + ' for class ' + self.inspect
134
- cnames = [] if cnames.nil?
135
- cnames << arg.to_s if cnames.index(arg.to_s).nil?
136
- #puts '\tcnames2=' + cnames.inspect
137
- callbacks['#{callback}'] = cnames
138
- end
139
- end
140
-
141
- endofeval
142
- end
143
- #puts 'base methods=' + self.methods.inspect
144
-
145
-
146
116
  def self.inherited(base)
147
117
  #puts 'SimpleRecord::Base is inherited by ' + base.inspect
148
- setup_callbacks(base)
118
+ Callbacks.setup_callbacks(base)
149
119
 
150
120
  # base.has_strings :id
151
121
  base.has_dates :created, :updated
@@ -154,59 +124,6 @@ module SimpleRecord
154
124
 
155
125
  end
156
126
 
157
- def self.setup_callbacks(base)
158
- instance_eval <<-endofeval
159
-
160
- def callbacks
161
- @callbacks ||= {}
162
- @callbacks
163
- end
164
-
165
- def self.defined_attributes
166
- #puts 'class defined_attributes'
167
- @attributes ||= {}
168
- @attributes
169
- end
170
-
171
- endofeval
172
-
173
- @@callbacks.each do |callback|
174
- class_eval <<-endofeval
175
-
176
- def run_#{callback}
177
- # puts 'CLASS CALLBACKS for ' + self.inspect + ' = ' + self.class.callbacks.inspect
178
- return true if self.class.callbacks.nil?
179
- cnames = self.class.callbacks['#{callback}']
180
- cnames = [] if cnames.nil?
181
- #cnames += super.class.callbacks['#{callback}'] unless super.class.callbacks.nil?
182
- # puts 'cnames for #{callback} = ' + cnames.inspect
183
- return true if cnames.nil?
184
- cnames.each { |name|
185
- #puts 'run_ #{name}'
186
- if eval(name) == false # nil should be an ok return, only looking for false
187
- return false
188
- end
189
- }
190
- #super.run_#{callback}
191
- return true
192
- end
193
-
194
- endofeval
195
- end
196
- end
197
-
198
-
199
- # Holds information about an attribute
200
- class Attribute
201
- attr_accessor :type, :options
202
-
203
- def initialize(type, options=nil)
204
- @type = type
205
- @options = options
206
- end
207
-
208
- end
209
-
210
127
 
211
128
  def defined_attributes_local
212
129
  #puts 'local defined_attributes'
@@ -252,14 +169,6 @@ module SimpleRecord
252
169
  super
253
170
  end
254
171
 
255
- =begin
256
- def self.get_domain_name
257
- # puts 'returning domain_name=' + @domain_name_for_class.to_s
258
- #return @domain_name_for_class
259
- return self.domain
260
- end
261
-
262
- =end
263
172
 
264
173
  def domain
265
174
  super # super.domain
@@ -276,8 +185,8 @@ module SimpleRecord
276
185
 
277
186
  def get_attribute_sdb(arg)
278
187
  # arg = arg.to_s
279
- puts "get_attribute_sdb(#{arg}) - #{arg.class.name}"
280
- puts 'self[]=' + self.inspect
188
+ # puts "get_attribute_sdb(#{arg}) - #{arg.class.name}"
189
+ # puts 'self[]=' + self.inspect
281
190
  ret = strip_array(self[arg])
282
191
  return ret
283
192
  end
@@ -317,217 +226,18 @@ module SimpleRecord
317
226
 
318
227
  def make_dirty(arg, value)
319
228
  arg = arg.to_s
320
- puts "Marking #{arg} dirty with #{value}"
229
+ # puts "Marking #{arg} dirty with #{value}"
321
230
  if @dirty.include?(arg)
322
231
  old = @dirty[arg]
323
- puts "Was already dirty #{old}"
232
+ # puts "Was already dirty #{old}"
324
233
  @dirty.delete(arg) if value == old
325
234
  else
326
235
  old = get_attribute(arg)
327
- puts "dirtifying #{old} to #{value}"
236
+ # puts "dirtifying #{old} to #{value}"
328
237
  @dirty[arg] = old if value != old
329
238
  end
330
239
  end
331
240
 
332
- def self.has_attributes(*args)
333
- args.each do |arg|
334
- arg_options = nil
335
- if arg.is_a?(Hash)
336
- # then attribute may have extra options
337
- arg_options = arg
338
- arg = arg_options[:name]
339
- end
340
- attr = SimpleRecord::Base::Attribute.new(:string, arg_options)
341
- defined_attributes[arg] = attr if defined_attributes[arg].nil?
342
-
343
- # define reader method
344
- arg_s = arg.to_s # to get rid of all the to_s calls
345
- send(:define_method, arg) do
346
- ret = get_attribute(arg)
347
- return ret
348
- end
349
-
350
- # define writer method
351
- send(:define_method, arg_s+"=") do |value|
352
- set(arg, value)
353
- end
354
-
355
- # Now for dirty methods: http://api.rubyonrails.org/classes/ActiveRecord/Dirty.html
356
- # define changed? method
357
- send(:define_method, arg_s + "_changed?") do
358
- @dirty.has_key?(arg_s)
359
- end
360
-
361
- # define change method
362
- send(:define_method, arg_s + "_change") do
363
- old_val = @dirty[arg_s]
364
- [old_val, get_attribute(arg_s)]
365
- end
366
-
367
- # define was method
368
- send(:define_method, arg_s + "_was") do
369
- old_val = @dirty[arg_s]
370
- old_val
371
- end
372
- end
373
- end
374
-
375
- def self.has_strings(*args)
376
- has_attributes(*args)
377
- end
378
-
379
- def self.has_ints(*args)
380
- has_attributes(*args)
381
- are_ints(*args)
382
- end
383
-
384
- def self.has_dates(*args)
385
- has_attributes(*args)
386
- are_dates(*args)
387
- end
388
-
389
- def self.has_booleans(*args)
390
- has_attributes(*args)
391
- are_booleans(*args)
392
- end
393
-
394
- def self.are_ints(*args)
395
- # puts 'calling are_ints: ' + args.inspect
396
- args.each do |arg|
397
- defined_attributes[arg].type = :int
398
- end
399
- end
400
-
401
- def self.are_dates(*args)
402
- args.each do |arg|
403
- defined_attributes[arg].type = :date
404
- end
405
- end
406
-
407
- def self.are_booleans(*args)
408
- args.each do |arg|
409
- defined_attributes[arg].type = :boolean
410
- end
411
- end
412
-
413
- @@virtuals=[]
414
-
415
- def self.has_virtuals(*args)
416
- @@virtuals = args
417
- args.each do |arg|
418
- #we just create the accessor functions here, the actual instance variable is created during initialize
419
- attr_accessor(arg)
420
- end
421
- end
422
-
423
- # One belongs_to association per call. Call multiple times if there are more than one.
424
- #
425
- # This method will also create an {association)_id method that will return the ID of the foreign object
426
- # without actually materializing it.
427
- def self.belongs_to(association_id, options = {})
428
- attribute = SimpleRecord::Base::Attribute.new(:belongs_to, options)
429
- defined_attributes[association_id] = attribute
430
- arg = association_id
431
- arg_s = arg.to_s
432
- arg_id = arg.to_s + '_id'
433
-
434
- # todo: should also handle foreign_key http://74.125.95.132/search?q=cache:KqLkxuXiBBQJ:wiki.rubyonrails.org/rails/show/belongs_to+rails+belongs_to&hl=en&ct=clnk&cd=1&gl=us
435
- # puts "arg_id=#{arg}_id"
436
- # puts "is defined? " + eval("(defined? #{arg}_id)").to_s
437
- # puts 'atts=' + @attributes.inspect
438
-
439
- # Define reader method
440
- send(:define_method, arg) do
441
- attribute = defined_attributes_local[arg]
442
- options2 = attribute.options # @@belongs_to_map[arg]
443
- class_name = options2[:class_name] || arg.to_s[0...1].capitalize + arg.to_s[1...arg.to_s.length]
444
-
445
- # Camelize classnames with underscores (ie my_model.rb --> MyModel)
446
- class_name = class_name.camelize
447
-
448
- # puts "attr=" + @attributes[arg_id].inspect
449
- # puts 'val=' + @attributes[arg_id][0].inspect unless @attributes[arg_id].nil?
450
- ret = nil
451
- arg_id = arg.to_s + '_id'
452
- if !@attributes[arg_id].nil? && @attributes[arg_id].size > 0 && @attributes[arg_id][0] != nil && @attributes[arg_id][0] != ''
453
- if !@@cache_store.nil?
454
- arg_id_val = @attributes[arg_id][0]
455
- cache_key = self.class.cache_key(class_name, arg_id_val)
456
- # puts 'cache_key=' + cache_key
457
- ret = @@cache_store.read(cache_key)
458
- # puts 'belongs_to incache=' + ret.inspect
459
- end
460
- if ret.nil?
461
- to_eval = "#{class_name}.find(@attributes['#{arg_id}'][0])"
462
- # puts 'to eval=' + to_eval
463
- begin
464
- ret = eval(to_eval) # (defined? #{arg}_id)
465
- rescue Aws::ActiveSdb::ActiveSdbError
466
- if $!.message.include? "Couldn't find"
467
- ret = nil
468
- else
469
- raise $!
470
- end
471
- end
472
-
473
- end
474
- end
475
- # puts 'ret=' + ret.inspect
476
- return ret
477
- end
478
-
479
-
480
- # Define writer method
481
- send(:define_method, arg.to_s + "=") do |value|
482
- set_belongs_to(arg, value)
483
- end
484
-
485
-
486
- # Define ID reader method for reading the associated objects id without getting the entire object
487
- send(:define_method, arg_id) do
488
- if !@attributes[arg_id].nil? && @attributes[arg_id].size > 0 && @attributes[arg_id][0] != nil && @attributes[arg_id][0] != ''
489
- return @attributes[arg_id][0]
490
- end
491
- return nil
492
- end
493
-
494
- # Define writer method for setting the _id directly without the associated object
495
- send(:define_method, arg_id + "=") do |value|
496
- if value.nil?
497
- self[arg_id] = nil unless self[arg_id].nil? # if it went from something to nil, then we have to remember and remove attribute on save
498
- else
499
- self[arg_id] = value
500
- end
501
- end
502
-
503
- send(:define_method, "create_"+arg.to_s) do |*params|
504
- newsubrecord=eval(arg.to_s.classify).new(*params)
505
- newsubrecord.save
506
- arg_id = arg.to_s + '_id'
507
- self[arg_id]=newsubrecord.id
508
- end
509
- end
510
-
511
- def self.has_many(*args)
512
- args.each do |arg|
513
- #okay, this creates an instance method with the pluralized name of the symbol passed to belongs_to
514
- send(:define_method, arg) do
515
- #when called, the method creates a new, very temporary instance of the Activerecordtosdb_subrecord class
516
- #It is passed the three initializers it needs:
517
- #note the first parameter is just a string by time new gets it, like "user"
518
- #the second and third parameters are still a variable when new gets it, like user_id
519
- return eval(%{Activerecordtosdb_subrecord_array.new('#{arg}', self.class.name ,id)})
520
- end
521
- end
522
- #Disclaimer: this whole funciton just seems crazy to me, and a bit inefficient. But it was the clearest way I could think to do it code wise.
523
- #It's bad programming form (imo) to have a class method require something that isn't passed to it through it's variables.
524
- #I couldn't pass the id when calling find, since the original find doesn't work that way, so I was left with this.
525
- end
526
-
527
- def self.has_one(*args)
528
-
529
- end
530
-
531
241
  def clear_errors
532
242
  @errors=SimpleRecord_errors.new
533
243
  end
@@ -558,38 +268,8 @@ module SimpleRecord
558
268
  end
559
269
 
560
270
 
561
- @@offset = 9223372036854775808
562
- @@padding = 20
563
- @@date_format = "%Y-%m-%dT%H:%M:%S"; # Time to second precision
564
-
565
- def self.pad_and_offset(x) # Change name to something more appropriate like ruby_to_sdb
566
- # todo: add Float, etc
567
- # puts 'padding=' + x.class.name + " -- " + x.inspect
568
- if x.kind_of? Integer
569
- x += @@offset
570
- x_str = x.to_s
571
- # pad
572
- x_str = '0' + x_str while x_str.size < 20
573
- return x_str
574
- elsif x.respond_to?(:iso8601)
575
- # puts x.class.name + ' responds to iso8601'
576
- #
577
- # There is an issue here where Time.iso8601 on an incomparable value to DateTime.iso8601.
578
- # Amazon suggests: 2008-02-10T16:52:01.000-05:00
579
- # "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
580
- #
581
- if x.is_a? DateTime
582
- x_str = x.getutc.strftime(@@date_format)
583
- elsif x.is_a? Time
584
- x_str = x.getutc.strftime(@@date_format)
585
- elsif x.is_a? Date
586
- x_str = x.strftime(@@date_format)
587
-
588
- end
589
- return x_str
590
- else
591
- return x
592
- end
271
+ def cache_store
272
+ @@cache_store
593
273
  end
594
274
 
595
275
  def domain_ok(ex)
@@ -646,7 +326,7 @@ module SimpleRecord
646
326
  # - :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.
647
327
  #
648
328
  def save(options={})
649
- puts 'SAVING: ' + self.inspect
329
+ # puts 'SAVING: ' + self.inspect
650
330
  clear_errors
651
331
  # todo: decide whether this should go before pre_save or after pre_save? pre_save dirties "updated" and perhaps other items due to callbacks
652
332
  if options[:dirty]
@@ -664,7 +344,7 @@ module SimpleRecord
664
344
  end
665
345
  to_delete = get_atts_to_delete # todo: this should use the @dirty hash now
666
346
  # puts 'done to_delete ' + to_delete.inspect
667
- puts 'options=' + options.inspect
347
+ # puts 'options=' + options.inspect
668
348
  SimpleRecord.stats.puts += 1
669
349
  if super(options)
670
350
  # puts 'SAVED super'
@@ -706,35 +386,6 @@ module SimpleRecord
706
386
  end
707
387
  end
708
388
 
709
- def pad_and_offset_ints_to_sdb()
710
-
711
- # defined_attributes_local.each_pair do |name, att_meta|
712
- # if att_meta.type == :int && !self[name.to_s].nil?
713
- # arr = @attributes[name.to_s]
714
- # arr.collect!{ |x| self.class.pad_and_offset(x) }
715
- # @attributes[name.to_s] = arr
716
- # end
717
- # end
718
- end
719
-
720
- def convert_dates_to_sdb()
721
-
722
- # defined_attributes_local.each_pair do |name, att_meta|
723
- # puts 'int encoding: ' + i.to_s
724
-
725
- # end
726
- end
727
-
728
- def self.pass_hash(value)
729
- hashed = Password::create_hash(value)
730
- encoded_value = Base64.encode64(hashed)
731
- encoded_value
732
- end
733
-
734
- def self.pass_hash_check(value, value_to_compare)
735
- unencoded_value = Base64.decode64(value)
736
- return Password::check(value_to_compare, unencoded_value)
737
- end
738
389
 
739
390
  def self.get_encryption_key()
740
391
  key = SimpleRecord.options[:encryption_key]
@@ -745,25 +396,6 @@ module SimpleRecord
745
396
  return key
746
397
  end
747
398
 
748
- def self.encrypt(value, key=nil)
749
- key = key || get_encryption_key()
750
- raise SimpleRecordError, "Encryption key must be defined on the attribute." if key.nil?
751
- encrypted_value = SimpleRecord::Encryptor.encrypt(:value => value, :key => key)
752
- encoded_value = Base64.encode64(encrypted_value)
753
- encoded_value
754
- end
755
-
756
-
757
- def self.decrypt(value, key=nil)
758
- puts "decrypt orig value #{value} "
759
- unencoded_value = Base64.decode64(value)
760
- raise SimpleRecordError, "Encryption key must be defined on the attribute." if key.nil?
761
- key = key || get_encryption_key()
762
- puts "decrypting #{unencoded_value} "
763
- decrypted_value = SimpleRecord::Encryptor.decrypt(:value => unencoded_value, :key => key)
764
- puts "decrypted #{unencoded_value} to #{decrypted_value}"
765
- decrypted_value
766
- end
767
399
 
768
400
  def pre_save(options)
769
401
 
@@ -918,127 +550,6 @@ module SimpleRecord
918
550
  end
919
551
  end
920
552
 
921
- # Convert value from SimpleDB String version to real ruby value.
922
- def sdb_to_ruby(name, value)
923
- puts 'sdb_to_ruby arg=' + name.inspect + ' - ' + name.class.name + ' - value=' + value.to_s
924
- return nil if value.nil?
925
- att_meta = defined_attributes_local[name.to_sym]
926
-
927
- if att_meta.options
928
- if att_meta.options[:encrypted]
929
- value = self.class.decrypt(value, att_meta.options[:encrypted])
930
- end
931
- if att_meta.options[:hashed]
932
- return PasswordHashed.new(value)
933
- end
934
- end
935
-
936
- if att_meta.type == :int
937
- value = Base.un_offset_int(value)
938
- elsif att_meta.type == :date
939
- value = to_date(value)
940
- elsif att_meta.type == :boolean
941
- value = to_bool(value)
942
- end
943
- value
944
- end
945
-
946
-
947
- def ruby_to_sdb(name, value)
948
-
949
- return nil if value.nil?
950
-
951
- name = name.to_s
952
-
953
- puts "Converting #{name} to sdb value=#{value}"
954
- puts "atts_local=" + defined_attributes_local.inspect
955
-
956
- att_meta = defined_attributes_local[name.to_sym]
957
-
958
- if att_meta.type == :int
959
- ret = self.class.pad_and_offset(value)
960
- elsif att_meta.type == :date
961
- ret = self.class.pad_and_offset(value)
962
- else
963
- ret = value.to_s
964
- end
965
-
966
-
967
- if att_meta.options
968
- if att_meta.options[:encrypted]
969
- puts "ENCRYPTING #{name} value #{value}"
970
- ret = self.class.encrypt(ret, att_meta.options[:encrypted])
971
- puts 'encrypted value=' + ret.to_s
972
- end
973
- if att_meta.options[:hashed]
974
- ret = self.class.pass_hash(ret)
975
- end
976
- end
977
-
978
- return ret.to_s
979
-
980
- end
981
-
982
- def wrap_if_required(arg, value, sdb_val)
983
- return nil if value.nil?
984
-
985
- arg_s = arg.to_s
986
- att_meta = defined_attributes_local[arg.to_sym]
987
- if att_meta.options
988
- if att_meta.options[:hashed]
989
- puts 'wrapping ' + arg_s
990
- return PasswordHashed.new(sdb_val)
991
- end
992
- end
993
- value
994
- end
995
-
996
- def to_date(x)
997
- if x.is_a?(String)
998
- DateTime.parse(x)
999
- else
1000
- x
1001
- end
1002
- end
1003
-
1004
- def to_bool(x)
1005
- if x.is_a?(String)
1006
- x == "true" || x == "1"
1007
- else
1008
- x
1009
- end
1010
- end
1011
-
1012
- def self.un_offset_int(x)
1013
- if x.is_a?(String)
1014
- x2 = x.to_i
1015
- # puts 'to_i=' + x2.to_s
1016
- x2 -= @@offset
1017
- # puts 'after subtracting offset='+ x2.to_s
1018
- x2
1019
- else
1020
- x
1021
- end
1022
- end
1023
-
1024
- def unpad(i, attributes)
1025
- if !attributes[i].nil?
1026
- # puts 'before=' + self[i].inspect
1027
- attributes[i].collect!{ |x|
1028
- un_offset_int(x)
1029
-
1030
- }
1031
- end
1032
- end
1033
-
1034
- def unpad_self
1035
- defined_attributes_local.each_pair do |name, att_meta|
1036
- if att_meta.type == :int
1037
- unpad(name, @attributes)
1038
- end
1039
- end
1040
- end
1041
-
1042
553
  def reload
1043
554
  super()
1044
555
  end
@@ -1108,7 +619,7 @@ module SimpleRecord
1108
619
  end
1109
620
  end
1110
621
  rescue Aws::AwsError, Aws::ActiveSdb::ActiveSdbError
1111
- puts "RESCUED: " + $!.message
622
+ # puts "RESCUED: " + $!.message
1112
623
  if ($!.message().index("NoSuchDomain") != nil)
1113
624
  # this is ok
1114
625
  elsif ($!.message() =~ @@regex_no_id)
@@ -1130,14 +641,14 @@ module SimpleRecord
1130
641
  if !conditions.nil? && conditions.size > 1
1131
642
  # all after first are values
1132
643
  conditions.collect! { |x|
1133
- self.pad_and_offset(x)
644
+ Translations.pad_and_offset(x)
1134
645
  }
1135
646
  end
1136
647
 
1137
648
  end
1138
649
 
1139
650
  def self.cache_results(results)
1140
- if !@@cache_store.nil? && !results.nil?
651
+ if !cache_store.nil? && !results.nil?
1141
652
  if results.is_a?(Array)
1142
653
  # todo: cache each result
1143
654
  else
@@ -1145,7 +656,7 @@ module SimpleRecord
1145
656
  id = results.id
1146
657
  cache_key = self.cache_key(class_name, id)
1147
658
  #puts 'caching result at ' + cache_key + ': ' + results.inspect
1148
- @@cache_store.write(cache_key, results, :expires_in =>30)
659
+ cache_store.write(cache_key, results, :expires_in =>30)
1149
660
  end
1150
661
  end
1151
662
  end
@@ -1316,28 +827,6 @@ module SimpleRecord
1316
827
 
1317
828
  end
1318
829
 
1319
- class PasswordHashed
1320
-
1321
- def initialize(value)
1322
- @value = value
1323
- end
1324
-
1325
- def hashed_value
1326
- @value
1327
- end
1328
-
1329
- # This allows you to compare an unhashed string to the hashed one.
1330
- def ==(val)
1331
- if val.is_a?(PasswordHashed)
1332
- return val.hashed_value == self.hashed_value
1333
- end
1334
- return SimpleRecord::Base.pass_hash_check(@value, val)
1335
- end
1336
-
1337
- def to_s
1338
- @value
1339
- end
1340
- end
1341
830
 
1342
831
  end
1343
832
 
@@ -0,0 +1,246 @@
1
+ module SimpleRecord
2
+ module Attributes
3
+ # For all things related to defining attributes.
4
+
5
+
6
+ def self.included(base)
7
+ #puts 'Callbacks included in ' + base.inspect
8
+ =begin
9
+ instance_eval <<-endofeval
10
+
11
+ def self.defined_attributes
12
+ #puts 'class defined_attributes'
13
+ @attributes ||= {}
14
+ @attributes
15
+ endendofeval
16
+ endofeval
17
+ =end
18
+
19
+ end
20
+
21
+ def defined_attributes
22
+ @attributes ||= {}
23
+ @attributes
24
+ end
25
+
26
+ def has_attributes(*args)
27
+ args.each do |arg|
28
+ arg_options = nil
29
+ if arg.is_a?(Hash)
30
+ # then attribute may have extra options
31
+ arg_options = arg
32
+ arg = arg_options[:name]
33
+ end
34
+ attr = Attribute.new(:string, arg_options)
35
+ defined_attributes[arg] = attr if defined_attributes[arg].nil?
36
+
37
+ # define reader method
38
+ arg_s = arg.to_s # to get rid of all the to_s calls
39
+ send(:define_method, arg) do
40
+ ret = get_attribute(arg)
41
+ return ret
42
+ end
43
+
44
+ # define writer method
45
+ send(:define_method, arg_s+"=") do |value|
46
+ set(arg, value)
47
+ end
48
+
49
+ # Now for dirty methods: http://api.rubyonrails.org/classes/ActiveRecord/Dirty.html
50
+ # define changed? method
51
+ send(:define_method, arg_s + "_changed?") do
52
+ @dirty.has_key?(arg_s)
53
+ end
54
+
55
+ # define change method
56
+ send(:define_method, arg_s + "_change") do
57
+ old_val = @dirty[arg_s]
58
+ [old_val, get_attribute(arg_s)]
59
+ end
60
+
61
+ # define was method
62
+ send(:define_method, arg_s + "_was") do
63
+ old_val = @dirty[arg_s]
64
+ old_val
65
+ end
66
+ end
67
+ end
68
+
69
+ def has_strings(*args)
70
+ has_attributes(*args)
71
+ end
72
+
73
+ def has_ints(*args)
74
+ has_attributes(*args)
75
+ are_ints(*args)
76
+ end
77
+
78
+ def has_dates(*args)
79
+ has_attributes(*args)
80
+ are_dates(*args)
81
+ end
82
+
83
+ def has_booleans(*args)
84
+ has_attributes(*args)
85
+ are_booleans(*args)
86
+ end
87
+
88
+ def are_ints(*args)
89
+ # puts 'calling are_ints: ' + args.inspect
90
+ args.each do |arg|
91
+ defined_attributes[arg].type = :int
92
+ end
93
+ end
94
+
95
+ def are_dates(*args)
96
+ args.each do |arg|
97
+ defined_attributes[arg].type = :date
98
+ end
99
+ end
100
+
101
+ def are_booleans(*args)
102
+ args.each do |arg|
103
+ defined_attributes[arg].type = :boolean
104
+ end
105
+ end
106
+
107
+ @@virtuals=[]
108
+
109
+ def has_virtuals(*args)
110
+ @@virtuals = args
111
+ args.each do |arg|
112
+ #we just create the accessor functions here, the actual instance variable is created during initialize
113
+ attr_accessor(arg)
114
+ end
115
+ end
116
+
117
+ # One belongs_to association per call. Call multiple times if there are more than one.
118
+ #
119
+ # This method will also create an {association)_id method that will return the ID of the foreign object
120
+ # without actually materializing it.
121
+ def belongs_to(association_id, options = {})
122
+ attribute = Attribute.new(:belongs_to, options)
123
+ defined_attributes[association_id] = attribute
124
+ arg = association_id
125
+ arg_s = arg.to_s
126
+ arg_id = arg.to_s + '_id'
127
+
128
+ # todo: should also handle foreign_key http://74.125.95.132/search?q=cache:KqLkxuXiBBQJ:wiki.rubyonrails.org/rails/show/belongs_to+rails+belongs_to&hl=en&ct=clnk&cd=1&gl=us
129
+ # puts "arg_id=#{arg}_id"
130
+ # puts "is defined? " + eval("(defined? #{arg}_id)").to_s
131
+ # puts 'atts=' + @attributes.inspect
132
+
133
+ # Define reader method
134
+ send(:define_method, arg) do
135
+ attribute = defined_attributes_local[arg]
136
+ options2 = attribute.options # @@belongs_to_map[arg]
137
+ class_name = options2[:class_name] || arg.to_s[0...1].capitalize + arg.to_s[1...arg.to_s.length]
138
+
139
+ # Camelize classnames with underscores (ie my_model.rb --> MyModel)
140
+ class_name = class_name.camelize
141
+
142
+ # puts "attr=" + @attributes[arg_id].inspect
143
+ # puts 'val=' + @attributes[arg_id][0].inspect unless @attributes[arg_id].nil?
144
+ ret = nil
145
+ arg_id = arg.to_s + '_id'
146
+ if !@attributes[arg_id].nil? && @attributes[arg_id].size > 0 && @attributes[arg_id][0] != nil && @attributes[arg_id][0] != ''
147
+ if !cache_store.nil?
148
+ arg_id_val = @attributes[arg_id][0]
149
+ cache_key = self.class.cache_key(class_name, arg_id_val)
150
+ # puts 'cache_key=' + cache_key
151
+ ret = cache_store.read(cache_key)
152
+ # puts 'belongs_to incache=' + ret.inspect
153
+ end
154
+ if ret.nil?
155
+ to_eval = "#{class_name}.find(@attributes['#{arg_id}'][0])"
156
+ # puts 'to eval=' + to_eval
157
+ begin
158
+ ret = eval(to_eval) # (defined? #{arg}_id)
159
+ rescue Aws::ActiveSdb::ActiveSdbError
160
+ if $!.message.include? "Couldn't find"
161
+ ret = nil
162
+ else
163
+ raise $!
164
+ end
165
+ end
166
+
167
+ end
168
+ end
169
+ # puts 'ret=' + ret.inspect
170
+ return ret
171
+ end
172
+
173
+
174
+ # Define writer method
175
+ send(:define_method, arg.to_s + "=") do |value|
176
+ set_belongs_to(arg, value)
177
+ end
178
+
179
+
180
+ # Define ID reader method for reading the associated objects id without getting the entire object
181
+ send(:define_method, arg_id) do
182
+ if !@attributes[arg_id].nil? && @attributes[arg_id].size > 0 && @attributes[arg_id][0] != nil && @attributes[arg_id][0] != ''
183
+ return @attributes[arg_id][0]
184
+ end
185
+ return nil
186
+ end
187
+
188
+ # Define writer method for setting the _id directly without the associated object
189
+ send(:define_method, arg_id + "=") do |value|
190
+ if value.nil?
191
+ self[arg_id] = nil unless self[arg_id].nil? # if it went from something to nil, then we have to remember and remove attribute on save
192
+ else
193
+ self[arg_id] = value
194
+ end
195
+ end
196
+
197
+ send(:define_method, "create_"+arg.to_s) do |*params|
198
+ newsubrecord=eval(arg.to_s.classify).new(*params)
199
+ newsubrecord.save
200
+ arg_id = arg.to_s + '_id'
201
+ self[arg_id]=newsubrecord.id
202
+ end
203
+ end
204
+
205
+ def has_many(*args)
206
+ args.each do |arg|
207
+ #okay, this creates an instance method with the pluralized name of the symbol passed to belongs_to
208
+ send(:define_method, arg) do
209
+ #when called, the method creates a new, very temporary instance of the Activerecordtosdb_subrecord class
210
+ #It is passed the three initializers it needs:
211
+ #note the first parameter is just a string by time new gets it, like "user"
212
+ #the second and third parameters are still a variable when new gets it, like user_id
213
+ return eval(%{Activerecordtosdb_subrecord_array.new('#{arg}', self.class.name ,id)})
214
+ end
215
+ end
216
+ #Disclaimer: this whole funciton just seems crazy to me, and a bit inefficient. But it was the clearest way I could think to do it code wise.
217
+ #It's bad programming form (imo) to have a class method require something that isn't passed to it through it's variables.
218
+ #I couldn't pass the id when calling find, since the original find doesn't work that way, so I was left with this.
219
+ end
220
+
221
+ def has_one(*args)
222
+
223
+ end
224
+
225
+ def self.handle_virtuals(attrs)
226
+ @@virtuals.each do |virtual|
227
+ #we first copy the information for the virtual to an instance variable of the same name
228
+ eval("@#{virtual}=attrs['#{virtual}']")
229
+ #and then remove the parameter before it is passed to initialize, so that it is NOT sent to SimpleDB
230
+ eval("attrs.delete('#{virtual}')")
231
+ end
232
+ end
233
+
234
+ # Holds information about an attribute
235
+ class Attribute
236
+ attr_accessor :type, :options
237
+
238
+ def initialize(type, options=nil)
239
+ @type = type
240
+ @options = options
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  module SimpleRecord::Callbacks
3
2
  #this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
4
3
  #this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
@@ -12,6 +11,66 @@ module SimpleRecord::Callbacks
12
11
  def self.included(base)
13
12
  #puts 'Callbacks included in ' + base.inspect
14
13
 
14
+ # puts "setup callbacks #{base.inspect}"
15
+ base.instance_eval <<-endofeval
16
+
17
+ def callbacks
18
+ @callbacks ||= {}
19
+ @callbacks
20
+ end
21
+
22
+
23
+ endofeval
24
+
25
+ @@callbacks.each do |callback|
26
+ base.class_eval <<-endofeval
27
+
28
+ def run_#{callback}
29
+ # puts 'CLASS CALLBACKS for ' + self.inspect + ' = ' + self.class.callbacks.inspect
30
+ return true if self.class.callbacks.nil?
31
+ cnames = self.class.callbacks['#{callback}']
32
+ cnames = [] if cnames.nil?
33
+ #cnames += super.class.callbacks['#{callback}'] unless super.class.callbacks.nil?
34
+ # puts 'cnames for #{callback} = ' + cnames.inspect
35
+ return true if cnames.nil?
36
+ cnames.each { |name|
37
+ #puts 'run_ #{name}'
38
+ if eval(name) == false # nil should be an ok return, only looking for false
39
+ return false
40
+ end
41
+ }
42
+ #super.run_#{callback}
43
+ return true
44
+ end
45
+
46
+ endofeval
47
+
48
+ #this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
49
+ #this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
50
+ #basically, this is how we recreate the callback functions
51
+ base.instance_eval <<-endofeval
52
+
53
+ # puts 'defining callback=' + callback + ' for ' + self.inspect
54
+ #we first have to make an initialized array for each of the callbacks, to prevent problems if they are not called
55
+
56
+ def #{callback}(*args)
57
+ # puts 'callback called in ' + self.inspect + ' with ' + args.inspect
58
+
59
+ #make_dirty(arg_s, value)
60
+ #self[arg.to_s]=value
61
+ #puts 'value in callback #{callback}=' + value.to_s
62
+ args.each do |arg|
63
+ cnames = callbacks['#{callback}']
64
+ #puts '\tcnames1=' + cnames.inspect + ' for class ' + self.inspect
65
+ cnames = [] if cnames.nil?
66
+ cnames << arg.to_s if cnames.index(arg.to_s).nil?
67
+ #puts '\tcnames2=' + cnames.inspect
68
+ callbacks['#{callback}'] = cnames
69
+ end
70
+ end
71
+
72
+ endofeval
73
+ end
15
74
  end
16
75
 
17
76
  def before_destroy()
@@ -19,4 +78,10 @@ module SimpleRecord::Callbacks
19
78
 
20
79
  def after_destroy()
21
80
  end
81
+
82
+
83
+ def self.setup_callbacks(base)
84
+
85
+ end
86
+
22
87
  end
@@ -0,0 +1,240 @@
1
+ # This module defines all the methods that perform data translations for storage and retrieval.
2
+ module SimpleRecord
3
+ module Translations
4
+
5
+ @@offset = 9223372036854775808
6
+ @@padding = 20
7
+ @@date_format = "%Y-%m-%dT%H:%M:%S"; # Time to second precision
8
+
9
+ def ruby_to_sdb(name, value)
10
+
11
+ return nil if value.nil?
12
+
13
+ name = name.to_s
14
+
15
+ puts "Converting #{name} to sdb value=#{value}"
16
+ puts "atts_local=" + defined_attributes_local.inspect
17
+
18
+ att_meta = defined_attributes_local[name.to_sym]
19
+
20
+ if att_meta.type == :int
21
+ ret = Translations.pad_and_offset(value)
22
+ elsif att_meta.type == :date
23
+ ret = Translations.pad_and_offset(value)
24
+ else
25
+ ret = value.to_s
26
+ end
27
+
28
+
29
+ if att_meta.options
30
+ if att_meta.options[:encrypted]
31
+ puts "ENCRYPTING #{name} value #{value}"
32
+ ret = Translations.encrypt(ret, att_meta.options[:encrypted])
33
+ puts 'encrypted value=' + ret.to_s
34
+ end
35
+ if att_meta.options[:hashed]
36
+ ret = Translations.pass_hash(ret)
37
+ end
38
+ end
39
+
40
+ return ret.to_s
41
+
42
+ end
43
+
44
+
45
+ # Convert value from SimpleDB String version to real ruby value.
46
+ def sdb_to_ruby(name, value)
47
+ puts 'sdb_to_ruby arg=' + name.inspect + ' - ' + name.class.name + ' - value=' + value.to_s
48
+ return nil if value.nil?
49
+ att_meta = defined_attributes_local[name.to_sym]
50
+
51
+ if att_meta.options
52
+ if att_meta.options[:encrypted]
53
+ value = Translations.decrypt(value, att_meta.options[:encrypted])
54
+ end
55
+ if att_meta.options[:hashed]
56
+ return PasswordHashed.new(value)
57
+ end
58
+ end
59
+
60
+ if att_meta.type == :int
61
+ value = Translations.un_offset_int(value)
62
+ elsif att_meta.type == :date
63
+ value = to_date(value)
64
+ elsif att_meta.type == :boolean
65
+ value = to_bool(value)
66
+ end
67
+ value
68
+ end
69
+
70
+
71
+ def self.pad_and_offset(x) # Change name to something more appropriate like ruby_to_sdb
72
+ # todo: add Float, etc
73
+ # puts 'padding=' + x.class.name + " -- " + x.inspect
74
+ if x.kind_of? Integer
75
+ x += @@offset
76
+ x_str = x.to_s
77
+ # pad
78
+ x_str = '0' + x_str while x_str.size < 20
79
+ return x_str
80
+ elsif x.respond_to?(:iso8601)
81
+ # puts x.class.name + ' responds to iso8601'
82
+ #
83
+ # There is an issue here where Time.iso8601 on an incomparable value to DateTime.iso8601.
84
+ # Amazon suggests: 2008-02-10T16:52:01.000-05:00
85
+ # "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
86
+ #
87
+ if x.is_a? DateTime
88
+ x_str = x.getutc.strftime(@@date_format)
89
+ elsif x.is_a? Time
90
+ x_str = x.getutc.strftime(@@date_format)
91
+ elsif x.is_a? Date
92
+ x_str = x.strftime(@@date_format)
93
+
94
+ end
95
+ return x_str
96
+ else
97
+ return x
98
+ end
99
+ end
100
+
101
+
102
+ def wrap_if_required(arg, value, sdb_val)
103
+ return nil if value.nil?
104
+
105
+ arg_s = arg.to_s
106
+ att_meta = defined_attributes_local[arg.to_sym]
107
+ if att_meta && att_meta.options
108
+ if att_meta.options[:hashed]
109
+ puts 'wrapping ' + arg_s
110
+ return PasswordHashed.new(sdb_val)
111
+ end
112
+ end
113
+ value
114
+ end
115
+
116
+ def to_date(x)
117
+ if x.is_a?(String)
118
+ DateTime.parse(x)
119
+ else
120
+ x
121
+ end
122
+ end
123
+
124
+ def to_bool(x)
125
+ if x.is_a?(String)
126
+ x == "true" || x == "1"
127
+ else
128
+ x
129
+ end
130
+ end
131
+
132
+ def self.un_offset_int(x)
133
+ if x.is_a?(String)
134
+ x2 = x.to_i
135
+ # puts 'to_i=' + x2.to_s
136
+ x2 -= @@offset
137
+ # puts 'after subtracting offset='+ x2.to_s
138
+ x2
139
+ else
140
+ x
141
+ end
142
+ end
143
+
144
+ def unpad(i, attributes)
145
+ if !attributes[i].nil?
146
+ # puts 'before=' + self[i].inspect
147
+ attributes[i].collect!{ |x|
148
+ un_offset_int(x)
149
+
150
+ }
151
+ end
152
+ end
153
+
154
+ def unpad_self
155
+ defined_attributes_local.each_pair do |name, att_meta|
156
+ if att_meta.type == :int
157
+ unpad(name, @attributes)
158
+ end
159
+ end
160
+ end
161
+
162
+
163
+ def self.encrypt(value, key=nil)
164
+ key = key || get_encryption_key()
165
+ raise SimpleRecordError, "Encryption key must be defined on the attribute." if key.nil?
166
+ encrypted_value = SimpleRecord::Encryptor.encrypt(:value => value, :key => key)
167
+ encoded_value = Base64.encode64(encrypted_value)
168
+ encoded_value
169
+ end
170
+
171
+
172
+ def self.decrypt(value, key=nil)
173
+ puts "decrypt orig value #{value} "
174
+ unencoded_value = Base64.decode64(value)
175
+ raise SimpleRecordError, "Encryption key must be defined on the attribute." if key.nil?
176
+ key = key || get_encryption_key()
177
+ puts "decrypting #{unencoded_value} "
178
+ decrypted_value = SimpleRecord::Encryptor.decrypt(:value => unencoded_value, :key => key)
179
+ puts "decrypted #{unencoded_value} to #{decrypted_value}"
180
+ decrypted_value
181
+ end
182
+
183
+
184
+ def pad_and_offset_ints_to_sdb()
185
+
186
+ # defined_attributes_local.each_pair do |name, att_meta|
187
+ # if att_meta.type == :int && !self[name.to_s].nil?
188
+ # arr = @attributes[name.to_s]
189
+ # arr.collect!{ |x| self.class.pad_and_offset(x) }
190
+ # @attributes[name.to_s] = arr
191
+ # end
192
+ # end
193
+ end
194
+
195
+ def convert_dates_to_sdb()
196
+
197
+ # defined_attributes_local.each_pair do |name, att_meta|
198
+ # puts 'int encoding: ' + i.to_s
199
+
200
+ # end
201
+ end
202
+
203
+ def self.pass_hash(value)
204
+ hashed = Password::create_hash(value)
205
+ encoded_value = Base64.encode64(hashed)
206
+ encoded_value
207
+ end
208
+
209
+ def self.pass_hash_check(value, value_to_compare)
210
+ unencoded_value = Base64.decode64(value)
211
+ return Password::check(value_to_compare, unencoded_value)
212
+ end
213
+
214
+ end
215
+
216
+
217
+ class PasswordHashed
218
+
219
+ def initialize(value)
220
+ @value = value
221
+ end
222
+
223
+ def hashed_value
224
+ @value
225
+ end
226
+
227
+ # This allows you to compare an unhashed string to the hashed one.
228
+ def ==(val)
229
+ if val.is_a?(PasswordHashed)
230
+ return val.hashed_value == self.hashed_value
231
+ end
232
+ return Translations.pass_hash_check(@value, val)
233
+ end
234
+
235
+ def to_s
236
+ @value
237
+ end
238
+ end
239
+
240
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.41
4
+ version: 1.1.42
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis Reeder
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-12-30 00:00:00 -08:00
14
+ date: 2010-01-01 00:00:00 -08:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -34,12 +34,14 @@ extra_rdoc_files:
34
34
  - README.markdown
35
35
  files:
36
36
  - lib/simple_record.rb
37
+ - lib/simple_record/attributes.rb
37
38
  - lib/simple_record/callbacks.rb
38
39
  - lib/simple_record/encryptor.rb
39
40
  - lib/simple_record/errors.rb
40
41
  - lib/simple_record/password.rb
41
42
  - lib/simple_record/results_array.rb
42
43
  - lib/simple_record/stats.rb
44
+ - lib/simple_record/translations.rb
43
45
  - README.markdown
44
46
  has_rdoc: true
45
47
  homepage: http://github.com/appoxy/simple_record/