redis_props 0.0.1.alpha → 0.2
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 +5 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +95 -0
- data/Rakefile +2 -0
- data/config +3 -0
- data/lib/nest.rb +58 -0
- data/lib/redis_props/version.rb +3 -0
- data/lib/redis_props.rb +109 -0
- data/redis_props.gemspec +28 -0
- data/spec/redis_props/redis_props_spec.rb +147 -0
- data/spec/spec_helper.rb +19 -0
- metadata +57 -41
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Obie Fernandez
|
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,95 @@
|
|
1
|
+
# RedisProps
|
2
|
+
|
3
|
+
A simple way to annotate ActiveRecord objects with properties that are stored in Redis instead
|
4
|
+
of your relational database.
|
5
|
+
Perfect for adding attributes to your models that you won't have to worry about querying
|
6
|
+
or reporting on later. Examples include flags, preferences, etc.
|
7
|
+
|
8
|
+
If you provide a default value for a property definition, then RedisProps provides typecasting based on the type
|
9
|
+
inferred
|
10
|
+
|
11
|
+
Properties are lazily loaded when their accessors are invoked, but saved immediately when set. In other words,
|
12
|
+
don't rely on them to have transactional behavior along with the rest of your ActiveRecord attributes. (However, there
|
13
|
+
are plans to add a transactional modes soon.)
|
14
|
+
|
15
|
+
The current version is extracted from working code in DueProps http://dueprops.com
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
gem 'redis_props'
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install redis_props
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
The `RedisProps` module depends on `Redis.current` (provided by the `redis` gem) being set.
|
34
|
+
Key names and namespaces are generated using a customized version of a 1-file library called `Nest`
|
35
|
+
hat we bundled into this gem.
|
36
|
+
|
37
|
+
To add RedisProps to your models just include `RedisProps` in your ActiveRecord class.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class Dog < ActiveRecord::Base
|
41
|
+
include RedisProps
|
42
|
+
|
43
|
+
redis_props :has_medical_condition do
|
44
|
+
define :fleas, default: false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
>> dog = Dog.create(name: "Fido")
|
49
|
+
=> <Dog id: 1, name: "Fido", created_at: "2012-05-13 02:15:35", updated_at: "2012-05-13 02:15:35">
|
50
|
+
|
51
|
+
>> dog.has_medical_condition_fleas?
|
52
|
+
=> false
|
53
|
+
|
54
|
+
>> dog.has_medical_condition_fleas = true
|
55
|
+
=> true
|
56
|
+
|
57
|
+
>> dog = Dog.find_by_name("Fido")
|
58
|
+
=> <Dog id: 1, name: "Fido", created_at: "2012-05-13 02:15:35", updated_at: "2012-05-13 02:15:35">
|
59
|
+
|
60
|
+
>> dog.has_medical_condition_fleas?
|
61
|
+
=> true
|
62
|
+
```
|
63
|
+
|
64
|
+
In addition to the `define` method, you'll get a protected `r` method that returns a namespaced key that
|
65
|
+
can be used for manual redis operations.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
>> dog = Dog.create
|
69
|
+
=> <Dog id: 5, name: nil, created_at: "2012-05-13 02:15:35", updated_at: "2012-05-13 02:15:35">
|
70
|
+
|
71
|
+
>> dog.send(:r)
|
72
|
+
=> "Dog:5"
|
73
|
+
|
74
|
+
>> dog.has_medical_condition_fleas = true
|
75
|
+
=> true
|
76
|
+
|
77
|
+
>> dog.send(:r).redis.keys
|
78
|
+
=> ["Dog:5:has_medical_condition_fleas"]
|
79
|
+
```
|
80
|
+
|
81
|
+
## TODOS
|
82
|
+
These are ranked in order of very likely to get done soon to less likely to get done soon (or maybe not at all.)
|
83
|
+
|
84
|
+
* Add documentation for type inference based on default values
|
85
|
+
* batch saving of attributes instead of instant
|
86
|
+
* record locking and transactions
|
87
|
+
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
1. Fork it
|
92
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
93
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
94
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
95
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/config
ADDED
data/lib/nest.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright (c) 2010 Michel Martens & Damian Janowski
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require "redis"
|
22
|
+
|
23
|
+
class Nest < String
|
24
|
+
VERSION = "1.1.0"
|
25
|
+
|
26
|
+
METHODS = [:append, :blpop, :brpop, :brpoplpush, :decr, :decrby,
|
27
|
+
:del, :exists, :expire, :expireat, :get, :getbit, :getrange, :getset,
|
28
|
+
:hdel, :hexists, :hget, :hgetall, :hincrby, :hkeys, :hlen, :hmget,
|
29
|
+
:hmset, :hset, :hsetnx, :hvals, :incr, :incrby, :lindex, :linsert,
|
30
|
+
:llen, :lpop, :lpush, :lpushx, :lrange, :lrem, :lset, :ltrim, :move,
|
31
|
+
:persist, :publish, :rename, :renamenx, :rpop, :rpoplpush, :rpush,
|
32
|
+
:rpushx, :sadd, :scard, :sdiff, :sdiffstore, :set, :setbit, :setex,
|
33
|
+
:setnx, :setrange, :sinter, :sinterstore, :sismember, :smembers,
|
34
|
+
:smove, :sort, :spop, :srandmember, :srem, :strlen, :subscribe,
|
35
|
+
:sunion, :sunionstore, :ttl, :type, :unsubscribe, :watch, :zadd,
|
36
|
+
:zcard, :zcount, :zincrby, :zinterstore, :zrange, :zrangebyscore,
|
37
|
+
:zrank, :zrem, :zremrangebyrank, :zremrangebyscore, :zrevrange,
|
38
|
+
:zrevrangebyscore, :zrevrank, :zscore, :zunionstore]
|
39
|
+
|
40
|
+
attr :redis
|
41
|
+
|
42
|
+
def initialize(key, redis = Redis.current)
|
43
|
+
key = key.to_redis_key if key.respond_to?(:to_redis_key)
|
44
|
+
super(key)
|
45
|
+
@redis = redis
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](key)
|
49
|
+
key = key.to_redis_key if key.respond_to?(:to_redis_key)
|
50
|
+
self.class.new("#{self}:#{key}", redis)
|
51
|
+
end
|
52
|
+
|
53
|
+
METHODS.each do |meth|
|
54
|
+
define_method(meth) do |*args, &block|
|
55
|
+
redis.send(meth, self, *args, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/redis_props.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'redis_props/version'
|
2
|
+
require 'nest'
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
module RedisProps
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def r
|
10
|
+
self.class.r[id]
|
11
|
+
end
|
12
|
+
protected :r
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def r
|
16
|
+
@redis_nest ||= Nest.new(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Specifies a set of properties to be stored in Redis
|
20
|
+
# for this ActiveRecord object instance.
|
21
|
+
#
|
22
|
+
# Options are:
|
23
|
+
# <tt>:defer</tt> - Specifies that the attributes in this context should be flushed
|
24
|
+
# to Redis only when the ActiveRecord object is saved. This option defaults to +false+
|
25
|
+
# and the default behavior is to read and write values to/from Redis immediately as
|
26
|
+
# they are accessed.
|
27
|
+
#
|
28
|
+
# <tt>:touch</tt> - Specifies that this ActiveRecord object's +updated_at+ field should
|
29
|
+
# be updated on save (aka "touching the object") when you write new attributes values,
|
30
|
+
# even if no database-backed attributes were changed. This option is occasionally
|
31
|
+
# vital when dealing with cache invalidation in Rails. If you specify
|
32
|
+
# a symbol (vs :true), then that attribute will be updated with the current
|
33
|
+
# time instead of the updated_at/on attribute.
|
34
|
+
# This option defaults to +false+.
|
35
|
+
#
|
36
|
+
def props(context_name, opts={}, &block)
|
37
|
+
PropsContext.new(context_name, self, opts, block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class PropsContext
|
42
|
+
def initialize(context_name, klass, opts, block)
|
43
|
+
@context_name, @klass, @opts = context_name, klass, opts
|
44
|
+
instance_exec(&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def define(name, d_opts={})
|
48
|
+
add_methods_to(@klass, "#{@context_name}_#{name}", d_opts, @opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def add_methods_to(klass, d_name, d_opts, ctx_opts)
|
54
|
+
klass.class_eval do
|
55
|
+
define_method("#{d_name}") do
|
56
|
+
instance_variable_get("@#{d_name}") || begin
|
57
|
+
val = r[d_name].get
|
58
|
+
val = val.nil? ? d_opts[:default] : PropsContext.typecaster(d_opts[:default]).call(val)
|
59
|
+
instance_variable_set("@#{d_name}", val)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
define_method("#{d_name}?") do
|
63
|
+
!!send(d_name)
|
64
|
+
end
|
65
|
+
define_method("#{d_name}=") do |val|
|
66
|
+
instance_variable_set("@#{d_name}", val)
|
67
|
+
r[d_name].set val.to_s
|
68
|
+
if touch = ctx_opts[:touch]
|
69
|
+
if touch == true
|
70
|
+
if respond_to?(:updated_at)
|
71
|
+
self.updated_at = Time.now
|
72
|
+
elsif respond_to?(:updated_on)
|
73
|
+
self.updated_at = Time.now
|
74
|
+
else
|
75
|
+
raise "updated timestamp column does not exist for use with the :touch option"
|
76
|
+
end
|
77
|
+
elsif respond_to? touch
|
78
|
+
send("#{touch}=", Time.now)
|
79
|
+
else
|
80
|
+
raise "#{touch} timestamp column specified as :touch option does not exist"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
after_destroy -> { r[d_name].del }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.typecaster(default_value)
|
89
|
+
case default_value
|
90
|
+
when TrueClass, FalseClass
|
91
|
+
@boolean ||= lambda {|v| ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(v)}
|
92
|
+
when Float
|
93
|
+
@float ||= lambda {|v| v.to_f }
|
94
|
+
when BigDecimal
|
95
|
+
@decimal ||= lambda {|v| v.to_d }
|
96
|
+
when Integer
|
97
|
+
@integer ||= lambda {|v| v.to_i }
|
98
|
+
when Range
|
99
|
+
@range ||= lambda {|v| eval(v) }
|
100
|
+
when Date
|
101
|
+
@date ||= lambda {|v| ActiveRecord::ConnectionAdapters::Column.send(:string_to_date, v) }
|
102
|
+
when Time
|
103
|
+
@time ||= lambda {|v| ActiveRecord::ConnectionAdapters::Column.send(:string_to_time, v) }
|
104
|
+
else
|
105
|
+
lambda {|v| v}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/redis_props.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/redis_props/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "redis_props"
|
6
|
+
gem.version = RedisProps::VERSION
|
7
|
+
gem.authors = ["Obie Fernandez"]
|
8
|
+
gem.email = ["obiefernandez@gmail.com"]
|
9
|
+
gem.description = %q{A simple way to annotate ActiveRecord objects with properties that are stored in Redis.
|
10
|
+
Perfect for adding attributes to your models that you won't have to worry about querying
|
11
|
+
or reporting on later. Examples include flags, preferences, etc.}
|
12
|
+
gem.summary = %q{Add non-relational attributes to your ActiveRecord objects.}
|
13
|
+
gem.homepage = "http://github.com/obie/redis_properties"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec"
|
21
|
+
gem.add_development_dependency "activesupport"
|
22
|
+
gem.add_development_dependency "sqlite3"
|
23
|
+
gem.add_development_dependency "pry-nav"
|
24
|
+
|
25
|
+
gem.add_runtime_dependency "activesupport"
|
26
|
+
gem.add_runtime_dependency "activerecord"
|
27
|
+
gem.add_runtime_dependency "redis"
|
28
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sqlite3'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
class Dog < ActiveRecord::Base
|
6
|
+
include RedisProps
|
7
|
+
|
8
|
+
establish_connection :adapter => 'sqlite3', :database => 'spec/foobar.db'
|
9
|
+
connection.create_table :dogs, :force => true do |t|
|
10
|
+
t.string :name
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
props :has_medical_condition do
|
15
|
+
define :fleas, default: false
|
16
|
+
define :pants, default: true
|
17
|
+
end
|
18
|
+
|
19
|
+
props :appearance, touch: true do
|
20
|
+
define :coat
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class User < ActiveRecord::Base
|
25
|
+
include RedisProps
|
26
|
+
|
27
|
+
establish_connection :adapter => 'sqlite3', :database => 'spec/foobar.db'
|
28
|
+
connection.create_table :users, :force => true do |t|
|
29
|
+
t.string :name
|
30
|
+
t.timestamp :saved_at
|
31
|
+
end
|
32
|
+
|
33
|
+
props :flags, touch: :saved_at do
|
34
|
+
define :weekly_digest, default: true
|
35
|
+
end
|
36
|
+
|
37
|
+
props :number_of do
|
38
|
+
define :records, default: 0
|
39
|
+
end
|
40
|
+
|
41
|
+
props :pets do
|
42
|
+
define :dogs, default: []
|
43
|
+
end
|
44
|
+
|
45
|
+
props :stats do
|
46
|
+
define :birthday, default: 20.years.ago
|
47
|
+
define :temperature, default: 98.7
|
48
|
+
define :last_seen, default: Date.today
|
49
|
+
define :pressure_range, default: 70..99
|
50
|
+
define :exact_weight, default: "190.908230984".to_d
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe RedisProps do
|
55
|
+
let(:clean_dog) { Dog.create!(name: "Wego") }
|
56
|
+
let(:dirty_dog) { Dog.create!(name: "Bodiddly") }
|
57
|
+
let(:user) { User.create!(name: "Hugo", saved_at: 1.day.ago) }
|
58
|
+
|
59
|
+
context "props options" do
|
60
|
+
it "should touch the AR object's updated_at if touch: true" do
|
61
|
+
orig_updated_at = clean_dog.updated_at
|
62
|
+
clean_dog.appearance_coat = "Scruffy"
|
63
|
+
clean_dog.save
|
64
|
+
Dog.find_by_name("Wego").updated_at.should_not == orig_updated_at
|
65
|
+
end
|
66
|
+
it "should touch the AR object's saved_at if touch: :saved_at" do
|
67
|
+
orig_saved_at = user.saved_at
|
68
|
+
user.flags_weekly_digest = false
|
69
|
+
user.save
|
70
|
+
User.find_by_name("Hugo").saved_at.should_not == orig_saved_at
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "props definitions" do
|
75
|
+
it "creates persistent attributes" do
|
76
|
+
dirty_dog.has_medical_condition_fleas = true
|
77
|
+
dirty_dog = Dog.find_by_name "Bodiddly"
|
78
|
+
dirty_dog.should have_medical_condition_fleas
|
79
|
+
end
|
80
|
+
|
81
|
+
it "sets defaults if provided" do
|
82
|
+
clean_dog.should have_medical_condition_pants
|
83
|
+
clean_dog.should_not have_medical_condition_fleas
|
84
|
+
end
|
85
|
+
|
86
|
+
context "provide type inference based on default values provided" do
|
87
|
+
it "works for booleans of all kinds" do
|
88
|
+
user.flags_weekly_digest = false
|
89
|
+
User.find(user.id).flags_weekly_digest.should == false
|
90
|
+
user.flags_weekly_digest = true
|
91
|
+
User.find(user.id).flags_weekly_digest.should == true
|
92
|
+
user.flags_weekly_digest = 1
|
93
|
+
User.find(user.id).flags_weekly_digest.should == true
|
94
|
+
user.flags_weekly_digest = 0
|
95
|
+
User.find(user.id).flags_weekly_digest.should == false
|
96
|
+
user.flags_weekly_digest = 't'
|
97
|
+
User.find(user.id).flags_weekly_digest.should == true
|
98
|
+
user.flags_weekly_digest = 'f'
|
99
|
+
User.find(user.id).flags_weekly_digest.should == false
|
100
|
+
end
|
101
|
+
it "works for integers" do
|
102
|
+
user.number_of_records = 42
|
103
|
+
User.find(user.id).number_of_records.should == 42
|
104
|
+
end
|
105
|
+
it "works for floats" do
|
106
|
+
user.stats_temperature = 99.9
|
107
|
+
User.find(user.id).stats_temperature.should == 99.9
|
108
|
+
end
|
109
|
+
it "works for times" do
|
110
|
+
new_birthday = 10.years.ago
|
111
|
+
user.stats_birthday = new_birthday
|
112
|
+
# todo: figure out why straight comparison is failing
|
113
|
+
User.find(user.id).stats_birthday.to_i.should == new_birthday.to_i
|
114
|
+
end
|
115
|
+
it "works for dates" do
|
116
|
+
new_last_seen = 10.days.ago.to_date
|
117
|
+
user.stats_last_seen = new_last_seen
|
118
|
+
User.find(user.id).stats_last_seen.should == new_last_seen
|
119
|
+
end
|
120
|
+
it "works for ranges" do
|
121
|
+
new_range = 90..114
|
122
|
+
user.stats_pressure_range = new_range
|
123
|
+
User.find(user.id).stats_pressure_range.should == new_range
|
124
|
+
end
|
125
|
+
it "works for decimals" do
|
126
|
+
user.stats_exact_weight = "230.29320".to_d
|
127
|
+
User.find(user.id).stats_exact_weight.should == "230.29320".to_d
|
128
|
+
end
|
129
|
+
it "works for relationships stored in arrays" do
|
130
|
+
user.pets_dogs << clean_dog
|
131
|
+
User.find(user.id).pets_dogs.should include(clean_dog)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "default behavior" do
|
137
|
+
it "is to remove associated redis keys when parent AR object is destroyed" do
|
138
|
+
user.class_eval { public :r }
|
139
|
+
user.flags_weekly_digest = false
|
140
|
+
user.r["flags_weekly_digest"].get.should == "false"
|
141
|
+
user.destroy
|
142
|
+
user.r["flags_weekly_digest"].get.should == nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'pry'
|
3
|
+
require 'redis_props'
|
4
|
+
|
5
|
+
module RedisProps
|
6
|
+
class Rails
|
7
|
+
def self.env
|
8
|
+
"test"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Redis.current = Redis.new(db: 13)
|
14
|
+
|
15
|
+
RSpec.configure do |c|
|
16
|
+
c.before(:each) { Redis.current.flushdb }
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord::Base.logger = Logger.new("test.log")
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_props
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: '0.2'
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Obie Fernandez
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: activesupport
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,21 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: sqlite3
|
38
|
-
requirement: &2151836040 !ruby/object:Gem::Requirement
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
41
|
none: false
|
40
42
|
requirements:
|
41
43
|
- - ! '>='
|
42
44
|
- !ruby/object:Gem::Version
|
43
45
|
version: '0'
|
44
|
-
type: :development
|
45
|
-
prerelease: false
|
46
|
-
version_requirements: *2151836040
|
47
46
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
49
|
-
requirement:
|
47
|
+
name: sqlite3
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
50
49
|
none: false
|
51
50
|
requirements:
|
52
51
|
- - ! '>='
|
@@ -54,21 +53,15 @@ dependencies:
|
|
54
53
|
version: '0'
|
55
54
|
type: :development
|
56
55
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
-
name: redis-namespace
|
60
|
-
requirement: &2151831420 !ruby/object:Gem::Requirement
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
57
|
none: false
|
62
58
|
requirements:
|
63
59
|
- - ! '>='
|
64
60
|
- !ruby/object:Gem::Version
|
65
61
|
version: '0'
|
66
|
-
type: :development
|
67
|
-
prerelease: false
|
68
|
-
version_requirements: *2151831420
|
69
62
|
- !ruby/object:Gem::Dependency
|
70
63
|
name: pry-nav
|
71
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
72
65
|
none: false
|
73
66
|
requirements:
|
74
67
|
- - ! '>='
|
@@ -76,10 +69,15 @@ dependencies:
|
|
76
69
|
version: '0'
|
77
70
|
type: :development
|
78
71
|
prerelease: false
|
79
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
80
78
|
- !ruby/object:Gem::Dependency
|
81
79
|
name: activesupport
|
82
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
83
81
|
none: false
|
84
82
|
requirements:
|
85
83
|
- - ! '>='
|
@@ -87,10 +85,15 @@ dependencies:
|
|
87
85
|
version: '0'
|
88
86
|
type: :runtime
|
89
87
|
prerelease: false
|
90
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
91
94
|
- !ruby/object:Gem::Dependency
|
92
95
|
name: activerecord
|
93
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
94
97
|
none: false
|
95
98
|
requirements:
|
96
99
|
- - ! '>='
|
@@ -98,10 +101,15 @@ dependencies:
|
|
98
101
|
version: '0'
|
99
102
|
type: :runtime
|
100
103
|
prerelease: false
|
101
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
102
110
|
- !ruby/object:Gem::Dependency
|
103
111
|
name: redis
|
104
|
-
requirement:
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
105
113
|
none: false
|
106
114
|
requirements:
|
107
115
|
- - ! '>='
|
@@ -109,18 +117,12 @@ dependencies:
|
|
109
117
|
version: '0'
|
110
118
|
type: :runtime
|
111
119
|
prerelease: false
|
112
|
-
version_requirements:
|
113
|
-
- !ruby/object:Gem::Dependency
|
114
|
-
name: redis-namespace
|
115
|
-
requirement: &2151824960 !ruby/object:Gem::Requirement
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
116
121
|
none: false
|
117
122
|
requirements:
|
118
123
|
- - ! '>='
|
119
124
|
- !ruby/object:Gem::Version
|
120
125
|
version: '0'
|
121
|
-
type: :runtime
|
122
|
-
prerelease: false
|
123
|
-
version_requirements: *2151824960
|
124
126
|
description: ! "A simple way to annotate ActiveRecord objects with properties that
|
125
127
|
are stored in Redis.\n Perfect for adding attributes to
|
126
128
|
your models that you won't have to worry about querying\n or
|
@@ -130,7 +132,19 @@ email:
|
|
130
132
|
executables: []
|
131
133
|
extensions: []
|
132
134
|
extra_rdoc_files: []
|
133
|
-
files:
|
135
|
+
files:
|
136
|
+
- .gitignore
|
137
|
+
- Gemfile
|
138
|
+
- LICENSE
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- config
|
142
|
+
- lib/nest.rb
|
143
|
+
- lib/redis_props.rb
|
144
|
+
- lib/redis_props/version.rb
|
145
|
+
- redis_props.gemspec
|
146
|
+
- spec/redis_props/redis_props_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
134
148
|
homepage: http://github.com/obie/redis_properties
|
135
149
|
licenses: []
|
136
150
|
post_install_message:
|
@@ -146,13 +160,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
160
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
161
|
none: false
|
148
162
|
requirements:
|
149
|
-
- - ! '
|
163
|
+
- - ! '>='
|
150
164
|
- !ruby/object:Gem::Version
|
151
|
-
version:
|
165
|
+
version: '0'
|
152
166
|
requirements: []
|
153
167
|
rubyforge_project:
|
154
|
-
rubygems_version: 1.8.
|
168
|
+
rubygems_version: 1.8.22
|
155
169
|
signing_key:
|
156
170
|
specification_version: 3
|
157
171
|
summary: Add non-relational attributes to your ActiveRecord objects.
|
158
|
-
test_files:
|
172
|
+
test_files:
|
173
|
+
- spec/redis_props/redis_props_spec.rb
|
174
|
+
- spec/spec_helper.rb
|