redis_object 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +8 -0
  5. data/README.markdown +179 -0
  6. data/Rakefile +10 -0
  7. data/lib/redis_object.rb +47 -0
  8. data/lib/redis_object/base.rb +408 -0
  9. data/lib/redis_object/collection.rb +388 -0
  10. data/lib/redis_object/defaults.rb +42 -0
  11. data/lib/redis_object/experimental/history.rb +49 -0
  12. data/lib/redis_object/ext/benchmark.rb +34 -0
  13. data/lib/redis_object/ext/cleaner.rb +14 -0
  14. data/lib/redis_object/ext/filters.rb +68 -0
  15. data/lib/redis_object/ext/script_cache.rb +92 -0
  16. data/lib/redis_object/ext/shardable.rb +18 -0
  17. data/lib/redis_object/ext/triggers.rb +101 -0
  18. data/lib/redis_object/ext/view_caching.rb +258 -0
  19. data/lib/redis_object/ext/views.rb +102 -0
  20. data/lib/redis_object/external_index.rb +25 -0
  21. data/lib/redis_object/indices.rb +97 -0
  22. data/lib/redis_object/inheritance_tracking.rb +23 -0
  23. data/lib/redis_object/keys.rb +37 -0
  24. data/lib/redis_object/storage.rb +93 -0
  25. data/lib/redis_object/storage/adapter.rb +46 -0
  26. data/lib/redis_object/storage/aws.rb +71 -0
  27. data/lib/redis_object/storage/mysql.rb +47 -0
  28. data/lib/redis_object/storage/redis.rb +119 -0
  29. data/lib/redis_object/timestamps.rb +74 -0
  30. data/lib/redis_object/tpl.rb +17 -0
  31. data/lib/redis_object/types.rb +276 -0
  32. data/lib/redis_object/validation.rb +89 -0
  33. data/lib/redis_object/version.rb +5 -0
  34. data/redis_object.gemspec +26 -0
  35. data/spec/adapter_spec.rb +43 -0
  36. data/spec/base_spec.rb +90 -0
  37. data/spec/benchmark_spec.rb +46 -0
  38. data/spec/collections_spec.rb +144 -0
  39. data/spec/defaults_spec.rb +56 -0
  40. data/spec/filters_spec.rb +29 -0
  41. data/spec/indices_spec.rb +45 -0
  42. data/spec/rename_class_spec.rb +96 -0
  43. data/spec/spec_helper.rb +38 -0
  44. data/spec/timestamp_spec.rb +28 -0
  45. data/spec/trigger_spec.rb +51 -0
  46. data/spec/types_spec.rb +103 -0
  47. data/spec/view_caching_spec.rb +130 -0
  48. data/spec/views_spec.rb +72 -0
  49. metadata +172 -0
