redis_object 1.0 → 1.1

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 (103) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +4 -0
  5. data/README.markdown +24 -15
  6. data/Rakefile +8 -0
  7. data/lib/redis_object.rb +11 -1
  8. data/lib/redis_object/base.rb +210 -60
  9. data/lib/redis_object/collection.rb +130 -100
  10. data/lib/redis_object/defaults.rb +21 -8
  11. data/lib/redis_object/{history.rb → experimental/history.rb} +0 -0
  12. data/lib/redis_object/ext/filters.rb +34 -16
  13. data/lib/redis_object/ext/script_cache.rb +92 -0
  14. data/lib/redis_object/ext/shardable.rb +18 -0
  15. data/lib/redis_object/ext/triggers.rb +75 -13
  16. data/lib/redis_object/ext/view_caching.rb +258 -0
  17. data/lib/redis_object/ext/views.rb +102 -0
  18. data/lib/redis_object/indices.rb +44 -39
  19. data/lib/redis_object/inheritance_tracking.rb +23 -0
  20. data/lib/redis_object/keys.rb +4 -4
  21. data/lib/redis_object/storage.rb +30 -1
  22. data/lib/redis_object/storage/adapter.rb +6 -3
  23. data/lib/redis_object/storage/redis.rb +98 -3
  24. data/lib/redis_object/timestamps.rb +42 -21
  25. data/lib/redis_object/types.rb +172 -30
  26. data/lib/redis_object/version.rb +1 -1
  27. data/redis_object.gemspec +1 -0
  28. data/spec/adapter_spec.rb +43 -0
  29. data/spec/base_spec.rb +41 -6
  30. data/spec/benchmark_spec.rb +46 -0
  31. data/spec/collections_spec.rb +144 -0
  32. data/spec/defaults_spec.rb +56 -0
  33. data/spec/filters_spec.rb +29 -0
  34. data/spec/indices_spec.rb +45 -0
  35. data/spec/rename_class_spec.rb +96 -0
  36. data/spec/spec_helper.rb +32 -1
  37. data/spec/timestamp_spec.rb +28 -0
  38. data/spec/trigger_spec.rb +51 -0
  39. data/spec/types_spec.rb +103 -0
  40. data/spec/view_caching_spec.rb +130 -0
  41. data/spec/views_spec.rb +72 -0
  42. metadata +111 -116
  43. data/doc/Object.html +0 -185
  44. data/doc/Seabright.html +0 -181
  45. data/doc/Seabright/Adapter.html +0 -442
  46. data/doc/Seabright/Collection.html +0 -797
  47. data/doc/Seabright/Collections.html +0 -635
  48. data/doc/Seabright/Collections/ClassMethods.html +0 -212
  49. data/doc/Seabright/ExternalIndex.html +0 -217
  50. data/doc/Seabright/History.html +0 -382
  51. data/doc/Seabright/History/ClassMethods.html +0 -276
  52. data/doc/Seabright/Indices.html +0 -324
  53. data/doc/Seabright/Indices/ClassMethods.html +0 -348
  54. data/doc/Seabright/Keys.html +0 -314
  55. data/doc/Seabright/Keys/ClassMethods.html +0 -276
  56. data/doc/Seabright/ObjectBase.html +0 -852
  57. data/doc/Seabright/ObjectBase/ClassMethods.html +0 -677
  58. data/doc/Seabright/RedisObject.html +0 -230
  59. data/doc/Seabright/References.html +0 -280
  60. data/doc/Seabright/Storage.html +0 -252
  61. data/doc/Seabright/Storage/ClassMethods.html +0 -276
  62. data/doc/Seabright/Storage/MySQL.html +0 -442
  63. data/doc/Seabright/Storage/Redis.html +0 -218
  64. data/doc/Seabright/Template.html +0 -212
  65. data/doc/Seabright/Template/ClassMethods.html +0 -166
  66. data/doc/Seabright/Timestamps.html +0 -292
  67. data/doc/Seabright/Timestamps/ClassMethods.html +0 -214
  68. data/doc/Seabright/Types.html +0 -410
  69. data/doc/Seabright/Types/ClassMethods.html +0 -308
  70. data/doc/created.rid +0 -17
  71. data/doc/images/add.png +0 -0
  72. data/doc/images/brick.png +0 -0
  73. data/doc/images/brick_link.png +0 -0
  74. data/doc/images/bug.png +0 -0
  75. data/doc/images/bullet_black.png +0 -0
  76. data/doc/images/bullet_toggle_minus.png +0 -0
  77. data/doc/images/bullet_toggle_plus.png +0 -0
  78. data/doc/images/date.png +0 -0
  79. data/doc/images/delete.png +0 -0
  80. data/doc/images/find.png +0 -0
  81. data/doc/images/loadingAnimation.gif +0 -0
  82. data/doc/images/macFFBgHack.png +0 -0
  83. data/doc/images/package.png +0 -0
  84. data/doc/images/page_green.png +0 -0
  85. data/doc/images/page_white_text.png +0 -0
  86. data/doc/images/page_white_width.png +0 -0
  87. data/doc/images/plugin.png +0 -0
  88. data/doc/images/ruby.png +0 -0
  89. data/doc/images/tag_blue.png +0 -0
  90. data/doc/images/tag_green.png +0 -0
  91. data/doc/images/transparent.png +0 -0
  92. data/doc/images/wrench.png +0 -0
  93. data/doc/images/wrench_orange.png +0 -0
  94. data/doc/images/zoom.png +0 -0
  95. data/doc/index.html +0 -125
  96. data/doc/js/darkfish.js +0 -153
  97. data/doc/js/jquery.js +0 -18
  98. data/doc/js/navigation.js +0 -142
  99. data/doc/js/search.js +0 -94
  100. data/doc/js/search_index.js +0 -1
  101. data/doc/js/searcher.js +0 -228
  102. data/doc/rdoc.css +0 -543
  103. data/doc/table_of_contents.html +0 -394
