simple_record 2.0.5 → 2.1.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/README.markdown +27 -1
- data/lib/simple_record.rb +165 -91
- data/lib/simple_record/active_sdb.rb +1 -1
- data/lib/simple_record/attributes.rb +3 -3
- data/lib/simple_record/callbacks.rb +45 -17
- data/lib/simple_record/sharding.rb +238 -238
- data/lib/simple_record/translations.rb +223 -223
- data/lib/simple_record/validations.rb +69 -0
- data/test/my_model.rb +71 -35
- data/test/my_sharded_model.rb +25 -20
- data/test/my_simple_model.rb +13 -0
- data/test/test_shards.rb +122 -119
- data/test/test_simple_record.rb +5 -22
- data/test/test_validations.rb +45 -0
- metadata +42 -53
- data/lib/simple_record/rails2.rb +0 -30
@@ -1,4 +1,31 @@
|
|
1
|
-
module SimpleRecord
|
1
|
+
module SimpleRecord
|
2
|
+
|
3
|
+
# For Rails3 support
|
4
|
+
module Callbacks3
|
5
|
+
|
6
|
+
# def destroy #:nodoc:
|
7
|
+
# _run_destroy_callbacks { super }
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# private
|
11
|
+
#
|
12
|
+
# def create_or_update #:nodoc:
|
13
|
+
# puts '3 create_or_update'
|
14
|
+
# _run_save_callbacks { super }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def create #:nodoc:
|
18
|
+
# puts '3 create'
|
19
|
+
# _run_create_callbacks { super }
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def update(*) #:nodoc:
|
23
|
+
# puts '3 update'
|
24
|
+
# _run_update_callbacks { super }
|
25
|
+
# end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Callbacks
|
2
29
|
#this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
|
3
30
|
#this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
|
4
31
|
#basically, this is how we recreate the callback functions
|
@@ -9,10 +36,10 @@ module SimpleRecord::Callbacks
|
|
9
36
|
"after_destroy"]
|
10
37
|
|
11
38
|
def self.included(base)
|
12
|
-
|
39
|
+
#puts 'Callbacks included in ' + base.inspect
|
13
40
|
|
14
41
|
# puts "setup callbacks #{base.inspect}"
|
15
|
-
|
42
|
+
base.instance_eval <<-endofeval
|
16
43
|
|
17
44
|
def callbacks
|
18
45
|
@callbacks ||= {}
|
@@ -20,18 +47,18 @@ module SimpleRecord::Callbacks
|
|
20
47
|
end
|
21
48
|
|
22
49
|
|
23
|
-
|
50
|
+
endofeval
|
24
51
|
|
25
|
-
|
26
|
-
|
52
|
+
@@callbacks.each do |callback|
|
53
|
+
base.class_eval <<-endofeval
|
27
54
|
|
28
55
|
def run_#{callback}
|
29
|
-
#
|
56
|
+
# puts 'CLASS CALLBACKS for ' + self.inspect + ' = ' + self.class.callbacks.inspect
|
30
57
|
return true if self.class.callbacks.nil?
|
31
58
|
cnames = self.class.callbacks['#{callback}']
|
32
59
|
cnames = [] if cnames.nil?
|
33
|
-
#cnames += super.class.callbacks['#{callback}'] unless super.class.callbacks.nil?
|
34
|
-
#
|
60
|
+
# cnames += super.class.callbacks['#{callback}'] unless super.class.callbacks.nil?
|
61
|
+
# puts 'cnames for #{callback} = ' + cnames.inspect
|
35
62
|
return true if cnames.nil?
|
36
63
|
cnames.each { |name|
|
37
64
|
#puts 'run_ #{name}'
|
@@ -39,16 +66,16 @@ module SimpleRecord::Callbacks
|
|
39
66
|
return false
|
40
67
|
end
|
41
68
|
}
|
42
|
-
|
69
|
+
# super.run_#{callback}
|
43
70
|
return true
|
44
71
|
end
|
45
72
|
|
46
|
-
|
73
|
+
endofeval
|
47
74
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
75
|
+
#this bit of code creates a "run_blank" function for everything value in the @@callbacks array.
|
76
|
+
#this function can then be inserted in the appropriate place in the save, new, destroy, etc overrides
|
77
|
+
#basically, this is how we recreate the callback functions
|
78
|
+
base.instance_eval <<-endofeval
|
52
79
|
|
53
80
|
# puts 'defining callback=' + callback + ' for ' + self.inspect
|
54
81
|
#we first have to make an initialized array for each of the callbacks, to prevent problems if they are not called
|
@@ -69,8 +96,8 @@ module SimpleRecord::Callbacks
|
|
69
96
|
end
|
70
97
|
end
|
71
98
|
|
72
|
-
|
73
|
-
|
99
|
+
endofeval
|
100
|
+
end
|
74
101
|
end
|
75
102
|
|
76
103
|
def before_destroy()
|
@@ -84,4 +111,5 @@ module SimpleRecord::Callbacks
|
|
84
111
|
|
85
112
|
end
|
86
113
|
|
114
|
+
end
|
87
115
|
end
|
@@ -1,282 +1,282 @@
|
|
1
1
|
module SimpleRecord
|
2
2
|
|
3
|
-
|
3
|
+
module Sharding
|
4
4
|
|
5
|
-
|
5
|
+
def self.included(base)
|
6
6
|
# base.extend ClassMethods
|
7
|
-
|
7
|
+
end
|
8
8
|
|
9
|
-
|
9
|
+
module ClassMethods
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def shard(options=nil)
|
12
|
+
@sharding_options = options
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def sharding_options
|
16
|
+
@sharding_options
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
def is_sharded?
|
20
|
+
@sharding_options
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
def find_sharded(*params)
|
24
|
+
puts 'find_sharded ' + params.inspect
|
25
25
|
|
26
|
-
|
26
|
+
options = params.size > 1 ? params[1] : {}
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
if options[:shard] # User specified shard.
|
29
|
+
shard = options[:shard]
|
30
|
+
domains = shard.is_a?(Array) ? (shard.collect { |x| prefix_shard_name(x) }) : [prefix_shard_name(shard)]
|
31
|
+
else
|
32
|
+
domains = sharded_domains
|
33
|
+
end
|
34
34
|
# puts "sharded_domains=" + domains.inspect
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
results = ShardedResults.new(params)
|
49
|
-
domains.each do |d|
|
50
|
-
p2 = params.dup
|
51
|
-
op2 = options.dup
|
52
|
-
op2[:from] = d
|
53
|
-
op2[:shard_find] = true
|
54
|
-
p2[1] = op2
|
55
|
-
rs = find(*p2)
|
56
|
-
if params.first == :first || single
|
57
|
-
return rs if rs
|
58
|
-
else
|
59
|
-
results.add_results rs
|
60
|
-
end
|
61
|
-
end
|
62
|
-
puts 'results=' + results.inspect
|
63
|
-
if params.first == :first || single
|
64
|
-
# Then we found nothing by this point so return nil
|
65
|
-
return nil
|
66
|
-
elsif params.first == :count
|
67
|
-
return results.sum_count
|
68
|
-
end
|
69
|
-
results
|
70
|
-
|
36
|
+
single = false
|
37
|
+
case params.first
|
38
|
+
when nil then
|
39
|
+
raise "Invalid parameters passed to find: nil."
|
40
|
+
when :all, :first, :count
|
41
|
+
# nada
|
42
|
+
else # single id
|
43
|
+
unless params.first.is_a?(Array)
|
44
|
+
single = true
|
71
45
|
end
|
46
|
+
end
|
72
47
|
|
73
|
-
|
74
|
-
|
75
|
-
|
48
|
+
results = ShardedResults.new(params)
|
49
|
+
domains.each do |d|
|
50
|
+
p2 = params.dup
|
51
|
+
op2 = options.dup
|
52
|
+
op2[:from] = d
|
53
|
+
op2[:shard_find] = true
|
54
|
+
p2[1] = op2
|
55
|
+
rs = find(*p2)
|
56
|
+
if params.first == :first || single
|
57
|
+
return rs if rs
|
58
|
+
else
|
59
|
+
results.add_results rs
|
60
|
+
end
|
61
|
+
end
|
62
|
+
puts 'results=' + results.inspect
|
63
|
+
if params.first == :first || single
|
64
|
+
# Then we found nothing by this point so return nil
|
65
|
+
return nil
|
66
|
+
elsif params.first == :count
|
67
|
+
return results.sum_count
|
68
|
+
end
|
69
|
+
results
|
76
70
|
|
77
|
-
|
78
|
-
"#{domain}_#{s}"
|
79
|
-
end
|
71
|
+
end
|
80
72
|
|
73
|
+
def shards
|
74
|
+
send(sharding_options[:shards])
|
75
|
+
end
|
81
76
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
77
|
+
def prefix_shard_name(s)
|
78
|
+
"#{domain}_#{s}"
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def sharded_domains
|
83
|
+
sharded_domains = []
|
84
|
+
shard_names = shards
|
85
|
+
shard_names.each do |s|
|
86
|
+
sharded_domains << prefix_shard_name(s)
|
90
87
|
end
|
88
|
+
sharded_domains
|
89
|
+
end
|
90
|
+
end
|
91
91
|
|
92
|
-
|
92
|
+
def sharded_domain
|
93
93
|
# puts 'getting sharded_domain'
|
94
|
-
|
94
|
+
options = self.class.sharding_options
|
95
95
|
# val = self.send(options[:on])
|
96
96
|
# puts "val=" + val.inspect
|
97
97
|
# shards = options[:shards] # is user passed in static array of shards
|
98
98
|
# if options[:shards].is_a?(Symbol)
|
99
99
|
# shards = self.send(shards)
|
100
100
|
# end
|
101
|
-
|
101
|
+
sharded_domain = "#{domain}_#{self.send(options[:map])}"
|
102
102
|
# puts "sharded_domain=" + sharded_domain.inspect
|
103
|
-
|
104
|
-
|
103
|
+
sharded_domain
|
104
|
+
end
|
105
105
|
|
106
|
-
|
107
|
-
|
106
|
+
class ShardedResults
|
107
|
+
include Enumerable
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
109
|
+
def initialize(params)
|
110
|
+
@params = params
|
111
|
+
@options = params.size > 1 ? params[1] : {}
|
112
|
+
@results_arrays = []
|
113
|
+
end
|
114
114
|
|
115
|
-
|
115
|
+
def add_results(rs)
|
116
116
|
# puts 'adding results=' + rs.inspect
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
def first
|
161
|
-
@results_arrays.first.first
|
162
|
-
end
|
163
|
-
|
164
|
-
def last
|
165
|
-
@results_arrays.last.last
|
166
|
-
end
|
167
|
-
|
168
|
-
def empty?
|
169
|
-
@results_arrays.each do |rs|
|
170
|
-
return false if !rs.empty?
|
171
|
-
end
|
172
|
-
true
|
173
|
-
end
|
174
|
-
|
175
|
-
def include?(obj)
|
176
|
-
@results_arrays.each do |rs|
|
177
|
-
x = rs.include?(obj)
|
178
|
-
return true if x
|
179
|
-
end
|
180
|
-
false
|
181
|
-
end
|
182
|
-
|
183
|
-
def size
|
184
|
-
return @size if @size
|
185
|
-
s = 0
|
186
|
-
@results_arrays.each do |rs|
|
187
|
-
# puts 'rs=' + rs.inspect
|
188
|
-
# puts 'rs.size=' + rs.size.inspect
|
189
|
-
s += rs.size
|
190
|
-
end
|
191
|
-
@size = s
|
192
|
-
s
|
193
|
-
end
|
194
|
-
|
195
|
-
def length
|
196
|
-
return size
|
197
|
-
end
|
198
|
-
|
199
|
-
def each(&blk)
|
200
|
-
i = 0
|
201
|
-
@results_arrays.each do |rs|
|
202
|
-
rs.each(&blk)
|
203
|
-
i+=1
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# for will_paginate support
|
208
|
-
def total_pages
|
209
|
-
# puts 'total_pages'
|
210
|
-
# puts @params[1][:per_page].to_s
|
211
|
-
return 1 if @params[1][:per_page].nil?
|
212
|
-
ret = (size / @params[1][:per_page].to_f).ceil
|
213
|
-
#puts 'ret=' + ret.to_s
|
214
|
-
ret
|
215
|
-
end
|
216
|
-
|
217
|
-
def current_page
|
218
|
-
return query_options[:page] || 1
|
219
|
-
end
|
220
|
-
|
221
|
-
def query_options
|
222
|
-
return @options
|
223
|
-
end
|
224
|
-
|
225
|
-
def total_entries
|
226
|
-
return size
|
227
|
-
end
|
228
|
-
|
229
|
-
# Helper method that is true when someone tries to fetch a page with a
|
230
|
-
# larger number than the last page. Can be used in combination with flashes
|
231
|
-
# and redirecting.
|
232
|
-
def out_of_bounds?
|
233
|
-
current_page > total_pages
|
234
|
-
end
|
235
|
-
|
236
|
-
# Current offset of the paginated collection. If we're on the first page,
|
237
|
-
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
|
238
|
-
# the offset is 30. This property is useful if you want to render ordinals
|
239
|
-
# side by side with records in the view: simply start with offset + 1.
|
240
|
-
def offset
|
241
|
-
(current_page - 1) * per_page
|
242
|
-
end
|
243
|
-
|
244
|
-
# current_page - 1 or nil if there is no previous page
|
245
|
-
def previous_page
|
246
|
-
current_page > 1 ? (current_page - 1) : nil
|
247
|
-
end
|
248
|
-
|
249
|
-
# current_page + 1 or nil if there is no next page
|
250
|
-
def next_page
|
251
|
-
current_page < total_pages ? (current_page + 1) : nil
|
252
|
-
end
|
117
|
+
@results_arrays << rs
|
118
|
+
end
|
119
|
+
|
120
|
+
# only used for count queries
|
121
|
+
def sum_count
|
122
|
+
x = 0
|
123
|
+
@results_arrays.each do |rs|
|
124
|
+
x += rs if rs
|
125
|
+
end
|
126
|
+
x
|
127
|
+
end
|
128
|
+
|
129
|
+
def <<(val)
|
130
|
+
raise "Not supported."
|
131
|
+
end
|
132
|
+
|
133
|
+
def element_at(index)
|
134
|
+
@results_arrays.each do |rs|
|
135
|
+
if rs.size > index
|
136
|
+
return rs[index]
|
137
|
+
end
|
138
|
+
index -= rs.size
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def [](*i)
|
143
|
+
if i.size == 1
|
144
|
+
# puts '[] i=' + i.to_s
|
145
|
+
index = i[0]
|
146
|
+
return element_at(index)
|
147
|
+
else
|
148
|
+
offset = i[0]
|
149
|
+
rows = i[1]
|
150
|
+
ret = []
|
151
|
+
x = offset
|
152
|
+
while x < (offset+rows)
|
153
|
+
ret << element_at(x)
|
154
|
+
x+=1
|
155
|
+
end
|
156
|
+
ret
|
157
|
+
end
|
158
|
+
end
|
253
159
|
|
160
|
+
def first
|
161
|
+
@results_arrays.first.first
|
162
|
+
end
|
254
163
|
|
255
|
-
|
256
|
-
|
257
|
-
|
164
|
+
def last
|
165
|
+
@results_arrays.last.last
|
166
|
+
end
|
258
167
|
|
259
|
-
|
260
|
-
|
261
|
-
|
168
|
+
def empty?
|
169
|
+
@results_arrays.each do |rs|
|
170
|
+
return false if !rs.empty?
|
171
|
+
end
|
172
|
+
true
|
173
|
+
end
|
262
174
|
|
175
|
+
def include?(obj)
|
176
|
+
@results_arrays.each do |rs|
|
177
|
+
x = rs.include?(obj)
|
178
|
+
return true if x
|
179
|
+
end
|
180
|
+
false
|
181
|
+
end
|
182
|
+
|
183
|
+
def size
|
184
|
+
return @size if @size
|
185
|
+
s = 0
|
186
|
+
@results_arrays.each do |rs|
|
187
|
+
# puts 'rs=' + rs.inspect
|
188
|
+
# puts 'rs.size=' + rs.size.inspect
|
189
|
+
s += rs.size
|
190
|
+
end
|
191
|
+
@size = s
|
192
|
+
s
|
193
|
+
end
|
194
|
+
|
195
|
+
def length
|
196
|
+
return size
|
197
|
+
end
|
198
|
+
|
199
|
+
def each(&blk)
|
200
|
+
i = 0
|
201
|
+
@results_arrays.each do |rs|
|
202
|
+
rs.each(&blk)
|
203
|
+
i+=1
|
263
204
|
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# for will_paginate support
|
208
|
+
def total_pages
|
209
|
+
# puts 'total_pages'
|
210
|
+
# puts @params[1][:per_page].to_s
|
211
|
+
return 1 if @params[1][:per_page].nil?
|
212
|
+
ret = (size / @params[1][:per_page].to_f).ceil
|
213
|
+
#puts 'ret=' + ret.to_s
|
214
|
+
ret
|
215
|
+
end
|
216
|
+
|
217
|
+
def current_page
|
218
|
+
return query_options[:page] || 1
|
219
|
+
end
|
220
|
+
|
221
|
+
def query_options
|
222
|
+
return @options
|
223
|
+
end
|
224
|
+
|
225
|
+
def total_entries
|
226
|
+
return size
|
227
|
+
end
|
228
|
+
|
229
|
+
# Helper method that is true when someone tries to fetch a page with a
|
230
|
+
# larger number than the last page. Can be used in combination with flashes
|
231
|
+
# and redirecting.
|
232
|
+
def out_of_bounds?
|
233
|
+
current_page > total_pages
|
234
|
+
end
|
235
|
+
|
236
|
+
# Current offset of the paginated collection. If we're on the first page,
|
237
|
+
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
|
238
|
+
# the offset is 30. This property is useful if you want to render ordinals
|
239
|
+
# side by side with records in the view: simply start with offset + 1.
|
240
|
+
def offset
|
241
|
+
(current_page - 1) * per_page
|
242
|
+
end
|
243
|
+
|
244
|
+
# current_page - 1 or nil if there is no previous page
|
245
|
+
def previous_page
|
246
|
+
current_page > 1 ? (current_page - 1) : nil
|
247
|
+
end
|
248
|
+
|
249
|
+
# current_page + 1 or nil if there is no next page
|
250
|
+
def next_page
|
251
|
+
current_page < total_pages ? (current_page + 1) : nil
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
def delete(item)
|
256
|
+
raise "Not supported"
|
257
|
+
end
|
258
|
+
|
259
|
+
def delete_at(index)
|
260
|
+
raise "Not supported"
|
261
|
+
end
|
264
262
|
|
265
|
-
|
266
|
-
|
267
|
-
|
263
|
+
end
|
264
|
+
|
265
|
+
# Some hashing algorithms
|
266
|
+
module Hashing
|
267
|
+
def self.sdbm_hash(str, len=str.length)
|
268
268
|
# puts 'sdbm_hash ' + str.inspect
|
269
|
-
|
270
|
-
|
271
|
-
|
269
|
+
hash = 0
|
270
|
+
len.times { |i|
|
271
|
+
c = str[i]
|
272
272
|
# puts "c=" + c.class.name + "--" + c.inspect + " -- " + c.ord.inspect
|
273
|
-
|
274
|
-
|
275
|
-
|
273
|
+
c = c.ord
|
274
|
+
hash = c + (hash << 6) + (hash << 16) - hash
|
275
|
+
}
|
276
276
|
# puts "hash=" + hash.inspect
|
277
|
-
|
278
|
-
|
279
|
-
end
|
277
|
+
return hash
|
278
|
+
end
|
280
279
|
end
|
280
|
+
end
|
281
281
|
|
282
282
|
end
|