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.
- 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,89 @@
|
|
1
|
+
module Seabright
|
2
|
+
|
3
|
+
module Validation
|
4
|
+
|
5
|
+
def valid?
|
6
|
+
ret = true
|
7
|
+
errors = []
|
8
|
+
self.class.required_fields.each do |k|
|
9
|
+
unless is_valid?(k)
|
10
|
+
errors.push "#{k} is not valid and is needed to process."
|
11
|
+
ret = false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
self.class.required_collections.each do |k|
|
15
|
+
unless has_collection?(k)
|
16
|
+
errors.push "Need a #{k} collection to proceed."
|
17
|
+
ret = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
ret
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_set?(k)
|
24
|
+
store.hexists(hkey,k)
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_valid?(k)
|
28
|
+
return false unless is_set?(k)
|
29
|
+
return true unless validations[k]
|
30
|
+
if val = get(k)
|
31
|
+
case validations[k].class
|
32
|
+
when Regexp
|
33
|
+
return false unless validations[k].match(val)
|
34
|
+
when Array
|
35
|
+
return false unless validations[k].include?(val)
|
36
|
+
else
|
37
|
+
if validations[k].class == Class
|
38
|
+
return false unless val.class == validations[k]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def validations
|
46
|
+
@validations ||= self.class.validations
|
47
|
+
end
|
48
|
+
|
49
|
+
module ClassMethods
|
50
|
+
|
51
|
+
def require_field(*args)
|
52
|
+
args.each do |k|
|
53
|
+
required_fields.push k.to_sym
|
54
|
+
end
|
55
|
+
end
|
56
|
+
alias_method :require_fields, :require_field
|
57
|
+
|
58
|
+
def require_collection(*args)
|
59
|
+
args.each do |k|
|
60
|
+
required_collections.push k.to_sym
|
61
|
+
end
|
62
|
+
end
|
63
|
+
alias_method :require_collections, :require_collection
|
64
|
+
|
65
|
+
def required_collections
|
66
|
+
@required_cols ||= []
|
67
|
+
end
|
68
|
+
|
69
|
+
def required_fields
|
70
|
+
@required ||= []
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate(k,rl)
|
74
|
+
validations[k] = rl
|
75
|
+
end
|
76
|
+
|
77
|
+
def validations
|
78
|
+
@validations ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.included(base)
|
84
|
+
base.extend(ClassMethods)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "redis_object/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "redis_object"
|
7
|
+
s.version = Seabright::RedisObject::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["John Bragg"]
|
10
|
+
s.email = ["john@seabrightstudios.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Maps arbitrary objects to a Redis store with indices and smart retrieval and storage mechanisms.}
|
13
|
+
s.description = %q{}
|
14
|
+
|
15
|
+
s.rubyforge_project = "redis_object"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.required_ruby_version = '>= 1.9.2'
|
22
|
+
s.add_dependency "utf8_utils"
|
23
|
+
s.add_dependency "redis"
|
24
|
+
s.add_dependency "yajl-ruby"
|
25
|
+
s.add_dependency "activesupport"
|
26
|
+
end
|
@@ -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
ADDED
@@ -0,0 +1,90 @@
|
|
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 be created" do
|
18
|
+
obj = ObjectTests::User.new
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can be created with an id" do
|
22
|
+
obj = ObjectTests::User.new("test")
|
23
|
+
obj.save
|
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
|
+
|
36
|
+
it "should be found by id" do
|
37
|
+
obj = ObjectTests::User.find("test")
|
38
|
+
obj.should_not be_nil
|
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
|
+
|
50
|
+
it "should save stuff" do
|
51
|
+
obj = ObjectTests::User.find("test")
|
52
|
+
obj.stuff = "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!")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should retrieve stuff" do
|
61
|
+
obj = ObjectTests::User.find("test")
|
62
|
+
obj.stuff.should eq("yay!")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can collect other objects" do
|
66
|
+
obj = ObjectTests::User.find("test")
|
67
|
+
obj2 = ObjectTests::Thingy.new("yay")
|
68
|
+
obj << obj2
|
69
|
+
obj.should have_collection(:thingies)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can reference other objects" do
|
73
|
+
obj = ObjectTests::User.find("test")
|
74
|
+
obj3 = ObjectTests::Doodad.new("woo")
|
75
|
+
obj.reference obj3
|
76
|
+
obj.should have_collection(:doodads)
|
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
|
+
|
84
|
+
it "should be deletable" do
|
85
|
+
obj = ObjectTests::User.find("test")
|
86
|
+
obj.delete!
|
87
|
+
ObjectTests::User.find("test").should be_nil
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
module BenchmarkSpec
|
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 BenchmarkedObject < RedisObject
|
19
|
+
|
20
|
+
TestValues.keys.each do |type|
|
21
|
+
send(type.to_sym,"a_#{type}".to_sym)
|
22
|
+
end
|
23
|
+
|
24
|
+
def aggregate
|
25
|
+
sleep 0.1
|
26
|
+
42.7
|
27
|
+
end
|
28
|
+
benchmark :aggregate
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Seabright::Benchmark do
|
33
|
+
before do
|
34
|
+
RedisObject.store.flushdb
|
35
|
+
end
|
36
|
+
|
37
|
+
it "benchmarks a call" do
|
38
|
+
|
39
|
+
obj = BenchmarkedObject.create(TestData)
|
40
|
+
|
41
|
+
obj.aggregate.should eq(42.7)
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
module CollectionSpec
|
4
|
+
|
5
|
+
class GrandDad < RedisObject; end
|
6
|
+
class Daddy < RedisObject; end
|
7
|
+
class Son < RedisObject; end
|
8
|
+
class GrandSon < RedisObject; end
|
9
|
+
|
10
|
+
describe Seabright::Collections do
|
11
|
+
before do
|
12
|
+
|
13
|
+
RedisObject.store.flushdb
|
14
|
+
@granddad = GrandDad.create("gramps")
|
15
|
+
@dad = Daddy.create("dad")
|
16
|
+
@son = Son.create("sun")
|
17
|
+
@sonny = Son.create("sonny")
|
18
|
+
@grandson = GrandSon.create("baby")
|
19
|
+
GrandDad << @dad
|
20
|
+
@granddad << @dad
|
21
|
+
@granddad << @son
|
22
|
+
@dad << @son
|
23
|
+
@dad.push @sonny
|
24
|
+
@son.reference @grandson
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can reference other objects" do
|
29
|
+
|
30
|
+
@granddad.daddies.count.should eq(1)
|
31
|
+
@granddad.sons.count.should eq(1)
|
32
|
+
@dad.sons.count.should eq(2)
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have appropriate collections" do
|
37
|
+
|
38
|
+
@granddad.has_collection?(:daddies).should eq(true)
|
39
|
+
@granddad.has_collection?(:sons).should eq(true)
|
40
|
+
@granddad.has_collection?(:grand_sons).should eq(false)
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should cache collection accessors" do
|
45
|
+
|
46
|
+
@granddad.daddies.should be_a(Seabright::Collection)
|
47
|
+
@granddad.daddy.should be_a(Daddy)
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should be able to iterate over collected items" do
|
52
|
+
|
53
|
+
@dad.sons.count.should eq(2)
|
54
|
+
@dad.sons.each do |s|
|
55
|
+
s.should be_a(Son)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
it "can find items in a collection" do
|
61
|
+
|
62
|
+
@dad = Daddy.find("dad")
|
63
|
+
@dad.sons.find(son_id: "sonny").first.should be_a(Son)
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can get one collection item" do
|
68
|
+
|
69
|
+
@dad = Daddy.find("dad")
|
70
|
+
@dad.son.should be_a(Son)
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can get one collection item" do
|
75
|
+
|
76
|
+
@dad = Daddy.find("dad")
|
77
|
+
@dad.get(:son).should be_a(Son)
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
it "can get backreferences of a type" do
|
82
|
+
|
83
|
+
@son.backreferences(GrandDad).each do |s|
|
84
|
+
s.should be_a(GrandDad)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
it "can dereference itself" do
|
90
|
+
|
91
|
+
@dad.dereference_from_backreferences
|
92
|
+
GrandDad.find("gramps").daddies.count.should eq(0)
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
it "can delete items from a collection" do
|
97
|
+
|
98
|
+
@dad.sons.delete(@sonny)
|
99
|
+
@dad.sons.find(son_id: "sonny").first.should be_a(NilClass)
|
100
|
+
@dad.sons.count.should eq(1)
|
101
|
+
|
102
|
+
@dad.delete_child(@son)
|
103
|
+
@dad.get(:sons).count.should eq(0)
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
it "can delete a collection" do
|
108
|
+
|
109
|
+
@dad.sons.remove!
|
110
|
+
@dad.collections.keys.should_not include(:sons)
|
111
|
+
@son.remove_collection!(:grand_sons)
|
112
|
+
@son.collections.keys.should_not include(:grand_sons)
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
it "retrieves via index" do
|
117
|
+
|
118
|
+
5.times do
|
119
|
+
@dad << Son.create
|
120
|
+
end
|
121
|
+
|
122
|
+
# @dad.sons.indexed(:created_at,3,true).count.should eq(3)
|
123
|
+
|
124
|
+
Son.indexed(:created_at,3,true).count.should eq(3)
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
it "can collect on classes themselves" do
|
129
|
+
|
130
|
+
5.times do
|
131
|
+
Daddy << Son.create
|
132
|
+
end
|
133
|
+
|
134
|
+
Daddy.get(:sons).should be_a(Seabright::Collection)
|
135
|
+
Daddy.get(:sons).count.should eq(5)
|
136
|
+
Daddy.get(:son).should be_a(Son)
|
137
|
+
Daddy.delete_child(Daddy.get(:son))
|
138
|
+
Daddy.get(:sons).count.should eq(4)
|
139
|
+
# Daddy.get(:sons).indexed(:created_at,3,true).count.should eq(3)
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|