@@ -2,20 +2,6 @@ module Seabright
2
2
 
3
3
  module Collections
4
4
 
5
- def dump
6
- require "utf8_utils"
7
- out = ["puts \"Creating: #{id}\""]
8
- s_id = id.gsub(/\W/,"_")
9
- out << "a#{s_id} = #{self.class.cname}.new(#{actual.to_s.tidy_bytes})"
10
- collections.each do |col|
11
- col.each do |sobj|
12
- out << sobj.dump(self)
13
- end
14
- end
15
- out << "a#{s_id}.save"
16
- out.join("\n")
17
- end
18
-
19
5
  def hkey_col(ident = nil)
20
6
  "#{hkey}:collections"
21
7
  end
@@ -24,49 +10,24 @@ module Seabright
24
10
  super(o_id)
25
11
  store.smembers(hkey_col).each do |name|
26
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
27
19
  end
28
- # TODO: Here's an example of dynamic method creation - should do this instead of method_missing + module intercepts...
29
- # %w[report alert error summary].each do |kind|
30
- # class_eval <<-END
31
- # if "#{kind}" == "summary"
32
- # def summaries
33
- # data_for_server[:summaries]
34
- # end
35
- # else
36
- # def #{kind}s
37
- # data_for_server[:#{kind}s]
38
- # end
39
- # end
40
- #
41
- # if "#{kind}" == "report"
42
- # def report(new_entry)
43
- # reports << new_entry
44
- # end
45
- # elsif "#{kind}" == "summary"
46
- # def summary(new_entry)
47
- # summaries << new_entry
48
- # end
49
- # else
50
- # def #{kind}(*fields)
51
- # #{kind}s << ( fields.first.is_a?(Hash) ?
52
- # fields.first :
53
- # {:subject => fields.first, :body => fields.last} )
54
- # end
55
- # end
56
- # alias_method :add_#{kind}, :#{kind}
57
- # END
58
- # end
59
20
  true
