simple_record 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2009-04-23
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/simple_record
6
+ lib/simple_record.rb
7
+ test/test_simple_record.rb
data/README.txt ADDED
@@ -0,0 +1,49 @@
1
+ = simple_record
2
+
3
+ http://code.google.com/p/simple-record/
4
+
5
+ == DESCRIPTION:
6
+
7
+ An ActiveRecord interface for SimpleDB that takes care of offsets and padding, etc.
8
+ Can be used as a drop in replacement for ActiveRecord in rails.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * FIX (list of features or problems)
13
+
14
+ == SYNOPSIS:
15
+
16
+ FIX (code sample of usage)
17
+
18
+ == REQUIREMENTS:
19
+
20
+ * FIX (list of requirements)
21
+
22
+ == INSTALL:
23
+
24
+ sudo gem install activesupport right_aws local_cache
25
+
26
+ == LICENSE:
27
+
28
+ (The MIT License)
29
+
30
+ Copyright (c) 2009 FIX
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining
33
+ a copy of this software and associated documentation files (the
34
+ 'Software'), to deal in the Software without restriction, including
35
+ without limitation the rights to use, copy, modify, merge, publish,
36
+ distribute, sublicense, and/or sell copies of the Software, and to
37
+ permit persons to whom the Software is furnished to do so, subject to
38
+ the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be
41
+ included in all copies or substantial portions of the Software.
42
+
43
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
44
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
45
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
46
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
47
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
48
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
49
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/simple_record.rb'
6
+
7
+ Hoe.new('simple_record', SimpleRecord::VERSION) do |p|
8
+ p.rubyforge_name = 'spacegems' # if different than lowercase project name
9
+ p.developer('Travis Reeder', 'travis@crankapps.com')
10
+ end
11
+
12
+ # vim: syntax=Ruby
data/bin/simple_record ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,867 @@
1
+ require 'right_aws'
2
+ require 'sdb/active_sdb'
3
+ require 'local_cache'
4
+
5
+ module SimpleRecord
6
+
7
+ VERSION = '1.0.0'
8
+
9
+ class Base < RightAws::ActiveSdb::Base
10
+
11
+ attr_accessor :errors
12
+ @@domain_prefix = ''
13
+ @domain_name_for_class = nil
14
+
15
+ @@cache_store = nil
16
+ # Set the cache to use
17
+ def self.cache_store=(cache)
18
+ @@cache_store = cache
19
+ end
20
+ def self.cache_store
21
+ return @@cache_store
22
+ end
23
+
24
+ def self.set_domain_prefix(prefix)
25
+ @@domain_prefix = prefix
26
+ end
27
+
28
+ # Sets the table name.
29
+ # Passes it to set_domain_name in ActiveSDB.
30
+ def self.set_table_name(table_name)
31
+ set_domain_name table_name
32
+ end
33
+
34
+ def self.set_domain_name(table_name)
35
+ # puts 'setting domain name for class ' + self.inspect + '=' + table_name
36
+ @domain_name_for_class = table_name
37
+ super
38
+ end
39
+
40
+ def self.get_domain_name
41
+ # puts 'returning domain_name=' + @domain_name_for_class.to_s
42
+ return @domain_name_for_class
43
+ end
44
+
45
+
46
+ # @@domain_name_for_class = nil
47
+
48
+ def domain
49
+ super # super.domain
50
+ end
51
+
52
+ def self.domain
53
+ return self.get_domain_name unless self.get_domain_name.nil?
54
+ d = super
55
+ domain_name_for_class = @@domain_prefix + d.to_s
56
+ self.set_domain_name(domain_name_for_class)
57
+ domain_name_for_class
58
+ end
59
+
60
+ #this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
61
+ #this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
62
+ #basically, this is how we recreate the callback functions
63
+ @@callbacks=["before_save", "before_create", "after_create", "before_update", "after_update", "after_save", "after_destroy"]
64
+ @@callbacks.each do |callback|
65
+ #we first have to make an initialized array for each of the callbacks, to prevent problems if they are not called
66
+ eval %{
67
+ @@#{callback}_names=[]
68
+
69
+ def self.#{callback}(*args)
70
+ args.each do |arg|
71
+ @@#{callback}_names << arg.to_s if @@#{callback}_names.index(arg.to_s).nil?
72
+ end
73
+ # asdf @@#{callback}_names=args.map{|arg| arg.to_s}
74
+ end
75
+
76
+ def run_#{callback}
77
+ @@#{callback}_names.each { |name|
78
+ unless eval(name)
79
+ return false
80
+ end
81
+ }
82
+ return true
83
+ end
84
+ }
85
+ end
86
+
87
+ def self.has_attributes(*args)
88
+ @@attributes = args
89
+ args.each do |arg|
90
+ send :define_method, arg do
91
+ ret = nil
92
+ if self[arg.to_s].class==Array
93
+ if self[arg.to_s].length==1
94
+ ret = self[arg.to_s][0]
95
+ else
96
+ ret = self[arg.to_s]
97
+ end
98
+ else
99
+ ret = self[arg.to_s]
100
+ end
101
+ return un_offset_if_int(arg, ret)
102
+ end
103
+
104
+ method_name = (arg.to_s+"=")
105
+ send(:define_method, method_name) do |value|
106
+ self[arg.to_s]=value# end
107
+ end
108
+ end
109
+ end
110
+
111
+ @@ints = []
112
+ def self.are_ints(*args)
113
+ # puts 'calling are_ints: ' + args.inspect
114
+ args.each do |arg|
115
+ # todo: maybe @@ints and @@dates should be maps for quicker lookups
116
+ @@ints << arg if @@ints.index(arg).nil?
117
+ end
118
+ # @@ints = args
119
+ # puts 'ints=' + @@ints.inspect
120
+ end
121
+
122
+ @@dates = []
123
+ def self.are_dates(*args)
124
+ args.each do |arg|
125
+ @@dates << arg if @@dates.index(arg).nil?
126
+ end
127
+ # @@dates = args
128
+ # puts 'dates=' + @@dates.inspect
129
+ end
130
+
131
+ @@virtuals=[]
132
+ def self.has_virtuals(*args)
133
+ @@virtuals = args
134
+ args.each do |arg|
135
+ #we just create the accessor functions here, the actual instance variable is created during initialize
136
+ attr_accessor(arg)
137
+ end
138
+ end
139
+
140
+ @@belongs_to_map = {}
141
+ # One belongs_to association per call. Call multiple times if there are more than one.
142
+ def self.belongs_to(association_id, options = {})
143
+ @@belongs_to_map[association_id] = options
144
+ arg = association_id
145
+
146
+ # 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
147
+ # puts "arg_id=#{arg}_id"
148
+ # puts "is defined? " + eval("(defined? #{arg}_id)").to_s
149
+ # puts 'atts=' + @attributes.inspect
150
+ send(:define_method, arg) do
151
+ options2 = @@belongs_to_map[arg]
152
+ class_name = options2[:class_name] || arg.to_s[0...1].capitalize + arg.to_s[1...arg.to_s.length]
153
+ # return eval("#{arg.to_s.capitalize}.find(self['#{arg}_id']) if !self['#{arg}_id'].nil?") # (defined? #{arg}_id)
154
+ # return eval("#{arg.to_s.capitalize}.find(#{arg}_id) if(defined? #{arg}_id)")
155
+ # ORIGINAL return eval("#{arg.to_s.capitalize}.find(#{arg}_id)")
156
+
157
+ # puts "attr=" + @attributes[arg_id].inspect
158
+ # puts 'val=' + @attributes[arg_id][0].inspect unless @attributes[arg_id].nil?
159
+ ret = nil
160
+ arg_id = arg.to_s + '_id'
161
+ if !@attributes[arg_id].nil? && @attributes[arg_id].size > 0
162
+ if !@@cache_store.nil?
163
+ arg_id_val = @attributes[arg_id][0]
164
+ cache_key = self.class.cache_key(class_name, arg_id_val)
165
+ # puts 'cache_key=' + cache_key
166
+ ret = @@cache_store.read(cache_key)
167
+ # puts 'belongs_to incache=' + ret.inspect
168
+ end
169
+ if ret.nil?
170
+ to_eval = "#{class_name}.find(@attributes['#{arg_id}'][0], :auto_load=>true)"
171
+ # puts 'to eval=' + to_eval
172
+ begin
173
+ ret = eval(to_eval) # (defined? #{arg}_id)
174
+ rescue RightAws::ActiveSdb::ActiveSdbError
175
+ if $!.message.include? "Couldn't find"
176
+ ret = nil
177
+ else
178
+ raise $!
179
+ end
180
+ end
181
+
182
+ end
183
+ end
184
+ # puts 'ret=' + ret.inspect
185
+ return ret
186
+ end
187
+ send(:define_method, arg.to_s+"=") do |value|
188
+ arg_id = arg.to_s + '_id'
189
+ if value.nil?
190
+ 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
191
+ else
192
+ self[arg_id]=value.id
193
+ end
194
+ end
195
+ send(:define_method, "create_"+arg.to_s) do |*params|
196
+ newsubrecord=eval(arg.to_s.classify).new(*params)
197
+ newsubrecord.save
198
+ arg_id = arg.to_s + '_id'
199
+ self[arg_id]=newsubrecord.id
200
+ end
201
+ end
202
+
203
+ def self.belongs_to_OLD_and_wrong(*args)
204
+ #create the accesor functions
205
+ args.each do |arg|
206
+ # puts 'belongs_to ' + arg.to_s
207
+ send(:define_method, arg) do
208
+ # return eval("#{arg.to_s.capitalize}.find(self['#{arg}_id']) if !self['#{arg}_id'].nil?") # (defined? #{arg}_id)
209
+ # return eval("#{arg.to_s.capitalize}.find(#{arg}_id) if(defined? #{arg}_id)")
210
+ # ORIGINAL return eval("#{arg.to_s.capitalize}.find(#{arg}_id)")
211
+ puts "arg_id=#{arg}_id"
212
+ # puts "is defined? " + eval("(defined? #{arg}_id)").to_s
213
+ # puts 'atts=' + @attributes.inspect
214
+ puts "attr=" + @attributes[arg.to_s + '_id'].inspect
215
+ puts 'val=' + @attributes[arg.to_s + '_id'][0].inspect unless @attributes[arg.to_s + '_id'].nil?
216
+ to_eval = "#{arg.to_s[0...1].capitalize + arg.to_s[1...arg.to_s.length]}.find(@attributes['#{arg}_id'][0], :auto_load=>true) unless @attributes['#{arg}_id'].nil?"
217
+ puts 'to eval=' + to_eval
218
+ ret = eval(to_eval) # (defined? #{arg}_id)
219
+ puts 'ret=' + ret.inspect
220
+ ret
221
+ end
222
+ end
223
+
224
+ args.each do |arg|
225
+ send(:define_method, arg.to_s+"=") do |value|
226
+ self[arg.to_s+"_id"]=value.id
227
+ end
228
+ end
229
+
230
+ #create the build_subrecord and creat_subrecord methods
231
+ #args.each do |arg|
232
+ # send(:define_method, "build_"+arg.to_s) do |*params|
233
+ # self.arg.to_s=eval(arg.to_s.classify).new
234
+ # end
235
+ #end
236
+
237
+ args.each do |arg|
238
+ send(:define_method, "create_"+arg.to_s) do |*params|
239
+ newsubrecord=eval(arg.to_s.classify).new(*params)
240
+ newsubrecord.save
241
+ self[arg.to_s+"_id"]=newsubrecord.id
242
+ end
243
+ end
244
+ end #belongs_to
245
+
246
+ # def self.has_many(*args)
247
+ # args.each do |arg|
248
+ # send(:define_method, arg) do
249
+ # return eval(%{#{(arg.to_s).classify}.find(:all, :conditions => ["#{(self.class.name).tableize.singularize}_id = ?",id])})
250
+ # end
251
+ # end
252
+ # end
253
+ def self.has_many(*args)
254
+ args.each do |arg|
255
+ #okay, this creates an instance method with the pluralized name of the symbol passed to belongs_to
256
+ send(:define_method, arg) do
257
+ #when called, the method creates a new, very temporary instance of the Activerecordtosdb_subrecord class
258
+ #It is passed the three initializers it needs:
259
+ #note the first parameter is just a string by time new gets it, like "user"
260
+ #the second and third parameters are still a variable when new gets it, like user_id
261
+ return eval(%{Activerecordtosdb_subrecord_array.new('#{arg}', self.class.name ,id)})
262
+ end
263
+ end
264
+ #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.
265
+ #It's bad programming form (imo) to have a class method require something that isn't passed to it through it's variables.
266
+ #I couldn't pass the id when calling find, since the original find doesn't work that way, so I was left with this.
267
+ end
268
+
269
+ def self.has_one(*args)
270
+
271
+ end
272
+
273
+ has_attributes :created, :updated
274
+ before_create :set_created, :set_updated
275
+ before_update :set_updated
276
+ are_dates :created, :updated
277
+
278
+ def set_created
279
+ # puts 'SETTING CREATED'
280
+ # @created = DateTime.now
281
+ self[:created] = DateTime.now
282
+ # @tester = 'some test value'
283
+ # self[:tester] = 'some test value'
284
+ end
285
+
286
+ def set_updated
287
+ # puts 'SETTING UPDATED'
288
+ # @updated = DateTime.now
289
+ self[:updated] = DateTime.now
290
+ # @tester = 'some test value updated'
291
+ end
292
+
293
+ def initialize(*params)
294
+ if params[0]
295
+ #we have to handle the virtuals. Right now, this assumes that all parameters are passed from inside an array
296
+ #this is the usually the case when the parameters are passed passed via POST and obtained from the params array
297
+ @@virtuals.each do |virtual|
298
+ #we first copy the information for the virtual to an instance variable of the same name
299
+ eval("@#{virtual}=params[0]['#{virtual}']")
300
+ #and then remove the parameter before it is passed to initialize, so that it is NOT sent to SimpleDB
301
+ eval("params[0].delete('#{virtual}')")
302
+ end
303
+ super(*params)
304
+ else
305
+ super()
306
+ end
307
+ @errors=Activerecordtosdb_errors.new
308
+ end
309
+
310
+
311
+ @@offset = 9223372036854775808
312
+ @@padding = 20
313
+
314
+ def self.pad_and_offset(x)
315
+ # todo: add Float, etc
316
+ if x.kind_of? Integer
317
+ x += @@offset
318
+ x_str = x.to_s
319
+ # pad
320
+ x_str = '0' + x_str while x_str.size < 20
321
+ return x_str
322
+ # elsif x.kind_of? Date
323
+ # Does RigthAWS convert dates already?
324
+ else
325
+ return x
326
+ end
327
+ end
328
+
329
+ def self.unpad_and_offset(x)
330
+ # todo: add Float, etc
331
+ if x.kind_of? Integer
332
+ x += @@offset
333
+ x_str = x.to_s
334
+ # pad
335
+ x_str = '0' + x_str while x_str.size < 20
336
+ return x_str
337
+ else
338
+ return x
339
+ end
340
+ end
341
+
342
+ def domain_ok(ex)
343
+ if (ex.message().index("NoSuchDomain") != nil)
344
+ self.class.create_domain
345
+ return true
346
+ end
347
+ return false
348
+ end
349
+
350
+ @create_domain_called = false
351
+
352
+ def save(*params)
353
+ # puts 'SAVING: ' + self.inspect
354
+
355
+ if respond_to?('validate')
356
+ validate
357
+ # puts 'AFTER VALIDATIONS, ERRORS=' + errors.inspect
358
+ if (!@errors.nil? && @errors.length > 0 )
359
+ # puts 'THERE ARE ERRORS, returning false'
360
+ return false
361
+ end
362
+ end
363
+
364
+ is_create = self[:id].nil?
365
+ ok = respond_to?('before_save') ? before_save : true
366
+ if ok
367
+ if is_create && respond_to?('before_create')
368
+ ok = before_create
369
+ elsif !is_create && respond_to?('before_update')
370
+ ok = before_update
371
+ end
372
+ end
373
+ if ok && run_before_save && is_create ? run_before_create : run_before_update
374
+ # puts 'ABOUT TO SAVE: ' + self.inspect
375
+ # First we gotta pad and offset
376
+ if !@@ints.nil?
377
+ for i in @@ints
378
+ # puts 'int encoding: ' + i.to_s
379
+ if !self[i.to_s].nil?
380
+ # puts 'before: ' + self[i.to_s].inspect
381
+ # puts @attributes.inspect
382
+ # puts @attributes[i.to_s].inspect
383
+ arr = @attributes[i.to_s]
384
+ arr.collect!{ |x|
385
+ self.class.pad_and_offset(x)
386
+ }
387
+ @attributes[i.to_s] = arr
388
+ # puts 'after: ' + @attributes[i.to_s].inspect
389
+ else
390
+ # puts 'was nil'
391
+ end
392
+ end
393
+ end
394
+ begin
395
+ # puts 'is frozen? ' + self.frozen?.to_s + ' - ' + self.inspect
396
+ to_delete = get_atts_to_delete
397
+ if super(*params)
398
+ # puts 'SAVED super'
399
+ self.class.cache_results(self)
400
+ delete_niled(to_delete)
401
+ if run_after_save && is_create ? run_after_create : run_after_update
402
+ return true
403
+ else
404
+ #I thought about calling destroy here, but rails doesn't behave that way, so neither will I
405
+ return false
406
+ end
407
+ else
408
+ return false
409
+ end
410
+ rescue RightAws::AwsError
411
+ puts "RESCUED in save: " + $!
412
+ if (domain_ok($!))
413
+ if !@create_domain_called
414
+ @create_domain_called = true
415
+ save(*params)
416
+ else
417
+ raise $!
418
+ end
419
+ else
420
+ raise $!
421
+ end
422
+ end
423
+ else
424
+ #@debug = "not saved"
425
+ return false
426
+ end
427
+ end
428
+
429
+ def save_attributes(*params)
430
+ ret = super(*params)
431
+ if ret
432
+ self.class.cache_results(self)
433
+ end
434
+ ret
435
+ end
436
+
437
+ def get_atts_to_delete
438
+ to_delete = []
439
+ @attributes.each do |key, value|
440
+ # puts 'value=' + value.inspect
441
+ if value.nil? || (value.is_a?(Array) && value.size == 0)
442
+ to_delete << key
443
+ end
444
+ end
445
+ return to_delete
446
+ end
447
+
448
+ #
449
+ # Usage: ClassName.delete id
450
+ # todo: move to RightAWS
451
+ #
452
+ def self.delete(id)
453
+ connection.delete_attributes(domain, id)
454
+ end
455
+
456
+ def delete_niled(to_delete)
457
+ if to_delete.size > 0
458
+ # puts 'Deleting attributes=' + to_delete.inspect
459
+ delete_attributes to_delete
460
+ end
461
+ end
462
+
463
+ def un_offset_if_int(arg, x)
464
+ if !@@ints.nil?
465
+ for i in @@ints
466
+ # puts 'unpadding: ' + i.to_s
467
+ # unpad and unoffset
468
+ if i == arg
469
+ # puts 'unoffsetting ' + x.to_s
470
+ x = un_offset_int(x)
471
+ end
472
+ end
473
+ end
474
+ if !@@dates.nil?
475
+ for d in @@dates
476
+ # puts 'converting created: ' + self['created'].inspect
477
+ if d == arg
478
+ x = DateTime.parse(d2)
479
+ end
480
+ # if !self[d].nil?
481
+ # self[d].collect!{ |d2|
482
+ # if d2.is_a?(String)
483
+ # DateTime.parse(d2)
484
+ # else
485
+ # d2
486
+ # end
487
+ # }
488
+ # end
489
+ # puts 'after=' + self['created'].inspect
490
+ end
491
+ end
492
+ x
493
+ end
494
+
495
+ def un_offset_int(x)
496
+ if x.is_a?(String)
497
+ x2 = x.to_i
498
+ # puts 'to_i=' + x2.to_s
499
+ x2 -= @@offset
500
+ # puts 'after subtracting offset='+ x2.to_s
501
+ x2
502
+ else
503
+ x
504
+ end
505
+ end
506
+
507
+ def unpad(i, attributes)
508
+ if !attributes[i].nil?
509
+ # puts 'before=' + self[i].inspect
510
+ attributes[i].collect!{ |x|
511
+ un_offset_int(x)
512
+
513
+ }
514
+ # for x in self[i]
515
+ # x = self[i][0].to_i
516
+ # x -= @@offset
517
+ # self[i] = x
518
+ # end
519
+ end
520
+ end
521
+
522
+ def unpad_self
523
+ if !@@ints.nil?
524
+ for i in @@ints
525
+ # puts 'unpadding: ' + i.to_s
526
+ # unpad and unoffset
527
+
528
+ unpad(i, @attributes)
529
+ end
530
+ end
531
+ end
532
+
533
+ def reload
534
+ super()
535
+ # puts 'decoding...'
536
+
537
+ =begin
538
+ This is done on getters now
539
+ if !@@dates.nil?
540
+ for d in @@dates
541
+ # puts 'converting created: ' + self['created'].inspect
542
+ if !self[d].nil?
543
+ self[d].collect!{ |d2|
544
+ if d2.is_a?(String)
545
+ DateTime.parse(d2)
546
+ else
547
+ d2
548
+ end
549
+ }
550
+ end
551
+ # puts 'after=' + self['created'].inspect
552
+ end
553
+ end
554
+ =end
555
+
556
+ # unpad_self
557
+ end
558
+
559
+ def update_attributes(*params)
560
+ return save_attributes(*params)
561
+ end
562
+
563
+ def destroy(*params)
564
+ if super(*params)
565
+ if run_after_destroy
566
+ return true
567
+ else
568
+ return false
569
+ end
570
+ else
571
+ return false
572
+ end
573
+ end
574
+
575
+ def self.quote_regexp(a, re)
576
+ a =~ re
577
+ #was there a match?
578
+ if $&
579
+ before=$`
580
+ middle=$&
581
+ after=$'
582
+
583
+ before =~ /'$/ #is there already a quote immediately before the match?
584
+ unless $&
585
+ return "#{before}'#{middle}'#{quote_regexp(after, re)}" #if not, put quotes around the match
586
+ else
587
+ return "#{before}#{middle}#{quote_regexp(after, re)}" #if so, assume it is quoted already and move on
588
+ end
589
+ else
590
+ #no match, just return the string
591
+ return a
592
+ end
593
+ end
594
+
595
+ def self.find(*params)
596
+ reload=true
597
+ all=false
598
+ select=false
599
+ select_attributes=[]
600
+
601
+ # puts 'FIND=' + params.inspect
602
+ # Pad and Offset number attributes
603
+ options = params[1]
604
+ # puts 'options=' + options.inspect
605
+ if !options.nil? && options.size > 0
606
+ conditions = options[:conditions]
607
+ if !conditions.nil? && conditions.size > 1
608
+ # all after first are values
609
+ conditions[1...conditions.size] = conditions[1...conditions.size].collect { |x|
610
+ self.pad_and_offset(x)
611
+ }
612
+ end
613
+ end
614
+ # puts 'after collect=' + params.inspect
615
+ =begin
616
+
617
+
618
+ params.each_with_index do |param, index|
619
+ #this is the easiest way to determine if it will be necessary to reload several rows or just one
620
+ #note that :all (or :first) is always params[0], and everything else is in a Hash in params[1]
621
+ if param==:all
622
+ all=true
623
+ end
624
+
625
+ if param.class==Hash
626
+ param.each do |key, value|
627
+ case key
628
+ when :reload;
629
+ reload=value #new option I'm adding so you can prevent reloading when it isn't necessary
630
+ when :conditions #conditions have to be reformatted to work with ActiveSDB
631
+ if value #value will sometimes be nil
632
+ query=sanitize_sql(value)#sanitize the conditions into an SQL query
633
+ query=query.gsub(/\w+\./, '') #this removes tables from the query, just a temporary measure, we need to inspect the table and alter query
634
+ #query=quote_regexp(query,/[a-z0-9._-]+/)#ActiveSDB.find needs quotes around the attributes and values
635
+ query=quote_regexp(query, /[^\s=']+/)
636
+ #next we put brackets around the entire query, and turn it into an array
637
+ #note, that these are 2 seperate things, and are both necessary for the function to work with ActiveSDB find
638
+ params[index][:conditions]= ["[#{query}]"]
639
+ end
640
+ #params[index][:conditions][0]= "[#{params[index][:conditions][0]}]"
641
+ #we then find any attributes in the query and put single quotes around them
642
+ #@@attributes.each do |attribute|
643
+ # params[index][:conditions][0]=params[index][:conditions][0].gsub(attribute.to_s,"'#{attribute.to_s}'")
644
+ #end
645
+
646
+ when :select #next we implement select functionality
647
+ if value #value will sometimes be nil
648
+ select=true
649
+ #if the selected attributes are in array for, we can assume we are good to go
650
+ if value.class==Array
651
+ select_attributes=value
652
+ else
653
+ #however if they are passed as comma seperated values in a string, we have to split it into an array.
654
+ #we remove all whitespace from the selected string, then split on comma
655
+ select_attributes=value.gsub(' ', '').split(',')
656
+ end
657
+ end
658
+ end
659
+ end
660
+ end
661
+ end
662
+
663
+ results = "scooby"
664
+ begin
665
+ results=super(*params)
666
+ if results
667
+ if reload
668
+ if select
669
+ if all
670
+ results.each do |row|
671
+ row.reload_attributes(select_attributes)
672
+ end
673
+ else
674
+ results.reload_attributes(select_attributes)
675
+ end
676
+ else
677
+ if all
678
+ results.each do |row|
679
+ row.reload
680
+ end
681
+ else
682
+ results.reload
683
+ end
684
+ end
685
+ end
686
+ end
687
+ rescue RightAws::AwsError
688
+ puts "RESCUED: " + $!
689
+ if ($!.is_a?(Hash))
690
+ puts "it's a hash..."
691
+ end
692
+ if ($!.message().index("NoSuchDomain") == nil)
693
+ raise $!
694
+ end
695
+ end
696
+ return results
697
+
698
+ =end
699
+ results = []
700
+ begin
701
+ results=super(*params)
702
+ cache_results(results)
703
+ rescue RightAws::AwsError
704
+ puts "RESCUED: " + $!
705
+ if ($!.message().index("NoSuchDomain") == nil)
706
+ raise $!
707
+ end
708
+ end
709
+ return results
710
+ end
711
+
712
+ def select(*params)
713
+ results = []
714
+ begin
715
+ results=super(*params)
716
+ cache_results(results)
717
+ rescue RightAws::AwsError
718
+ puts "RESCUED: " + $!
719
+ if ($!.message().index("NoSuchDomain") == nil)
720
+ raise $!
721
+ end
722
+ end
723
+ return results
724
+
725
+ end
726
+
727
+ def self.cache_results(results)
728
+ if !@@cache_store.nil? && !results.nil?
729
+ if results.is_a?(Array)
730
+ # todo: cache each result
731
+ else
732
+ class_name = results.class.name
733
+ id = results.id
734
+ cache_key = self.cache_key(class_name, id)
735
+ # puts 'caching result at ' + cache_key + ': ' + results.inspect
736
+ @@cache_store.write(cache_key, results, :expires_in =>10*60)
737
+ end
738
+ end
739
+ end
740
+
741
+ def self.cache_key(class_name, id)
742
+ return class_name + "/" + id.to_s
743
+ end
744
+
745
+
746
+
747
+ @@debug=""
748
+ def self.debug
749
+ @@debug
750
+ end
751
+
752
+ def self.sanitize_sql(*params)
753
+ return ActiveRecord::Base.sanitize_sql(*params)
754
+ end
755
+
756
+ def self.table_name
757
+ return @@domain_prefix + self.class.name.tableize
758
+ end
759
+
760
+ end
761
+
762
+ class Activerecordtosdb_errors
763
+ def initialize(*params)
764
+ super(*params)
765
+ @errors=[]
766
+ end
767
+
768
+ def add_to_base(value)
769
+ @errors+=[value]
770
+ end
771
+
772
+ def add(attribute, value)
773
+ @errors+=["#{attribute.to_s} #{value}"]
774
+ end
775
+
776
+ def count
777
+ return @errors.length
778
+ end
779
+
780
+ def length
781
+ return @errors.length
782
+ end
783
+
784
+ def size
785
+ return length
786
+ end
787
+
788
+ def full_messages
789
+ return @errors
790
+ end
791
+ end
792
+
793
+ class Activerecordtosdb_subrecord_array
794
+ def initialize(subname, referencename, referencevalue)
795
+ @subname=subname.classify
796
+ @referencename=referencename.tableize.singularize + "_id"
797
+ @referencevalue=referencevalue
798
+ end
799
+
800
+ # Performance optimization if you know the array should be empty
801
+
802
+ def init_empty
803
+ @records = []
804
+ end
805
+
806
+ def load
807
+ if @records.nil?
808
+ @records = find_all
809
+ end
810
+ return @records
811
+ end
812
+
813
+ def [](key)
814
+ return load[key]
815
+ end
816
+
817
+ def <<(ob)
818
+ return load << ob
819
+ end
820
+
821
+ def each(*params, &block)
822
+ return load.each(*params){|record| block.call(record)}
823
+ end
824
+
825
+ def find_all(*params)
826
+ find(:all, *params)
827
+ end
828
+
829
+ def build(*params)
830
+ params[0][@referencename]=@referencevalue
831
+ eval(@subname).new(*params)
832
+ end
833
+
834
+ def create(*params)
835
+ params[0][@referencename]=@referencevalue
836
+ record = eval(@subname).new(*params)
837
+ record.save
838
+ end
839
+
840
+ def find(*params)
841
+ query=[:first, {}]
842
+ #{:conditions=>"id=>1"}
843
+ if params[0]
844
+ if params[0]==:all
845
+ query[0]=:all
846
+ end
847
+ end
848
+
849
+ if params[1]
850
+ query[1]=params[1]
851
+ if query[1][:conditions]
852
+ query[1][:conditions]=SimpleRecord::Base.sanitize_sql(query[1][:conditions])+" AND "+ SimpleRecord::Base.sanitize_sql(["#{@referencename} = ?", @referencevalue])
853
+ #query[1][:conditions]=Activerecordtosdb.sanitize_sql(query[1][:conditions])+" AND id='#{@id}'"
854
+ else
855
+ query[1][:conditions]=["#{@referencename} = ?", @referencevalue]
856
+ #query[1][:conditions]="id='#{@id}'"
857
+ end
858
+ else
859
+ query[1][:conditions]=["#{@referencename} = ?", @referencevalue]
860
+ #query[1][:conditions]="id='#{@id}'"
861
+ end
862
+
863
+ return eval(@subname).find(*query)
864
+ end
865
+
866
+ end
867
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "simple_record"
3
+
4
+ class TestSimpleRecord < Test::Unit::TestCase
5
+ def test_sanity
6
+ flunk "write tests or I will kneecap you"
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Travis Reeder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-24 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.12.2
24
+ version:
25
+ description: |-
26
+ An ActiveRecord interface for SimpleDB that takes care of offsets and padding, etc.
27
+ Can be used as a drop in replacement for ActiveRecord in rails.
28
+ email:
29
+ - travis@crankapps.com
30
+ executables:
31
+ - simple_record
32
+ extensions: []
33
+
34
+ extra_rdoc_files:
35
+ - History.txt
36
+ - Manifest.txt
37
+ - README.txt
38
+ files:
39
+ - History.txt
40
+ - Manifest.txt
41
+ - README.txt
42
+ - Rakefile
43
+ - bin/simple_record
44
+ - lib/simple_record.rb
45
+ - test/test_simple_record.rb
46
+ has_rdoc: true
47
+ homepage: http://code.google.com/p/simple-record/
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --main
53
+ - README.txt
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project: spacegems
71
+ rubygems_version: 1.3.2
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: An ActiveRecord interface for SimpleDB that takes care of offsets and padding, etc
75
+ test_files:
76
+ - test/test_simple_record.rb