redis_object 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.coveralls.yml +1 -0
- data/.gitignore +6 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/README.markdown +179 -0
- data/Rakefile +10 -0
- data/lib/redis_object.rb +47 -0
- data/lib/redis_object/base.rb +408 -0
- data/lib/redis_object/collection.rb +388 -0
- data/lib/redis_object/defaults.rb +42 -0
- data/lib/redis_object/experimental/history.rb +49 -0
- data/lib/redis_object/ext/benchmark.rb +34 -0
- data/lib/redis_object/ext/cleaner.rb +14 -0
- data/lib/redis_object/ext/filters.rb +68 -0
- data/lib/redis_object/ext/script_cache.rb +92 -0
- data/lib/redis_object/ext/shardable.rb +18 -0
- data/lib/redis_object/ext/triggers.rb +101 -0
- data/lib/redis_object/ext/view_caching.rb +258 -0
- data/lib/redis_object/ext/views.rb +102 -0
- data/lib/redis_object/external_index.rb +25 -0
- data/lib/redis_object/indices.rb +97 -0
- data/lib/redis_object/inheritance_tracking.rb +23 -0
- data/lib/redis_object/keys.rb +37 -0
- data/lib/redis_object/storage.rb +93 -0
- data/lib/redis_object/storage/adapter.rb +46 -0
- data/lib/redis_object/storage/aws.rb +71 -0
- data/lib/redis_object/storage/mysql.rb +47 -0
- data/lib/redis_object/storage/redis.rb +119 -0
- data/lib/redis_object/timestamps.rb +74 -0
- data/lib/redis_object/tpl.rb +17 -0
- data/lib/redis_object/types.rb +276 -0
- data/lib/redis_object/validation.rb +89 -0
- data/lib/redis_object/version.rb +5 -0
- data/redis_object.gemspec +26 -0
- data/spec/adapter_spec.rb +43 -0
- data/spec/base_spec.rb +90 -0
- data/spec/benchmark_spec.rb +46 -0
- data/spec/collections_spec.rb +144 -0
- data/spec/defaults_spec.rb +56 -0
- data/spec/filters_spec.rb +29 -0
- data/spec/indices_spec.rb +45 -0
- data/spec/rename_class_spec.rb +96 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/timestamp_spec.rb +28 -0
- data/spec/trigger_spec.rb +51 -0
- data/spec/types_spec.rb +103 -0
- data/spec/view_caching_spec.rb +130 -0
- data/spec/views_spec.rb +72 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
data/spec/types_spec.rb
ADDED
@@ -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
|