redis_model 0.1.0.pre3
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 +187 -0
- data/Rakefile +15 -0
- data/lib/redis_model/adapters/paperclip.rb +51 -0
- data/lib/redis_model/attribute.rb +124 -0
- data/lib/redis_model/base.rb +67 -0
- data/lib/redis_model/belonged_to.rb +27 -0
- data/lib/redis_model/class_attribute.rb +50 -0
- data/lib/redis_model/configurations.rb +15 -0
- data/lib/redis_model/helpers/sorted_set_paginator.rb +80 -0
- data/lib/redis_model/intersected.rb +17 -0
- data/lib/redis_model/schema.rb +114 -0
- data/lib/redis_model/types/base.rb +32 -0
- data/lib/redis_model/types/base_value.rb +26 -0
- data/lib/redis_model/types/counter.rb +25 -0
- data/lib/redis_model/types/float.rb +17 -0
- data/lib/redis_model/types/hash.rb +53 -0
- data/lib/redis_model/types/integer.rb +17 -0
- data/lib/redis_model/types/list.rb +40 -0
- data/lib/redis_model/types/set.rb +59 -0
- data/lib/redis_model/types/sorted_set.rb +184 -0
- data/lib/redis_model/types/string.rb +11 -0
- data/lib/redis_model/types/timestamp.rb +26 -0
- data/lib/redis_model/version.rb +3 -0
- data/lib/redis_model.rb +37 -0
- data/redis_model.gemspec +28 -0
- data/spec/redis_model/attribute_spec.rb +77 -0
- data/spec/redis_model/base_spec.rb +34 -0
- data/spec/redis_model/class_attribute_spec.rb +16 -0
- data/spec/redis_model/helpers/sorted_set_paginator_spec.rb +33 -0
- data/spec/redis_model/schema_spec.rb +118 -0
- data/spec/redis_model/types/base_spec.rb +28 -0
- data/spec/redis_model/types/counter_spec.rb +32 -0
- data/spec/redis_model/types/float_spec.rb +20 -0
- data/spec/redis_model/types/hash_spec.rb +55 -0
- data/spec/redis_model/types/integer_spec.rb +22 -0
- data/spec/redis_model/types/list_spec.rb +55 -0
- data/spec/redis_model/types/set_spec.rb +62 -0
- data/spec/redis_model/types/sorted_set_spec.rb +303 -0
- data/spec/redis_model/types/string_spec.rb +28 -0
- data/spec/redis_model/types/timestamp_spec.rb +22 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/dynamic_class.rb +5 -0
- metadata +190 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d7a542c893f6a0451c7e008f352c6ed233696177
|
4
|
+
data.tar.gz: ee2e0ba63aabc72f0eaa61237d117ab0fef4ea9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee8f88e7a912a8388b6b6a71c4c57df60b2c0f9ad0ff570a1497d512cc967dad0357a2a5ff8d794e9dc648c9354b7818a2c5095f754c5b77a3f2fa368aec5014
|
7
|
+
data.tar.gz: 4083958ab4af9bac45607e21fdb14989f350096a38882de0c6b0e045eae2579f8a0f0f0158ccddaf8b9bebcd1a3d8535646fc49c236fabc9fc61f088d1449562
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Inbeom Hwang
|
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,187 @@
|
|
1
|
+
# RedisModel
|
2
|
+
|
3
|
+
RedisModel provides various types of interfaces to handle values on Redis from
|
4
|
+
applications, mostly with ORM including ActiveRecord. RedisModel is highly
|
5
|
+
customizable and tries to avoid polluting name space of previously defined
|
6
|
+
classes and modules.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'redis_model'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install redis_model
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
To cover as many use cases as possible, RedisModel provides three different
|
25
|
+
interfaces to handle values on Redis.
|
26
|
+
|
27
|
+
### Configuration
|
28
|
+
|
29
|
+
Keys of values on Redis in concern of RedisModel objects have naming convention
|
30
|
+
consisted of four parts:
|
31
|
+
|
32
|
+
- Application name (optional)
|
33
|
+
- Environment (optional)
|
34
|
+
- Class name converted to underscore notation
|
35
|
+
- Custom key label (optional)
|
36
|
+
|
37
|
+
It is recommended to set proper application name and environment (which is set
|
38
|
+
automatically using environment variables) to separate key namespace between
|
39
|
+
other applications and environments. It can be configured upon initial
|
40
|
+
bootstrapping stage (e.g. Rails initializers):
|
41
|
+
|
42
|
+
# In config/initializers/redis_model.rb:
|
43
|
+
|
44
|
+
RedisModel::Base.config do |config|
|
45
|
+
config.app_name = 'my_application' # Optional (recommended)
|
46
|
+
config.environment = 'production' # Optional
|
47
|
+
config.redis_url = 'redis://localhost:6379'
|
48
|
+
end
|
49
|
+
|
50
|
+
### Standalone Usage
|
51
|
+
|
52
|
+
Classes inherit `RedisModel::Base` can handle single type of value on Redis.
|
53
|
+
|
54
|
+
class SomeCounter < RedisModel::Base
|
55
|
+
data_type :counter # Required
|
56
|
+
end
|
57
|
+
|
58
|
+
All child classes inherit from `RedisModel::Base` must declare type of data
|
59
|
+
to handle. Available types include primitive Redis object types and some
|
60
|
+
helpers (sorted alphabetically):
|
61
|
+
|
62
|
+
- `:counter`
|
63
|
+
- `:float`
|
64
|
+
- `:hash`
|
65
|
+
- `:integer`
|
66
|
+
- `:list`
|
67
|
+
- `:set`
|
68
|
+
- `:sorted_set`
|
69
|
+
- `:string`
|
70
|
+
- `:timestamp`
|
71
|
+
|
72
|
+
For detailed specifications for each data type, refer documentation on modules
|
73
|
+
corresponding to data typees under `redis_model/types`.
|
74
|
+
|
75
|
+
If RedisModel is configured as above, all instances of `SomeCounter` refers to
|
76
|
+
the same key (which is `(application name):(environment):some_counter`). Key
|
77
|
+
label associated with instances can be retrieved by invoking
|
78
|
+
`RedisModel::Base#key_label` method. If it is necessary, you can define custom
|
79
|
+
key label for instances as:
|
80
|
+
|
81
|
+
class SomeCounter < RedisModel::Base
|
82
|
+
attr_reader :id
|
83
|
+
|
84
|
+
data_type :counter
|
85
|
+
|
86
|
+
# Uses return value of SomeCounter#id as custom label
|
87
|
+
custom_key_label &:id
|
88
|
+
end
|
89
|
+
|
90
|
+
After defining `custom_key_label`, key labels of instances becomes:
|
91
|
+
|
92
|
+
some_counter = SomeCounter.new.tap { |counter| counter.id = 1 }
|
93
|
+
|
94
|
+
some_counter.key_label
|
95
|
+
# => (application name):(environment):some_counter:1
|
96
|
+
|
97
|
+
Custom key label is useful if Redis values associated with instance attributes.
|
98
|
+
If it is needed to define multiple Redis values associated with an instance,
|
99
|
+
instance-level attributes will be more useful.
|
100
|
+
|
101
|
+
### Instance-level Attributes
|
102
|
+
|
103
|
+
Instances of classes mix in `RedisModel::Attribute` module can have multiple
|
104
|
+
number of attributes associated with values on Redis. Classes defined as models
|
105
|
+
in certain ORM can be a good example for this use case:
|
106
|
+
|
107
|
+
class User < ActiveRecord::Base
|
108
|
+
include RedisModel::Attribute
|
109
|
+
|
110
|
+
# Defines an instance-level attribute wired to Redis value.
|
111
|
+
redis_model_attribute :sign_in_count, :counter
|
112
|
+
end
|
113
|
+
|
114
|
+
After defining `redis_model_attribute`, getter method for the attribute is
|
115
|
+
defined as instance method of the class.
|
116
|
+
|
117
|
+
user = User.find(1)
|
118
|
+
|
119
|
+
user.sign_in_count.to_i
|
120
|
+
# => 0
|
121
|
+
|
122
|
+
user.sign_in_count.incr
|
123
|
+
|
124
|
+
user.sign_in_count.to_i
|
125
|
+
# => 1
|
126
|
+
|
127
|
+
Internally, `redis_model_attribute` defines a class inherit from
|
128
|
+
`RedisModel::Base` having name of attribute converted to camel case. In this
|
129
|
+
case, it will be defined as:
|
130
|
+
|
131
|
+
`User::SignInCount`
|
132
|
+
|
133
|
+
By this manner, Redis values referenced by the newly defined class has
|
134
|
+
consistent key label. By default, instance-level attributes use return value of
|
135
|
+
`#id` method of parent class as custom label. If it is required to use
|
136
|
+
different method of parent class as custom label, `:foreign_key` option should
|
137
|
+
be specified:
|
138
|
+
|
139
|
+
class User < ActiveRecord::Base
|
140
|
+
include RedisModel::Attribute
|
141
|
+
|
142
|
+
# User#name will be used as custom label.
|
143
|
+
redis_model_attribute :sign_in_count, :counter, foreign_key: :name
|
144
|
+
end
|
145
|
+
|
146
|
+
In many cases, it is required to define multiple attributes in single parent
|
147
|
+
class. As DSL defining single attribute is quite verbose compared to what it
|
148
|
+
does, `RedisModel::Attribute` provides helper to make multiple definitions
|
149
|
+
cleaner:
|
150
|
+
|
151
|
+
class User < ActiveRecord::Base
|
152
|
+
include RedisModel::Attribute
|
153
|
+
|
154
|
+
redis_model_attributes do
|
155
|
+
counter :sign_in_count
|
156
|
+
set :post_ids
|
157
|
+
set :order_ids
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
### Class-level Attributes
|
162
|
+
|
163
|
+
Ocasionally class-level attributes used as global scope variables are required.
|
164
|
+
Although it can be obtained by defining new class inherit from
|
165
|
+
`RedisModel::Base`, defining class attributes is cleaner and more concise.
|
166
|
+
|
167
|
+
class User < ActiveRecord::Base
|
168
|
+
include RedisModel::ClassAttribute
|
169
|
+
|
170
|
+
redis_class_attribue :registration_counter, :counter
|
171
|
+
end
|
172
|
+
|
173
|
+
User.registration_counter.to_i
|
174
|
+
# => 0
|
175
|
+
|
176
|
+
User.registration_counter.incr
|
177
|
+
|
178
|
+
User.registration_counter.to_i
|
179
|
+
# => 1
|
180
|
+
|
181
|
+
## Contributing
|
182
|
+
|
183
|
+
1. Fork it ( http://github.com/<my-github-username>/redis_model/fork )
|
184
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
185
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
186
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
187
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
|
+
require 'paperclip'
|
4
|
+
require 'paperclip/glue'
|
5
|
+
|
6
|
+
module RedisModel
|
7
|
+
module Adapters
|
8
|
+
# Public: Adapter for putting Paperclip attachment into RedisModel::Base-
|
9
|
+
# derived classes.
|
10
|
+
module Paperclip
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
include RedisModel::Attribute
|
13
|
+
|
14
|
+
included do
|
15
|
+
extend ActiveModel::Callbacks
|
16
|
+
include ActiveModel::Validations
|
17
|
+
include ::Paperclip::Glue
|
18
|
+
extend Override
|
19
|
+
|
20
|
+
define_model_callbacks :save, :destroy, :commit
|
21
|
+
end
|
22
|
+
|
23
|
+
def save
|
24
|
+
run_callbacks :save do
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy
|
30
|
+
run_callbacks :destroy do
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Override
|
36
|
+
def has_attached_file(*args)
|
37
|
+
define_paperclip_attributes_for(args.first)
|
38
|
+
|
39
|
+
super(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def define_paperclip_attributes_for(attachment)
|
43
|
+
redis_model_attribute :"#{attachment}_file_name", :string
|
44
|
+
redis_model_attribute :"#{attachment}_file_size", :integer
|
45
|
+
redis_model_attribute :"#{attachment}_content_type", :string
|
46
|
+
redis_model_attribute :"#{attachment}_updated_at", :timestamp
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module RedisModel
|
2
|
+
# Public: Implementations for instance-level Redis attributes for specified
|
3
|
+
# class.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#
|
7
|
+
# class User < ActiveRecord::Base
|
8
|
+
# includes RedisModel::Attribute
|
9
|
+
#
|
10
|
+
# redis_model_attribute :sign_in_count, :counter
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# user = User.find(1)
|
14
|
+
# user.sign_in_count.incr
|
15
|
+
# user.sign_in_count.to_i # Now it is set to 1
|
16
|
+
module Attribute
|
17
|
+
# Internal: Provides helper for DSL for RedisModel attributes which are
|
18
|
+
# defined in block of RedisModel::Attribute.redis_model_attributes method.
|
19
|
+
class DefinitionHelper
|
20
|
+
# Public: Returns class in which attributes are defined.
|
21
|
+
attr_reader :klass
|
22
|
+
|
23
|
+
# Public: Returns Hash of default options for attributes.
|
24
|
+
attr_reader :default_options
|
25
|
+
|
26
|
+
# Public: Defines a RedisModel attribute with given data type and name.
|
27
|
+
#
|
28
|
+
# attribute_name - Name of the attribute.
|
29
|
+
# options - Additional options for the attribute.
|
30
|
+
#
|
31
|
+
# Returns nothing.
|
32
|
+
#
|
33
|
+
# Signature
|
34
|
+
#
|
35
|
+
# <data_type>(attribute_name, options)
|
36
|
+
#
|
37
|
+
# data_type - Data type.
|
38
|
+
RedisModel::Schema::DATA_TYPES.each do |data_type, _|
|
39
|
+
define_method(data_type) do |*args|
|
40
|
+
column_name = args[0]
|
41
|
+
options = args[1] || {}
|
42
|
+
|
43
|
+
@klass.redis_model_attribute args.first, data_type, @default_options.merge(options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: Initializes new DefinitionHelper instance.
|
48
|
+
#
|
49
|
+
# klass - Class in which attributes are defined.
|
50
|
+
# options - Default options for attribute definitions.
|
51
|
+
#
|
52
|
+
# Returns newly instantiated RedisModel::Attribute::DefinitionHelper
|
53
|
+
# object.
|
54
|
+
def initialize(klass, default_options)
|
55
|
+
@klass = klass
|
56
|
+
@default_options = default_options
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module ClassMethods
|
61
|
+
# Public: Defines an instance-level attribute.
|
62
|
+
#
|
63
|
+
# attribute_name - Name of the attribute.
|
64
|
+
# type - Data type of the attribute.
|
65
|
+
# options - Additional options for the attribute definition.
|
66
|
+
# :foreign_key - Foreign key used for the attribute.
|
67
|
+
#
|
68
|
+
# Returns nothing.
|
69
|
+
def redis_model_attribute(attribute_name, type, options = {})
|
70
|
+
Class.new(RedisModel::BelongedTo) do
|
71
|
+
data_type type
|
72
|
+
|
73
|
+
if options[:foreign_key]
|
74
|
+
custom_key_label do |redis_model|
|
75
|
+
redis_model.parent.send(options[:foreign_key])
|
76
|
+
end
|
77
|
+
else
|
78
|
+
custom_key_label(&:parent_id)
|
79
|
+
end
|
80
|
+
end.tap do |klass|
|
81
|
+
const_set(attribute_name.to_s.camelize, klass)
|
82
|
+
define_redis_attribute_method(attribute_name, klass)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Public: Defines multiple instance-level attributes.
|
87
|
+
#
|
88
|
+
# block - A block contains definitions of multiple attributes.
|
89
|
+
#
|
90
|
+
# Example:
|
91
|
+
#
|
92
|
+
# class User
|
93
|
+
# redis_model_attributes
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# Returns nothing.
|
97
|
+
def redis_model_attributes(options = {}, &block)
|
98
|
+
RedisModel::Attribute::DefinitionHelper.new(self, options).tap do |definition_helper|
|
99
|
+
definition_helper.instance_eval(&block) if block_given?
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Internal: Defines getter/setter method for specified attribute.
|
104
|
+
#
|
105
|
+
# attribute_name - Name of the attribute.
|
106
|
+
# klass - Class for the attribute.
|
107
|
+
#
|
108
|
+
# Returns nothing.
|
109
|
+
def define_redis_attribute_method(attribute_name, klass)
|
110
|
+
define_method(attribute_name) do
|
111
|
+
klass.new(parent: self).to_value
|
112
|
+
end
|
113
|
+
|
114
|
+
define_method("#{attribute_name}=") do |value|
|
115
|
+
klass.new(parent: self).set(value)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.included(klass)
|
121
|
+
klass.extend ClassMethods
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module RedisModel
|
2
|
+
# Public: Parent class for classes associated with Redis attributes. It
|
3
|
+
# provides methods to manipulate values on Redis storage.
|
4
|
+
class Base
|
5
|
+
# Public: DSL which defines data type for classes extending
|
6
|
+
# RedisModel::Base. It is mandatory for child classes to indicate data type
|
7
|
+
# before manipulating Redis values.
|
8
|
+
#
|
9
|
+
# type - The Symbol indicating data type for the class.
|
10
|
+
#
|
11
|
+
# Examples
|
12
|
+
#
|
13
|
+
# class Foo < RedisModel::Base
|
14
|
+
# data_type :counter # Defines data type for instances of Foo class.
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# foo = Foo.new
|
18
|
+
# foo.incr
|
19
|
+
# foo.to_i # 1
|
20
|
+
#
|
21
|
+
# Returns nothing.
|
22
|
+
def self.data_type(type, options = {})
|
23
|
+
include RedisModel::Schema.register(self, options.merge(data_type: type))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Retrieves proper RedisModel::Schema object describes schema
|
27
|
+
# information for class being referenced.
|
28
|
+
#
|
29
|
+
# Returns RedisModel::Schema object for the class or its direct ancestor,
|
30
|
+
# nil if schema was not found.
|
31
|
+
def self.redis_model_schema
|
32
|
+
@schema ||= RedisModel::Schema.find(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: Global Redis connection object. It is initialized only once.
|
36
|
+
#
|
37
|
+
# Returns Redis connection for objects using RedisModel.
|
38
|
+
def self.connection
|
39
|
+
@@connection ||= Redis.new(url: RedisModel::Configurations.instance.redis_url)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Sets custom key label for the object.
|
43
|
+
#
|
44
|
+
# block - Required Block which returns custom key for given object.
|
45
|
+
#
|
46
|
+
# Returns nothing.
|
47
|
+
def self.custom_key_label(&block)
|
48
|
+
redis_model_schema.custom_key_label(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public: Retrieves key label for instantiated object.
|
52
|
+
#
|
53
|
+
# Returns the String label for the object.
|
54
|
+
def key_label
|
55
|
+
self.class.redis_model_schema.key_label(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Internal: Converts value stored in Redis to primitive Ruby object. It acts
|
59
|
+
# as a helper method which converts getters for certain types of RedisModel
|
60
|
+
# attributes to return primitive types.
|
61
|
+
#
|
62
|
+
# Returns the primitive value of the object if it is available.
|
63
|
+
def to_value
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RedisModel
|
2
|
+
# Internal: Base class for instance-level RedisModel attributes. Instances
|
3
|
+
# of this object should have reference to parent object.
|
4
|
+
class BelongedTo < RedisModel::Base
|
5
|
+
attr_accessor :parent, :parent_id
|
6
|
+
|
7
|
+
# Internal: Instantiates new RedisModel::BelongedTo object with given
|
8
|
+
# options.
|
9
|
+
#
|
10
|
+
# options - Options for new object.
|
11
|
+
# :parent - Parent object.
|
12
|
+
# :parent_id - ID of parent object.
|
13
|
+
#
|
14
|
+
# Returns newly instantiated RedisModel::BelongedTo object.
|
15
|
+
def initialize(attrs = {}, options = {})
|
16
|
+
@parent = attrs[:parent]
|
17
|
+
@parent_id = attrs[:parent_id]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Internal: ID of parent model.
|
21
|
+
#
|
22
|
+
# Returns ID of parent model.
|
23
|
+
def parent_id
|
24
|
+
@parent_id || @parent.id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'redis_model/base'
|
2
|
+
|
3
|
+
module RedisModel
|
4
|
+
# Public: Implementations for class-level RedisModel attributes.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# class User < ActiveRecord::Base
|
9
|
+
# include RedisModel::ClassAttribute
|
10
|
+
#
|
11
|
+
# redis_class_attribute :registration_count, :counter
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# User.registration_count.incr
|
15
|
+
# User.registration_count.to_i # Now it is set to 1
|
16
|
+
module ClassAttribute
|
17
|
+
module ClassMethods
|
18
|
+
# Public: Defines a RedisModel class attribute with given data type and
|
19
|
+
# name.
|
20
|
+
def redis_class_attribute(attribute_name, type)
|
21
|
+
new_klass = Class.new(RedisModel::Base) do
|
22
|
+
data_type type
|
23
|
+
end
|
24
|
+
|
25
|
+
const_set(attribute_name.to_s.camelize, new_klass)
|
26
|
+
define_redis_class_attribute_method(attribute_name, new_klass)
|
27
|
+
end
|
28
|
+
|
29
|
+
def redis_class_attribute_classes
|
30
|
+
@redis_class_attribute_classes ||= Hash.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def define_redis_class_attribute_method(attribute_name, new_klass)
|
34
|
+
singleton_class.class_eval do
|
35
|
+
define_method(attribute_name) do
|
36
|
+
new_klass.new.to_value
|
37
|
+
end
|
38
|
+
|
39
|
+
define_method("#{attribute_name}=") do |value|
|
40
|
+
new_klass.new.set(value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.included(klass)
|
47
|
+
klass.extend ClassMethods
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RedisModel
|
2
|
+
class Configurations
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_accessor :redis_url, :app_name, :environment
|
6
|
+
|
7
|
+
def environment
|
8
|
+
@environment || (defined?(Rails) ? Rails.env : (ENV['RAILS_ENV'] || ENV['RACK_ENV']))
|
9
|
+
end
|
10
|
+
|
11
|
+
def redis_url
|
12
|
+
@redis_url || 'redis://localhost:6379'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'kaminari'
|
2
|
+
require 'kaminari/models/array_extension'
|
3
|
+
|
4
|
+
module RedisModel
|
5
|
+
module Helpers
|
6
|
+
# Public: Pagination helper for elements contained in a sorted set defined
|
7
|
+
# based on RedisModel.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# paginator = RedisModel::Helpers::SortedSetPaginator.new(sorted_set_object)
|
12
|
+
#
|
13
|
+
# paginator.per(10).page(2)
|
14
|
+
# # => Returns objects in the second page.
|
15
|
+
#
|
16
|
+
# Elements in sorted set is ordered in descending score order, which is
|
17
|
+
# consistent with that of RedisModel::Types::SortedSet handles the object.
|
18
|
+
class SortedSetPaginator
|
19
|
+
include Enumerable
|
20
|
+
|
21
|
+
def initialize(sorted_set)
|
22
|
+
@sorted_set = sorted_set
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_default_options
|
26
|
+
@per ||= 20
|
27
|
+
@page ||= 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def page(page_number)
|
31
|
+
@page = page_number.to_i
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def per(number_per_page)
|
37
|
+
@per = number_per_page.to_i
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def since_id(id)
|
43
|
+
@since = id
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def max_id(id)
|
49
|
+
@max = id
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def each(&block)
|
55
|
+
result.each do |element|
|
56
|
+
yield element
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def result
|
61
|
+
load_default_options
|
62
|
+
|
63
|
+
@result ||= (@since || @max) ? result_with_score : result_with_rank
|
64
|
+
end
|
65
|
+
|
66
|
+
def result_with_score
|
67
|
+
raw_result = @sorted_set.get_range(@since || '-inf', @max || '+inf')
|
68
|
+
|
69
|
+
Kaminari.paginate_array(raw_result).page(@page).per(@per)
|
70
|
+
end
|
71
|
+
|
72
|
+
def result_with_rank
|
73
|
+
from = (@page - 1) * @per
|
74
|
+
to = from + @per - 1
|
75
|
+
|
76
|
+
@sorted_set.get_range_by_rank(from, to)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RedisModel
|
2
|
+
class Intersected < RedisModel::Base
|
3
|
+
attr_reader :key_label
|
4
|
+
|
5
|
+
data_type :sorted_set
|
6
|
+
|
7
|
+
def initialize(sets, seed = rand(256))
|
8
|
+
@sets = sets
|
9
|
+
@key_label = (@sets.map(&:key_label) + [DateTime.current.to_i, seed]).compact.join(':')
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate(expire_in = nil)
|
13
|
+
RedisModel::Base.connection.zinterstore @key_label, @sets.map(&:key_label)
|
14
|
+
RedisModel::Base.connection.expire(@key_label, expire_in) if expire_in
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|