60
21
  end
61
22
 
62
23
  def delete_child(obj)
63
- if col = collections[obj.collection_name]
64
- col.delete obj.hkey
24
+ if col = get_collection(obj.collection_name)
25
+ col.delete obj
65
26
  end
66
27
  end
67
28
 
68
29
  def collection_name
69
- self.class.plname.underscore.to_sym
30
+ self.class.collection_name
70
31
  end
71
32
 
72
33
  def ref_key(ident = nil)
@@ -74,14 +35,18 @@ module Seabright
74
35
  end
75
36
 
76
37
  def reference(obj)
77
- name = obj.collection_name
78
- store.sadd hkey_col, name
79
- collections[name.to_s] ||= Seabright::Collection.load(name,self)
80
- collections[name.to_s] << obj.hkey
38
+ raise "Not an object." unless obj.is_a?(RedisObject)
39
+ get_collection(obj.collection_name) << obj.hkey
81
40
  obj.referenced_by self
82
41
  end
83
- alias_method :<<, :reference
84
- alias_method :push, :reference
42
+
43
+ def <<(obj)
44
+ reference obj
45
+ end
46
+
47
+ def push(obj)
48
+ reference obj
49
+ end
85
50
 
86
51
  def remove_collection!(name)
87
52
  store.srem hkey_col, name
@@ -124,11 +89,23 @@ module Seabright
124
89
  end
125
90
 
126
91
  def has_collection?(name)
127
- store.sismember(hkey_col,name.to_s)
92
+ collection_names.include?(name.to_s)
128
93
  end
129
94
 
130
95
  def get_collection(name)
131
- collections[name.to_s] ||= Collection.load(name,self)
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
132
109
  collections[name.to_s]
133
110
  end
134
111
 
@@ -136,22 +113,77 @@ module Seabright
136
113
  @collections ||= {}
137
114
  end
138
115
 
116
+ def collection_names
117
+ @collection_names ||= store.smembers(hkey_col)
118
+ end
119
+
139
120
  def mset(dat)
140
121
  dat.select! {|k,v| !collections[k.to_s] }
141
122
  super(dat)
142
123
  end
143
124
 
144
125
  def set(k,v)
145
- @data ? super(k,v) : collections[k.to_s] ? get_collection(k.to_s).replace(v) : super(k,v)
126
+ @data ? super(k,v) : has_collection?(k) ? get_collection(k.to_s).replace(v) : super(k,v)
146
127
  v
147
128
  end
148
129
 
149
130
  module ClassMethods
150
131
 
151
- def hkey_col(ident = id)
132
+ def hkey_col(ident = nil)
152
133
  "#{hkey(ident)}:collections"
153
134
  end
154
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
+
155
187
  end
156
188
 
157
189
  def self.included(base)
@@ -162,24 +194,26 @@ module Seabright
162
194
 
163
195
  class Collection < Array
164
196
 
165
- def initialize(name,parent)
197
+ include Seabright::CachedScripts
198
+
199
+ def initialize(name,owner)
166
200
  @name = name.to_s
167
- @parent = parent
201
+ @owner = owner
168
202
  end
169
203
 
170
204
  def remove!
171
- @parent.remove_collection! @name
205
+ @owner.remove_collection! @name
172
206
  end
173
207
 
174
208
  def latest
175
- indexed(:created_at,5,true).first
209
+ indexed(:created_at,5,true).first || first
176
210
  end
177
211
 
178
212
  def indexed(idx,num=-1,reverse=false)
179
213
  keys = keys_by_index(idx,num,reverse)
180
214
  out = Enumerator.new do |y|
181
215
  keys.each do |member|
182
- if a = RedisObject.find_by_key(member)
216
+ if a = class_const.find_by_key(member)
183
217
  y << a