@@ -0,0 +1,388 @@
1
+ module Seabright
2
+
3
+ module Collections
4
+
5
+ def hkey_col(ident = nil)
6
+ "#{hkey}:collections"
7
+ end
8
+
9
+ def load(o_id)
10
+ super(o_id)
11
+ store.smembers(hkey_col).each do |name|
12
+ collections[name] = Seabright::Collection.load(name,self)
13
+ define_access(name) do
14
+ get_collection(name)
15
+ end
16
+ define_access(name.to_s.singularize) do
17
+ get_collection(name).latest
18
+ end
19
+ end
20
+ true
21
+ end
22
+
23
+ def delete_child(obj)
24
+ if col = get_collection(obj.collection_name)
25
+ col.delete obj
26
+ end
27
+ end
28
+
29
+ def collection_name
30
+ self.class.collection_name
31
+ end
32
+
33
+ def ref_key(ident = nil)
34
+ "#{hkey}:backreferences"
35
+ end
36
+
37
+ def reference(obj)
38
+ raise "Not an object." unless obj.is_a?(RedisObject)
39
+ get_collection(obj.collection_name) << obj.hkey
40
+ obj.referenced_by self
41
+ end
42
+
43
+ def <<(obj)
44
+ reference obj
45
+ end
46
+
47
+ def push(obj)
48
+ reference obj
49
+ end
50
+
51
+ def remove_collection!(name)
52
+ store.srem hkey_col, name
53
+ end
54
+
55
+ def referenced_by(obj)
56
+ store.sadd(ref_key,obj.hkey)
57
+ end
58
+
59
+ def backreferences(cls = nil)
60
+ out = store.smembers(ref_key).map do |backreference_hkey|
61
+ obj = RedisObject.find_by_key(backreference_hkey)
62
+ if cls && !obj.is_a?(cls)
63
+ nil
64
+ else
65
+ obj
66
+ end
67
+ end
68
+ out.compact
69
+ end
70
+
71
+ def dereference_from(obj)
72
+ obj.get_collection(collection_name).delete(hkey)
73
+ end
74
+
75
+ def dereference_from_backreferences
76
+ backreferences.each do |backreference|
77
+ dereference_from(backreference)
78
+ end
79
+ end
80
+
81
+ def get(k)
82
+ if has_collection?(k)
83
+ get_collection(k)
84
+ elsif has_collection?(pk = k.to_s.pluralize)
85
+ get_collection(pk).first
86
+ else
87
+ super(k)
88
+ end
89
+ end
90
+
91
+ def has_collection?(name)
92
+ collection_names.include?(name.to_s)
93
+ end
94
+
95
+ def get_collection(name)
96
+ if has_collection?(name)
97
+ collections[name.to_s] ||= Collection.load(name,self)
98
+ else
99
+ store.sadd hkey_col, name
100
+ @collection_names << name.to_s
101
+ collections[name.to_s] ||= Collection.load(name,self)
102
+ define_access(name.to_s.pluralize) do
103
+ get_collection(name)
104
+ end
105
+ define_access(name.to_s.singularize) do
106
+ get_collection(name).latest
107
+ end
108
+ end
109
+ collections[name.to_s]
110
+ end
111
+
112
+ def collections
113
+ @collections ||= {}
114
+ end
115
+
116
+ def collection_names
117
+ @collection_names ||= store.smembers(hkey_col)
118
+ end
119
+
120
+ def mset(dat)
121
+ dat.select! {|k,v| !collections[k.to_s] }
122
+ super(dat)
123
+ end
124
+
125
+ def set(k,v)
126
+ @data ? super(k,v) : has_collection?(k) ? get_collection(k.to_s).replace(v) : super(k,v)
127
+ v
128
+ end
129
+
130
+ module ClassMethods
131
+
132
+ def hkey_col(ident = nil)
133
+ "#{hkey(ident)}:collections"
134
+ end
135
+
136
+ def delete_child(obj)
137
+ if col = get_collection(obj.collection_name)
138
+ col.delete obj
139
+ end
140
+ end
141
+
142
+ def collection_name
143
+ self.name.split('::').last.pluralize.underscore.to_sym
144
+ end
145
+
146
+ def reference(obj)
147
+ name = obj.collection_name
148
+ store.sadd hkey_col, name
149
+ get_collection(name) << obj.hkey
150
+ end
151
+
152
+ def <<(obj)
153
+ reference obj
154
+ end
155
+
156
+ def push(obj)
157
+ reference obj
158
+ end
159
+
160
+ def remove_collection!(name)
161
+ store.srem hkey_col, name
162
+ end
163
+
164
+ def get(k)
165
+ if has_collection?(k)
166
+ get_collection(k)
167
+ elsif has_collection?(pk = k.to_s.pluralize)
168
+ get_collection(pk).first
169
+ else
170
+ super(k)
171
+ end
172
+ end
173
+
174
+ def has_collection?(name)
175
+ store.sismember(hkey_col,name.to_s)
176
+ end
177
+
178
+ def get_collection(name)
179
+ collections[name.to_s] ||= Collection.load(name,self)
180
+ collections[name.to_s]
181
+ end
182
+
183
+ def collections
184
+ @collections ||= {}
185
+ end
186
+
187
+ end
188
+
189
+ def self.included(base)
190
+ base.extend(ClassMethods)
191
+ end
192
+
193
+ end
194
+
195
+ class Collection < Array
196
+
197
+ include Seabright::CachedScripts
198
+
199
+ def initialize(name,owner)
200
+ @name = name.to_s
201
+ @owner = owner
202
+ end
203
+
204
+ def remove!
205
+ @owner.remove_collection! @name
206
+ end
207
+
208
+ def latest
209
+ indexed(:created_at,5,true).first || first
210
+ end
211
+
212
+ def indexed(idx,num=-1,reverse=false)
213
+ keys = keys_by_index(idx,num,reverse)
214
+ out = Enumerator.new do |y|
215
+ keys.each do |member|
216
+ if a = class_const.find_by_key(member)
217
+ y << a
218
+ end
219
+ end
220
+ end
221
+ if block_given?
222
+ out.each do |itm|
223
+ yield itm
224
+ end
225
+ else
226
+ out
227
+ end
228
+ end
229
+
230
+ def temp_key
231
+ "#{key}::zintersect_temp::#{RedisObject.new_id(4)}"
232
+ end
233
+
234
+ RedisObject::ScriptSources::FwdScript = "redis.call('ZINTERSTORE', KEYS[1], 2, KEYS[2], KEYS[3], 'WEIGHTS', 1, 0)\nlocal keys = redis.call('ZRANGE', KEYS[1], 0, KEYS[4])\nredis.call('DEL', KEYS[1])\nreturn keys".freeze
235
+ RedisObject::ScriptSources::RevScript = "redis.call('ZINTERSTORE', KEYS[1], 2, KEYS[2], KEYS[3], 'WEIGHTS', 1, 0)\nlocal keys = redis.call('ZREVRANGE', KEYS[1], 0, KEYS[4])\nredis.call('DEL', KEYS[1])\nreturn keys".freeze
236
+
237
+ def keys_by_index(idx,num=-1,reverse=false)
238
+ keys = run_script(reverse ? :RevScript : :FwdScript, [temp_key, index_key(idx), key, num])
239
+ Enumerator.new do |y|
240
+ keys.each do |member|
241
+ y << member
242
+ end
243
+ end
244
+ end
245
+
246
+ def index_key(idx)
247
+ class_const.index_key(idx)
248
+ end
249
+
250
+ def item_key(k)
251
+ "#{class_const}:#{k}_h"
252
+ end
253
+
254
+ def find(k)
255
+ if k.is_a? String
256
+ return real_at(item_key(k))
257
+ elsif k.is_a? Hash
258
+ return match(k)
259
+ elsif k.is_a? Integer
260
+ return real_at(at(k))
261
+ end
262
+ return nil
263
+ end
264
+
265
+ def [](k)
266
+ find k
267
+ end
268
+
269
+ def match(pkt)
270
+ Enumerator.new do |y|
271
+ each do |i|
272
+ if pkt.map {|hk,va| i.get(hk)==va }.all?
273
+ y << i
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ def real_at(key)
280
+ class_const.find_by_key(key)
281
+ end
282
+
283
+ def objects
284
+ each.to_a
285
+ end
286
+
287
+ def first
288
+ class_const.find_by_key(super)
289
+ end
290
+
291
+ def last
292
+ class_const.find_by_key(super)
293
+ end
294
+
295
+ def each
296
+ out = Enumerator.new do |y|
297
+ each_index do |key|
298
+ if a = class_const.find_by_key(at(key))
299
+ y << a
300
+ end
301
+ end
302
+ end
303
+ if block_given?
304
+ out.each do |a|
305
+ yield a
306
+ end
307
+ else
308
+ out
309
+ end
310
+ end
311
+
312
+ def cleanup!
313
+ each_index do |key|
314
+ unless a = class_const.find_by_key(at(key))
315
+ puts "Deleting #{key} because not #{a.inspect}" if DEBUG
316
+ delete at(key)
317
+ end
318
+ end
319
+ if size < 1
320
+ puts "Deleting collection #{@name} because empty" if DEBUG
321
+ remove!
322
+ end
323
+ end
324
+
325
+ def map(&block)
326
+ each.map(&block)
327
+ end
328
+
329
+ def select(&block)
330
+ return nil unless block_given?
331
+ Enumerator.new do |y|
332
+ each_index do |key|
333
+ if (a = class_const.find_by_key(at(key))) && block.call(a)
334
+ y << a
335
+ end
336
+ end
337
+ end
338
+ end
339
+
340
+ def delete(obj)
341
+ k = obj.class == String ? obj : obj.hkey
342
+ store.zrem(key,k)
343
+ super(k)
344
+ end
345
+
346
+ def clear!
347
+ store.zrem(key,self.join(" "))
348
+ end
349
+
350
+ def <<(obj)
351
+ k = obj.class == String ? obj : obj.hkey
352
+ store.zadd(key,store.zcount(key,"-inf", "+inf"),k)
353
+ super(k)
354
+ end
355
+
356
+ def push(obj)
357
+ self << obj
358
+ end
359
+
360
+ def class_const
361
+ self.class.class_const_for(@name)
362
+ end
363
+
364
+ def store
365
+ class_const.store
366
+ end
367
+
368
+ def key
369
+ "#{@owner ? "#{@owner.key}:" : ""}COLLECTION:#{@name}"
370
+ end
371
+
372
+ class << self
373
+
374
+ def load(name,owner)
375
+ out = new(name,owner)
376
+ out.replace class_const_for(name).store.zrange(out.key,0,-1)
377
+ out
378
+ end
379
+
380
+ def class_const_for(name)
381
+ Object.const_get(name.to_s.classify.to_sym) rescue RedisObject
382
+ end
383
+
384
+ end
385
+
386
+ end
387
+
388
+ end
@@ -0,0 +1,42 @@
1
+ module Seabright
2
+ module DefaultValues
3
+
4
+ module ClassMethods
5
+
6
+ def default_vals
7
+ @default_vals ||= {}
8
+ end
9
+
10
+ def intercept_for_defaults!
11
+ return if @intercepted_for_defaults
12
+ self.class_eval do
13
+
14
+ alias_method :undefaulted_get, :get unless method_defined?(:undefaulted_get)
15
+ def get(k)
16
+ if !is_set?(k) && (d = self.class.default_vals[k.to_sym]) && !d.nil?
17
+ return d
18
+ end
19
+ undefaulted_get(k)
20
+ end
21
+
22
+ end
23
+ @intercepted_for_defaults = true
24
+ end
25
+
26
+ def register_default(k,vl)
27
+ default_vals[k.to_sym] = vl
28
+ intercept_for_defaults!
29
+ end
30
+
31
+ def default_for(k,vl)
32
+ register_default k, vl
33
+ end
34
+
35
+ end
36
+
37
+ def self.included(base)
38
+ base.extend(ClassMethods)
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ module Seabright
2
+ module History
3
+
4
+ def save_history?
5
+ save_history || self.class.save_history?
6
+ end
7
+
8
+ def store_image
9
+ store.zadd history_key, Time.now.to_i, to_json
10
+ end
11
+
12
+ def history(num=5,reverse=false)
13
+ parser = Yajl::Parser
14
+ store.send(reverse ? :zrevrange : :zrange, history_key, 0, num).collect do |member|
15
+ parser.parse(member)
16
+ end
17
+ end
18
+
19
+ def history_key(ident = nil)
20
+ "#{key}_history"
21
+ end
22
+
23
+ def save
24
+ super
25
+ store_image if save_history?
26
+ end
27
+
28
+ module ClassMethods
29
+
30
+ def save_history!(v=true)
31
+ @@save_history = v
32
+ end
33
+
34
+ def save_history?
35
+ @@save_history ||= false
36
+ end
37
+
38
+ def history_key(ident = id)
39
+ "#{key(ident)}_history"
40
+ end
41
+
42
+ end
43
+
44
+ def self.included(base)
45
+ base.extend(ClassMethods)
46
+ end
47
+
48
+ end
49
+ end