redis_object 1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
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