simple_record 1.1.41 → 1.1.42

Sign up to get free protection for your applications and to get access to all the features.
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/