redpear 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/redpear/connection.rb +110 -17
- data/lib/redpear/model/expiration.rb +18 -0
- data/lib/redpear/model/factory_girl.rb +27 -0
- data/lib/redpear/model/finders.rb +37 -0
- data/lib/redpear/model/machinist.rb +54 -0
- data/lib/redpear/model.rb +186 -63
- data/lib/redpear/schema/collection.rb +7 -7
- data/lib/redpear/{column.rb → schema/column.rb} +12 -6
- data/lib/redpear/schema/index.rb +22 -0
- data/lib/redpear/schema/score.rb +13 -0
- data/lib/redpear/schema.rb +9 -6
- data/lib/redpear/store/base.rb +120 -0
- data/lib/redpear/store/counter.rb +44 -0
- data/lib/redpear/store/enumerable.rb +8 -0
- data/lib/redpear/store/hash.rb +120 -0
- data/lib/redpear/store/list.rb +144 -0
- data/lib/redpear/store/lock.rb +108 -0
- data/lib/redpear/store/set.rb +147 -0
- data/lib/redpear/store/sorted_set.rb +239 -0
- data/lib/redpear/store/value.rb +66 -0
- data/lib/redpear/store.rb +11 -0
- data/lib/redpear.rb +7 -22
- metadata +45 -30
- data/lib/redpear/core_ext/stringify_keys.rb +0 -17
- data/lib/redpear/counters.rb +0 -26
- data/lib/redpear/expiration.rb +0 -27
- data/lib/redpear/finders.rb +0 -59
- data/lib/redpear/index.rb +0 -31
- data/lib/redpear/machinist.rb +0 -51
- data/lib/redpear/members.rb +0 -83
- data/lib/redpear/namespace.rb +0 -79
- data/lib/redpear/nest.rb +0 -100
- data/lib/redpear/persistence.rb +0 -147
- data/lib/redpear/zindex.rb +0 -26
- data/lib/redpear/zmembers.rb +0 -68
data/lib/redpear/connection.rb
CHANGED
@@ -1,28 +1,121 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# == Redpear::Connection
|
2
|
+
#
|
3
|
+
# Abstract connection class, with support for master/slave sharding.
|
4
|
+
# @see Redpear::Connection#initialize for examples
|
5
|
+
#
|
6
|
+
class Redpear::Connection
|
3
7
|
|
4
|
-
|
8
|
+
MASTER_METHODS = %w|
|
9
|
+
append auth
|
10
|
+
bgrewriteaof bgsave blpop brpop brpoplpush
|
11
|
+
config
|
12
|
+
decr decrby del discard
|
13
|
+
exec expire expireat
|
14
|
+
flushall flushdb getset
|
15
|
+
hset hsetnx hincrby hmset hdel
|
16
|
+
incr incrby
|
17
|
+
linsert lpop lpush lpushx lrem lset ltrim
|
18
|
+
mapped_hmset mapped_mset mapped_msetnx
|
19
|
+
move mset msetnx multi
|
20
|
+
persist pipelined psubscribe punsubscribe quit
|
21
|
+
rename renamenx rpop rpoplpush rpush rpushx
|
22
|
+
sadd save sdiffstore set setbit
|
23
|
+
setex setnx setrange sinterstore
|
24
|
+
shutdown smove spop srem subscribe
|
25
|
+
sunionstore sync synchronize
|
26
|
+
unsubscribe unwatch watch
|
27
|
+
zadd zincrby zinterstore zrem
|
28
|
+
zremrangebyrank zremrangebyscore zunionstore
|
29
|
+
|.freeze
|
5
30
|
|
6
|
-
|
7
|
-
|
8
|
-
|
31
|
+
SLAVE_METHODS = %w|
|
32
|
+
dbsize debug get getbit getrange
|
33
|
+
echo exists
|
34
|
+
hget hmget hexists hlen hkeys hvals hgetall
|
35
|
+
info keys lastsave lindex llen lrange
|
36
|
+
mapped_hmget mapped_mget mget monitor
|
37
|
+
object ping publish randomkey
|
38
|
+
scard sdiff select sinter sismember slaveof
|
39
|
+
smembers sort srandmember strlen substr sunion
|
40
|
+
ttl type
|
41
|
+
zcard zcount zrange zrangebyscore zrank
|
42
|
+
zrevrange zrevrangebyscore zrevrank zscore
|
43
|
+
|.freeze
|
44
|
+
|
45
|
+
# @return [Symbol] ther current connection, either :master or :slave
|
46
|
+
attr_reader :current
|
47
|
+
attr_accessor :master, :slave
|
48
|
+
|
49
|
+
# Constructor, accepts a master connection and an optional slave connection.
|
50
|
+
# Connections can be instances of Redis::Client, URL strings, or e.g.
|
51
|
+
# ConnectionPool objects. Examples:
|
52
|
+
#
|
53
|
+
# # Use current redis client as master and slave
|
54
|
+
# Redpear::Connection.new Redis.current
|
55
|
+
#
|
56
|
+
# # Use current redis client as slave and a remote URL as master
|
57
|
+
# Redpear::Connection.new "redis://master.host:6379", Redis.current
|
58
|
+
#
|
59
|
+
# # Use a connection pool - https://github.com/mperham/connection_pool
|
60
|
+
# slave_pool = ConnectionPool.new(:size => 5, :timeout => 5) { Redis.connect("redis://slave.host:6379") }
|
61
|
+
# Redpear::Connection.new "redis://master.host:6379", slave_pool
|
62
|
+
#
|
63
|
+
# @param [Redis::Client|String|ConnectionPool] master
|
64
|
+
# The master connection, defaults to `Redis.current`
|
65
|
+
# @param [Redis::Client|String|ConnectionPool] slave
|
66
|
+
# The (optional) slave connection, defaults to master
|
67
|
+
def initialize(master = Redis.current, slave = nil)
|
68
|
+
@master = _connect(master)
|
69
|
+
@slave = _connect(slave || master)
|
70
|
+
@transaction = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param [Symbol] name
|
74
|
+
# Either :master or :slave
|
75
|
+
# @yield
|
76
|
+
# Perform a block with the given connection
|
77
|
+
def on(name)
|
78
|
+
@current = send(name)
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
@current = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Run a transaction, prevents accidental transaction nesting
|
85
|
+
def transaction(&block)
|
86
|
+
if @transaction
|
87
|
+
yield
|
88
|
+
else
|
89
|
+
begin
|
90
|
+
@transaction = true
|
91
|
+
multi(&block)
|
92
|
+
ensure
|
93
|
+
@transaction = nil
|
94
|
+
end
|
9
95
|
end
|
10
|
-
|
96
|
+
end
|
11
97
|
|
12
|
-
|
13
|
-
|
14
|
-
|
98
|
+
MASTER_METHODS.each do |meth|
|
99
|
+
define_method(meth) do |*a, &b|
|
100
|
+
(current || master).send(meth, *a, &b)
|
15
101
|
end
|
102
|
+
end
|
16
103
|
|
17
|
-
|
18
|
-
|
19
|
-
|
104
|
+
SLAVE_METHODS.each do |meth|
|
105
|
+
define_method(meth) do |*a, &b|
|
106
|
+
(current || slave).send(meth, *a, &b)
|
20
107
|
end
|
108
|
+
end
|
21
109
|
|
22
|
-
|
23
|
-
|
24
|
-
|
110
|
+
private
|
111
|
+
|
112
|
+
def _connect(conn)
|
113
|
+
case conn
|
114
|
+
when String
|
115
|
+
Redis.connect(:url => conn)
|
116
|
+
else
|
117
|
+
conn
|
118
|
+
end
|
25
119
|
end
|
26
120
|
|
27
|
-
end
|
28
121
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Redpear::Model::Expiration
|
2
|
+
|
3
|
+
# Expires the record.
|
4
|
+
# @overload expire(time)
|
5
|
+
# @param [Time] time The time to expire the record at
|
6
|
+
# @overload expire(number)
|
7
|
+
# @param [Integer] number Expire in `number` of seconds from now
|
8
|
+
def expire(value)
|
9
|
+
attributes.expire(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Integer] the period this record has to live.
|
13
|
+
# May return nil for non-expiring records and non-existing records.
|
14
|
+
def ttl
|
15
|
+
attributes.ttl
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'factory_girl'
|
2
|
+
require 'redpear/model'
|
3
|
+
|
4
|
+
class Redpear::Model
|
5
|
+
|
6
|
+
# FactoryGirl module for your tests/specs. Example:
|
7
|
+
#
|
8
|
+
# require 'redpear/model/factory_girl'
|
9
|
+
#
|
10
|
+
# FactoryGirl.define do
|
11
|
+
# factory :post do
|
12
|
+
# title { "A Title" }
|
13
|
+
# created_at { Time.at(1313131313) }
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
module FactoryGirl
|
18
|
+
|
19
|
+
# @return [Boolean] always true. FactoryGirl requires it.
|
20
|
+
def save!
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
include FactoryGirl
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Redpear::Model::Finders
|
2
|
+
extend Redpear::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
# @return [Integer] the number of total records
|
7
|
+
def count
|
8
|
+
members.size
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [String] id the ID to check
|
12
|
+
# @return [Boolean] true or false
|
13
|
+
def exists?(id)
|
14
|
+
!id.nil? && members.include?(id)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [String] id the ID of the record to find
|
18
|
+
# @return [Redpear::Model] a record, or nil when not found
|
19
|
+
def find(id)
|
20
|
+
instantiate(id) if exists?(id)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array<Redpear::Model>] all records
|
24
|
+
def all
|
25
|
+
members.map &method(:find)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @yield over each available record
|
29
|
+
# @yieldparam [Redpear::Model] record
|
30
|
+
def find_each
|
31
|
+
members.each do |id|
|
32
|
+
yield find(id)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'machinist'
|
2
|
+
require 'redpear/model'
|
3
|
+
|
4
|
+
class Redpear::Model
|
5
|
+
extend ::Machinist::Machinable
|
6
|
+
|
7
|
+
# Used internally by Machinist
|
8
|
+
# @return [Class] the blueprint class
|
9
|
+
def self.blueprint_class
|
10
|
+
self::Machinist::Blueprint
|
11
|
+
end
|
12
|
+
|
13
|
+
# Machinist module for your tests/specs. Example:
|
14
|
+
#
|
15
|
+
# # spec/support/blueprints.rb
|
16
|
+
# require "redpear/model/machinist"
|
17
|
+
#
|
18
|
+
# Post.blueprint do
|
19
|
+
# title { "A Title" }
|
20
|
+
# created_at { 2.days.ago }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
module Machinist
|
24
|
+
|
25
|
+
class Blueprint < ::Machinist::Blueprint
|
26
|
+
|
27
|
+
def make!(attributes = {})
|
28
|
+
make(attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def lathe_class #:nodoc:
|
32
|
+
Lathe
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class Lathe < ::Machinist::Lathe
|
38
|
+
protected
|
39
|
+
|
40
|
+
def make_one_value(attribute, args)
|
41
|
+
return unless block_given?
|
42
|
+
raise_argument_error(attribute) unless args.empty?
|
43
|
+
yield
|
44
|
+
end
|
45
|
+
|
46
|
+
def assign_attribute(key, value) #:nodoc:
|
47
|
+
@assigned_attributes[key.to_sym] = value
|
48
|
+
@object[key] = value
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/redpear/model.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require "redpear/core_ext/stringify_keys"
|
1
|
+
require 'redpear'
|
3
2
|
|
4
3
|
=begin
|
5
4
|
Redis is a simple key/value store, hence storing structured data can be a
|
6
|
-
challenge. Redpear allows you to store/find/associate "records" in a Redis
|
7
|
-
very efficiently, minimising IO operations and storage space where possible.
|
5
|
+
challenge. Redpear::Model allows you to store/find/associate "records" in a Redis
|
6
|
+
DB very efficiently, minimising IO operations and storage space where possible.
|
8
7
|
|
9
8
|
For example:
|
10
9
|
|
@@ -18,53 +17,100 @@ For example:
|
|
18
17
|
index :post_id
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
post = Post.save :title => "Hi!", :body => "I'm a new post"
|
24
|
-
comment = Comment.save :post_id => post.id, :body => "I like this!"
|
25
|
-
|
26
|
-
Redpear is VERY lightweight. Compared with other ORMs, it offers raw speed at
|
27
|
-
the expense of convenience.
|
20
|
+
Redpear::Model is VERY lightweight. It is optimised for raw speed at the
|
21
|
+
expense of convenience.
|
28
22
|
=end
|
29
23
|
class Redpear::Model < Hash
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
include Redpear::Expiration
|
34
|
-
include Redpear::Counters
|
24
|
+
autoload :Finders, 'redpear/model/finders'
|
25
|
+
autoload :Expiration, 'redpear/model/expiration'
|
26
|
+
|
35
27
|
include Redpear::Schema
|
36
|
-
include
|
28
|
+
include Finders
|
29
|
+
include Expiration
|
30
|
+
|
31
|
+
class << self
|
32
|
+
|
33
|
+
alias_method :create, :new
|
34
|
+
|
35
|
+
# @param [Redpear::Connection] define a custom connection
|
36
|
+
attr_writer :connection
|
37
|
+
|
38
|
+
# @param [String] define a custom scope
|
39
|
+
attr_writer :scope
|
40
|
+
|
41
|
+
# @return [Redpear::Connection] the connection
|
42
|
+
def connection
|
43
|
+
@connection ||= (superclass.respond_to?(:connection) ? superclass.connection : Redpear::Connection.new)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String] the scope of this model. Example:
|
47
|
+
# Comment.scope # => "comments"
|
48
|
+
def scope
|
49
|
+
@scope ||= "#{name.split('::').last.downcase}s"
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [multiple] tokens
|
53
|
+
# The tokens to add to the scope
|
54
|
+
# @return [String] the full scope.
|
55
|
+
# Examples:
|
56
|
+
# Comment.nested_key(123) # => "comments:123"
|
57
|
+
# Comment.nested_key("abc", 123) # => "comments:abc:123"
|
58
|
+
def nested_key(*tokens)
|
59
|
+
[scope, *tokens].join(':')
|
60
|
+
end
|
37
61
|
|
38
|
-
|
39
|
-
|
62
|
+
# @return [Redpear::Store::Set] the IDs of all existing records
|
63
|
+
def members
|
64
|
+
@_members ||= Redpear::Store::Set.new nested_key(:~), connection
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Redpear::Store::Counter] the generator of primary keys
|
68
|
+
def pk_counter
|
69
|
+
@_pk_counter ||= Redpear::Store::Counter.new nested_key(:+), connection
|
70
|
+
end
|
71
|
+
|
72
|
+
# Runs a bulk-operation.
|
73
|
+
# @yield operations that should be run in the transaction
|
74
|
+
def transaction(&block)
|
75
|
+
connection.transaction(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Destroys a record. Example:
|
79
|
+
# @param [String] id the ID of the record to destroy
|
80
|
+
# @return [Redpear::Model] the destroyed record
|
81
|
+
def destroy(id)
|
82
|
+
instantiate(id).tap(&:destroy)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Allocate an instance
|
86
|
+
def instantiate(id)
|
87
|
+
instance = allocate
|
88
|
+
instance.send :store, 'id', id.to_s
|
89
|
+
instance
|
90
|
+
end
|
91
|
+
private :instantiate
|
92
|
+
|
93
|
+
end
|
40
94
|
|
41
95
|
def initialize(attrs = {})
|
42
96
|
super()
|
43
|
-
|
44
|
-
@__loaded__ = true
|
97
|
+
store 'id', (attrs.delete("id") || attrs.delete(:id) || self.class.pk_counter.next).to_s
|
45
98
|
update(attrs)
|
99
|
+
self.class.members.add(id)
|
46
100
|
end
|
47
101
|
|
48
|
-
#
|
49
|
-
# @return [String]
|
102
|
+
# @return [String] the ID of this record
|
50
103
|
def id
|
51
|
-
|
52
|
-
value.to_s if value
|
53
|
-
end
|
54
|
-
|
55
|
-
# ID accessor
|
56
|
-
# @param [Object] id
|
57
|
-
def id=(value)
|
58
|
-
self["id"] = value.to_s
|
104
|
+
fetch 'id'
|
59
105
|
end
|
60
106
|
|
61
107
|
# Custom comparator, inspired by ActiveRecord::Base#==
|
62
108
|
# @param [Object] other the comparison object
|
63
|
-
# @return [Boolean] true, if +other+ is persisted and ID
|
109
|
+
# @return [Boolean] true, if +other+ is persisted and ID
|
64
110
|
def ==(other)
|
65
111
|
case other
|
66
112
|
when Redpear::Model
|
67
|
-
other.instance_of?(self.class) &&
|
113
|
+
other.instance_of?(self.class) && other.id == id
|
68
114
|
else
|
69
115
|
super
|
70
116
|
end
|
@@ -76,57 +122,134 @@ class Redpear::Model < Hash
|
|
76
122
|
id.hash
|
77
123
|
end
|
78
124
|
|
79
|
-
#
|
125
|
+
# Reads and (caches) a single value
|
126
|
+
# @param [String] name
|
127
|
+
# The name of the attributes
|
80
128
|
# @return [Object]
|
129
|
+
# The attribute value
|
81
130
|
def [](name)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
131
|
+
return if frozen?
|
132
|
+
|
133
|
+
column = self.class.columns[name]
|
134
|
+
return super if column.nil? || key?(column)
|
135
|
+
|
136
|
+
value = case column
|
137
|
+
when Redpear::Schema::Score
|
138
|
+
column.members[id]
|
139
|
+
when Redpear::Schema::Column
|
140
|
+
attributes[column]
|
88
141
|
end
|
142
|
+
|
143
|
+
store column, column.type_cast(value)
|
89
144
|
end
|
90
145
|
|
91
|
-
#
|
146
|
+
# Write a single attribute
|
92
147
|
# @param [String] name
|
148
|
+
# The name of the attributes
|
93
149
|
# @param [Object] value
|
150
|
+
# The value to store
|
94
151
|
def []=(name, value)
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
super
|
152
|
+
column = self.class.columns[name] || return
|
153
|
+
delete column.to_s
|
154
|
+
store_attribute attributes, column, value
|
99
155
|
end
|
100
156
|
|
101
|
-
#
|
102
|
-
# @param [
|
103
|
-
#
|
157
|
+
# Increments the value of a counter attribute
|
158
|
+
# @param [String] name
|
159
|
+
# The column name to increment
|
160
|
+
# @param [Integer] by
|
161
|
+
# Increment by this value
|
162
|
+
def increment(name, by = 1)
|
163
|
+
column = self.class.columns[name]
|
164
|
+
return false unless column && column.type == :counter
|
165
|
+
|
166
|
+
store column, attributes.increment(column, by)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Decrements the value of a counter attribute
|
170
|
+
# @param [String|Symbol] name
|
171
|
+
# The column name to decrement
|
172
|
+
# @param [Integer] by
|
173
|
+
# Decrement by this value
|
174
|
+
def decrement(name, by = 1)
|
175
|
+
increment name, -by
|
176
|
+
end
|
177
|
+
|
178
|
+
# Bulk-updates the model
|
104
179
|
# @return [Hash]
|
105
|
-
def
|
106
|
-
|
107
|
-
|
108
|
-
|
180
|
+
def update(hash)
|
181
|
+
clear
|
182
|
+
self.class.transaction do
|
183
|
+
bulk = {}
|
184
|
+
hash.each do |name, value|
|
185
|
+
column = self.class.columns[name] || next
|
186
|
+
store_attribute bulk, column, value
|
187
|
+
end
|
188
|
+
attributes.merge! bulk
|
189
|
+
end
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
# Clear all the cached attributes, but keep ID
|
194
|
+
# @return [Hash] self
|
195
|
+
def clear
|
196
|
+
value = self.id
|
197
|
+
super
|
198
|
+
ensure
|
199
|
+
store 'id', value
|
200
|
+
end
|
201
|
+
|
202
|
+
# Returns the attributes store
|
203
|
+
# @return [Redpear::Store::Hash] attributes
|
204
|
+
def attributes
|
205
|
+
@_attributes ||= Redpear::Store::Hash.new self.class.nested_key("", id), self.class.connection
|
206
|
+
end
|
207
|
+
|
208
|
+
# Return lookups, relevant to this record
|
209
|
+
# @return [Array<Redpear::Store::Enumerable>] the lookups this record is related to
|
210
|
+
def lookups
|
211
|
+
@_lookups ||= [self.class.members] + self.class.columns.indicies.map {|i| i.for(self) }
|
212
|
+
end
|
213
|
+
|
214
|
+
# Destroy the record.
|
215
|
+
# @return [Boolean] true if successful
|
216
|
+
def destroy
|
217
|
+
lookups # Build before transaction
|
218
|
+
self.class.transaction do
|
219
|
+
lookups.each {|l| l.delete(id) }
|
220
|
+
attributes.purge!
|
221
|
+
end
|
222
|
+
freeze
|
109
223
|
end
|
110
224
|
|
111
225
|
# Show information about this record
|
112
226
|
# @return [String]
|
113
227
|
def inspect
|
114
|
-
__ensure_loaded__
|
115
228
|
"#<#{self.class.name} #{super}>"
|
116
229
|
end
|
117
230
|
|
118
|
-
#
|
119
|
-
|
120
|
-
|
121
|
-
attrs = (attrs ? attrs.stringify_keys : {})
|
122
|
-
attrs["id"] = attrs["id"].to_s if attrs["id"]
|
123
|
-
super
|
231
|
+
# Cache a key/value pair
|
232
|
+
def store(key, value)
|
233
|
+
super key.to_s, value
|
124
234
|
end
|
125
235
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
236
|
+
# Store an attribute in target
|
237
|
+
def store_attribute(target, column, value)
|
238
|
+
value = column.encode_value(value)
|
239
|
+
|
240
|
+
case column
|
241
|
+
when Redpear::Schema::Score
|
242
|
+
column.members[id] = value unless value.nil?
|
243
|
+
when Redpear::Schema::Index
|
244
|
+
column.members(value).add(id) unless value.nil?
|
245
|
+
target[column] = value
|
246
|
+
when Redpear::Schema::Column
|
247
|
+
target[column] = value
|
130
248
|
end
|
131
249
|
|
250
|
+
value
|
251
|
+
end
|
252
|
+
protected :store, :store_attribute
|
253
|
+
private :fetch, :delete, :delete_if, :keep_if, :merge!, :reject!, :select!, :replace
|
254
|
+
|
132
255
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Stores the column information
|
2
2
|
class Redpear::Schema::Collection < Array
|
3
3
|
|
4
|
-
# @param [multiple] the column definition. Please see Redpear::Column#initialize
|
4
|
+
# @param [multiple] the column definition. Please see Redpear::Schema::Column#initialize
|
5
5
|
def store(klass, *args)
|
6
6
|
reset!
|
7
7
|
klass.new(*args).tap do |col|
|
@@ -14,16 +14,16 @@ class Redpear::Schema::Collection < Array
|
|
14
14
|
@names ||= lookup.keys
|
15
15
|
end
|
16
16
|
|
17
|
-
# @return [Array] the names of the indices only
|
18
|
-
def indices
|
19
|
-
@indices ||= select(&:index?)
|
20
|
-
end
|
21
|
-
|
22
17
|
# @return [Hash] the column lookup, indexed by name
|
23
18
|
def lookup
|
24
19
|
@lookup ||= inject({}) {|r, c| r.update c.to_s => c }
|
25
20
|
end
|
26
21
|
|
22
|
+
# @return [Array] only the index columns
|
23
|
+
def indicies
|
24
|
+
@indicies ||= to_a.select {|i| i.is_a?(Redpear::Schema::Index) }
|
25
|
+
end
|
26
|
+
|
27
27
|
# @param [String] the column name
|
28
28
|
# @return [Boolean] if name is part of the collection
|
29
29
|
def include?(name)
|
@@ -31,7 +31,7 @@ class Redpear::Schema::Collection < Array
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# @param [String] the column name
|
34
|
-
# @return [Redpear::Column] the column for the given name
|
34
|
+
# @return [Redpear::Schema::Column] the column for the given name
|
35
35
|
def [](name)
|
36
36
|
lookup[name.to_s]
|
37
37
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class Redpear::Column < String
|
1
|
+
class Redpear::Schema::Column < String
|
2
2
|
attr_reader :type, :model
|
3
3
|
|
4
4
|
# Creates a new column.
|
@@ -35,6 +35,17 @@ class Redpear::Column < String
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
# Encodes a value, for storage
|
39
|
+
# @return [Object] the encoded value
|
40
|
+
def encode_value(value)
|
41
|
+
case value
|
42
|
+
when Time
|
43
|
+
value.to_i
|
44
|
+
else
|
45
|
+
value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
38
49
|
# @return [String] the column name
|
39
50
|
def name
|
40
51
|
to_s
|
@@ -50,9 +61,4 @@ class Redpear::Column < String
|
|
50
61
|
true
|
51
62
|
end
|
52
63
|
|
53
|
-
# @return [Boolean] true if the column is an index
|
54
|
-
def index?
|
55
|
-
is_a? Redpear::Index
|
56
|
-
end
|
57
|
-
|
58
64
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Redpear::Schema::Index < Redpear::Schema::Column
|
2
|
+
|
3
|
+
# @param [Redpear::Model] record the owner record
|
4
|
+
# @return [Redpear::Store::Set] the set holding the IDs for `record's` index
|
5
|
+
def for(record)
|
6
|
+
members record.send(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param [String] index value
|
10
|
+
# @return [Redpear::Store::Set] the set holding the IDs for the given `foreign_key`
|
11
|
+
def members(value)
|
12
|
+
value = '_' if value.nil?
|
13
|
+
Redpear::Store::Set.new nested_key(name, value), model.connection
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def nested_key(*tokens)
|
19
|
+
model.nested_key(:~, *tokens)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Redpear::Schema::Score < Redpear::Schema::Index
|
2
|
+
|
3
|
+
# @return [Redpear::Store::Set] the set holding the IDs for `record's` index
|
4
|
+
def for(*)
|
5
|
+
members
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Redpear::Store::SortedSet] the sorted set holding the pairs
|
9
|
+
def members(*)
|
10
|
+
@members ||= Redpear::Store::SortedSet.new nested_key(name), model.connection
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|