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,34 +2,54 @@ module Seabright
2
2
  module Timestamps
3
3
 
4
4
  def update_timestamps
5
- return if @@time_irrelevant
5
+ # return unless self.class.time_matters?
6
6
  set(:created_at, Time.now) if !is_set?(:created_at)
7
7
  set(:updated_at, Time.now)
8
8
  end
9
9
 
10
- def mset(dat)
11
- super(dat)
12
- set(:updated_at, Time.now)
13
- end
14
-
15
- def set(k,v)
16
- super(k,v)
17
- set(:updated_at, Time.now) unless k.to_sym == :updated_at
18
- end
19
-
20
- def save
21
- super
22
- update_timestamps
23
- end
24
-
25
10
  module ClassMethods
26
11
 
27
- def time_matters_not!
28
- @@time_irrelevant = true
29
- @@sort_indices.delete(:created_at)
30
- @@sort_indices.delete(:updated_at)
12
+ def intercept_sets_for_timestamps!
13
+ return if @intercepted_sets_for_timestamps
14
+ self.class_eval do
15
+ alias_method :untimestamped_set, :set unless method_defined?(:untimestamped_set)
16
+ def set(k,v)
17
+ ret = untimestamped_set(k,v)
18
+ set(:updated_at, Time.now) unless k.to_sym == :updated_at
19
+ ret
20
+ end
21
+ alias_method :untimestamped_mset, :mset unless method_defined?(:untimestamped_mset)
22
+ def mset(dat)
23
+ ret = untimestamped_mset(dat)
24
+ set(:updated_at, Time.now)
25
+ ret
26
+ end
27
+ alias_method :untimestamped_setnx, :setnx unless method_defined?(:untimestamped_setnx)
28
+ def setnx(k,v)
29
+ ret = untimestamped_setnx(k,v)
30
+ set(:updated_at, Time.now) unless k.to_sym == :updated_at
31
+ ret
32
+ end
33
+ alias_method :untimestamped_save, :save unless method_defined?(:untimestamped_save)
34
+ def save
35
+ ret = untimestamped_save()
36
+ update_timestamps
37
+ ret
38
+ end
39
+ end
40
+ @intercepted_sets_for_timestamps = true
31
41
  end
32
42
 
43
+ # def time_matters?
44
+ # @time_irrelevant != true
45
+ # end
46
+ #
47
+ # def time_matters_not!
48
+ # @time_irrelevant = true
49
+ # sort_indices.delete(:created_at)
50
+ # sort_indices.delete(:updated_at)
51
+ # end
52
+ #
33
53
  def recently_created(num=5)
34
54
  self.indexed(:created_at,num,true)
35
55
  end
@@ -41,12 +61,13 @@ module Seabright
41
61
  end
42
62
 
43
63
  def self.included(base)
44
- @@time_irrelevant = false
64
+ # @time_irrelevant = false
45
65
  base.send(:sort_by,:created_at)
46
66
  base.send(:sort_by,:updated_at)
47
67
  base.send(:register_format,:created_at, :date)
48
68
  base.send(:register_format,:updated_at, :date)
49
69
  base.extend(ClassMethods)
70
+ base.intercept_sets_for_timestamps!
50
71
  end
51
72
 
52
73
  end
@@ -22,7 +22,12 @@ module Seabright
22
22
  end
23
23
 
24
24
  def format_date(val)
25
- val.instance_of?(DateTime) ? val : DateTime.parse(val)
25
+ begin
26
+ val.is_a?(DateTime) || val.is_a?(Date) || val.is_a?(Time) ? val : ( val.is_a?(String) ? DateTime.parse(val) : nil )
27
+ rescue StandardError => e
28
+ puts "Could not parse value as date using Date.parse. Returning nil instead. Value: #{val.inspect}\nError: #{e.inspect}" if DEBUG
29
+ nil
30
+ end
26
31
  end
27
32
 
28
33
  def score_date(val)
@@ -30,7 +35,11 @@ module Seabright
30
35
  end
31
36
 
32
37
  def format_array(val)
33
- eval val
38
+ Yajl::Parser.new(:symbolize_keys => true).parse(val)
39
+ end
40
+
41
+ def save_array(val)
42
+ Yajl::Encoder.encode(val)
34
43
  end
