redis-attrs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +146 -0
- data/Rakefile +1 -0
- data/lib/redis-attrs.rb +92 -0
- data/lib/redis-attrs/base.rb +20 -0
- data/lib/redis-attrs/boolean.rb +9 -0
- data/lib/redis-attrs/complex.rb +47 -0
- data/lib/redis-attrs/date.rb +9 -0
- data/lib/redis-attrs/float.rb +9 -0
- data/lib/redis-attrs/hash.rb +10 -0
- data/lib/redis-attrs/integer.rb +9 -0
- data/lib/redis-attrs/list.rb +10 -0
- data/lib/redis-attrs/scalar.rb +38 -0
- data/lib/redis-attrs/string.rb +6 -0
- data/lib/redis-attrs/time.rb +9 -0
- data/lib/redis-attrs/version.rb +5 -0
- data/redis-attrs.gemspec +24 -0
- data/spec/redis_attrs_spec.rb +172 -0
- data/spec/spec_helper.rb +12 -0
- metadata +134 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ernesto Garcia
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# Redis::Attrs - Add attributes to Ruby classes backed by Redis
|
2
|
+
|
3
|
+
This gem is an amalgamation of the ideas found within the [redis_props][redis_props]
|
4
|
+
and [redis-objects][redis-objects] gems, plus a few new ideas here and there. It
|
5
|
+
provides a way to define, on any Ruby class, some attributes that are backed by
|
6
|
+
[Redis][redis] behind the curtain.
|
7
|
+
|
8
|
+
Here are some of the characteristics that define this library:
|
9
|
+
|
10
|
+
- Easy to integrate directly with existing ORMs - ActiveRecord, DataMapper, etc.
|
11
|
+
- Not confined to ORMs. Use it in whatever Ruby classes you want.
|
12
|
+
- It does work better with ORMs because it requires each object to provide a
|
13
|
+
unique id identifying it, something already provided by most ORMs out of the box.
|
14
|
+
- Supports scalar value types, as well as more complex value types such as
|
15
|
+
collection types, counters and locks.
|
16
|
+
- Integers are returned as integers, rather than '17'. The same holds for dates,
|
17
|
+
times, floats, etc.
|
18
|
+
- Collection types can be assigned a Ruby-style collection to set their whole
|
19
|
+
content at once, resetting whatever content there was in the Redis key.
|
20
|
+
- The user can add support for more scalar data types with no built-in support.
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
gem 'redis-attrs'
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
|
34
|
+
$ gem install redis-attrs
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Start by defining some attributes on your class:
|
39
|
+
|
40
|
+
class Film
|
41
|
+
include Redis::Attrs
|
42
|
+
redis_attrs :title => :string, :length => :integer
|
43
|
+
redis_attrs :released_on => :date, :cast => :list
|
44
|
+
|
45
|
+
# Remember that the objects need an id for this to work
|
46
|
+
attr_reader :id
|
47
|
+
def initialize(id)
|
48
|
+
@id = id
|
49
|
+
end
|
50
|
+
|
51
|
+
def presentation_title
|
52
|
+
"#{title} (#{released_on.year})"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Then you can use those attributes as you would regularly, but internally they are
|
57
|
+
reading from and writing to Redis.
|
58
|
+
|
59
|
+
>> film = Film.new(3)
|
60
|
+
>> film.title = "Argo"
|
61
|
+
>> film.released_on = "2012-10-12"
|
62
|
+
>> puts film.presentation_title
|
63
|
+
Argo (2012)
|
64
|
+
>> puts film.cast.size
|
65
|
+
0
|
66
|
+
>> film.cast = ["Ben Affleck", "Alan Arkin", "Brian Cranston"]
|
67
|
+
>> puts film.cast.size
|
68
|
+
3
|
69
|
+
>> puts film.cast[-3]
|
70
|
+
Ben Affleck
|
71
|
+
|
72
|
+
`Redis::Attrs` will work on _any_ class that provides an `id` method that returns
|
73
|
+
a unique value. `Redis::Attrs` will automatically create keys that are unique to
|
74
|
+
each object, in the format:
|
75
|
+
|
76
|
+
class_name:id:attr_name
|
77
|
+
|
78
|
+
### Supported types
|
79
|
+
|
80
|
+
`Redis::Attrs` supports the following scalar types: `string`, `integer`, `float`,
|
81
|
+
`boolean`, `date` and `time`. These are automatically serialized and deserialized
|
82
|
+
when written to and read from Redis.
|
83
|
+
|
84
|
+
In addition, the library also supports some collection types and a couple other
|
85
|
+
non-scalar types: `list`, `hash`, `set`, `sorted_set`, `counter` and `lock`. These
|
86
|
+
are all implemented using the [redis-objects][redis-objects] gem, each type handled
|
87
|
+
by a class that encapsulate all Redis logic around them.
|
88
|
+
|
89
|
+
### Defining new scalar types
|
90
|
+
|
91
|
+
In addition to the predefined scalar types listed above, the user can define its
|
92
|
+
own scalar types, by subclassing `Redis::Attrs::Scalar` and defining how to serialize
|
93
|
+
and deserialize its values.
|
94
|
+
|
95
|
+
The following example defines a data-type that stores its values serialized as JSON.
|
96
|
+
The `serialize` and `deserialize` methods define how this process is done. After
|
97
|
+
registering the type with `Redis::Attrs`, a new attribute is added to the class
|
98
|
+
`Film` defined above.
|
99
|
+
|
100
|
+
class JSONScalar < Redis::Attrs::Scalar
|
101
|
+
def serialize(value)
|
102
|
+
value.to_json
|
103
|
+
end
|
104
|
+
|
105
|
+
def deserialize(value)
|
106
|
+
JSON.parse(value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
Redis::Attrs.register_type(:json, JSONScalar)
|
111
|
+
|
112
|
+
class Film
|
113
|
+
redis_attrs :director => :json
|
114
|
+
end
|
115
|
+
|
116
|
+
After the definitions above, more complex data structures could be stored as a single
|
117
|
+
scalar value, by being serialized as JSON.
|
118
|
+
|
119
|
+
>> film = Film.new(1)
|
120
|
+
>> film.director = { "first_name" => "Ben", "last_name" => "Affleck" }
|
121
|
+
>> puts Redis::Attrs.redis.get("film:1:director")
|
122
|
+
{"first_name":"Ben","last_name":"Affleck"}
|
123
|
+
|
124
|
+
### Attribute configuration options
|
125
|
+
|
126
|
+
The complex attribute types support some configuration options, mostly specific to
|
127
|
+
each type. When an attribute needs to be configured with some of these options, then
|
128
|
+
it must be declared with the singular version of the method `redis_attrs`, like below:
|
129
|
+
|
130
|
+
redis_attr :crawl, :lock, :expiration => 15.minutes
|
131
|
+
redis_attr :cast, :list, :marshal => true
|
132
|
+
|
133
|
+
For more details about the supported configuration options for each of the complex
|
134
|
+
data types, please refer to the [redis-objects][redis-objects] gem.
|
135
|
+
|
136
|
+
## Contributing
|
137
|
+
|
138
|
+
1. Fork it
|
139
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
140
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
141
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
142
|
+
5. Create new Pull Request
|
143
|
+
|
144
|
+
[redis]: http://redis.io
|
145
|
+
[redis_props]: http://github.com/obie/redis_props
|
146
|
+
[redis-objects]: http://github.com/nateware/redis-objects
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/redis-attrs.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require "redis-attrs/version"
|
2
|
+
require "redis"
|
3
|
+
require "active_support/inflector"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
module Attrs
|
7
|
+
def self.redis
|
8
|
+
@redis ||= Redis.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.redis=(r)
|
12
|
+
raise ArgumentError, "Redis Attrs: Invalid Redis instance" unless r.is_a?(Redis)
|
13
|
+
@redis = r
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def redis
|
18
|
+
Redis::Attrs.redis
|
19
|
+
end
|
20
|
+
|
21
|
+
def redis_key_prefix
|
22
|
+
@redis_key_refix ||= ActiveSupport::Inflector.underscore(self.name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def redis_attrs(attrs = nil)
|
26
|
+
@redis_attrs ||= []
|
27
|
+
return @redis_attrs if attrs.nil?
|
28
|
+
attrs.each do |name, type|
|
29
|
+
redis_attr name, type
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def redis_attr(name, type, options = {})
|
34
|
+
@redis_attrs ||= []
|
35
|
+
klass = Redis::Attrs::supported_types[type]
|
36
|
+
raise ArgumentError, "Unknown Redis::Attr type #{type}" if klass.nil?
|
37
|
+
attr = klass.new(self, name, type, options)
|
38
|
+
@redis_attrs << attr
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
def redis
|
44
|
+
Redis::Attrs.redis
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.included(receiver)
|
49
|
+
receiver.extend(ClassMethods)
|
50
|
+
receiver.send(:include, InstanceMethods)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.supported_types
|
54
|
+
@supported_types ||= {
|
55
|
+
# Scalar types
|
56
|
+
string: Redis::Attrs::String,
|
57
|
+
boolean: Redis::Attrs::Boolean,
|
58
|
+
date: Redis::Attrs::Date,
|
59
|
+
time: Redis::Attrs::Time,
|
60
|
+
integer: Redis::Attrs::Integer,
|
61
|
+
float: Redis::Attrs::Float,
|
62
|
+
|
63
|
+
# Complex types
|
64
|
+
counter: Redis::Attrs::Complex,
|
65
|
+
lock: Redis::Attrs::Complex,
|
66
|
+
hash: Redis::Attrs::Hash,
|
67
|
+
list: Redis::Attrs::List,
|
68
|
+
set: Redis::Attrs::List,
|
69
|
+
sorted_set: Redis::Attrs::Hash,
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.register_type(type, klass)
|
74
|
+
type = type.to_sym
|
75
|
+
raise ArgumentError, "Redis attr type #{type} is already defined" if supported_types.include?(type)
|
76
|
+
raise ArgumentError, "Class implementing new type #{type} must be a subclass of Redis::Attrs::Scalar" unless klass.ancestors.include?(Scalar)
|
77
|
+
@supported_types[type] = klass
|
78
|
+
end
|
79
|
+
|
80
|
+
autoload :Base, 'redis-attrs/base'
|
81
|
+
autoload :Scalar, 'redis-attrs/scalar'
|
82
|
+
autoload :String, 'redis-attrs/string'
|
83
|
+
autoload :Boolean, 'redis-attrs/boolean'
|
84
|
+
autoload :Date, 'redis-attrs/date'
|
85
|
+
autoload :Time, 'redis-attrs/time'
|
86
|
+
autoload :Integer, 'redis-attrs/integer'
|
87
|
+
autoload :Float, 'redis-attrs/float'
|
88
|
+
autoload :Complex, 'redis-attrs/complex'
|
89
|
+
autoload :List, 'redis-attrs/list'
|
90
|
+
autoload :Hash, 'redis-attrs/hash'
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Redis
|
2
|
+
module Attrs
|
3
|
+
class Base
|
4
|
+
attr_reader :klass, :name, :type, :options
|
5
|
+
|
6
|
+
def initialize(klass, name, type, options)
|
7
|
+
@klass, @name, @type, @options = klass, name, type, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def redis
|
11
|
+
Redis::Attrs.redis
|
12
|
+
end
|
13
|
+
|
14
|
+
def redis_key(id)
|
15
|
+
raise "id attribute is required in order to properly store Redis attributes" if id.nil?
|
16
|
+
"#{klass.redis_key_prefix}:#{id}:#{name}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "redis/hash_key"
|
2
|
+
require "redis/list"
|
3
|
+
require "redis/set"
|
4
|
+
require "redis/sorted_set"
|
5
|
+
require "redis/lock"
|
6
|
+
require "redis/counter"
|
7
|
+
|
8
|
+
class Redis
|
9
|
+
module Attrs
|
10
|
+
class Complex < Base
|
11
|
+
|
12
|
+
def initialize(klass, name, type, options)
|
13
|
+
super
|
14
|
+
attr = self
|
15
|
+
attr_class = self.class.redis_object_class[self.type]
|
16
|
+
|
17
|
+
# Define the getter
|
18
|
+
klass.send(:define_method, name) do
|
19
|
+
instance_variable_get("@#{name}") || begin
|
20
|
+
obj = attr_class.new attr.redis_key(id), redis, options
|
21
|
+
instance_variable_set("@#{name}", obj)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO: Add support for collection setters
|
26
|
+
if self.respond_to?(:setter)
|
27
|
+
klass.send(:define_method, "#{name}=") do |value|
|
28
|
+
obj = send(name)
|
29
|
+
attr.setter(id, obj, value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.redis_object_class
|
35
|
+
@@redis_object_class ||= {
|
36
|
+
lock: Redis::Lock,
|
37
|
+
counter: Redis::Counter,
|
38
|
+
hash: Redis::HashKey,
|
39
|
+
list: Redis::List,
|
40
|
+
set: Redis::Set,
|
41
|
+
sorted_set: Redis::SortedSet,
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "time"
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module Attrs
|
5
|
+
class Scalar < Base
|
6
|
+
|
7
|
+
def initialize(klass, name, type, options)
|
8
|
+
super
|
9
|
+
attr = self
|
10
|
+
|
11
|
+
# Define the getter
|
12
|
+
klass.send(:define_method, name) do
|
13
|
+
value = redis.get attr.redis_key(id)
|
14
|
+
value.nil? ? nil : attr.deserialize(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Define the setter
|
18
|
+
klass.send(:define_method, "#{name}=") do |value|
|
19
|
+
if value.nil?
|
20
|
+
redis.del attr.redis_key(id)
|
21
|
+
else
|
22
|
+
value = attr.serialize(value)
|
23
|
+
redis.set attr.redis_key(id), value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def serialize(value)
|
29
|
+
value.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def deserialize(value)
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/redis-attrs.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'redis-attrs/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "redis-attrs"
|
8
|
+
gem.version = Redis::Attrs::VERSION
|
9
|
+
gem.authors = ["Ernesto Garcia"]
|
10
|
+
gem.email = ["gnapse@gmail.com"]
|
11
|
+
gem.description = %q{A module that allows Ruby objects to define attributes backed by a Redis data store. Works with any class or ORM.}
|
12
|
+
gem.summary = %q{Add persistent object attributes backed by redis}
|
13
|
+
gem.homepage = "http://github.com/gnapse/redis-attrs"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec"
|
21
|
+
gem.add_dependency "redis"
|
22
|
+
gem.add_dependency "activesupport"
|
23
|
+
gem.add_dependency "redis-objects"
|
24
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
class Film
|
5
|
+
include Redis::Attrs
|
6
|
+
redis_attrs title: :string, released_on: :date, length: :integer
|
7
|
+
redis_attrs created_at: :time, rating: :float, featured: :boolean
|
8
|
+
|
9
|
+
def id
|
10
|
+
1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class JSONScalar < Redis::Attrs::Scalar
|
15
|
+
def serialize(value)
|
16
|
+
value.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
def deserialize(value)
|
20
|
+
JSON.parse(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Redis::Attrs do
|
25
|
+
let(:redis) { Redis::Attrs.redis }
|
26
|
+
let(:film) { Film.new }
|
27
|
+
|
28
|
+
it "has a version number" do
|
29
|
+
Redis::Attrs::VERSION.should_not be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when included in a class" do
|
33
|
+
it "makes the class respond to .redis_attrs" do
|
34
|
+
Film.should respond_to(:redis_attrs)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "provides the class and its instances with a .redis interface" do
|
38
|
+
Film.should respond_to(:redis)
|
39
|
+
film.should respond_to(:redis)
|
40
|
+
|
41
|
+
Film.redis.should be_a(Redis)
|
42
|
+
Film.redis.should equal(film.redis)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".redis_attrs" do
|
47
|
+
it "adds getters and setters for the attributes defined" do
|
48
|
+
%w(title released_on length created_at rating featured).each do |attr|
|
49
|
+
film.should respond_to(attr)
|
50
|
+
film.should respond_to("#{attr}=")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with no paremeters" do
|
55
|
+
it "returns a list of all Redis attributes defined for the class" do
|
56
|
+
Film.redis_attrs.should be_a(Array)
|
57
|
+
Film.redis_attrs.count.should == 6
|
58
|
+
Film.redis_attrs.map(&:name).should == [:title, :released_on, :length, :created_at, :rating, :featured]
|
59
|
+
Film.redis_attrs.map(&:type).should == [:string, :date, :integer, :time, :float, :boolean]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "getters" do
|
65
|
+
let(:now) { Time.parse("2013-02-22 22:31:12 -0500") }
|
66
|
+
|
67
|
+
it "return nil by default" do
|
68
|
+
film.title.should be_nil
|
69
|
+
film.released_on.should be_nil
|
70
|
+
film.length.should be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "return whatever was last set with the corresponding setter" do
|
74
|
+
film.title = "Argo"
|
75
|
+
film.title.should == "Argo"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "keep the original value type" do
|
79
|
+
film.released_on = Date.parse("2012-10-12")
|
80
|
+
film.released_on.should == Date.parse("2012-10-12")
|
81
|
+
film.created_at = now
|
82
|
+
film.created_at.should == now
|
83
|
+
film.length = 135
|
84
|
+
film.length.should == 135
|
85
|
+
film.rating = 8.2
|
86
|
+
film.rating.should == 8.2
|
87
|
+
film.featured = true
|
88
|
+
film.featured.should == true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "setters" do
|
93
|
+
it "set the corresponding key in Redis" do
|
94
|
+
film.title = "Argo"
|
95
|
+
redis.get("film:1:title").should == "Argo"
|
96
|
+
|
97
|
+
film.rating = 8.1
|
98
|
+
redis.get("film:1:rating").should == "8.1"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "unset the key when being assigned nil" do
|
102
|
+
film.rating = 8.1
|
103
|
+
redis.keys.should include("film:1:rating")
|
104
|
+
|
105
|
+
film.rating = nil
|
106
|
+
redis.keys.should_not include("film:1:rating")
|
107
|
+
redis.get("film:1:rating").should be_nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ".register_type" do
|
112
|
+
it "allows to define support for scalar value types not covered by the library" do
|
113
|
+
Redis::Attrs.register_type(:json, JSONScalar)
|
114
|
+
Film.redis_attrs director: :json
|
115
|
+
film.director = { first_name: "Ben", last_name: "Affleck" }
|
116
|
+
redis.keys.should include("film:1:director")
|
117
|
+
redis.get("film:1:director").should == { first_name: "Ben", last_name: "Affleck" }.to_json
|
118
|
+
film.director.should == { "first_name" => "Ben", "last_name" => "Affleck" }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "collection attributes" do
|
123
|
+
it "support lists" do
|
124
|
+
Film.redis_attrs cast: :list
|
125
|
+
film.cast.should be_empty
|
126
|
+
film.cast = ["Ben Affleck", "Alan Arkin", "John Goodman", "Ben Affleck"]
|
127
|
+
film.cast.size.should == 4
|
128
|
+
end
|
129
|
+
|
130
|
+
it "support hashes" do
|
131
|
+
Film.redis_attrs crew: :hash
|
132
|
+
film.crew.should be_empty
|
133
|
+
film.crew = { costume: "John Doe", makeup: "Jane Doe", camera: "James Doe" }
|
134
|
+
film.crew.size.should == 3
|
135
|
+
film.crew.keys.should == %w(costume makeup camera)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "support sets" do
|
139
|
+
Film.redis_attrs producers: :set
|
140
|
+
film.producers.should be_empty
|
141
|
+
film.producers = ["Grant Heslov", "Ben Affleck", "George Clooney", "Ben Affleck"]
|
142
|
+
film.producers.size.should == 3
|
143
|
+
end
|
144
|
+
|
145
|
+
it "support sorted sets" do
|
146
|
+
Film.redis_attrs rankings: :sorted_set
|
147
|
+
film.rankings.should be_empty
|
148
|
+
film.rankings = { "oscars" => 3, "golden globe" => 1, "bafta" => 2 }
|
149
|
+
film.rankings.first.should == "golden globe"
|
150
|
+
film.rankings.last.should == "oscars"
|
151
|
+
film.rankings.members.should == ["golden globe", "bafta", "oscars"]
|
152
|
+
end
|
153
|
+
|
154
|
+
it "support counters" do
|
155
|
+
Film.redis_attrs awards_count: :counter
|
156
|
+
film.awards_count.value.should == 0
|
157
|
+
film.awards_count.incr
|
158
|
+
film.awards_count.value.should == 1
|
159
|
+
end
|
160
|
+
|
161
|
+
it "support locks" do
|
162
|
+
Film.redis_attrs playing: :lock
|
163
|
+
film.playing.lock { }
|
164
|
+
end
|
165
|
+
|
166
|
+
it "support specifying configuration options" do
|
167
|
+
require "active_support/core_ext/numeric/time"
|
168
|
+
Film.redis_attr :watching, :lock, :expiration => 3.hours
|
169
|
+
film.watching.options[:expiration].should == 3.hours
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis-attrs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ernesto Garcia
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: redis
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: redis-objects
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: A module that allows Ruby objects to define attributes backed by a Redis
|
79
|
+
data store. Works with any class or ORM.
|
80
|
+
email:
|
81
|
+
- gnapse@gmail.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- .rspec
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE.txt
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- lib/redis-attrs.rb
|
93
|
+
- lib/redis-attrs/base.rb
|
94
|
+
- lib/redis-attrs/boolean.rb
|
95
|
+
- lib/redis-attrs/complex.rb
|
96
|
+
- lib/redis-attrs/date.rb
|
97
|
+
- lib/redis-attrs/float.rb
|
98
|
+
- lib/redis-attrs/hash.rb
|
99
|
+
- lib/redis-attrs/integer.rb
|
100
|
+
- lib/redis-attrs/list.rb
|
101
|
+
- lib/redis-attrs/scalar.rb
|
102
|
+
- lib/redis-attrs/string.rb
|
103
|
+
- lib/redis-attrs/time.rb
|
104
|
+
- lib/redis-attrs/version.rb
|
105
|
+
- redis-attrs.gemspec
|
106
|
+
- spec/redis_attrs_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
homepage: http://github.com/gnapse/redis-attrs
|
109
|
+
licenses: []
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.8.24
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: Add persistent object attributes backed by redis
|
132
|
+
test_files:
|
133
|
+
- spec/redis_attrs_spec.rb
|
134
|
+
- spec/spec_helper.rb
|