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 +21 -532
- data/lib/simple_record/attributes.rb +246 -0
- data/lib/simple_record/callbacks.rb +66 -1
- data/lib/simple_record/translations.rb +240 -0
- metadata +4 -2
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
|
-
|
|
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
|
-
|
|
562
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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/
|