35
44
 
36
45
  def format_number(val)
@@ -58,72 +67,205 @@ module Seabright
58
67
  val=="true"
59
68
  end
60
69
 
61
- def score_boolean(val)
62
- val ? 1 : 0
63
- end
64
-
65
- def get(k)
66
- enforce_format(k,super(k))
70
+ def save_boolean(val)
71
+ val === true ? "true" : "false"
67
72
  end
68
73
 
69
- def mset(dat)
70
- dat.merge!(dat) {|k,v1,v2| save_format(k,v1) }
71
- super(dat)
72
- end
73
-
74
- def set(k,v)
75
- super(k,save_format(k,v))
74
+ def score_boolean(val)
75
+ val ? 1 : 0
76
76
  end
77
77
 
78
78
  module ClassMethods
79
79
 
80
80
  def date(k)
81
- field_formats[k] = :format_date
82
- score_formats[k] = :score_date
81
+ set_field_format(k, :format_date)
82
+ set_score_format(k, :score_date)
83
83
  end
84
84
 
85
85
  def number(k)
86
- field_formats[k] = :format_number
87
- score_formats[k] = :score_number
86
+ set_field_format(k, :format_number)
87
+ set_score_format(k, :score_number)
88
88
  end
89
89
  alias_method :int, :number
90
90
 
91
91
  def float(k)
92
- field_formats[k] = :format_float
93
- score_formats[k] = :score_float
92
+ set_field_format(k, :format_float)
93
+ set_score_format(k, :score_float)
94
94
  end
95
95
 
96
96
  def bool(k)
97
- field_formats[k] = :format_boolean
98
- score_formats[k] = :score_boolean
97
+ set_field_format(k, :format_boolean)
98
+ set_score_format(k, :score_boolean)
99
+ set_save_format(k, :save_boolean)
99
100
  end
100
101
  alias_method :boolean, :bool
101
102
 
102
103
  def array(k)
103
- field_formats[k] = :format_array
104
+ set_field_format(k, :format_array)
105
+ set_save_format(k, :save_array)
104
106
  end
105
107
 
106
108
  def json(k)
107
- field_formats[k] = :format_json
108
- save_formats[k] = :save_json
109
+ set_field_format(k, :format_json)
110
+ set_save_format(k, :save_json)
109
111
  end
110
112
 
111
113
  def field_formats
112
- @@field_formats ||= {}
114
+ @field_formats_hash ||= (defined?(superclass.field_formats) ? superclass.field_formats.clone : {})
113
115
  end
114
116
 
115
117
  def score_formats
116
- @@score_formats ||= {}
118
+ @score_formats_hash ||= (defined?(superclass.score_formats) ? superclass.score_formats.clone : {})
117
119
  end
118
120
 
119
121
  def save_formats
120
- @@save_formats ||= {}
122
+ @save_formats_hash ||= (defined?(superclass.save_formats) ? superclass.save_formats.clone : {})
123
+ end
124
+
125
+ def set_field_format(k, v)
126
+ field_formats_set_locally.add(k)
127
+ field_formats[k] = v
128
+ update_child_class_field_formats(k, v)
129
+ intercept_for_typing!
130
+ end
131
+
132
+ def field_formats_set_locally
133
+ @field_formats_set_locally_set ||= Set.new
134
+ end
135
+
136
+ def inherit_field_format(k, v)
137
+ unless fields_formats_set_locally.include? k
138
+ field_formats[k] = v
139
+ update_child_class_field_formats(k, v)
140
+ end
141
+ intercept_for_typing!
142
+ end
143
+
144
+ def update_child_class_field_formats(k, v)
145
+ child_classes.each do |child_class|
146
+ child_class.inherit_field_format(k, v)
147
+ end
148
+ end
149
+
150
+ def set_score_format(k, v)
151
+ score_formats_set_locally.add(k)
152
+ score_formats[k] = v
153
+ update_child_class_score_formats(k, v)
154
+ intercept_for_typing!
155
+ end
156
+
157
+ def score_formats_set_locally
158
+ @score_formats_set_locally_set ||= Set.new
159
+ end
160
+
161
+ def inherit_score_format(k, v)
162
+ unless scores_formats_set_locally.include? k
163
+ score_formats[k] = v
164
+ update_child_class_score_formats(k, v)
165
+ end
166
+ intercept_for_typing!
167
+ end
168
+
169
+ def update_child_class_score_formats(k, v)
170
+ child_classes.each do |child_class|
171
+ child_class.inherit_score_format(k, v)
172
+ end
173
+ end
174
+
175
+ def set_save_format(k, v)
176
+ save_formats_set_locally.add(k)
177
+ save_formats[k] = v
178
+ update_child_class_save_formats(k, v)
179
+ intercept_for_typing!
180
+ end
181
+
182
+ def save_formats_set_locally
183
+ @save_formats_set_locally_set ||= Set.new
184
+ end
185
+
186
+ def inherit_save_format(k, v)
187
+ unless save_formats_set_locally.include? k
188
+ save_formats[k] = v
189
+ update_child_class_save_formats(k, v)
190
+ end
191
+ intercept_for_typing!
192
+ end
193
+
194
+ def update_child_class_save_formats(k, v)
195
+ child_classes.each do |child_class|
196
+ child_class.inherit_save_format(k, v)
197
+ end
121
198
  end
