redis_model 0.1.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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