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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +187 -0
  6. data/Rakefile +15 -0
  7. data/lib/redis_model/adapters/paperclip.rb +51 -0
  8. data/lib/redis_model/attribute.rb +124 -0
  9. data/lib/redis_model/base.rb +67 -0
  10. data/lib/redis_model/belonged_to.rb +27 -0
  11. data/lib/redis_model/class_attribute.rb +50 -0
  12. data/lib/redis_model/configurations.rb +15 -0
  13. data/lib/redis_model/helpers/sorted_set_paginator.rb +80 -0
  14. data/lib/redis_model/intersected.rb +17 -0
  15. data/lib/redis_model/schema.rb +114 -0
  16. data/lib/redis_model/types/base.rb +32 -0
  17. data/lib/redis_model/types/base_value.rb +26 -0
  18. data/lib/redis_model/types/counter.rb +25 -0
  19. data/lib/redis_model/types/float.rb +17 -0
  20. data/lib/redis_model/types/hash.rb +53 -0
  21. data/lib/redis_model/types/integer.rb +17 -0
  22. data/lib/redis_model/types/list.rb +40 -0
  23. data/lib/redis_model/types/set.rb +59 -0
  24. data/lib/redis_model/types/sorted_set.rb +184 -0
  25. data/lib/redis_model/types/string.rb +11 -0
  26. data/lib/redis_model/types/timestamp.rb +26 -0
  27. data/lib/redis_model/version.rb +3 -0
  28. data/lib/redis_model.rb +37 -0
  29. data/redis_model.gemspec +28 -0
  30. data/spec/redis_model/attribute_spec.rb +77 -0
  31. data/spec/redis_model/base_spec.rb +34 -0
  32. data/spec/redis_model/class_attribute_spec.rb +16 -0
  33. data/spec/redis_model/helpers/sorted_set_paginator_spec.rb +33 -0
  34. data/spec/redis_model/schema_spec.rb +118 -0
  35. data/spec/redis_model/types/base_spec.rb +28 -0
  36. data/spec/redis_model/types/counter_spec.rb +32 -0
  37. data/spec/redis_model/types/float_spec.rb +20 -0
  38. data/spec/redis_model/types/hash_spec.rb +55 -0
  39. data/spec/redis_model/types/integer_spec.rb +22 -0
  40. data/spec/redis_model/types/list_spec.rb +55 -0
  41. data/spec/redis_model/types/set_spec.rb +62 -0
  42. data/spec/redis_model/types/sorted_set_spec.rb +303 -0
  43. data/spec/redis_model/types/string_spec.rb +28 -0
  44. data/spec/redis_model/types/timestamp_spec.rb +22 -0
  45. data/spec/spec_helper.rb +13 -0
  46. data/spec/support/dynamic_class.rb +5 -0
  47. 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in redis_model.gemspec
4
+ gemspec
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,15 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
7
+
8
+ task :console do
9
+ require 'irb'
10
+ require 'irb/completion'
11
+ require 'redis_model'
12
+
13
+ ARGV.clear
14
+ IRB.start
15
+ end
@@ -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