184
218
  end
185
219
  end
@@ -194,18 +228,16 @@ module Seabright
194
228
  end
195
229
 
196
230
  def temp_key
197
- "zintersect_temp"
231
+ "#{key}::zintersect_temp::#{RedisObject.new_id(4)}"
198
232
  end
199
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
+
200
237
  def keys_by_index(idx,num=-1,reverse=false)
201
- keys = nil
202
- store.multi do
203
- store.zinterstore(temp_key, [index_key(idx), key], {:weights => ["1","0"]})
204
- keys = store.send(reverse ? :zrevrange : :zrange, temp_key, 0, num)
205
- store.del temp_key
206
- end
238
+ keys = run_script(reverse ? :RevScript : :FwdScript, [temp_key, index_key(idx), key, num])
207
239
  Enumerator.new do |y|
208
- keys.value.each do |member|
240
+ keys.each do |member|
209
241
  y << member
210
242
  end
211
243
  end
@@ -224,12 +256,15 @@ module Seabright
224
256
  return real_at(item_key(k))
225
257
  elsif k.is_a? Hash
226
258
  return match(k)
227
- elsif k.is_a? Number
259
+ elsif k.is_a? Integer
228
260
  return real_at(at(k))
229
261
  end
230
262
  return nil
231
263
  end
232
- alias_method :[], :find
264
+
265
+ def [](k)
266
+ find k
267
+ end
233
268
 
234
269
  def match(pkt)
235
270
  Enumerator.new do |y|
@@ -242,29 +277,25 @@ module Seabright
242
277
  end
243
278
 
244
279
  def real_at(key)
245
- RedisObject.find_by_key(key)
280
+ class_const.find_by_key(key)
246
281
  end
247
282
 
248
- # def [](idx)
249
- # class_const.find_by_key(at(idx))
250
- # end
251
-
252
283
  def objects
253
284
  each.to_a
254
285
  end
255
286
 
256
287
  def first
257
- RedisObject.find_by_key(super)
288
+ class_const.find_by_key(super)
258
289
  end
259
290
 
260
291
  def last
261
- RedisObject.find_by_key(super)
292
+ class_const.find_by_key(super)
262
293
  end
263
294
 
264
295
  def each
265
296
  out = Enumerator.new do |y|
266
297
  each_index do |key|
267
- if a = RedisObject.find_by_key(at(key))
298
+ if a = class_const.find_by_key(at(key))
268
299
  y << a
269
300
  end
270
301
  end
@@ -280,7 +311,7 @@ module Seabright
280
311
 
281
312
  def cleanup!
282
313
  each_index do |key|
283
- unless a = RedisObject.find_by_key(at(key))
314
+ unless a = class_const.find_by_key(at(key))
284
315
  puts "Deleting #{key} because not #{a.inspect}" if DEBUG
285
316
  delete at(key)
286
317
  end
@@ -299,7 +330,7 @@ module Seabright
299
330
  return nil unless block_given?
300
331
  Enumerator.new do |y|
301
332
  each_index do |key|
302
- if (a = RedisObject.find_by_key(at(key))) && block.call(a)
333
+ if (a = class_const.find_by_key(at(key))) && block.call(a)
303
334
  y << a
304
335
  end
305
336
  end
@@ -321,36 +352,35 @@ module Seabright
321
352
  store.zadd(key,store.zcount(key,"-inf", "+inf"),k)
322
353
  super(k)
323
354
  end
324
- alias_method :push, :<<
355
+
356
+ def push(obj)
357
+ self << obj
358
+ end
325
359
 
326
360
  def class_const
327
- Object.const_get(@name.to_s.classify.to_sym)
361
+ self.class.class_const_for(@name)
362
+ end
363
+
364
+ def store
365
+ class_const.store
328
366
  end
329
367
 
330
368
  def key