122
199
 
123
200
  def register_format(k,fmt)
124
201
  send(fmt, k)
125
202
  end
126
203
 
204
+ def describe
205
+ all_keys.inject({}) do |acc,(k,v)|
206
+ if field_formats[k.to_sym]
207
+ acc[k.to_sym] ||= [field_formats[k.to_sym].to_s.gsub(/^format_/,'').to_sym, 0]
208
+ else
209
+ acc[k.to_sym] ||= [:string, 0]
210
+ end
211
+ acc[k.to_sym][1] += 1
212
+ acc
213
+ end
214
+ end
215
+
216
+ def dump_schema(file)
217
+ child_classes_set.sort {|a,b| a.name <=> b.name}.each do |child|
218
+ file.puts "# #{child.name}"
219
+ # sort fields by number of instances found
220
+ child.describe.sort {|a,b| b[1][1] <=> a[1][1]}.each do |field,(type, count)|
221
+ file.puts "#{field}: #{type} (#{count})"
222
+ end
223
+ file.puts
224
+ end
225
+ end
226
+
227
+ def intercept_for_typing!
228
+ return if @intercepted_for_typing
229
+ self.class_eval do
230
+
231
+ alias_method :untyped_get, :get unless method_defined?(:untyped_get)
232
+ def get(k)
233
+ enforce_format(k,untyped_get(k))
234
+ end
235
+
236
+ alias_method :untyped_mset, :mset unless method_defined?(:untyped_mset)
237
+ def mset(dat)
238
+ dat.merge!(dat) {|k,v1,v2| save_format(k,v1) }
239
+ untyped_mset(dat)
240
+ end
241
+
242
+ alias_method :untyped_set, :set unless method_defined?(:untyped_set)
243
+ def set(k,v)
244
+ untyped_set(k,save_format(k,v))
245
+ end
246
+
247
+ alias_method :untyped_setnx, :setnx unless method_defined?(:untyped_setnx)
248
+ def setnx(k,v)
249
+ untyped_setnx(k,save_format(k,v))
250
+ end
251
+
252
+ end
253
+ @intercepted_for_typing = true
254
+ end
255
+
256
+ private
257
+
258
+ def all_keys(limit=100)
259
+ steps = 0
260
+ all.inject([]) do |acc,obj|
261
+ store.hkeys(obj.hkey).each do |k|
262
+ acc << k.to_sym
263
+ end
264
+ steps += 1
265
+ return acc if steps >= limit
266
+ acc
267
+ end
268
+ end
127
269
  end
128
270
 
129
271
  def self.included(base)
@@ -131,4 +273,4 @@ module Seabright
131
273
  end
132
274
 
133
275
  end
134
- end
276
+ end
@@ -1,5 +1,5 @@
1
1
  module Seabright
2
2
  class RedisObject
3
- VERSION = "1.0"
3
+ VERSION = "1.1"
4
4
  end
5
5
  end
data/redis_object.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
  s.required_ruby_version = '>= 1.9.2'
22
+ s.add_dependency "utf8_utils"
22
23
  s.add_dependency "redis"
23
24
  s.add_dependency "yajl-ruby"
