chimera 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/History.txt +4 -0
- data/Manifest.txt +57 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +114 -0
- data/Rakefile +30 -0
- data/doc/NOTES +11 -0
- data/doc/examples/config.yml +16 -0
- data/doc/redis6379.conf +132 -0
- data/lib/chimera.rb +33 -0
- data/lib/chimera/associations.rb +146 -0
- data/lib/chimera/attributes.rb +52 -0
- data/lib/chimera/base.rb +95 -0
- data/lib/chimera/config.rb +9 -0
- data/lib/chimera/error.rb +12 -0
- data/lib/chimera/finders.rb +49 -0
- data/lib/chimera/geo_indexes.rb +76 -0
- data/lib/chimera/indexes.rb +177 -0
- data/lib/chimera/persistence.rb +70 -0
- data/lib/chimera/redis_objects.rb +345 -0
- data/lib/redis.rb +373 -0
- data/lib/redis/counter.rb +94 -0
- data/lib/redis/dist_redis.rb +149 -0
- data/lib/redis/hash_ring.rb +135 -0
- data/lib/redis/helpers/core_commands.rb +46 -0
- data/lib/redis/helpers/serialize.rb +25 -0
- data/lib/redis/list.rb +122 -0
- data/lib/redis/lock.rb +83 -0
- data/lib/redis/objects.rb +100 -0
- data/lib/redis/objects/counters.rb +132 -0
- data/lib/redis/objects/lists.rb +45 -0
- data/lib/redis/objects/locks.rb +71 -0
- data/lib/redis/objects/sets.rb +46 -0
- data/lib/redis/objects/values.rb +56 -0
- data/lib/redis/pipeline.rb +21 -0
- data/lib/redis/set.rb +156 -0
- data/lib/redis/value.rb +35 -0
- data/lib/riak_raw.rb +100 -0
- data/lib/typhoeus.rb +55 -0
- data/lib/typhoeus/.gitignore +1 -0
- data/lib/typhoeus/easy.rb +253 -0
- data/lib/typhoeus/filter.rb +28 -0
- data/lib/typhoeus/hydra.rb +210 -0
- data/lib/typhoeus/multi.rb +34 -0
- data/lib/typhoeus/remote.rb +306 -0
- data/lib/typhoeus/remote_method.rb +108 -0
- data/lib/typhoeus/remote_proxy_object.rb +48 -0
- data/lib/typhoeus/request.rb +124 -0
- data/lib/typhoeus/response.rb +39 -0
- data/lib/typhoeus/service.rb +20 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/models.rb +49 -0
- data/test/test_chimera.rb +238 -0
- data/test/test_helper.rb +7 -0
- metadata +243 -0
data/lib/redis/list.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
class Redis
|
2
|
+
#
|
3
|
+
# Class representing a Redis list. Instances of Redis::List are designed to
|
4
|
+
# behave as much like Ruby arrays as possible.
|
5
|
+
#
|
6
|
+
class List
|
7
|
+
require 'enumerator'
|
8
|
+
include Enumerable
|
9
|
+
require 'redis/helpers/core_commands'
|
10
|
+
include Redis::Helpers::CoreCommands
|
11
|
+
require 'redis/helpers/serialize'
|
12
|
+
include Redis::Helpers::Serialize
|
13
|
+
|
14
|
+
attr_reader :key, :options, :redis
|
15
|
+
|
16
|
+
def initialize(key, redis=$redis, options={})
|
17
|
+
@key = key
|
18
|
+
@redis = redis
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
# Works like push. Can chain together: list << 'a' << 'b'
|
23
|
+
def <<(value)
|
24
|
+
push(value)
|
25
|
+
self # for << 'a' << 'b'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Add a member to the end of the list. Redis: RPUSH
|
29
|
+
def push(value)
|
30
|
+
redis.rpush(key, to_redis(value))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Remove a member from the end of the list. Redis: RPOP
|
34
|
+
def pop
|
35
|
+
from_redis redis.rpop(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add a member to the start of the list. Redis: LPUSH
|
39
|
+
def unshift(value)
|
40
|
+
redis.lpush(key, to_redis(value))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Remove a member from the start of the list. Redis: LPOP
|
44
|
+
def shift
|
45
|
+
from_redis redis.lpop(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return all values in the list. Redis: LRANGE(0,-1)
|
49
|
+
def values
|
50
|
+
from_redis range(0, -1)
|
51
|
+
end
|
52
|
+
alias_method :get, :values
|
53
|
+
|
54
|
+
# Same functionality as Ruby arrays. If a single number is given, return
|
55
|
+
# just the element at that index using Redis: LINDEX. Otherwise, return
|
56
|
+
# a range of values using Redis: LRANGE.
|
57
|
+
def [](index, length=nil)
|
58
|
+
if index.is_a? Range
|
59
|
+
range(index.first, index.last)
|
60
|
+
elsif length
|
61
|
+
range(index, length)
|
62
|
+
else
|
63
|
+
at(index)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Delete the element(s) from the list that match name. If count is specified,
|
68
|
+
# only the first-N (if positive) or last-N (if negative) will be removed.
|
69
|
+
# Use .del to completely delete the entire key.
|
70
|
+
# Redis: LREM
|
71
|
+
def delete(name, count=0)
|
72
|
+
redis.lrem(key, count, name) # weird api
|
73
|
+
end
|
74
|
+
|
75
|
+
# Iterate through each member of the set. Redis::Objects mixes in Enumerable,
|
76
|
+
# so you can also use familiar methods like +collect+, +detect+, and so forth.
|
77
|
+
def each(&block)
|
78
|
+
values.each(&block)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Return a range of values from +start_index+ to +end_index+. Can also use
|
82
|
+
# the familiar list[start,end] Ruby syntax. Redis: LRANGE
|
83
|
+
def range(start_index, end_index)
|
84
|
+
from_redis redis.lrange(key, start_index, end_index)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return the value at the given index. Can also use familiar list[index] syntax.
|
88
|
+
# Redis: LINDEX
|
89
|
+
def at(index)
|
90
|
+
from_redis redis.lindex(key, index)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Return the first element in the list. Redis: LINDEX(0)
|
94
|
+
def first
|
95
|
+
at(0)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return the last element in the list. Redis: LINDEX(-1)
|
99
|
+
def last
|
100
|
+
at(-1)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return the length of the list. Aliased as size. Redis: LLEN
|
104
|
+
def length
|
105
|
+
redis.llen(key)
|
106
|
+
end
|
107
|
+
alias_method :size, :length
|
108
|
+
|
109
|
+
# Returns true if there are no elements in the list. Redis: LLEN == 0
|
110
|
+
def empty?
|
111
|
+
length == 0
|
112
|
+
end
|
113
|
+
|
114
|
+
def ==(x)
|
115
|
+
values == x
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_s
|
119
|
+
values.join(', ')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/redis/lock.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
class Redis
|
2
|
+
#
|
3
|
+
# Class representing a lock. This functions like a proxy class, in
|
4
|
+
# that you can say @object.lock_name { block } to use the lock and also
|
5
|
+
# @object.counter_name.clear to reset on it. You can use this
|
6
|
+
# directly, but it is better to use the lock :foo class method in your
|
7
|
+
# class to define a lock.
|
8
|
+
#
|
9
|
+
class Lock
|
10
|
+
class LockTimeout < StandardError; end #:nodoc:
|
11
|
+
|
12
|
+
attr_reader :key, :options, :redis
|
13
|
+
def initialize(key, redis=$redis, options={})
|
14
|
+
@key = key
|
15
|
+
@redis = redis
|
16
|
+
@options = options
|
17
|
+
@options[:timeout] ||= 5
|
18
|
+
@redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Clear the lock. Should only be needed if there's a server crash
|
22
|
+
# or some other event that gets locks in a stuck state.
|
23
|
+
def clear
|
24
|
+
redis.del(key)
|
25
|
+
end
|
26
|
+
alias_method :delete, :clear
|
27
|
+
|
28
|
+
# Get the lock and execute the code block. Any other code that needs the lock
|
29
|
+
# (on any server) will spin waiting for the lock up to the :timeout
|
30
|
+
# that was specified when the lock was defined.
|
31
|
+
def lock(&block)
|
32
|
+
start = Time.now
|
33
|
+
gotit = false
|
34
|
+
expiration = nil
|
35
|
+
while Time.now - start < @options[:timeout]
|
36
|
+
expiration = generate_expiration
|
37
|
+
# Use the expiration as the value of the lock.
|
38
|
+
gotit = redis.setnx(key, expiration)
|
39
|
+
break if gotit
|
40
|
+
|
41
|
+
# Lock is being held. Now check to see if it's expired (if we're using
|
42
|
+
# lock expiration).
|
43
|
+
# See "Handling Deadlocks" section on http://code.google.com/p/redis/wiki/SetnxCommand
|
44
|
+
if !@options[:expiration].nil?
|
45
|
+
old_expiration = redis.get(key).to_f
|
46
|
+
|
47
|
+
if old_expiration < Time.now.to_f
|
48
|
+
# If it's expired, use GETSET to update it.
|
49
|
+
expiration = generate_expiration
|
50
|
+
old_expiration = redis.getset(key, expiration).to_f
|
51
|
+
|
52
|
+
# Since GETSET returns the old value of the lock, if the old expiration
|
53
|
+
# is still in the past, we know no one else has expired the locked
|
54
|
+
# and we now have it.
|
55
|
+
if old_expiration < Time.now.to_f
|
56
|
+
gotit = true
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
sleep 0.1
|
63
|
+
end
|
64
|
+
raise LockTimeout, "Timeout on lock #{key} exceeded #{@options[:timeout]} sec" unless gotit
|
65
|
+
begin
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
# We need to be careful when cleaning up the lock key. If we took a really long
|
69
|
+
# time for some reason, and the lock expired, someone else may have it, and
|
70
|
+
# it's not safe for us to remove it. Check how much time has passed since we
|
71
|
+
# wrote the lock key and only delete it if it hasn't expired (or we're not using
|
72
|
+
# lock expiration)
|
73
|
+
if @options[:expiration].nil? || expiration > Time.now.to_f
|
74
|
+
redis.del(key)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate_expiration
|
80
|
+
@options[:expiration].nil? ? 1 : (Time.now + @options[:expiration].to_f + 1).to_f
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Redis::Objects - Lightweight object layer around redis-rb
|
2
|
+
# See README.rdoc for usage and approach.
|
3
|
+
|
4
|
+
class Redis
|
5
|
+
#
|
6
|
+
# Redis::Objects enables high-performance atomic operations in your app
|
7
|
+
# by leveraging the atomic features of the Redis server. To use Redis::Objects,
|
8
|
+
# first include it in any class you want. (This example uses an ActiveRecord
|
9
|
+
# subclass, but that is *not* required.) Then, use +counter+ and +lock+
|
10
|
+
# to define your primitives:
|
11
|
+
#
|
12
|
+
# class Game < ActiveRecord::Base
|
13
|
+
# include Redis::Objects
|
14
|
+
#
|
15
|
+
# counter :joined_players
|
16
|
+
# counter :active_players
|
17
|
+
# set :player_ids
|
18
|
+
# lock :archive_game
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# The, you can use these counters both for bookeeping and as atomic actions:
|
22
|
+
#
|
23
|
+
# @game = Game.find(id)
|
24
|
+
# @game_user = @game.joined_players.increment do |val|
|
25
|
+
# break if val > @game.max_players
|
26
|
+
# gu = @game.game_users.create!(:user_id => @user.id)
|
27
|
+
# @game.active_players.increment
|
28
|
+
# gu
|
29
|
+
# end
|
30
|
+
# if @game_user.nil?
|
31
|
+
# # game is full - error screen
|
32
|
+
# else
|
33
|
+
# # success
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
#
|
37
|
+
#
|
38
|
+
module Objects
|
39
|
+
dir = File.expand_path(__FILE__.sub(/\.rb$/,''))
|
40
|
+
|
41
|
+
autoload :Counters, File.join(dir, 'counters')
|
42
|
+
autoload :Values, File.join(dir, 'values')
|
43
|
+
autoload :Lists, File.join(dir, 'lists')
|
44
|
+
autoload :Sets, File.join(dir, 'sets')
|
45
|
+
autoload :Locks, File.join(dir, 'locks')
|
46
|
+
|
47
|
+
class NotConnected < StandardError; end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
# def redis=(conn) @redis = conn end
|
51
|
+
# def redis
|
52
|
+
# @redis ||= $redis || raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
|
53
|
+
# end
|
54
|
+
|
55
|
+
def included(klass)
|
56
|
+
# Core (this file)
|
57
|
+
#klass.instance_variable_set('@redis', @redis)
|
58
|
+
klass.instance_variable_set('@redis_objects', {})
|
59
|
+
klass.send :include, InstanceMethods
|
60
|
+
klass.extend ClassMethods
|
61
|
+
|
62
|
+
# Pull in each object type
|
63
|
+
klass.send :include, Redis::Objects::Counters
|
64
|
+
klass.send :include, Redis::Objects::Values
|
65
|
+
klass.send :include, Redis::Objects::Lists
|
66
|
+
klass.send :include, Redis::Objects::Sets
|
67
|
+
klass.send :include, Redis::Objects::Locks
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Class methods that appear in your class when you include Redis::Objects.
|
72
|
+
module ClassMethods
|
73
|
+
#attr_accessor :redis, :redis_objects
|
74
|
+
attr_accessor :redis_objects
|
75
|
+
|
76
|
+
# Set the Redis prefix to use. Defaults to model_name
|
77
|
+
def prefix=(prefix) @prefix = prefix end
|
78
|
+
def prefix #:nodoc:
|
79
|
+
@prefix ||= self.name.to_s.
|
80
|
+
sub(%r{(.*::)}, '').
|
81
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
82
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
83
|
+
downcase
|
84
|
+
end
|
85
|
+
|
86
|
+
def field_key(name, id) #:nodoc:
|
87
|
+
"#{prefix}:#{id}:#{name}"
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# Instance methods that appear in your class when you include Redis::Objects.
|
93
|
+
module InstanceMethods
|
94
|
+
def redis() self.class.connection end
|
95
|
+
def field_key(name) #:nodoc:
|
96
|
+
self.class.field_key(name, id)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# This is the class loader, for use as "include Redis::Objects::Counters"
|
2
|
+
# For the object itself, see "Redis::Counter"
|
3
|
+
require 'redis/counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
class UndefinedCounter < StandardError; end #:nodoc:
|
7
|
+
class MissingID < StandardError; end #:nodoc:
|
8
|
+
|
9
|
+
module Counters
|
10
|
+
def self.included(klass)
|
11
|
+
klass.instance_variable_set('@initialized_counters', {})
|
12
|
+
klass.send :include, InstanceMethods
|
13
|
+
klass.extend ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
# Class methods that appear in your class when you include Redis::Objects.
|
17
|
+
module ClassMethods
|
18
|
+
attr_reader :initialized_counters
|
19
|
+
|
20
|
+
# Define a new counter. It will function like a regular instance
|
21
|
+
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
22
|
+
def counter(name, options={})
|
23
|
+
options[:start] ||= 0
|
24
|
+
options[:type] ||= options[:start] == 0 ? :increment : :decrement
|
25
|
+
@redis_objects[name] = options.merge(:type => :counter)
|
26
|
+
if options[:global]
|
27
|
+
instance_eval <<-EndMethods
|
28
|
+
def #{name}
|
29
|
+
@#{name} ||= Redis::Counter.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
30
|
+
end
|
31
|
+
EndMethods
|
32
|
+
class_eval <<-EndMethods
|
33
|
+
def #{name}
|
34
|
+
self.class.#{name}
|
35
|
+
end
|
36
|
+
EndMethods
|
37
|
+
else
|
38
|
+
class_eval <<-EndMethods
|
39
|
+
def #{name}
|
40
|
+
@#{name} ||= Redis::Counter.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
41
|
+
end
|
42
|
+
EndMethods
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get the current value of the counter. It is more efficient
|
47
|
+
# to use the instance method if possible.
|
48
|
+
def get_counter(name, id=nil)
|
49
|
+
verify_counter_defined!(name, id)
|
50
|
+
initialize_counter!(name, id)
|
51
|
+
redis.get(field_key(name, id)).to_i
|
52
|
+
end
|
53
|
+
|
54
|
+
# Increment a counter with the specified name and id. Accepts a block
|
55
|
+
# like the instance method. See Redis::Objects::Counter for details.
|
56
|
+
def increment_counter(name, id=nil, by=1, &block)
|
57
|
+
verify_counter_defined!(name, id)
|
58
|
+
initialize_counter!(name, id)
|
59
|
+
value = redis.incr(field_key(name, id), by).to_i
|
60
|
+
block_given? ? rewindable_block(:decrement_counter, name, id, value, &block) : value
|
61
|
+
end
|
62
|
+
|
63
|
+
# Decrement a counter with the specified name and id. Accepts a block
|
64
|
+
# like the instance method. See Redis::Objects::Counter for details.
|
65
|
+
def decrement_counter(name, id=nil, by=1, &block)
|
66
|
+
verify_counter_defined!(name, id)
|
67
|
+
initialize_counter!(name, id)
|
68
|
+
value = redis.decr(field_key(name, id), by).to_i
|
69
|
+
block_given? ? rewindable_block(:increment_counter, name, id, value, &block) : value
|
70
|
+
end
|
71
|
+
|
72
|
+
# Reset a counter to its starting value.
|
73
|
+
def reset_counter(name, id=nil, to=nil)
|
74
|
+
verify_counter_defined!(name, id)
|
75
|
+
to = @redis_objects[name][:start] if to.nil?
|
76
|
+
redis.set(field_key(name, id), to)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def verify_counter_defined!(name, id) #:nodoc:
|
82
|
+
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless @redis_objects.has_key?(name)
|
83
|
+
if id.nil? and !@redis_objects[name][:global]
|
84
|
+
raise Redis::Objects::MissingID, "Missing ID for non-global counter #{self.name}##{name}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def initialize_counter!(name, id) #:nodoc:
|
89
|
+
key = field_key(name, id)
|
90
|
+
unless @initialized_counters[key]
|
91
|
+
redis.setnx(key, @redis_objects[name][:start])
|
92
|
+
end
|
93
|
+
@initialized_counters[key] = true
|
94
|
+
end
|
95
|
+
|
96
|
+
# Implements increment/decrement blocks on a class level
|
97
|
+
def rewindable_block(rewind, name, id, value, &block) #:nodoc:
|
98
|
+
# Unfortunately this is almost exactly duplicated from Redis::Counter
|
99
|
+
raise ArgumentError, "Missing block to rewindable_block somehow" unless block_given?
|
100
|
+
ret = nil
|
101
|
+
begin
|
102
|
+
ret = yield value
|
103
|
+
rescue
|
104
|
+
send(rewind, name, id)
|
105
|
+
raise
|
106
|
+
end
|
107
|
+
send(rewind, name, id) if ret.nil?
|
108
|
+
ret
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Instance methods that appear in your class when you include Redis::Objects.
|
113
|
+
module InstanceMethods
|
114
|
+
# Increment a counter.
|
115
|
+
# It is more efficient to use increment_[counter_name] directly.
|
116
|
+
# This is mainly just for completeness to override ActiveRecord.
|
117
|
+
def increment(name, by=1)
|
118
|
+
raise(ActiveRedis::Errors::NotSavedError) if self.new?
|
119
|
+
send(name).increment(by)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Decrement a counter.
|
123
|
+
# It is more efficient to use increment_[counter_name] directly.
|
124
|
+
# This is mainly just for completeness to override ActiveRecord.
|
125
|
+
def decrement(name, by=1)
|
126
|
+
raise(ActiveRedis::Errors::NotSavedError) if self.new?
|
127
|
+
send(name).decrement(by)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# This is the class loader, for use as "include Redis::Objects::Lists"
|
2
|
+
# For the object itself, see "Redis::List"
|
3
|
+
require 'redis/list'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module Lists
|
7
|
+
def self.included(klass)
|
8
|
+
klass.send :include, InstanceMethods
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Class methods that appear in your class when you include Redis::Objects.
|
13
|
+
module ClassMethods
|
14
|
+
# Define a new list. It will function like a regular instance
|
15
|
+
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
16
|
+
def list(name, options={})
|
17
|
+
@redis_objects[name] = options.merge(:type => :list)
|
18
|
+
if options[:global]
|
19
|
+
instance_eval <<-EndMethods
|
20
|
+
def #{name}
|
21
|
+
@#{name} ||= Redis::List.new(field_key(:#{name}, ''), redis, @redis_objects[:#{name}])
|
22
|
+
end
|
23
|
+
EndMethods
|
24
|
+
class_eval <<-EndMethods
|
25
|
+
def #{name}
|
26
|
+
self.class.#{name}
|
27
|
+
end
|
28
|
+
EndMethods
|
29
|
+
else
|
30
|
+
class_eval <<-EndMethods
|
31
|
+
def #{name}
|
32
|
+
raise(ActiveRedis::Errors::NotSavedError) if self.new?
|
33
|
+
@#{name} ||= Redis::List.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
|
34
|
+
end
|
35
|
+
EndMethods
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Instance methods that appear in your class when you include Redis::Objects.
|
41
|
+
module InstanceMethods
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|