331
- "#{@parent ? "#{@parent.key}:" : ""}COLLECTION:#{@name}"
369
+ "#{@owner ? "#{@owner.key}:" : ""}COLLECTION:#{@name}"
332
370
  end
333
371
 
334
372
  class << self
335
373
 
336
- def load(name,parent)
337
- out = new(name,parent)
338
- out.replace store.zrange(out.key,0,-1)
374
+ def load(name,owner)
375
+ out = new(name,owner)
376
+ out.replace class_const_for(name).store.zrange(out.key,0,-1)
339
377
  out
340
378
  end
341
379
 
342
- private
343
-
344
- def store
345
- @@store ||= RedisObject.store
380
+ def class_const_for(name)
381
+ Object.const_get(name.to_s.classify.to_sym) rescue RedisObject
346
382
  end
347
-
348
- end
349
-
350
- private
351
-
352
- def store
353
- @@store ||= RedisObject.store
383
+
354
384
  end
355
385
 
356
386
  end
@@ -1,23 +1,36 @@
1
1
  module Seabright
2
2
  module DefaultValues
3
3
 
4
- def get(k)
5
- if (d = self.class.default_vals[k.to_sym]) && !d.nil?
6
- return d unless is_set?(k)
7
- end
8
- super(k)
9
- end
10
-
11
4
  module ClassMethods
12
5
 
13
6
  def default_vals
14
7
  @default_vals ||= {}
15
8
  end
16
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
+
17
26
  def register_default(k,vl)
18
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
19
33
  end
20
- alias_method :default_for, :register_default
21
34
 
22
35
  end
23
36
 
@@ -1,19 +1,43 @@
1
1
  module Seabright
2
2
  module Filters
3
3
 
4
- def filtered(method,*args)
5
- if filters = self.class.filters_for(method)
6
- filters.each do |f|
7
- args = send(f,*args)
4
+ module ClassMethods
5
+
6
+ def intercept_for_filters!
7
+ return if @intercept_for_filters
8
+ self.class_eval do
9
+
10
+ def filtered_method_call(method,*args)
11
+ if filters = self.class.filters_for(method)
12
+ filters.each do |f|
13
+ args = send(f,*args)
14
+ end
15
+ end
16
+ send("unfiltered_#{method.to_s}".to_sym,*args)
17
+ end
18
+
19
+ alias_method :unfiltered_get, :get unless method_defined?(:unfiltered_get)
20
+ def get(k)
21
+ filtered_method_call(:get,k)
22
+ end
23
+
24
+ alias_method :unfiltered_set, :set unless method_defined?(:unfiltered_set)
25
+ def set(k,v)
26
+ filtered_method_call(:set,k,v)
27
+ end
28
+
29
+ alias_method :unfiltered_setnx, :setnx unless method_defined?(:unfiltered_setnx)
30
+ def setnx(k,v)
31
+ filtered_method_call(:setnx,k,v)
32
+ end
33
+
8
34
  end
35
+ @intercept_for_filters = true
9
36
  end
10
- method(*args)
11
- end
12
-
13
- module ClassMethods
14
37
 
15
38
  def set_filter(filter)
16
39
  filter_method(:set,filter)
40
+ filter_method(:setnx,filter)
17
41
  end
18
42
 
19
43
  def get_filter(filter)
@@ -21,15 +45,9 @@ module Seabright
21
45
  end
22
46
 
23
47
  def filter_method(method, filter)
24
- unless method_filters[method.to_sym]
25
- method_filters[method.to_sym] ||= []
26
- filter!(method.to_sym)
27
- end
48
+ method_filters[method.to_sym] ||= []
28
49
  method_filters[method.to_sym] << filter.to_sym unless method_filters[method.to_sym].include?(filter.to_sym)
29
- end
30
-
31
- def filter!(method)
32
- self.class.instance_exec("")
50
+ intercept_for_filters!
33
51
  end
34
52
 
35
53
  def method_filters