simple_record 1.0.10 → 1.1.17
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/README.markdown +200 -0
- data/lib/results_array.rb +137 -0
- data/lib/simple_record.rb +498 -263
- data/lib/stats.rb +16 -0
- data/test/my_child_model.rb +34 -0
- data/test/my_model.rb +47 -0
- data/test/paging_array_test.rb +15 -0
- data/test/temp_test.rb +63 -0
- data/test/test_simple_record.rb +227 -43
- metadata +24 -39
- data/History.txt +0 -6
- data/Manifest.txt +0 -7
- data/README.txt +0 -52
- data/Rakefile +0 -12
- data/bin/simple_record +0 -3
data/README.markdown
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
# SimpleRecord - ActiveRecord for SimpleDB
|
2
|
+
|
3
|
+
An ActiveRecord interface for SimpleDB. Can be used as a drop in replacement for ActiveRecord in rails.
|
4
|
+
|
5
|
+
## Discussion Group
|
6
|
+
|
7
|
+
[http://groups.google.com/group/simple-record]
|
8
|
+
|
9
|
+
## Issue Tracking
|
10
|
+
|
11
|
+
[http://appoxy.lighthouseapp.com/projects/38366-simplerecord/overview]
|
12
|
+
|
13
|
+
## Getting Started
|
14
|
+
|
15
|
+
1. Install gems
|
16
|
+
|
17
|
+
ONE TIME: Be sure to add the new gemcutter source:
|
18
|
+
|
19
|
+
- gem install gemcutter
|
20
|
+
- gem tumble
|
21
|
+
|
22
|
+
THEN (if you already have http://gemcutter.org in your sources and it MUST be above rubyforge.org):
|
23
|
+
|
24
|
+
gem install aws uuidtools http_connection simple_record
|
25
|
+
|
26
|
+
2. Create a model
|
27
|
+
|
28
|
+
require 'simple_record'
|
29
|
+
|
30
|
+
class MyModel < SimpleRecord::Base
|
31
|
+
has_attributes :name
|
32
|
+
has_ints :age
|
33
|
+
end
|
34
|
+
|
35
|
+
More about ModelAttributes below.
|
36
|
+
|
37
|
+
3. Setup environment
|
38
|
+
|
39
|
+
AWS_ACCESS_KEY_ID='XXXX'
|
40
|
+
AWS_SECRET_ACCESS_KEY='YYYY'
|
41
|
+
SimpleRecord.establish_connection(AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY)
|
42
|
+
|
43
|
+
4. Go to town
|
44
|
+
|
45
|
+
# Store a model object to SimpleDB
|
46
|
+
mm = MyModel.new
|
47
|
+
mm.name = "Travis"
|
48
|
+
mm.age = 32
|
49
|
+
mm.save
|
50
|
+
id = mm.id
|
51
|
+
|
52
|
+
# Get an object from SimpleDB
|
53
|
+
mm2 = MyModel.find(id)
|
54
|
+
puts 'got=' + mm2.name + ' and he/she is ' + mm.age.to_s + ' years old'
|
55
|
+
# Or more advanced queries? mms = MyModel?.find(:all, ["age=?", 32], :order=>"name", :limit=>10)
|
56
|
+
|
57
|
+
|
58
|
+
## Attributes and modifiers for models
|
59
|
+
|
60
|
+
NOTE: All objects will automatically have :id, :created, :updated attributes.
|
61
|
+
|
62
|
+
### has_attributes
|
63
|
+
|
64
|
+
Add string attributes.
|
65
|
+
|
66
|
+
class MyModel < SimpleRecord::Base
|
67
|
+
has_attributes :name
|
68
|
+
end
|
69
|
+
|
70
|
+
### has_ints, has_dates and has_booleans
|
71
|
+
|
72
|
+
Lets simple_record know that certain attributes defined in has_attributes should be treated as integers, dates or booleans. This is required because SimpleDB only has strings so SimpleRecord needs to know how to convert, pad, offset, etc.
|
73
|
+
|
74
|
+
class MyModel < SimpleRecord::Base
|
75
|
+
has_attributes :name
|
76
|
+
has_ints :age, :height
|
77
|
+
has_dates :birthday
|
78
|
+
has_booleans :is_nerd
|
79
|
+
end
|
80
|
+
|
81
|
+
### belongs_to
|
82
|
+
|
83
|
+
Creates a many-to-one relationship. Can only have one per belongs_to call.
|
84
|
+
|
85
|
+
class MyModel < SimpleRecord::Base
|
86
|
+
belongs_to :school
|
87
|
+
has_attributes :name
|
88
|
+
has_ints :age, :height
|
89
|
+
has_dates :birthday
|
90
|
+
has_booleans :is_nerd
|
91
|
+
end
|
92
|
+
|
93
|
+
Which requires another class called 'School' or you can specify the class explicitly with:
|
94
|
+
|
95
|
+
belongs_to :school, :class_name => "Institution"
|
96
|
+
|
97
|
+
### set_table_name or set_domain_name
|
98
|
+
|
99
|
+
If you want to use a custom domain for a model object, you can specify it with set_table_name (or set_domain_name).
|
100
|
+
|
101
|
+
class SomeModel < SimpleRecord::Base
|
102
|
+
set_table_name :different_model
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
## Configuration
|
107
|
+
|
108
|
+
### Domain Prefix
|
109
|
+
|
110
|
+
To set a global prefix across all your models, use:
|
111
|
+
|
112
|
+
SimpleRecord::Base.set_domain_prefix("myprefix_")
|
113
|
+
|
114
|
+
### Connection Modes
|
115
|
+
|
116
|
+
There are 4 different connection modes:
|
117
|
+
|
118
|
+
* per_request (default) - opens and closes a new connection to simpledb for every simpledb request. Not the best performance, but it's safe and can handle many concurrent requests at the same time (unlike single mode).
|
119
|
+
* single - one connection across the entire application, not recommended unless the app is used by a single person.
|
120
|
+
* per_thread - a connection is used for every thread in the application. This is good, but the catch is that you have to ensure to close the connection.
|
121
|
+
* pool - NOT IMPLEMENTED YET - opens a maximum number of connections and round robins them for any simpledb request.
|
122
|
+
|
123
|
+
You set the mode when you call establish_connection:
|
124
|
+
|
125
|
+
SimpleRecord.establish_connection(AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, :connection_mode=>:per_thread)
|
126
|
+
|
127
|
+
We recommend per_thread with explicitly closing the connection after each Rails request (not to be mistaken for a SimpleDB request) or pool for rails apps.
|
128
|
+
|
129
|
+
For rails, be sure to add this to your Application controller if using per_thread mode:
|
130
|
+
|
131
|
+
after_filter :close_sdb_connection
|
132
|
+
|
133
|
+
def close_sdb_connection
|
134
|
+
SimpleRecord.close_connection
|
135
|
+
end
|
136
|
+
|
137
|
+
## SimpleRecord on Rails
|
138
|
+
|
139
|
+
You don't really have to do anything except have your models extends SimpleRecord::Base instead of ActiveRecord::Base, but here are some tips you can use.
|
140
|
+
|
141
|
+
### Change Connection Mode
|
142
|
+
|
143
|
+
Use per_thread connection mode and close the connection after each request.
|
144
|
+
|
145
|
+
after_filter :close_sdb_connection
|
146
|
+
|
147
|
+
def close_sdb_connection
|
148
|
+
SimpleRecord.close_connection
|
149
|
+
end
|
150
|
+
|
151
|
+
### Disable ActiveRecord so you don't have to setup another database
|
152
|
+
|
153
|
+
This is most helpful on windows so Rails doesn't need sqlite or mysql gems/drivers installed which are painful to install on windows. In environment.rb, add 'config.frameworks -= [ :active_record ]', should look something like:
|
154
|
+
|
155
|
+
Rails::Initializer.run do |config|
|
156
|
+
config.frameworks -= [ :active_record ]
|
157
|
+
....
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
## Tips and Tricks and Things to Know
|
162
|
+
|
163
|
+
### Automagic Stuff
|
164
|
+
|
165
|
+
|
166
|
+
#### Automatic common fields
|
167
|
+
|
168
|
+
Every object will automatically get the following attributes so you don't need to define them:
|
169
|
+
|
170
|
+
* id - UUID string
|
171
|
+
* created - set when first save
|
172
|
+
* updated - set every time you save/update
|
173
|
+
|
174
|
+
|
175
|
+
#### belongs_to foreign keys/IDs are accessible without touching the database
|
176
|
+
|
177
|
+
If you had the following in your model:
|
178
|
+
|
179
|
+
belongs_to :something
|
180
|
+
|
181
|
+
Then in addition to being able to access the something object with:
|
182
|
+
|
183
|
+
o.something
|
184
|
+
|
185
|
+
or setting it with:
|
186
|
+
|
187
|
+
o.something = someo
|
188
|
+
|
189
|
+
You can also access the ID for something directly with:
|
190
|
+
|
191
|
+
o.something_id
|
192
|
+
|
193
|
+
or
|
194
|
+
|
195
|
+
o.something_id = x
|
196
|
+
|
197
|
+
## Kudos
|
198
|
+
|
199
|
+
Special thanks to Garrett Cox for creating Activerecord2sdb which SimpleRecord is based on:
|
200
|
+
http://activrecord2sdb.rubyforge.org/
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module SimpleRecord
|
2
|
+
|
3
|
+
#
|
4
|
+
# We need to make this behave as if the full set were loaded into the array.
|
5
|
+
class ResultsArray
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :next_token, :clz, :params, :items, :i
|
9
|
+
|
10
|
+
|
11
|
+
def initialize(clz=nil, params=[], items=[], next_token=nil)
|
12
|
+
@clz = clz
|
13
|
+
#puts 'class=' + clz.inspect
|
14
|
+
@params = params
|
15
|
+
if @params.size <= 1
|
16
|
+
options = {}
|
17
|
+
@params[1] = options
|
18
|
+
end
|
19
|
+
@items = items
|
20
|
+
@currentset_items = items
|
21
|
+
@next_token = next_token
|
22
|
+
@i = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def << (val)
|
26
|
+
@items << val
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](*i)
|
30
|
+
puts 'i.inspect=' + i.inspect
|
31
|
+
puts i.size.to_s
|
32
|
+
i.each do |x|
|
33
|
+
puts 'x=' + x.inspect + " -- " + x.class.name
|
34
|
+
end
|
35
|
+
if i.size == 1
|
36
|
+
# either fixnum or range
|
37
|
+
x = i[0]
|
38
|
+
if x.is_a?(Fixnum)
|
39
|
+
load_to(x)
|
40
|
+
else
|
41
|
+
# range
|
42
|
+
end_val = x.exclude_end? ? x.end-1 : x.end
|
43
|
+
load_to(end_val)
|
44
|
+
end
|
45
|
+
elsif i.size == 2
|
46
|
+
# two fixnums
|
47
|
+
end_val = i[0] + i[1]
|
48
|
+
load_to(end_val)
|
49
|
+
end
|
50
|
+
@items[*i]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Will load items from SimpleDB up to i.
|
54
|
+
def load_to(i)
|
55
|
+
return if @items.size >= i
|
56
|
+
while @items.size < i && !@next_token.nil?
|
57
|
+
load_next_token_set
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def first
|
62
|
+
@items[0]
|
63
|
+
end
|
64
|
+
|
65
|
+
def last
|
66
|
+
@items[@items.length-1]
|
67
|
+
end
|
68
|
+
|
69
|
+
def empty?
|
70
|
+
@items.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
def include?(obj)
|
74
|
+
@items.include?(obj)
|
75
|
+
end
|
76
|
+
|
77
|
+
def size
|
78
|
+
# puts 'SIZE count=' + @count.inspect
|
79
|
+
return @count if @count
|
80
|
+
params_for_count = params.dup
|
81
|
+
params_for_count[0] = :count
|
82
|
+
#puts 'params_for_count=' + params_for_count.inspect
|
83
|
+
@count = clz.find(*params_for_count)
|
84
|
+
# puts '@count=' + @count.to_s
|
85
|
+
@count
|
86
|
+
end
|
87
|
+
|
88
|
+
def length
|
89
|
+
return size
|
90
|
+
end
|
91
|
+
|
92
|
+
def each(&blk)
|
93
|
+
options = @params[1]
|
94
|
+
limit = options[:limit]
|
95
|
+
|
96
|
+
@currentset_items.each do |v|
|
97
|
+
# puts @i.to_s
|
98
|
+
yield v
|
99
|
+
@i += 1
|
100
|
+
if !limit.nil? && @i >= limit
|
101
|
+
return
|
102
|
+
end
|
103
|
+
end
|
104
|
+
return if @clz.nil?
|
105
|
+
|
106
|
+
# no more items, but is there a next token?
|
107
|
+
unless @next_token.nil?
|
108
|
+
#puts 'finding more items...'
|
109
|
+
#puts 'params in block=' + params.inspect
|
110
|
+
#puts "i from results_array = " + @i.to_s
|
111
|
+
|
112
|
+
load_next_token_set
|
113
|
+
each(&blk)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def load_next_token_set
|
118
|
+
options = @params[1]
|
119
|
+
options[:next_token] = @next_token
|
120
|
+
res = @clz.find(*@params)
|
121
|
+
@currentset_items = res.items # get the real items array from the ResultsArray
|
122
|
+
@currentset_items.each do |item|
|
123
|
+
@items << item
|
124
|
+
end
|
125
|
+
@next_token = res.next_token
|
126
|
+
end
|
127
|
+
|
128
|
+
def delete(item)
|
129
|
+
@items.delete(item)
|
130
|
+
end
|
131
|
+
|
132
|
+
def delete_at(index)
|
133
|
+
@items.delete_at(index)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
data/lib/simple_record.rb
CHANGED
@@ -22,32 +22,194 @@
|
|
22
22
|
# puts 'got=' + mm2.name + ' and he/she is ' + mm.age.to_s + ' years old'
|
23
23
|
|
24
24
|
|
25
|
-
|
26
25
|
require 'right_aws'
|
27
26
|
require 'sdb/active_sdb'
|
27
|
+
#require 'results_array' # why the heck isn't this picking up???
|
28
|
+
require File.expand_path(File.dirname(__FILE__) + "/results_array")
|
29
|
+
require File.expand_path(File.dirname(__FILE__) + "/stats")
|
28
30
|
|
29
31
|
module SimpleRecord
|
30
32
|
|
31
|
-
|
33
|
+
@@stats = SimpleRecord::Stats.new
|
34
|
+
|
35
|
+
def self.stats
|
36
|
+
@@stats
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a new handle to an Sdb account. All handles share the same per process or per thread
|
40
|
+
# HTTP connection to Amazon Sdb. Each handle is for a specific account.
|
41
|
+
# The +params+ are passed through as-is to RightAws::SdbInterface.new
|
42
|
+
# Params:
|
43
|
+
# { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
|
44
|
+
# :port => 443 # Amazon service port: 80(default) or 443
|
45
|
+
# :protocol => 'https' # Amazon service protocol: 'http'(default) or 'https'
|
46
|
+
# :signature_version => '0' # The signature version : '0' or '1'(default)
|
47
|
+
# :connection_mode => :default # options are
|
48
|
+
# :default (will use best known safe (as in won't need explicit close) option, may change in the future)
|
49
|
+
# :per_request (opens and closes a connection on every request to SDB)
|
50
|
+
# :single (one thread across entire app)
|
51
|
+
# :per_thread (one connection per thread)
|
52
|
+
# :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
|
53
|
+
# :logger => Logger Object # Logger instance: logs to STDOUT if omitted
|
54
|
+
def self.establish_connection(aws_access_key=nil, aws_secret_key=nil, params={})
|
55
|
+
RightAws::ActiveSdb.establish_connection(aws_access_key, aws_secret_key, params)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.close_connection()
|
59
|
+
RightAws::ActiveSdb.close_connection
|
60
|
+
end
|
61
|
+
|
62
|
+
module Callbacks
|
63
|
+
#this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
|
64
|
+
#this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
|
65
|
+
#basically, this is how we recreate the callback functions
|
66
|
+
@@callbacks=["before_validation", "before_validation_on_create", "before_validation_on_update",
|
67
|
+
"after_validation", "after_validation_on_create", "after_validation_on_update",
|
68
|
+
"before_save", "before_create", "before_update",
|
69
|
+
"after_create", "after_update", "after_save",
|
70
|
+
"after_destroy"]
|
71
|
+
|
72
|
+
def self.included(base)
|
73
|
+
#puts 'Callbacks included in ' + base.inspect
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
32
77
|
|
33
78
|
class Base < RightAws::ActiveSdb::Base
|
34
79
|
|
80
|
+
include SimpleRecord::Callbacks
|
81
|
+
|
82
|
+
|
83
|
+
def initialize(attrs={})
|
84
|
+
super
|
85
|
+
# 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
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# todo: move into Callbacks module
|
90
|
+
#this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
|
91
|
+
#this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
|
92
|
+
#basically, this is how we recreate the callback functions
|
93
|
+
@@callbacks.each do |callback|
|
94
|
+
instance_eval <<-endofeval
|
95
|
+
|
96
|
+
#puts 'doing callback=' + callback + ' for ' + self.inspect
|
97
|
+
#we first have to make an initialized array for each of the callbacks, to prevent problems if they are not called
|
98
|
+
|
99
|
+
def #{callback}(*args)
|
100
|
+
#puts 'callback called in ' + self.inspect + ' with ' + args.inspect
|
101
|
+
|
102
|
+
#make_dirty(arg_s, value)
|
103
|
+
#self[arg.to_s]=value
|
104
|
+
#puts 'value in callback #{callback}=' + value.to_s
|
105
|
+
args.each do |arg|
|
106
|
+
cnames = callbacks['#{callback}']
|
107
|
+
#puts '\tcnames1=' + cnames.inspect + ' for class ' + self.inspect
|
108
|
+
cnames = [] if cnames.nil?
|
109
|
+
cnames << arg.to_s if cnames.index(arg.to_s).nil?
|
110
|
+
#puts '\tcnames2=' + cnames.inspect
|
111
|
+
callbacks['#{callback}'] = cnames
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
endofeval
|
116
|
+
end
|
117
|
+
#puts 'base methods=' + self.methods.inspect
|
118
|
+
|
119
|
+
|
120
|
+
def self.inherited(base)
|
121
|
+
#puts 'SimpleRecord::Base is inherited by ' + base.inspect
|
122
|
+
setup_callbacks(base)
|
123
|
+
|
124
|
+
base.has_dates :created, :updated
|
125
|
+
base.before_create :set_created, :set_updated
|
126
|
+
base.before_update :set_updated
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.setup_callbacks(base)
|
131
|
+
instance_eval <<-endofeval
|
132
|
+
|
133
|
+
def callbacks
|
134
|
+
@callbacks ||= {}
|
135
|
+
@callbacks
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.defined_attributes
|
139
|
+
#puts 'class defined_attributes'
|
140
|
+
@attributes ||= {}
|
141
|
+
@attributes
|
142
|
+
end
|
143
|
+
|
144
|
+
endofeval
|
145
|
+
|
146
|
+
@@callbacks.each do |callback|
|
147
|
+
class_eval <<-endofeval
|
148
|
+
|
149
|
+
def run_#{callback}
|
150
|
+
# puts 'CLASS CALLBACKS for ' + self.inspect + ' = ' + self.class.callbacks.inspect
|
151
|
+
return true if self.class.callbacks.nil?
|
152
|
+
cnames = self.class.callbacks['#{callback}']
|
153
|
+
cnames = [] if cnames.nil?
|
154
|
+
#cnames += super.class.callbacks['#{callback}'] unless super.class.callbacks.nil?
|
155
|
+
# puts 'cnames for #{callback} = ' + cnames.inspect
|
156
|
+
return true if cnames.nil?
|
157
|
+
cnames.each { |name|
|
158
|
+
#puts 'run_ #{name}'
|
159
|
+
if eval(name) == false # nil should be an ok return, only looking for false
|
160
|
+
return false
|
161
|
+
end
|
162
|
+
}
|
163
|
+
#super.run_#{callback}
|
164
|
+
return true
|
165
|
+
end
|
166
|
+
|
167
|
+
endofeval
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
# Holds information about an attribute
|
173
|
+
class Attribute
|
174
|
+
attr_accessor :type, :options
|
175
|
+
|
176
|
+
def initialize(type)
|
177
|
+
@type = type
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def defined_attributes_local
|
184
|
+
#puts 'local defined_attributes'
|
185
|
+
ret = self.class.defined_attributes
|
186
|
+
ret.merge!(self.class.superclass.defined_attributes) if self.class.superclass.respond_to?(:defined_attributes)
|
187
|
+
end
|
188
|
+
|
189
|
+
|
35
190
|
attr_accessor :errors
|
36
|
-
|
37
|
-
@
|
191
|
+
|
192
|
+
@domain_prefix = ''
|
193
|
+
class << self;
|
194
|
+
attr_accessor :domain_prefix;
|
195
|
+
end
|
196
|
+
|
197
|
+
#@domain_name_for_class = nil
|
38
198
|
|
39
199
|
@@cache_store = nil
|
40
200
|
# Set the cache to use
|
41
201
|
def self.cache_store=(cache)
|
42
202
|
@@cache_store = cache
|
43
203
|
end
|
204
|
+
|
44
205
|
def self.cache_store
|
45
206
|
return @@cache_store
|
46
207
|
end
|
47
208
|
|
48
209
|
# If you want a domain prefix for all your models, set it here.
|
49
210
|
def self.set_domain_prefix(prefix)
|
50
|
-
|
211
|
+
#puts 'set_domain_prefix=' + prefix
|
212
|
+
self.domain_prefix = prefix
|
51
213
|
end
|
52
214
|
|
53
215
|
# Same as set_table_name
|
@@ -58,110 +220,135 @@ module SimpleRecord
|
|
58
220
|
# Sets the domain name for this class
|
59
221
|
def self.set_domain_name(table_name)
|
60
222
|
# puts 'setting domain name for class ' + self.inspect + '=' + table_name
|
61
|
-
|
223
|
+
#@domain_name_for_class = table_name
|
62
224
|
super
|
63
225
|
end
|
64
226
|
|
65
|
-
|
227
|
+
=begin
|
228
|
+
def self.get_domain_name
|
66
229
|
# puts 'returning domain_name=' + @domain_name_for_class.to_s
|
67
|
-
return @domain_name_for_class
|
230
|
+
#return @domain_name_for_class
|
231
|
+
return self.domain
|
68
232
|
end
|
69
233
|
|
234
|
+
=end
|
70
235
|
|
71
236
|
def domain
|
72
237
|
super # super.domain
|
73
238
|
end
|
74
239
|
|
75
240
|
def self.domain
|
76
|
-
return self.get_domain_name unless self.get_domain_name.nil?
|
241
|
+
#return self.get_domain_name unless self.get_domain_name.nil?
|
77
242
|
d = super
|
78
|
-
|
79
|
-
|
243
|
+
#puts 'in self.domain, d=' + d.to_s + ' domain_prefix=' + SimpleRecord::Base.domain_prefix.to_s
|
244
|
+
domain_name_for_class = SimpleRecord::Base.domain_prefix + d.to_s
|
245
|
+
#self.set_domain_name(domain_name_for_class)
|
80
246
|
domain_name_for_class
|
81
247
|
end
|
82
248
|
|
83
|
-
#this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
|
84
|
-
#this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
|
85
|
-
#basically, this is how we recreate the callback functions
|
86
|
-
@@callbacks=["before_save", "before_create", "after_create", "before_update", "after_update", "after_save", "after_destroy"]
|
87
|
-
@@callbacks.each do |callback|
|
88
|
-
#we first have to make an initialized array for each of the callbacks, to prevent problems if they are not called
|
89
|
-
eval %{
|
90
|
-
@@#{callback}_names=[]
|
91
249
|
|
92
|
-
|
93
|
-
|
94
|
-
|
250
|
+
# Since SimpleDB supports multiple attributes per value, the values are an array.
|
251
|
+
# This method will return the value unwrapped if it's the only, otherwise it will return the array.
|
252
|
+
def get_attribute(arg)
|
253
|
+
arg = arg.to_s
|
254
|
+
if self[arg].class==Array
|
255
|
+
if self[arg].length==1
|
256
|
+
ret = self[arg][0]
|
257
|
+
else
|
258
|
+
ret = self[arg]
|
259
|
+
end
|
260
|
+
else
|
261
|
+
ret = self[arg]
|
262
|
+
end
|
263
|
+
ret
|
95
264
|
end
|
96
|
-
# asdf @@#{callback}_names=args.map{|arg| arg.to_s}
|
97
|
-
end
|
98
265
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
}
|
105
|
-
return true
|
106
|
-
end
|
107
|
-
}
|
266
|
+
def make_dirty(arg, value)
|
267
|
+
# todo: only set dirty if it changed
|
268
|
+
#puts 'making dirty arg=' + arg.to_s + ' --- ' + @dirty.inspect
|
269
|
+
@dirty[arg] = get_attribute(arg) # Store old value (not sure if we need it?)
|
270
|
+
#puts 'end making dirty ' + @dirty.inspect
|
108
271
|
end
|
109
272
|
|
110
273
|
def self.has_attributes(*args)
|
111
|
-
@@attributes = args
|
112
274
|
args.each do |arg|
|
275
|
+
defined_attributes[arg] = SimpleRecord::Base::Attribute.new(:string) if defined_attributes[arg].nil?
|
113
276
|
# define reader method
|
114
|
-
|
277
|
+
arg_s = arg.to_s # to get rid of all the to_s calls
|
278
|
+
send(:define_method, arg) do
|
115
279
|
ret = nil
|
116
|
-
|
117
|
-
if self[arg.to_s].length==1
|
118
|
-
ret = self[arg.to_s][0]
|
119
|
-
else
|
120
|
-
ret = self[arg.to_s]
|
121
|
-
end
|
122
|
-
else
|
123
|
-
ret = self[arg.to_s]
|
124
|
-
end
|
280
|
+
ret = get_attribute(arg)
|
125
281
|
return nil if ret.nil?
|
126
282
|
return un_offset_if_int(arg, ret)
|
127
283
|
end
|
128
284
|
|
129
285
|
# define writer method
|
130
|
-
|
131
|
-
|
132
|
-
self[
|
286
|
+
send(:define_method, arg_s+"=") do |value|
|
287
|
+
make_dirty(arg_s, value)
|
288
|
+
self[arg_s]=value
|
289
|
+
end
|
290
|
+
|
291
|
+
# Now for dirty methods: http://api.rubyonrails.org/classes/ActiveRecord/Dirty.html
|
292
|
+
# define changed? method
|
293
|
+
send(:define_method, arg_s + "_changed?") do
|
294
|
+
@dirty.has_key?(arg_s)
|
295
|
+
end
|
296
|
+
|
297
|
+
# define change method
|
298
|
+
send(:define_method, arg_s + "_change") do
|
299
|
+
old_val = @dirty[arg_s]
|
300
|
+
return nil if old_val.nil?
|
301
|
+
[old_val, get_attribute(arg_s)]
|
302
|
+
end
|
303
|
+
|
304
|
+
# define was method
|
305
|
+
send(:define_method, arg_s + "_was") do
|
306
|
+
old_val = @dirty[arg_s]
|
307
|
+
old_val
|
133
308
|
end
|
134
309
|
end
|
135
310
|
end
|
136
311
|
|
137
|
-
|
312
|
+
def self.has_strings(*args)
|
313
|
+
has_attributes(*args)
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.has_ints(*args)
|
317
|
+
has_attributes(*args)
|
318
|
+
are_ints(*args)
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.has_dates(*args)
|
322
|
+
has_attributes(*args)
|
323
|
+
are_dates(*args)
|
324
|
+
end
|
325
|
+
|
326
|
+
def self.has_booleans(*args)
|
327
|
+
has_attributes(*args)
|
328
|
+
are_booleans(*args)
|
329
|
+
end
|
330
|
+
|
138
331
|
def self.are_ints(*args)
|
139
332
|
# puts 'calling are_ints: ' + args.inspect
|
140
333
|
args.each do |arg|
|
141
|
-
|
142
|
-
@@ints << arg if @@ints.index(arg).nil?
|
334
|
+
defined_attributes[arg].type = :int
|
143
335
|
end
|
144
|
-
# @@ints = args
|
145
|
-
# puts 'ints=' + @@ints.inspect
|
146
336
|
end
|
147
337
|
|
148
|
-
@@dates = []
|
149
338
|
def self.are_dates(*args)
|
150
339
|
args.each do |arg|
|
151
|
-
|
340
|
+
defined_attributes[arg].type = :date
|
152
341
|
end
|
153
|
-
# @@dates = args
|
154
|
-
# puts 'dates=' + @@dates.inspect
|
155
342
|
end
|
156
343
|
|
157
|
-
@@booleans = []
|
158
344
|
def self.are_booleans(*args)
|
159
345
|
args.each do |arg|
|
160
|
-
|
346
|
+
defined_attributes[arg].type = :boolean
|
161
347
|
end
|
162
348
|
end
|
163
349
|
|
164
350
|
@@virtuals=[]
|
351
|
+
|
165
352
|
def self.has_virtuals(*args)
|
166
353
|
@@virtuals = args
|
167
354
|
args.each do |arg|
|
@@ -170,14 +357,17 @@ module SimpleRecord
|
|
170
357
|
end
|
171
358
|
end
|
172
359
|
|
173
|
-
@@belongs_to_map = {}
|
174
360
|
# One belongs_to association per call. Call multiple times if there are more than one.
|
175
361
|
#
|
176
362
|
# This method will also create an {association)_id method that will return the ID of the foreign object
|
177
363
|
# without actually materializing it.
|
178
364
|
def self.belongs_to(association_id, options = {})
|
179
|
-
|
365
|
+
attribute = SimpleRecord::Base::Attribute.new(:belongs_to)
|
366
|
+
defined_attributes[association_id] = attribute
|
367
|
+
attribute.options = options
|
368
|
+
#@@belongs_to_map[association_id] = options
|
180
369
|
arg = association_id
|
370
|
+
arg_s = arg.to_s
|
181
371
|
arg_id = arg.to_s + '_id'
|
182
372
|
|
183
373
|
# 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
|
@@ -187,8 +377,13 @@ module SimpleRecord
|
|
187
377
|
|
188
378
|
# Define reader method
|
189
379
|
send(:define_method, arg) do
|
190
|
-
|
380
|
+
attribute = defined_attributes_local[arg]
|
381
|
+
options2 = attribute.options # @@belongs_to_map[arg]
|
191
382
|
class_name = options2[:class_name] || arg.to_s[0...1].capitalize + arg.to_s[1...arg.to_s.length]
|
383
|
+
|
384
|
+
# Camelize classnames with underscores (ie my_model.rb --> MyModel)
|
385
|
+
class_name = class_name.camelize
|
386
|
+
|
192
387
|
# puts "attr=" + @attributes[arg_id].inspect
|
193
388
|
# puts 'val=' + @attributes[arg_id][0].inspect unless @attributes[arg_id].nil?
|
194
389
|
ret = nil
|
@@ -202,7 +397,7 @@ module SimpleRecord
|
|
202
397
|
# puts 'belongs_to incache=' + ret.inspect
|
203
398
|
end
|
204
399
|
if ret.nil?
|
205
|
-
to_eval = "#{class_name}.find(@attributes['#{arg_id}'][0]
|
400
|
+
to_eval = "#{class_name}.find(@attributes['#{arg_id}'][0])"
|
206
401
|
# puts 'to eval=' + to_eval
|
207
402
|
begin
|
208
403
|
ret = eval(to_eval) # (defined? #{arg}_id)
|
@@ -220,7 +415,21 @@ module SimpleRecord
|
|
220
415
|
return ret
|
221
416
|
end
|
222
417
|
|
223
|
-
|
418
|
+
|
419
|
+
# Define writer method
|
420
|
+
send(:define_method, arg.to_s + "=") do |value|
|
421
|
+
arg_id = arg.to_s + '_id'
|
422
|
+
if value.nil?
|
423
|
+
make_dirty(arg_id, nil)
|
424
|
+
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
|
425
|
+
else
|
426
|
+
make_dirty(arg_id, value.id)
|
427
|
+
self[arg_id]=value.id
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
|
432
|
+
# Define ID reader method for reading the associated objects id without getting the entire object
|
224
433
|
send(:define_method, arg_id) do
|
225
434
|
if !@attributes[arg_id].nil? && @attributes[arg_id].size > 0 && @attributes[arg_id][0] != nil && @attributes[arg_id][0] != ''
|
226
435
|
return @attributes[arg_id][0]
|
@@ -228,13 +437,12 @@ module SimpleRecord
|
|
228
437
|
return nil
|
229
438
|
end
|
230
439
|
|
231
|
-
# Define writer method
|
232
|
-
send(:define_method,
|
233
|
-
arg_id = arg.to_s + '_id'
|
440
|
+
# Define writer method for setting the _id directly without the associated object
|
441
|
+
send(:define_method, arg_id + "=") do |value|
|
234
442
|
if value.nil?
|
235
|
-
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
|
443
|
+
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
|
236
444
|
else
|
237
|
-
self[arg_id]=value
|
445
|
+
self[arg_id] = value
|
238
446
|
end
|
239
447
|
end
|
240
448
|
|
@@ -266,27 +474,8 @@ module SimpleRecord
|
|
266
474
|
|
267
475
|
end
|
268
476
|
|
269
|
-
has_attributes :created, :updated
|
270
|
-
before_create :set_created, :set_updated
|
271
|
-
before_update :set_updated
|
272
|
-
are_dates :created, :updated
|
273
|
-
|
274
|
-
def set_created
|
275
|
-
# puts 'SETTING CREATED'
|
276
|
-
# @created = DateTime.now
|
277
|
-
self[:created] = DateTime.now
|
278
|
-
# @tester = 'some test value'
|
279
|
-
# self[:tester] = 'some test value'
|
280
|
-
end
|
281
|
-
|
282
|
-
def set_updated
|
283
|
-
# puts 'SETTING UPDATED'
|
284
|
-
# @updated = DateTime.now
|
285
|
-
self[:updated] = DateTime.now
|
286
|
-
# @tester = 'some test value updated'
|
287
|
-
end
|
288
|
-
|
289
477
|
def initialize(*params)
|
478
|
+
|
290
479
|
if params[0]
|
291
480
|
#we have to handle the virtuals. Right now, this assumes that all parameters are passed from inside an array
|
292
481
|
#this is the usually the case when the parameters are passed passed via POST and obtained from the params array
|
@@ -301,6 +490,34 @@ module SimpleRecord
|
|
301
490
|
super()
|
302
491
|
end
|
303
492
|
@errors=SimpleRecord_errors.new
|
493
|
+
@dirty = {}
|
494
|
+
|
495
|
+
|
496
|
+
end
|
497
|
+
|
498
|
+
def clear_errors
|
499
|
+
@errors=SimpleRecord_errors.new
|
500
|
+
end
|
501
|
+
|
502
|
+
def []=(attribute, values)
|
503
|
+
make_dirty(attribute, values)
|
504
|
+
super
|
505
|
+
end
|
506
|
+
|
507
|
+
|
508
|
+
def set_created
|
509
|
+
# puts 'SETTING CREATED'
|
510
|
+
# @created = DateTime.now
|
511
|
+
self[:created] = DateTime.now
|
512
|
+
# @tester = 'some test value'
|
513
|
+
# self[:tester] = 'some test value'
|
514
|
+
end
|
515
|
+
|
516
|
+
def set_updated
|
517
|
+
# puts 'SETTING UPDATED'
|
518
|
+
# @updated = DateTime.now
|
519
|
+
self[:updated] = DateTime.now
|
520
|
+
# @tester = 'some test value updated'
|
304
521
|
end
|
305
522
|
|
306
523
|
|
@@ -310,7 +527,7 @@ module SimpleRecord
|
|
310
527
|
|
311
528
|
def self.pad_and_offset(x)
|
312
529
|
# todo: add Float, etc
|
313
|
-
|
530
|
+
# puts 'padding=' + x.class.name + " -- " + x.inspect
|
314
531
|
if x.kind_of? Integer
|
315
532
|
x += @@offset
|
316
533
|
x_str = x.to_s
|
@@ -318,7 +535,7 @@ module SimpleRecord
|
|
318
535
|
x_str = '0' + x_str while x_str.size < 20
|
319
536
|
return x_str
|
320
537
|
elsif x.respond_to?(:iso8601)
|
321
|
-
|
538
|
+
# puts x.class.name + ' responds to iso8601'
|
322
539
|
#
|
323
540
|
# There is an issue here where Time.iso8601 on an incomparable value to DateTime.iso8601.
|
324
541
|
# Amazon suggests: 2008-02-10T16:52:01.000-05:00
|
@@ -329,7 +546,7 @@ module SimpleRecord
|
|
329
546
|
else
|
330
547
|
x_str = x.getutc.strftime(@@date_format)
|
331
548
|
end
|
332
|
-
|
549
|
+
# puts 'utc=' + x_str
|
333
550
|
return x_str
|
334
551
|
else
|
335
552
|
return x
|
@@ -345,24 +562,71 @@ module SimpleRecord
|
|
345
562
|
return false
|
346
563
|
end
|
347
564
|
|
565
|
+
def valid?
|
566
|
+
errors.clear
|
567
|
+
|
568
|
+
# run_callbacks(:validate)
|
569
|
+
validate
|
570
|
+
|
571
|
+
if new_record?
|
572
|
+
# run_callbacks(:validate_on_create)
|
573
|
+
validate_on_create
|
574
|
+
else
|
575
|
+
# run_callbacks(:validate_on_update)
|
576
|
+
validate_on_update
|
577
|
+
end
|
578
|
+
|
579
|
+
errors.empty?
|
580
|
+
end
|
581
|
+
|
582
|
+
def new_record?
|
583
|
+
# todo: new_record in activesdb should align with how we're defining a new record here, ie: if id is nil
|
584
|
+
super
|
585
|
+
end
|
586
|
+
|
587
|
+
def invalid?
|
588
|
+
!valid?
|
589
|
+
end
|
590
|
+
|
591
|
+
def validate
|
592
|
+
true
|
593
|
+
end
|
594
|
+
|
595
|
+
def validate_on_create
|
596
|
+
true
|
597
|
+
end
|
598
|
+
|
599
|
+
def validate_on_update
|
600
|
+
true
|
601
|
+
end
|
602
|
+
|
348
603
|
@create_domain_called = false
|
349
604
|
|
350
|
-
|
605
|
+
# Options:
|
606
|
+
# - :except => Array of attributes to NOT save
|
607
|
+
# - :dirty => true - Will only store attributes that were modified
|
608
|
+
#
|
609
|
+
def save(options={})
|
351
610
|
# puts 'SAVING: ' + self.inspect
|
611
|
+
clear_errors
|
352
612
|
is_create = self[:id].nil?
|
353
|
-
ok = pre_save(
|
613
|
+
ok = pre_save(options)
|
354
614
|
if ok
|
355
615
|
begin
|
356
616
|
# puts 'is frozen? ' + self.frozen?.to_s + ' - ' + self.inspect
|
357
|
-
|
358
|
-
|
617
|
+
if options[:dirty] # Only used in simple_record right now
|
618
|
+
options[:dirty_atts] = @dirty
|
619
|
+
end
|
620
|
+
to_delete = get_atts_to_delete # todo: this should use the @dirty hash now
|
621
|
+
# puts 'saving'
|
622
|
+
if super(options)
|
359
623
|
# puts 'SAVED super'
|
360
624
|
self.class.cache_results(self)
|
361
625
|
delete_niled(to_delete)
|
362
|
-
if
|
626
|
+
if (is_create ? run_after_create : run_after_update) && run_after_save
|
627
|
+
# puts 'all good?'
|
363
628
|
return true
|
364
629
|
else
|
365
|
-
#I thought about calling destroy here, but rails doesn't behave that way, so neither will I
|
366
630
|
return false
|
367
631
|
end
|
368
632
|
else
|
@@ -373,7 +637,7 @@ module SimpleRecord
|
|
373
637
|
if (domain_ok($!))
|
374
638
|
if !@create_domain_called
|
375
639
|
@create_domain_called = true
|
376
|
-
save(
|
640
|
+
save(options)
|
377
641
|
else
|
378
642
|
raise $!
|
379
643
|
end
|
@@ -388,54 +652,57 @@ module SimpleRecord
|
|
388
652
|
end
|
389
653
|
|
390
654
|
def pad_and_offset_ints_to_sdb()
|
391
|
-
|
392
|
-
|
655
|
+
|
656
|
+
defined_attributes_local.each_pair do |name, att_meta|
|
393
657
|
# puts 'int encoding: ' + i.to_s
|
394
|
-
|
658
|
+
if att_meta.type == :int && !self[name.to_s].nil?
|
395
659
|
# puts 'before: ' + self[i.to_s].inspect
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
660
|
+
# puts @attributes.inspect
|
661
|
+
# puts @attributes[i.to_s].inspect
|
662
|
+
arr = @attributes[name.to_s]
|
663
|
+
arr.collect!{ |x| self.class.pad_and_offset(x) }
|
664
|
+
@attributes[name.to_s] = arr
|
401
665
|
# puts 'after: ' + @attributes[i.to_s].inspect
|
402
|
-
else
|
403
|
-
# puts 'was nil'
|
404
|
-
end
|
405
666
|
end
|
406
667
|
end
|
407
668
|
end
|
408
|
-
|
409
|
-
|
410
|
-
|
669
|
+
|
670
|
+
def convert_dates_to_sdb()
|
671
|
+
|
672
|
+
defined_attributes_local.each_pair do |name, att_meta|
|
411
673
|
# puts 'int encoding: ' + i.to_s
|
412
|
-
|
674
|
+
if att_meta.type == :date && !self[name.to_s].nil?
|
413
675
|
# puts 'before: ' + self[i.to_s].inspect
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
676
|
+
# puts @attributes.inspect
|
677
|
+
# puts @attributes[i.to_s].inspect
|
678
|
+
arr = @attributes[name.to_s]
|
679
|
+
#puts 'padding date=' + i.to_s
|
680
|
+
arr.collect!{ |x| self.class.pad_and_offset(x) }
|
681
|
+
@attributes[name.to_s] = arr
|
420
682
|
# puts 'after: ' + @attributes[i.to_s].inspect
|
421
|
-
|
422
|
-
|
423
|
-
end
|
683
|
+
else
|
684
|
+
# puts 'was nil'
|
424
685
|
end
|
425
686
|
end
|
426
687
|
end
|
427
688
|
|
428
|
-
def pre_save(
|
429
|
-
|
430
|
-
|
689
|
+
def pre_save(options)
|
690
|
+
|
691
|
+
is_create = self[:id].nil?
|
692
|
+
ok = run_before_validation && (is_create ? run_before_validation_on_create : run_before_validation_on_update)
|
693
|
+
return false unless ok
|
694
|
+
|
695
|
+
validate
|
696
|
+
is_create ? validate_on_create : validate_on_update
|
431
697
|
# puts 'AFTER VALIDATIONS, ERRORS=' + errors.inspect
|
432
|
-
|
698
|
+
if (!@errors.nil? && @errors.length > 0 )
|
433
699
|
# puts 'THERE ARE ERRORS, returning false'
|
434
|
-
|
435
|
-
end
|
700
|
+
return false
|
436
701
|
end
|
437
702
|
|
438
|
-
|
703
|
+
ok = run_after_validation && (is_create ? run_after_validation_on_create : run_after_validation_on_update)
|
704
|
+
return false unless ok
|
705
|
+
|
439
706
|
ok = respond_to?('before_save') ? before_save : true
|
440
707
|
if ok
|
441
708
|
if is_create && respond_to?('before_create')
|
@@ -445,7 +712,7 @@ module SimpleRecord
|
|
445
712
|
end
|
446
713
|
end
|
447
714
|
if ok
|
448
|
-
ok = run_before_save && is_create ? run_before_create : run_before_update
|
715
|
+
ok = run_before_save && (is_create ? run_before_create : run_before_update)
|
449
716
|
end
|
450
717
|
if ok
|
451
718
|
# puts 'ABOUT TO SAVE: ' + self.inspect
|
@@ -465,6 +732,7 @@ module SimpleRecord
|
|
465
732
|
end
|
466
733
|
|
467
734
|
def get_atts_to_delete
|
735
|
+
# todo: this should use the @dirty hash now
|
468
736
|
to_delete = []
|
469
737
|
@attributes.each do |key, value|
|
470
738
|
# puts 'value=' + value.inspect
|
@@ -477,20 +745,24 @@ module SimpleRecord
|
|
477
745
|
|
478
746
|
# Run pre_save on each object, then runs batch_put_attributes
|
479
747
|
# Returns
|
480
|
-
def self.batch_save(objects)
|
748
|
+
def self.batch_save(objects, options={})
|
481
749
|
results = []
|
482
750
|
to_save = []
|
483
751
|
if objects && objects.size > 0
|
484
752
|
objects.each do |o|
|
485
|
-
ok = o.pre_save
|
753
|
+
ok = o.pre_save(options)
|
486
754
|
raise "Pre save failed on object [" + o.inspect + "]" if !ok
|
487
755
|
results << ok
|
488
|
-
next if !ok
|
756
|
+
next if !ok # todo: this shouldn't be here should it? raises above
|
489
757
|
o.pre_save2
|
490
758
|
to_save << RightAws::SdbInterface::Item.new(o.id, o.attributes, true)
|
759
|
+
if to_save.size == 25 # Max amount SDB will accept
|
760
|
+
connection.batch_put_attributes(domain, to_save)
|
761
|
+
to_save.clear
|
762
|
+
end
|
491
763
|
end
|
492
764
|
end
|
493
|
-
connection.batch_put_attributes(domain, to_save)
|
765
|
+
connection.batch_put_attributes(domain, to_save) if to_save.size > 0
|
494
766
|
results
|
495
767
|
end
|
496
768
|
|
@@ -510,51 +782,14 @@ module SimpleRecord
|
|
510
782
|
end
|
511
783
|
|
512
784
|
def un_offset_if_int(arg, x)
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
end
|
522
|
-
end
|
523
|
-
if !@@dates.nil?
|
524
|
-
for d in @@dates
|
525
|
-
# puts 'converting created: ' + self['created'].inspect
|
526
|
-
if d == arg
|
527
|
-
x = to_date(x)
|
528
|
-
end
|
529
|
-
# if !self[d].nil?
|
530
|
-
# self[d].collect!{ |d2|
|
531
|
-
# if d2.is_a?(String)
|
532
|
-
# DateTime.parse(d2)
|
533
|
-
# else
|
534
|
-
# d2
|
535
|
-
# end
|
536
|
-
# }
|
537
|
-
# end
|
538
|
-
# puts 'after=' + self['created'].inspect
|
539
|
-
end
|
540
|
-
end
|
541
|
-
if !@@booleans.nil?
|
542
|
-
for b in @@booleans
|
543
|
-
# puts 'converting created: ' + self['created'].inspect
|
544
|
-
if b == arg
|
545
|
-
x = to_bool(x)
|
546
|
-
end
|
547
|
-
# if !self[d].nil?
|
548
|
-
# self[d].collect!{ |d2|
|
549
|
-
# if d2.is_a?(String)
|
550
|
-
# DateTime.parse(d2)
|
551
|
-
# else
|
552
|
-
# d2
|
553
|
-
# end
|
554
|
-
# }
|
555
|
-
# end
|
556
|
-
# puts 'after=' + self['created'].inspect
|
557
|
-
end
|
785
|
+
att_meta = defined_attributes_local[arg]
|
786
|
+
# puts 'int encoding: ' + i.to_s
|
787
|
+
if att_meta.type == :int
|
788
|
+
x = un_offset_int(x)
|
789
|
+
elsif att_meta.type == :date
|
790
|
+
x = to_date(x)
|
791
|
+
elsif att_meta.type == :boolean
|
792
|
+
x = to_bool(x)
|
558
793
|
end
|
559
794
|
x
|
560
795
|
end
|
@@ -606,40 +841,15 @@ module SimpleRecord
|
|
606
841
|
end
|
607
842
|
|
608
843
|
def unpad_self
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
# unpad and unoffset
|
613
|
-
|
614
|
-
unpad(i, @attributes)
|
844
|
+
defined_attributes_local.each_pair do |name, att_meta|
|
845
|
+
if att_meta.type == :int
|
846
|
+
unpad(name, @attributes)
|
615
847
|
end
|
616
848
|
end
|
617
849
|
end
|
618
850
|
|
619
851
|
def reload
|
620
852
|
super()
|
621
|
-
# puts 'decoding...'
|
622
|
-
|
623
|
-
=begin
|
624
|
-
This is done on getters now
|
625
|
-
if !@@dates.nil?
|
626
|
-
for d in @@dates
|
627
|
-
# puts 'converting created: ' + self['created'].inspect
|
628
|
-
if !self[d].nil?
|
629
|
-
self[d].collect!{ |d2|
|
630
|
-
if d2.is_a?(String)
|
631
|
-
DateTime.parse(d2)
|
632
|
-
else
|
633
|
-
d2
|
634
|
-
end
|
635
|
-
}
|
636
|
-
end
|
637
|
-
# puts 'after=' + self['created'].inspect
|
638
|
-
end
|
639
|
-
end
|
640
|
-
=end
|
641
|
-
|
642
|
-
# unpad_self
|
643
853
|
end
|
644
854
|
|
645
855
|
def update_attributes(*params)
|
@@ -678,31 +888,48 @@ This is done on getters now
|
|
678
888
|
end
|
679
889
|
end
|
680
890
|
|
891
|
+
@@regex_no_id = /.*Couldn't find.*with ID.*/
|
892
|
+
|
893
|
+
#
|
894
|
+
# Usage:
|
895
|
+
# Find by ID:
|
896
|
+
# MyModel.find(ID)
|
897
|
+
#
|
898
|
+
# Query:
|
899
|
+
# MyModel.find(:all, ["name = ?", name], :order=>"created desc", :limit=>10)
|
900
|
+
#
|
681
901
|
def self.find(*params)
|
682
|
-
|
683
|
-
|
684
|
-
all=false
|
685
|
-
select=false
|
902
|
+
#puts 'params=' + params.inspect
|
903
|
+
q_type = :all
|
686
904
|
select_attributes=[]
|
687
905
|
|
688
906
|
if params.size > 0
|
689
|
-
|
690
|
-
first = params[0] == :first
|
907
|
+
q_type = params[0]
|
691
908
|
end
|
692
909
|
|
693
|
-
|
694
910
|
# Pad and Offset number attributes
|
695
|
-
options =
|
696
|
-
|
697
|
-
|
698
|
-
#
|
911
|
+
options = {}
|
912
|
+
if params.size > 1
|
913
|
+
options = params[1]
|
914
|
+
#puts 'options=' + options.inspect
|
915
|
+
#puts 'after collect=' + options.inspect
|
916
|
+
convert_condition_params(options)
|
917
|
+
end
|
918
|
+
#puts 'params2=' + params.inspect
|
699
919
|
|
700
|
-
results = all ? [] : nil
|
920
|
+
results = q_type == :all ? [] : nil
|
701
921
|
begin
|
702
922
|
results=super(*params)
|
703
|
-
|
923
|
+
#puts 'params3=' + params.inspect
|
924
|
+
SimpleRecord.stats.selects += 1
|
925
|
+
if q_type != :count
|
926
|
+
cache_results(results)
|
927
|
+
if results.is_a?(Array)
|
928
|
+
results = SimpleRecord::ResultsArray.new(self, params, results, next_token)
|
929
|
+
end
|
930
|
+
end
|
704
931
|
rescue RightAws::AwsError, RightAws::ActiveSdb::ActiveSdbError
|
705
|
-
puts "RESCUED: " +
|
932
|
+
puts "RESCUED: " + $!.message
|
706
933
|
if ($!.message().index("NoSuchDomain") != nil)
|
707
934
|
# this is ok
|
708
935
|
elsif ($!.message() =~ @@regex_no_id)
|
@@ -714,48 +941,20 @@ This is done on getters now
|
|
714
941
|
return results
|
715
942
|
end
|
716
943
|
|
717
|
-
@@regex_no_id = /.*Couldn't find.*with ID.*/
|
718
944
|
def self.select(*params)
|
719
|
-
|
720
|
-
all=false
|
721
|
-
select=false
|
722
|
-
select_attributes=[]
|
723
|
-
|
724
|
-
if params.size > 0
|
725
|
-
all = params[0] == :all
|
726
|
-
first = params[0] == :first
|
727
|
-
end
|
728
|
-
|
729
|
-
options = params[1]
|
730
|
-
convert_condition_params(options)
|
731
|
-
|
732
|
-
results = all ? [] : nil
|
733
|
-
begin
|
734
|
-
results=super(*params)
|
735
|
-
cache_results(results)
|
736
|
-
rescue RightAws::AwsError, RightAws::ActiveSdb::ActiveSdbError
|
737
|
-
if ($!.message().index("NoSuchDomain") != nil)
|
738
|
-
# this is ok
|
739
|
-
elsif ($!.message() =~ @@regex_no_id)
|
740
|
-
results = nil
|
741
|
-
else
|
742
|
-
raise $!
|
743
|
-
end
|
744
|
-
end
|
745
|
-
return results
|
746
|
-
|
945
|
+
return find(*params)
|
747
946
|
end
|
748
947
|
|
749
948
|
def self.convert_condition_params(options)
|
750
|
-
if
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
end
|
949
|
+
return if options.nil?
|
950
|
+
conditions = options[:conditions]
|
951
|
+
if !conditions.nil? && conditions.size > 1
|
952
|
+
# all after first are values
|
953
|
+
conditions.collect! { |x|
|
954
|
+
self.pad_and_offset(x)
|
955
|
+
}
|
758
956
|
end
|
957
|
+
|
759
958
|
end
|
760
959
|
|
761
960
|
def self.cache_results(results)
|
@@ -766,8 +965,8 @@ This is done on getters now
|
|
766
965
|
class_name = results.class.name
|
767
966
|
id = results.id
|
768
967
|
cache_key = self.cache_key(class_name, id)
|
769
|
-
#
|
770
|
-
@@cache_store.write(cache_key, results, :expires_in =>
|
968
|
+
#puts 'caching result at ' + cache_key + ': ' + results.inspect
|
969
|
+
@@cache_store.write(cache_key, results, :expires_in =>30)
|
771
970
|
end
|
772
971
|
end
|
773
972
|
end
|
@@ -776,9 +975,8 @@ This is done on getters now
|
|
776
975
|
return class_name + "/" + id.to_s
|
777
976
|
end
|
778
977
|
|
779
|
-
|
780
|
-
|
781
978
|
@@debug=""
|
979
|
+
|
782
980
|
def self.debug
|
783
981
|
@@debug
|
784
982
|
end
|
@@ -788,7 +986,27 @@ This is done on getters now
|
|
788
986
|
end
|
789
987
|
|
790
988
|
def self.table_name
|
791
|
-
return
|
989
|
+
return domain
|
990
|
+
end
|
991
|
+
|
992
|
+
def changed
|
993
|
+
return @dirty.keys
|
994
|
+
end
|
995
|
+
|
996
|
+
def changed?
|
997
|
+
return @dirty.size > 0
|
998
|
+
end
|
999
|
+
|
1000
|
+
def changes
|
1001
|
+
ret = {}
|
1002
|
+
#puts 'in CHANGES=' + @dirty.inspect
|
1003
|
+
@dirty.each_pair {|key, value| ret[key] = [value, get_attribute(key)]}
|
1004
|
+
return ret
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def mark_as_old
|
1008
|
+
super
|
1009
|
+
@dirty = {}
|
792
1010
|
end
|
793
1011
|
|
794
1012
|
end
|
@@ -822,6 +1040,14 @@ This is done on getters now
|
|
822
1040
|
def full_messages
|
823
1041
|
return @errors
|
824
1042
|
end
|
1043
|
+
|
1044
|
+
def clear
|
1045
|
+
@errors.clear
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def empty?
|
1049
|
+
@errors.empty?
|
1050
|
+
end
|
825
1051
|
end
|
826
1052
|
|
827
1053
|
class Activerecordtosdb_subrecord_array
|
@@ -852,6 +1078,14 @@ This is done on getters now
|
|
852
1078
|
return load << ob
|
853
1079
|
end
|
854
1080
|
|
1081
|
+
def count
|
1082
|
+
return load.count
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
def size
|
1086
|
+
return count
|
1087
|
+
end
|
1088
|
+
|
855
1089
|
def each(*params, &block)
|
856
1090
|
return load.each(*params){|record| block.call(record)}
|
857
1091
|
end
|
@@ -899,3 +1133,4 @@ This is done on getters now
|
|
899
1133
|
|
900
1134
|
end
|
901
1135
|
end
|
1136
|
+
|