redis_object 0.5.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.
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