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 +6 -0
- data/Manifest.txt +7 -0
- data/README.txt +49 -0
- data/Rakefile +12 -0
- data/bin/simple_record +3 -0
- data/lib/simple_record.rb +867 -0
- data/test/test_simple_record.rb +8 -0
- metadata +76 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
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,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
|
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
|