active_redis_orm 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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/active_redis_orm.gemspec +23 -0
- data/lib/active_redis/all_list.rb +70 -0
- data/lib/active_redis/attributes.rb +423 -0
- data/lib/active_redis/dirty_attributes.rb +16 -0
- data/lib/active_redis/dirty_objects/array.rb +19 -0
- data/lib/active_redis/dirty_objects/hash.rb +31 -0
- data/lib/active_redis/dirty_objects/sorted_set.rb +18 -0
- data/lib/active_redis/save.rb +62 -0
- data/lib/active_redis/timestamps.rb +23 -0
- data/lib/active_redis/version.rb +3 -0
- data/lib/active_redis_orm.rb +112 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0a3b7f868710c028f3d75dfc45ceed2c744c09fa
|
4
|
+
data.tar.gz: 8416a2080bcf63c28c1fd62c6326f54fcd1e1148
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e21996c2ec2bf8ebf299590cecb579c85838b668a09aac0cb38ee84ebfd33d2b6ff145c00e4d42746c7c43b7be991628a20d06dad8cc7f9a3c5ff53d5c90fea4
|
7
|
+
data.tar.gz: 6602b1f0a98a312f73c5f1f8087526e4c20cff4b442020dac713c2ef5eefefb8e12c26bb5e56c03259a87b495151ad7c35d704da514e8b12241ba82cf01719c6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tom Caspy
|
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,29 @@
|
|
1
|
+
# ActiveRedisOrm
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'active_redis_orm'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install active_redis_orm
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/active_redis_orm/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'active_redis/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "active_redis_orm"
|
8
|
+
spec.version = ActiveRedisOrm::VERSION
|
9
|
+
spec.authors = ["Tom Caspy"]
|
10
|
+
spec.email = ["tom@tikalk.com"]
|
11
|
+
spec.summary = %q{ActiveRedis is an ORM for redis written in Ruby.}
|
12
|
+
spec.description = %q{ActiveRedis is a Ruby ORM for Redis, using ActiveModel, heavily influenced by the ActiveRecord and Mongoid gems}
|
13
|
+
spec.homepage = "https://github.com/SpotIM/active_redis_orm"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module AllList
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.after_create :add_to_all_lists
|
6
|
+
base.after_destroy :remove_from_all_lists
|
7
|
+
class << base
|
8
|
+
attr_accessor :all_lists
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_to_all_lists
|
13
|
+
self.class.all_lists ||= []
|
14
|
+
self.class.all_lists.each do |name, options|
|
15
|
+
ListWriter.new(self, name, options).add
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_from_all_lists
|
20
|
+
self.class.all_lists ||= []
|
21
|
+
self.class.all_lists.each do |name, options|
|
22
|
+
ListWriter.new(self, name, options).remove
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def list(name, options={})
|
28
|
+
self.all_lists ||= {}
|
29
|
+
self.all_lists[name.to_sym] ||= options
|
30
|
+
class_eval %Q{
|
31
|
+
def self.#{name}_ids
|
32
|
+
Redis::SortedSet.new(list_key(:#{name}))
|
33
|
+
end
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def list_key(name)
|
38
|
+
"#{redis_namespace.pluralize}:#{name}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class ListWriter
|
43
|
+
def initialize(object, name, options = {})
|
44
|
+
@object, @name, @options = object, name, options
|
45
|
+
end
|
46
|
+
|
47
|
+
def add
|
48
|
+
ActiveRedis.redis.zadd(key, Time.now.to_f, @object.id) if should_add?
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove
|
52
|
+
ActiveRedis.redis.zrem(key, @object.id)
|
53
|
+
end
|
54
|
+
|
55
|
+
def key
|
56
|
+
@object.class.list_key(@name)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def should_add?
|
62
|
+
if @options[:if].present?
|
63
|
+
@options[:if].call(@object)
|
64
|
+
else
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,423 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module AttributeMethods
|
3
|
+
def allow_mass_assignment?(attr)
|
4
|
+
self.class.attr_accessible?(attr)
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_attributes(attrs)
|
8
|
+
if attrs
|
9
|
+
attrs.each do |attr, value|
|
10
|
+
send("#{attr}=", value) if allow_mass_assignment?(attr) && respond_to?("#{attr}=")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_attributes(attrs)
|
16
|
+
set_attributes(attrs)
|
17
|
+
save
|
18
|
+
end
|
19
|
+
|
20
|
+
def after_set(field_name)
|
21
|
+
expire_field(field_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def expire_field(field_name)
|
25
|
+
expires_in = self.class.attribute_options[field_name.to_sym][:expires_in]
|
26
|
+
if expires_in.to_i > 0
|
27
|
+
ActiveRedis.redis.expire(send("#{field_name}_redis_key"), expires_in.to_i)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module Attributes
|
33
|
+
def attr_accessible(*attrs)
|
34
|
+
@attr_accessible = attrs
|
35
|
+
end
|
36
|
+
|
37
|
+
def attr_accessible?(attr)
|
38
|
+
@attr_accessible.nil? || @attr_accessible.include?(attr)
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_field(field_name, options)
|
42
|
+
define_attribute_methods field_name
|
43
|
+
class_eval %Q{
|
44
|
+
def #{field_name}_redis_key
|
45
|
+
"#{self.redis_namespace}:\#{id}:#{field_name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def refresh_#{field_name}!
|
49
|
+
@attributes[:#{field_name}] = nil
|
50
|
+
#{field_name}
|
51
|
+
end
|
52
|
+
|
53
|
+
def destroy_#{field_name}
|
54
|
+
Redis::Value.new(#{field_name}_redis_key).delete
|
55
|
+
end
|
56
|
+
}
|
57
|
+
if options[:finder_field]
|
58
|
+
finder_field(field_name, options)
|
59
|
+
end
|
60
|
+
case options[:type]
|
61
|
+
when :set
|
62
|
+
set_field(field_name, options)
|
63
|
+
when :sorted_set
|
64
|
+
sorted_set_field(field_name, options)
|
65
|
+
when :list
|
66
|
+
list_field(field_name, options)
|
67
|
+
when :hash
|
68
|
+
hash_field(field_name, options)
|
69
|
+
when :float
|
70
|
+
float_field(field_name, options)
|
71
|
+
when :int, :integer
|
72
|
+
integer_field(field_name, options)
|
73
|
+
when :counter
|
74
|
+
counter_field(field_name, options)
|
75
|
+
when :boolean
|
76
|
+
boolean_field(field_name, options)
|
77
|
+
when :date
|
78
|
+
date_field(field_name, options)
|
79
|
+
when :time, :datetime, :timestamp
|
80
|
+
time_field(field_name, options)
|
81
|
+
else
|
82
|
+
string_field(field_name, options)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def string_field(field_name, options)
|
87
|
+
class_eval %Q{
|
88
|
+
def #{field_name}_object
|
89
|
+
Redis::Value.new(#{field_name}_redis_key)
|
90
|
+
end
|
91
|
+
|
92
|
+
def #{field_name}
|
93
|
+
@attributes[:#{field_name}] ||= begin
|
94
|
+
value = #{field_name}_object.value
|
95
|
+
value.nil? ? value : value.to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def #{field_name}=(value)
|
100
|
+
#{field_name}_will_change!
|
101
|
+
@attributes[:#{field_name}] = value
|
102
|
+
end
|
103
|
+
|
104
|
+
def set_#{field_name}(value)
|
105
|
+
#{field_name}_object.value = value.last
|
106
|
+
after_set(:#{field_name})
|
107
|
+
end
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
def integer_field(field_name, options)
|
112
|
+
class_eval %Q{
|
113
|
+
def #{field_name}_object
|
114
|
+
Redis::Value.new(#{field_name}_redis_key)
|
115
|
+
end
|
116
|
+
|
117
|
+
def #{field_name}
|
118
|
+
@attributes[:#{field_name}] ||= begin
|
119
|
+
value = #{field_name}_object.value
|
120
|
+
value.nil? ? value : value.to_i
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def #{field_name}=(value)
|
125
|
+
#{field_name}_will_change!
|
126
|
+
@attributes[:#{field_name}] = value.to_i
|
127
|
+
end
|
128
|
+
|
129
|
+
def set_#{field_name}(value)
|
130
|
+
#{field_name}_object.value = value.last.to_i
|
131
|
+
after_set(:#{field_name})
|
132
|
+
end
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def float_field(field_name, options)
|
137
|
+
class_eval %Q{
|
138
|
+
def #{field_name}_object
|
139
|
+
Redis::Value.new(#{field_name}_redis_key)
|
140
|
+
end
|
141
|
+
|
142
|
+
def #{field_name}
|
143
|
+
@attributes[:#{field_name}] ||= begin
|
144
|
+
value = #{field_name}_object.value
|
145
|
+
value.nil? ? value : value.to_f
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def #{field_name}=(value)
|
150
|
+
#{field_name}_will_change!
|
151
|
+
@attributes[:#{field_name}] = value.to_f
|
152
|
+
end
|
153
|
+
|
154
|
+
def set_#{field_name}(value)
|
155
|
+
#{field_name}_object.value = value.last.to_f
|
156
|
+
after_set(:#{field_name})
|
157
|
+
end
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
def boolean_field(field_name, options)
|
162
|
+
class_eval %Q{
|
163
|
+
def #{field_name}_object
|
164
|
+
Redis::Value.new(#{field_name}_redis_key)
|
165
|
+
end
|
166
|
+
|
167
|
+
def #{field_name}
|
168
|
+
return @attributes[:#{field_name}] if @attributes[:#{field_name}].boolean?
|
169
|
+
@attributes[:#{field_name}] ||= begin
|
170
|
+
value = #{field_name}_object.value
|
171
|
+
value = "false" unless value.present?
|
172
|
+
value.to_bool
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def #{field_name}?
|
177
|
+
!!#{field_name}
|
178
|
+
end
|
179
|
+
|
180
|
+
def #{field_name}=(value)
|
181
|
+
#{field_name}_will_change!
|
182
|
+
if !value.boolean?
|
183
|
+
value = value.to_bool
|
184
|
+
else
|
185
|
+
value = value
|
186
|
+
end
|
187
|
+
|
188
|
+
@attributes[:#{field_name}] = value
|
189
|
+
end
|
190
|
+
|
191
|
+
def set_#{field_name}(value)
|
192
|
+
#{field_name}_object.value = value.last.to_s
|
193
|
+
after_set(:#{field_name})
|
194
|
+
end
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
def counter_field(field_name, options)
|
199
|
+
class_eval %Q{
|
200
|
+
def #{field_name}_object
|
201
|
+
Redis::Counter.new(#{field_name}_redis_key)
|
202
|
+
end
|
203
|
+
|
204
|
+
def #{field_name}
|
205
|
+
@attributes[:#{field_name}] ||= #{field_name}_object.value.to_i
|
206
|
+
end
|
207
|
+
|
208
|
+
def #{field_name}=(#{field_name})
|
209
|
+
#{field_name}_will_change!
|
210
|
+
@attributes[:#{field_name}] = #{field_name}.to_i
|
211
|
+
end
|
212
|
+
|
213
|
+
def set_#{field_name}(value)
|
214
|
+
#{field_name}_object.value = value.last.to_i
|
215
|
+
after_set(:#{field_name})
|
216
|
+
end
|
217
|
+
|
218
|
+
def #{field_name}_inc(count)
|
219
|
+
@attributes[:#{field_name}] = #{field_name}_object.increment(count)
|
220
|
+
end
|
221
|
+
|
222
|
+
def #{field_name}_dec(count)
|
223
|
+
@attributes[:#{field_name}] = #{field_name}_object.decrement(count)
|
224
|
+
end
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
def list_field(field_name, options)
|
229
|
+
class_eval %Q{
|
230
|
+
def #{field_name}_object
|
231
|
+
Redis::List.new(#{field_name}_redis_key)
|
232
|
+
end
|
233
|
+
|
234
|
+
def #{field_name}
|
235
|
+
@attributes[:#{field_name}] ||= begin
|
236
|
+
ActiveRedis::DirtyObjects::Array.new(#{field_name}_object.values)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def #{field_name}=(array)
|
241
|
+
if !array.is_a?(Array) && array.present?
|
242
|
+
array = [array]
|
243
|
+
end
|
244
|
+
#{field_name}.replace(array) if array.is_a?(Array)
|
245
|
+
|
246
|
+
#{field_name}
|
247
|
+
end
|
248
|
+
|
249
|
+
def #{field_name}_force_load
|
250
|
+
@attributes[:#{field_name}] = #{field_name}_object.values
|
251
|
+
end
|
252
|
+
|
253
|
+
def #{field_name}_count
|
254
|
+
#{field_name}_object.count
|
255
|
+
end
|
256
|
+
|
257
|
+
def set_#{field_name}(value)
|
258
|
+
changes = value.first.last
|
259
|
+
changes[:additions].each do |addition|
|
260
|
+
#{field_name}_object.push(addition)
|
261
|
+
end
|
262
|
+
changes[:drops].each do |drop|
|
263
|
+
#{field_name}_object.delete(drop)
|
264
|
+
end
|
265
|
+
after_set(:#{field_name})
|
266
|
+
end
|
267
|
+
}
|
268
|
+
end
|
269
|
+
|
270
|
+
def set_field(field_name, options)
|
271
|
+
class_eval %Q{
|
272
|
+
def #{field_name}_object
|
273
|
+
Redis::Set.new(#{field_name}_redis_key)
|
274
|
+
end
|
275
|
+
|
276
|
+
def #{field_name}
|
277
|
+
@attributes[:#{field_name}] ||= begin
|
278
|
+
ActiveRedis::DirtyObjects::Array.new(#{field_name}_object.members)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def #{field_name}=(array)
|
283
|
+
#{field_name}.replace(array)
|
284
|
+
end
|
285
|
+
|
286
|
+
def #{field_name}_count
|
287
|
+
#{field_name}_object.count
|
288
|
+
end
|
289
|
+
|
290
|
+
def set_#{field_name}(value)
|
291
|
+
changes = value.first.last
|
292
|
+
changes[:additions].each do |addition|
|
293
|
+
#{field_name}_object.add(addition)
|
294
|
+
end
|
295
|
+
changes[:drops].each do |drop|
|
296
|
+
#{field_name}_object.delete(drop)
|
297
|
+
end
|
298
|
+
after_set(:#{field_name})
|
299
|
+
end
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
def sorted_set_field(field_name, options)
|
304
|
+
class_eval %Q{
|
305
|
+
def #{field_name}_object
|
306
|
+
Redis::SortedSet.new(#{field_name}_redis_key)
|
307
|
+
end
|
308
|
+
|
309
|
+
def #{field_name}
|
310
|
+
@attributes[:#{field_name}] ||= begin
|
311
|
+
ActiveRedis::DirtyObjects::SortedSet.new(#{field_name}_object.members)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def #{field_name}_count
|
316
|
+
#{field_name}_object.count
|
317
|
+
end
|
318
|
+
|
319
|
+
def set_#{field_name}(value)
|
320
|
+
changes = value.first.last
|
321
|
+
changes[:additions].each do |addition|
|
322
|
+
#{field_name}_object[changes[:hash].key(addition)] = addition
|
323
|
+
end
|
324
|
+
changes[:drops].each do |drop|
|
325
|
+
#{field_name}_object.delete(drop)
|
326
|
+
end
|
327
|
+
after_set(:#{field_name})
|
328
|
+
end
|
329
|
+
}
|
330
|
+
end
|
331
|
+
|
332
|
+
def hash_field(field_name, options)
|
333
|
+
class_eval %Q{
|
334
|
+
def #{field_name}_hash_set
|
335
|
+
Redis::HashKey.new(#{field_name}_redis_key)
|
336
|
+
end
|
337
|
+
|
338
|
+
def #{field_name}
|
339
|
+
@attributes[:#{field_name}] ||= begin
|
340
|
+
ActiveRedis::DirtyObjects::Hash[#{field_name}_hash_set.all]
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def #{field_name}=(value)
|
345
|
+
#{field_name}.replace(value)
|
346
|
+
end
|
347
|
+
|
348
|
+
def set_#{field_name}(value)
|
349
|
+
changes = value.first.last
|
350
|
+
changes[:additions].each do |addition|
|
351
|
+
#{field_name}_hash_set[addition] = changes[:hash][addition]
|
352
|
+
end
|
353
|
+
changes[:drops].each do |drop|
|
354
|
+
#{field_name}_hash_set.delete(drop)
|
355
|
+
end
|
356
|
+
after_set(:#{field_name})
|
357
|
+
end
|
358
|
+
}
|
359
|
+
end
|
360
|
+
|
361
|
+
def date_field(field_name, options)
|
362
|
+
string_field(field_name, options)
|
363
|
+
class_eval %Q{
|
364
|
+
def #{field_name}
|
365
|
+
@attributes[:#{field_name}] ||= begin
|
366
|
+
value = #{field_name}_object.value
|
367
|
+
if value.blank?
|
368
|
+
nil
|
369
|
+
else
|
370
|
+
Date.parse(value)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def set_#{field_name}(value)
|
376
|
+
#{field_name}_object.value = value.last.to_s
|
377
|
+
after_set(:#{field_name})
|
378
|
+
end
|
379
|
+
}
|
380
|
+
end
|
381
|
+
|
382
|
+
def time_field(field_name, options)
|
383
|
+
string_field(field_name, options)
|
384
|
+
class_eval %Q{
|
385
|
+
def #{field_name}
|
386
|
+
@attributes[:#{field_name}] ||= begin
|
387
|
+
value = #{field_name}_object.value
|
388
|
+
if value.blank?
|
389
|
+
nil
|
390
|
+
else
|
391
|
+
Time.at(value.to_i)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def set_#{field_name}(value)
|
397
|
+
#{field_name}_object.value = value.last.to_i
|
398
|
+
after_set(:#{field_name})
|
399
|
+
end
|
400
|
+
}
|
401
|
+
end
|
402
|
+
|
403
|
+
def finder_field(field_name, options)
|
404
|
+
before_save do
|
405
|
+
if send("#{field_name}_changed?")
|
406
|
+
ActiveRedis.redis.del(self.class.finder_key(field_name, send("#{field_name}_was")))
|
407
|
+
ActiveRedis.redis.set(self.class.finder_key(field_name, send(field_name)), id)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
class_eval %Q{
|
412
|
+
def self.find_by_#{field_name}(value)
|
413
|
+
id = Redis.current.get(finder_key("#{field_name}", value))
|
414
|
+
if id.present?
|
415
|
+
self.find(id)
|
416
|
+
else
|
417
|
+
nil
|
418
|
+
end
|
419
|
+
end
|
420
|
+
}
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module DirtyAttributes
|
3
|
+
def check_for_changes
|
4
|
+
@changed_attributes ||= {}
|
5
|
+
self.class.attribute_definitions.each do |attribute, options|
|
6
|
+
next unless @attributes.key?(attribute.to_sym)
|
7
|
+
value = send(attribute)
|
8
|
+
if value.class.name.start_with?("ActiveRedis::DirtyObjects")
|
9
|
+
if value.dirty?
|
10
|
+
@changed_attributes[attribute.to_s] = [value.original, value.changes]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module DirtyObjects
|
3
|
+
class Array < ::Array
|
4
|
+
attr_reader :original
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
@original = dup
|
8
|
+
end
|
9
|
+
|
10
|
+
def dirty?
|
11
|
+
@original != self
|
12
|
+
end
|
13
|
+
|
14
|
+
def changes
|
15
|
+
{additions: self - @original, drops: @original - self}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module DirtyObjects
|
3
|
+
class Hash < ::Hash
|
4
|
+
def self.[](*args)
|
5
|
+
hash = super
|
6
|
+
hash.original = hash.dup
|
7
|
+
hash
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :original
|
11
|
+
|
12
|
+
def dirty?
|
13
|
+
@original != self
|
14
|
+
end
|
15
|
+
|
16
|
+
def changes
|
17
|
+
{additions: updated_keys, drops: dropped_keys, hash: self}
|
18
|
+
end
|
19
|
+
|
20
|
+
def dropped_keys
|
21
|
+
@original.keys - self.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def updated_keys
|
25
|
+
self.keys.select do |key|
|
26
|
+
@original[key] != self[key]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module DirtyObjects
|
3
|
+
class SortedSet < ActiveRedis::DirtyObjects::Array
|
4
|
+
attr_accessor :hash
|
5
|
+
attr_reader :original
|
6
|
+
|
7
|
+
def []=(score, value)
|
8
|
+
self.push(score)
|
9
|
+
@hash ||= {}
|
10
|
+
@hash[value] = score
|
11
|
+
end
|
12
|
+
|
13
|
+
def changes
|
14
|
+
super.merge(hash: @hash)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module Save
|
3
|
+
#TODO: add if valid?
|
4
|
+
def save(options={})
|
5
|
+
unless options[:validate] == false
|
6
|
+
return false if invalid?(save_context)
|
7
|
+
end
|
8
|
+
|
9
|
+
if dirty?
|
10
|
+
case save_context
|
11
|
+
when :create
|
12
|
+
perform_create
|
13
|
+
when :update
|
14
|
+
perform_update
|
15
|
+
end
|
16
|
+
true
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def new_record?
|
23
|
+
!!@new_record
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def perform_save
|
29
|
+
run_callbacks :save do
|
30
|
+
Redis.current.pipelined do
|
31
|
+
self.changes.each do |key, value|
|
32
|
+
send("set_#{key}", value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@new_record = false
|
36
|
+
@changed_attributes.clear
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def perform_update
|
41
|
+
run_callbacks :update do
|
42
|
+
perform_save
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def perform_create
|
47
|
+
run_callbacks :create do
|
48
|
+
#TODO: check if no object exists under that id
|
49
|
+
@id ||= SecureRandom.uuid().delete('-')
|
50
|
+
perform_save
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def save_context
|
55
|
+
if new_record?
|
56
|
+
:create
|
57
|
+
else
|
58
|
+
:update
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveRedis
|
2
|
+
module Timestamps
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
field :created_at, type: :time
|
6
|
+
field :updated_at, type: :time
|
7
|
+
|
8
|
+
before_save :set_updated_at_to_now
|
9
|
+
before_create :set_created_at_to_now
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def set_updated_at_to_now
|
16
|
+
self.updated_at = Time.now
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_created_at_to_now
|
20
|
+
self.created_at = Time.now
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "active_redis/attributes"
|
2
|
+
require "active_redis/save"
|
3
|
+
require "active_redis/dirty_attributes"
|
4
|
+
require "active_redis/dirty_objects/array"
|
5
|
+
require "active_redis/dirty_objects/hash"
|
6
|
+
require "active_redis/dirty_objects/sorted_set"
|
7
|
+
require "active_redis/all_list"
|
8
|
+
require "active_redis/timestamps"
|
9
|
+
|
10
|
+
module ActiveRedis
|
11
|
+
mattr_accessor :redis
|
12
|
+
self.redis ||= Redis.current
|
13
|
+
class Base
|
14
|
+
include ActiveModel::Model
|
15
|
+
extend ActiveModel::Callbacks
|
16
|
+
extend ActiveModel::Naming
|
17
|
+
include ActiveModel::Serialization
|
18
|
+
include ActiveModel::Dirty
|
19
|
+
include ActiveModel::Validations
|
20
|
+
|
21
|
+
include ActiveRedis::DirtyAttributes
|
22
|
+
include ActiveRedis::Save
|
23
|
+
include ActiveRedis::AttributeMethods
|
24
|
+
|
25
|
+
attr_accessor :id
|
26
|
+
attr_reader :attributes
|
27
|
+
|
28
|
+
define_model_callbacks :create, :update, :save, :destroy
|
29
|
+
include ActiveRedis::AllList
|
30
|
+
|
31
|
+
#initialize with hash
|
32
|
+
def initialize(*args)
|
33
|
+
@attributes = {}
|
34
|
+
if args.first.is_a?(Hash) || args.empty?
|
35
|
+
set_attributes(args.first)
|
36
|
+
@new_record = true
|
37
|
+
elsif args.first.is_a?(String)
|
38
|
+
@id = args.first
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
self.class == other.class && self.id == other.id && id.present?
|
44
|
+
end
|
45
|
+
|
46
|
+
def reload!
|
47
|
+
@attributes = {}
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def dirty?
|
52
|
+
check_for_changes
|
53
|
+
self.changes.present?
|
54
|
+
end
|
55
|
+
|
56
|
+
def destroy
|
57
|
+
run_callbacks :destroy do
|
58
|
+
self.class.attributes.each do |attribute|
|
59
|
+
send("destroy_#{attribute}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
include ActiveRedis::Attributes
|
66
|
+
attr_accessor :attribute_definitions
|
67
|
+
|
68
|
+
def find(id)
|
69
|
+
new(id)
|
70
|
+
end
|
71
|
+
|
72
|
+
def create(*args)
|
73
|
+
object = new(*args)
|
74
|
+
object.save
|
75
|
+
object
|
76
|
+
end
|
77
|
+
|
78
|
+
def field(field_name, options = {})
|
79
|
+
self.attribute_definitions ||= {}
|
80
|
+
self.attribute_definitions[field_name.to_sym] = options
|
81
|
+
define_field(field_name, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def redis_namespace
|
85
|
+
self.name.underscore
|
86
|
+
end
|
87
|
+
|
88
|
+
def finder_key(field_name, value)
|
89
|
+
"#{redis_namespace.pluralize}:#{field_name}_to_#{redis_namespace}_id:#{value}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def attributes
|
93
|
+
attribute_options.keys
|
94
|
+
end
|
95
|
+
|
96
|
+
def attribute_options
|
97
|
+
self.attribute_definitions ||= {}
|
98
|
+
attribute_definitions
|
99
|
+
end
|
100
|
+
|
101
|
+
def inherited(klass)
|
102
|
+
class << klass
|
103
|
+
self.class_eval do
|
104
|
+
define_method :attribute_options do
|
105
|
+
self.attribute_definitions.merge(superclass.attribute_options)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_redis_orm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Caspy
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: ActiveRedis is a Ruby ORM for Redis, using ActiveModel, heavily influenced
|
42
|
+
by the ActiveRecord and Mongoid gems
|
43
|
+
email:
|
44
|
+
- tom@tikalk.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- active_redis_orm.gemspec
|
55
|
+
- lib/active_redis/all_list.rb
|
56
|
+
- lib/active_redis/attributes.rb
|
57
|
+
- lib/active_redis/dirty_attributes.rb
|
58
|
+
- lib/active_redis/dirty_objects/array.rb
|
59
|
+
- lib/active_redis/dirty_objects/hash.rb
|
60
|
+
- lib/active_redis/dirty_objects/sorted_set.rb
|
61
|
+
- lib/active_redis/save.rb
|
62
|
+
- lib/active_redis/timestamps.rb
|
63
|
+
- lib/active_redis/version.rb
|
64
|
+
- lib/active_redis_orm.rb
|
65
|
+
homepage: https://github.com/SpotIM/active_redis_orm
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.2.2
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: ActiveRedis is an ORM for redis written in Ruby.
|
89
|
+
test_files: []
|