simple_record 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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