24
25
  s.add_dependency "activesupport"
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module ObjectTests
4
+ class User < RedisObject
5
+ use_store :global
6
+ end
7
+ class Thingy < RedisObject
8
+
9
+ end
10
+ class Doodad < RedisObject
11
+
12
+ end
13
+ end
14
+
15
+ describe RedisObject do
16
+
17
+ it "can reconnect to redis" do
18
+ RedisObject.reconnect!
19
+ RedisObject.store.reconnect!
20
+ end
21
+
22
+ it "can dump to a file" do
23
+ obj = ObjectTests::User.create("test")
24
+ RedisObject.dump_stores_to_files("/tmp")
25
+ RedisObject.store.flushdb
26
+ end
27
+
28
+ it "can restore from a file" do
29
+ RedisObject.restore_stores_from_files("/tmp")
30
+ ObjectTests::User.find("test").should be_a(ObjectTests::User)
31
+ end
32
+
33
+ it "can get all stores" do
34
+ RedisObject.stores.count.should eq(1)
35
+ end
36
+
37
+ it "can reset stores" do
38
+ RedisObject.stores.each do |(name,store)|
39
+ store.reset
40
+ end
41
+ end
42
+
43
+ end
data/spec/base_spec.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
- DEBUG = false
4
-
5
3
  module ObjectTests
6
4
  class User < RedisObject
7
-
5
+ use_store :global
8
6
  end
9
7
  class Thingy < RedisObject
10
8
 
@@ -15,41 +13,78 @@ module ObjectTests
15
13
  end
16
14
 
17
15
  describe RedisObject do
16
+
18
17
  it "can be created" do
19
18
  obj = ObjectTests::User.new
20
19
  end
20
+
21
21
  it "can be created with an id" do
22
22
  obj = ObjectTests::User.new("test")
23
23
  obj.save
24
24
  end
25
+
26
+ it "can create an id" do
27
+ obj = ObjectTests::User.new("test")
28
+ obj.new_id
29
+ end
30
+
31
+ it "can reserve an id" do
32
+ obj = ObjectTests::User.new("test")
33
+ obj.reserve("test")
34
+ end
35
+
25
36
  it "should be found by id" do
26
37
  obj = ObjectTests::User.find("test")
27
38
  obj.should_not be_nil
28
39
  end
40
+
41
+ it "can recollect objects" do
42
+ ObjectTests::User.recollect!
43
+ end
44
+
45
+ it "get get the first object (random)" do
46
+ obj = ObjectTests::User.first
47
+ obj.should be_a(ObjectTests::User)
48
+ end
49
+
29
50
  it "should save stuff" do
30
51
  obj = ObjectTests::User.find("test")
31
52
  obj.stuff = "yay!"
32
- obj.stuff.should == "yay!"
53
+ obj[:stuff].should eq("yay!")
54
+ obj[:stuff] = "yayyay!"
55
+ obj.stuff.should eq("yayyay!")
56
+ obj.stuff = "yay!"
57
+ obj.stuff.should eq("yay!")
33
58
  end
59
+
34
60
  it "should retrieve stuff" do
35
61
  obj = ObjectTests::User.find("test")
36
- obj.stuff.should == "yay!"
62
+ obj.stuff.should eq("yay!")
37
63
  end
64
+
38
65
  it "can collect other objects" do
39
66
  obj = ObjectTests::User.find("test")
40
67
  obj2 = ObjectTests::Thingy.new("yay")
41
68
  obj << obj2
42
69
  obj.should have_collection(:thingies)
43
70
  end
71
+
44
72
  it "can reference other objects" do
45
73
  obj = ObjectTests::User.find("test")
46
74
  obj3 = ObjectTests::Doodad.new("woo")
47
75
  obj.reference obj3
48
76
  obj.should have_collection(:doodads)
49
77
  end
78
+
79
+ it "can convert raw to json" do
80
+ obj = ObjectTests::User.find("test")
81
+ obj.to_json.length.should > 0
82
+ end
83
+
50
84
  it "should be deletable" do
51
85
  obj = ObjectTests::User.find("test")
52
86
  obj.delete!
53
87
  ObjectTests::User.find("test").should be_nil
54
88
  end
55
- end
89
+
90
+ end