redpear 0.6.4 → 0.7.0
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/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
|