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,56 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module DefaultSpec
4
+
5
+ TestValues = {
6
+ date: Date.today,
7
+ number: 27,
8
+ int: 356192,
9
+ float: 72.362517,
10
+ bool: true,
11
+ # boolean: false,
12
+ # array: [:test1,:test2],
13
+ json: {test1: true, test2: "false"}
14
+ }
15
+
16
+ TestData = TestValues.inject({}){|acc,(k,v)| acc["a_#{k}".to_sym] = v; acc }
17
+
18
+ # Delicious pizza!
19
+ class DefaultedObject < RedisObject
20
+
21
+ TestValues.keys.each do |type|
22
+ send(type.to_sym,"a_#{type}".to_sym)
23
+ end
24
+ default_for :testy, "test"
25
+ default_for :a_number, 42
26
+
27
+ end
28
+
29
+ describe Seabright::DefaultValues do
30
+ before do
31
+ RedisObject.store.flushdb
32
+ end
33
+
34
+ it "returns default value" do
35
+
36
+ obj = DefaultedObject.create(TestData)
37
+
38
+ obj.testy.should eq("test")
39
+ obj.get(:a_number).should eq(27)
40
+ obj.unset(:a_number)
41
+ obj.get(:a_number).should eq(42)
42
+
43
+ end
44
+
45
+ it "returns default value after unset" do
46
+
47
+ obj = DefaultedObject.create(TestData)
48
+
49
+ obj.get(:a_number).should eq(27)
50
+ obj.unset(:a_number)
51
+ obj.get(:a_number).should eq(42)
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module FilterSpec
4
+
5
+ class FilteredObject < RedisObject
6
+
7
+ def double(k,v)
8
+ (k == :testy) && v ? [:testy,v*2] : [k,v]
9
+ end
10
+ set_filter :double
11
+
12
+ end
13
+
14
+ describe Seabright::Filters do
15
+ before do
16
+ RedisObject.store.flushdb
17
+ end
18
+
19
+ it "modifies set and gets correctly" do
20
+
21
+ obj = FilteredObject.create("test")
22
+
23
+ obj.set(:testy,"test")
24
+ obj.get(:testy).should eq("testtest")
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module IndexSpec
4
+
5
+ TestValues = {
6
+ date: Date.today,
7
+ number: 27,
8
+ int: 356192,
9
+ float: 72.362517,
10
+ bool: true,
11
+ # boolean: false,
12
+ # array: [:test1,:test2],
13
+ json: {test1: true, test2: "false"}
14
+ }
15
+
16
+ TestData = TestValues.inject({}){|acc,(k,v)| acc["a_#{k}".to_sym] = v; acc }
17
+
18
+ # Delicious pizza!
19
+ class IndexedObject < RedisObject
20
+
21
+ TestValues.keys.each do |type|
22
+ send(type.to_sym,"a_#{type}".to_sym)
23
+ end
24
+
25
+ sort_by :a_number
26
+
27
+ end
28
+
29
+ describe Seabright::Indices do
30
+ before do
31
+ RedisObject.store.flushdb
32
+ end
33
+
34
+ it "indexes on integer field" do
35
+
36
+ 5.times do
37
+ obj = IndexedObject.create(a_number: Random.rand(100))
38
+ end
39
+
40
+ IndexedObject.indexed(:a_number,3,true).count.should eq(3)
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,96 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module RenameClassSpec
4
+ # Delicious pizza!
5
+ class Pizza < RedisObject; end
6
+ # We love toppings but our current name Topping is too ambiguous!
7
+ class Topping < RedisObject; end
8
+ # Let's change the class name to PizzaTopping!
9
+ class PizzaTopping < RedisObject; end
10
+
11
+ describe Seabright::Storage::Redis, "#rename_class" do
12
+ before do
13
+ RedisObject.store.flushdb
14
+
15
+ mozzarella = Topping.create(:mozzarella)
16
+ basil = Topping.create(:basil)
17
+ tomato = Topping.create(:tomato)
18
+ garlic = Topping.create(:garlic)
19
+ oregano = Topping.create(:oregano)
20
+ olive_oil = Topping.create(:olive_oil)
21
+
22
+ marinara = Pizza.create(:marinara)
23
+ [ tomato, garlic, oregano, olive_oil ].each{|topping| marinara << topping }
24
+
25
+ margherita = Pizza.create(:margherita)
26
+ [ tomato, mozzarella, basil, olive_oil ].each{|topping| margherita << topping }
27
+ end
28
+
29
+ it "setup works" do
30
+ margherita = Pizza.find(:margherita)
31
+ marinara = Pizza.find(:marinara)
32
+ mozzarella = Topping.find(:mozzarella)
33
+ basil = Topping.find(:basil)
34
+ tomato = Topping.find(:tomato)
35
+ garlic = Topping.find(:garlic)
36
+ oregano = Topping.find(:oregano)
37
+ olive_oil = Topping.find(:olive_oil)
38
+
39
+ [ mozzarella, basil, tomato, garlic, oregano, olive_oil ].each do |topping|
40
+ topping.get(:class).should == Topping.name
41
+ topping.get(:key).should == "RenameClassSpec::Topping:#{topping.id}"
42
+ topping.get(:topping_id).should == topping.id
43
+ end
44
+
45
+ Topping.indexed(:created_at).first.should_not be_nil
46
+ Topping.indexed(:created_at).count.should == 5
47
+
48
+ [margherita, marinara].each do |pizza|
49
+ pizza.pizza_toppings.should be_nil
50
+ pizza.toppings.count.should == 4
51
+ pizza.toppings.should include(tomato.hkey)
52
+ pizza.toppings.should include(olive_oil.hkey)
53
+ end
54
+
55
+ margherita.toppings.should include(basil.hkey)
56
+ margherita.toppings.should include(mozzarella.hkey)
57
+ marinara.toppings.should include(oregano.hkey)
58
+ marinara.toppings.should include(garlic.hkey)
59
+ end
60
+
61
+ it "renames a class" do
62
+ RedisObject.store.rename_class(:"RenameClassSpec::Topping", :"RenameClassSpec::PizzaTopping")
63
+
64
+ margherita = Pizza.find(:margherita)
65
+ marinara = Pizza.find(:marinara)
66
+ mozzarella = PizzaTopping.find(:mozzarella)
67
+ basil = PizzaTopping.find(:basil)
68
+ tomato = PizzaTopping.find(:tomato)
69
+ garlic = PizzaTopping.find(:garlic)
70
+ oregano = PizzaTopping.find(:oregano)
71
+ olive_oil = PizzaTopping.find(:olive_oil)
72
+
73
+ [ mozzarella, basil, tomato, garlic, oregano, olive_oil ].each do |topping|
74
+ topping.get(:class).should == PizzaTopping.name
75
+ topping.get(:key).should == "RenameClassSpec::PizzaTopping:#{topping.id}"
76
+ topping.get(:"renameclassspec::pizzatopping_id").should == topping.id
77
+ # topping.get(:topping_id).should == nil
78
+ end
79
+
80
+ [margherita, marinara].each do |pizza|
81
+ pizza.toppings.should be_nil
82
+ pizza.pizza_toppings.count.should == 4
83
+ pizza.pizza_toppings.should include(tomato.hkey)
84
+ pizza.pizza_toppings.should include(olive_oil.hkey)
85
+ end
86
+
87
+ margherita.pizza_toppings.should include(basil.hkey)
88
+ margherita.pizza_toppings.should include(mozzarella.hkey)
89
+ marinara.pizza_toppings.should include(oregano.hkey)
90
+ marinara.pizza_toppings.should include(garlic.hkey)
91
+
92
+ PizzaTopping.indexed(:created_at).first.should_not be_nil
93
+ PizzaTopping.indexed(:created_at).count.should == 5
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,38 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+ $LOAD_PATH.unshift File.dirname(__FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'rspec'
7
+
8
+ require 'simplecov'
9
+ require 'coveralls'
10
+
11
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
12
+ SimpleCov::Formatter::HTMLFormatter,
13
+ Coveralls::SimpleCov::Formatter
14
+ ]
15
+ SimpleCov.start do
16
+ add_filter "_spec.rb"
17
+ add_filter "/experimental/"
18
+ add_group "Extensions", "lib/redis_object/ext/"
19
+ end
20
+
21
+ require 'redis_object'
22
+
23
+ class DebugMode
24
+ def initialize(o=nil); @opts = o; end
25
+ def verbose?; debug? && !!@opts[:verbose]; end
26
+ def debug?; !!@opts; end
27
+ end
28
+ Debug = if ENV['DEBUG']
29
+ require 'debugger'
30
+ DEBUG = true
31
+ DebugMode.new(verbose:true)
32
+ else
33
+ DEBUG = false
34
+ DebugMode.new
35
+ end
36
+
37
+ raise 'must specify TEST_DB' unless ENV['TEST_DB']
38
+ RedisObject.configure_store({adapter:'Redis', db:ENV['TEST_DB']},:global,:alias)
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module TriggerSpec
4
+
5
+ class TimestampedObject < RedisObject;end
6
+
7
+ class UnTimestampedObject < RedisObject
8
+ # time_matters_not!
9
+ end
10
+
11
+ describe Seabright::Triggers do
12
+ before do
13
+ RedisObject.store.flushdb
14
+ end
15
+
16
+ it "should get recently created object" do
17
+
18
+ (1..5).each do |n|
19
+ TimestampedObject.create(n.to_s)
20
+ sleep Random.rand(1.0)
21
+ end
22
+
23
+ TimestampedObject.recently_created.first.id.should eq("5")
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module TriggerSpec
4
+
5
+ class TriggeredObject < RedisObject
6
+
7
+ int :raw_phone
8
+ bool :mailed_flag
9
+ bool :mail_was_sent_for_realz
10
+ date :srsly_updated
11
+
12
+ def track_phone_raw(k,v)
13
+ set(:raw_phone, v.gsub(/[^0-9]/,'').to_i)
14
+ end
15
+
16
+ trigger_on_set :phone, :track_phone_raw
17
+
18
+ def sent_mail(k,v)
19
+ set(:mail_was_sent_for_realz, true)
20
+ end
21
+
22
+ def updated_redundant(k,v)
23
+ set(:srsly_updated, Time.now)
24
+ end
25
+
26
+ trigger_on_set :mailed_flag, :sent_mail
27
+ trigger_on_update :updated_redundant
28
+
29
+ end
30
+
31
+ describe Seabright::Triggers do
32
+ before do
33
+ RedisObject.store.flushdb
34
+ end
35
+
36
+ it "triggers on a call" do
37
+
38
+ obj = TriggeredObject.create("trig")
39
+
40
+ obj.phone = "(970) 555-1212"
41
+ obj.raw_phone.should eq(9705551212)
42
+ success = obj.setnx(:mailed_flag,true)
43
+ success.should eq(true)
44
+ obj.mail_was_sent_for_realz.should eq(true)
45
+ success = obj.setnx(:mailed_flag,true)
46
+ success.should eq(false)
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,103 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module TypeSpec
4
+
5
+ TestValues = {
6
+ date: Date.today,
7
+ number: 27,
8
+ int: 356192,
9
+ float: 72.362517,
10
+ bool: true,
11
+ boolean: false,
12
+ array: ["test1","test2"],
13
+ json: {test1: true, test2: "false"}
14
+ }
15
+
16
+ TestData = TestValues.inject({}){|acc,(k,v)| acc["a_#{k}".to_sym] = v; acc }
17
+
18
+ class TypedObject < RedisObject
19
+ TestValues.keys.each do |type|
20
+ send(type.to_sym,"a_#{type}".to_sym)
21
+ end
22
+ end
23
+
24
+ class TypedChild < TypedObject; end
25
+
26
+ describe Seabright::Types do
27
+ before do
28
+ RedisObject.store.flushdb
29
+ end
30
+
31
+ it "can be created via data packet" do
32
+
33
+ obj = TypedObject.create(TestData)
34
+
35
+ TestData.each do |k,v|
36
+ obj.get(k).should eq(v)
37
+ end
38
+
39
+ end
40
+
41
+ it "inherits types" do
42
+
43
+ obj = TypedChild.create(TestData)
44
+
45
+ TestData.each do |k,v|
46
+ obj.get(k).should eq(v)
47
+ end
48
+
49
+ end
50
+
51
+ it "can be instantiated (new) via data packet" do
52
+
53
+ obj = TypedObject.new(TestData)
54
+
55
+ TestData.each do |k,v|
56
+ obj.get(k).should eq(v)
57
+ end
58
+
59
+ end
60
+
61
+ it "can be created via individual sets" do
62
+
63
+ obj = TypedObject.new
64
+
65
+ TestData.each do |k,v|
66
+ obj.set(k,v)
67
+ obj.get(k).should eq(v)
68
+ end
69
+
70
+ end
71
+
72
+ it "gets correct values after being found" do
73
+
74
+ objc = TypedObject.create(TestData)
75
+ obj = TypedObject.find(objc.id)
76
+
77
+ TestData.each do |k,v|
78
+ obj.get(k).should eq(v)
79
+ end
80
+
81
+ end
82
+
83
+ it "nullifies non-date date value" do
84
+
85
+ obj = TypedObject.new
86
+
87
+ obj.a_date = "sjahfgasjfg"
88
+ obj.a_date.should eq(nil)
89
+
90
+ end
91
+
92
+ it "describes itself" do
93
+
94
+ obj = TypedObject.create(TestData)
95
+ objc = TypedChild.create(TestData)
96
+
97
+ desc = TypedObject.describe
98
+ TypedObject.dump_schema(File.open("/tmp/redisobject_dump_test","w"))
99
+
100
+ end
101
+
102
+ end
103
+ end