redis-attrs 0.0.1
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/.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
|