activemodel-caching 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +162 -0
- data/Rakefile +12 -0
- data/lib/active_model/caching/version.rb +7 -0
- data/lib/active_model/caching.rb +515 -0
- data/lib/activemodel/caching.rb +3 -0
- data/sig/activemodel/caching.rbs +6 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3e5807404927b4493c98793dc6c099499ab0c4debe20508b48f9f901ccb0c6b7
|
4
|
+
data.tar.gz: aba1ed74fe1f01fa24fe90d6783422ca13a3f4b1b94d20efc9374a187afed52b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f741d748d3f3580af8524554da1f1146d7762f406bba7279332c36fd097cb5d4f40213a54ab8c097e5c281187d54272600f1eab5d45d855a864177be488083d0
|
7
|
+
data.tar.gz: 5ebfb56ecb0debd6b2afa33ced087e7eefbd36bef5ba39705c32ad8eceff9fcc29fdeefa2c22af0eb0039351b55aac727a402dccf6a9fd565bb2bf845b81bb4c
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.0
|
3
|
+
|
4
|
+
Metrics/BlockLength:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Metrics/LineLength:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/ModuleLength:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/ClassVars:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/StringLiterals:
|
26
|
+
EnforcedStyle: double_quotes
|
27
|
+
|
28
|
+
Style/StringLiteralsInInterpolation:
|
29
|
+
EnforcedStyle: double_quotes
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Emmanuel Cousin
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# ActiveModel::Caching
|
2
|
+
|
3
|
+
A library providing easy-to-use object-level caching methods for various data types in a Ruby on Rails application, allowing you to cache different attribute types directly on your models.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'activemodel-caching'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install activemodel-caching
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
To use the caching methods provided by `ActiveModel::Caching`, include the module in your model and set up the cache store.
|
28
|
+
|
29
|
+
### Setup
|
30
|
+
|
31
|
+
You can set up a cache store globally in an initializer, for example, in `config/initializers/active_model_caching.rb`:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
ActiveModel::Caching.setup do |config|
|
35
|
+
config.cache_store = ActiveSupport::Cache::MemoryStore.new # or any other cache store you prefer
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
If you're using Rails, you can also default to Rails cache if you prefer:
|
40
|
+
```ruby
|
41
|
+
ActiveModel::Caching.setup do |config|
|
42
|
+
config.cache_store = Rails.cache
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
### Basic Usage
|
47
|
+
|
48
|
+
To enable caching for an attribute, simply call one of the `cache_*` methods in your model class. Here are the methods you can use:
|
49
|
+
|
50
|
+
- `cache_string` - Caches a string value.
|
51
|
+
- `cache_integer` - Caches an integer value.
|
52
|
+
- `cache_decimal` - Caches a decimal value.
|
53
|
+
- `cache_datetime` - Caches a datetime value.
|
54
|
+
- `cache_flag` - Caches a boolean flag.
|
55
|
+
- `cache_float` - Caches a float value.
|
56
|
+
- `cache_enum` - Caches an enumerated value.
|
57
|
+
- `cache_json` - Caches a JSON object.
|
58
|
+
- `cache_list` - Caches an ordered list with an optional limit.
|
59
|
+
- `cache_unique_list` - Caches a unique list with an optional limit.
|
60
|
+
- `cache_set` - Caches a unique set with an optional limit.
|
61
|
+
- `cache_ordered_set` - Caches an ordered set with an optional limit.
|
62
|
+
- `cache_slots` - Caches available "slots" (e.g., seats) with helper methods.
|
63
|
+
- `cache_slot` - Caches a single-slot availability.
|
64
|
+
- `cache_counter` - Caches a counter that can be incremented and reset.
|
65
|
+
- `cache_limiter` - Caches a limited counter, enforcing a maximum count.
|
66
|
+
- `cache_hash` - Caches a hash structure.
|
67
|
+
- `cache_boolean` - Caches a boolean value.
|
68
|
+
|
69
|
+
#### Example
|
70
|
+
|
71
|
+
Here’s how you might define a model with various cached attributes:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class User
|
75
|
+
include ActiveModel::Caching
|
76
|
+
|
77
|
+
cache_string :session_token
|
78
|
+
cache_integer :view_count
|
79
|
+
cache_decimal :account_balance
|
80
|
+
cache_datetime :last_login
|
81
|
+
cache_flag :is_active
|
82
|
+
cache_enum :status, %w[active inactive suspended]
|
83
|
+
cache_json :preferences
|
84
|
+
cache_list :recent_searches, limit: 10
|
85
|
+
cache_set :tags, limit: 5
|
86
|
+
cache_slots :seats, available: 100
|
87
|
+
cache_counter :login_count
|
88
|
+
cache_boolean :is_verified
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
With these, you’ll automatically have generated methods for interacting with the cache.
|
93
|
+
|
94
|
+
### Detailed Method Descriptions
|
95
|
+
|
96
|
+
- **`cache_string(attribute_name, expires_in: nil)`**: Caches a string attribute.
|
97
|
+
- Example: `cache_string :username`
|
98
|
+
|
99
|
+
- **`cache_integer(attribute_name, expires_in: nil)`**: Caches an integer attribute.
|
100
|
+
- Example: `cache_integer :view_count`
|
101
|
+
|
102
|
+
- **`cache_decimal(attribute_name, expires_in: nil)`**: Caches a decimal attribute.
|
103
|
+
- Example: `cache_decimal :account_balance`
|
104
|
+
|
105
|
+
- **`cache_datetime(attribute_name, expires_in: nil)`**: Caches a datetime attribute.
|
106
|
+
- Example: `cache_datetime :last_login`
|
107
|
+
|
108
|
+
- **`cache_flag(attribute_name, expires_in: nil)`**: Caches a boolean flag.
|
109
|
+
- Example: `cache_flag :is_active`
|
110
|
+
|
111
|
+
- **`cache_enum(attribute_name, options, expires_in: nil)`**: Caches an enumerated value.
|
112
|
+
- Example: `cache_enum :status, %w[active inactive suspended]`
|
113
|
+
|
114
|
+
- **`cache_json(attribute_name, expires_in: nil)`**: Caches a JSON object.
|
115
|
+
- Example: `cache_json :user_preferences`
|
116
|
+
|
117
|
+
- **`cache_list(attribute_name, limit: nil, expires_in: nil)`**: Caches an ordered list, with an optional limit.
|
118
|
+
- Example: `cache_list :recent_posts, limit: 5`
|
119
|
+
|
120
|
+
- **`cache_set(attribute_name, limit: nil, expires_in: nil)`**: Caches a unique set, with an optional limit.
|
121
|
+
- Example: `cache_set :tags, limit: 10`
|
122
|
+
|
123
|
+
- **`cache_slots(attribute_name, available:, expires_in: nil)`**: Caches a set number of slots with helper methods.
|
124
|
+
- Example: `cache_slots :seats, available: 100`
|
125
|
+
|
126
|
+
- **`cache_counter(attribute_name, expires_in: nil)`**: Caches a counter.
|
127
|
+
- Example: `cache_counter :login_count`
|
128
|
+
|
129
|
+
- **`cache_boolean(attribute_name, expires_in: nil)`**: Caches a boolean value.
|
130
|
+
- Example: `cache_boolean :is_verified`
|
131
|
+
|
132
|
+
### Example Methods
|
133
|
+
|
134
|
+
For each cached attribute, methods are generated for getting and setting values. For example:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
user = User.new
|
138
|
+
|
139
|
+
# Cache a string
|
140
|
+
user.session_token = "abc123"
|
141
|
+
puts user.session_token # => "abc123"
|
142
|
+
|
143
|
+
# Increment a counter
|
144
|
+
user.increment_login_count
|
145
|
+
puts user.login_count # => 1
|
146
|
+
|
147
|
+
# Reserve a slot
|
148
|
+
if user.available_seats?
|
149
|
+
user.reserve_seats!
|
150
|
+
end
|
151
|
+
|
152
|
+
# Reset a slot
|
153
|
+
user.reset_seats!
|
154
|
+
```
|
155
|
+
|
156
|
+
## Contributing
|
157
|
+
|
158
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/your-username/active_model-caching](https://github.com/your-username/active_model-caching).
|
159
|
+
|
160
|
+
## License
|
161
|
+
|
162
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,515 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "caching/version"
|
4
|
+
|
5
|
+
require "base64"
|
6
|
+
require "bigdecimal/util"
|
7
|
+
require "json"
|
8
|
+
require "active_support"
|
9
|
+
require "active_support/time"
|
10
|
+
|
11
|
+
module ActiveModel
|
12
|
+
# Provides with a set of methods allowing to cache data structures at the object level
|
13
|
+
module Caching
|
14
|
+
mattr_accessor :cache_store
|
15
|
+
@@cache_store = ActiveSupport::Cache::MemoryStore.new
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def setup
|
19
|
+
yield self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
class_methods do
|
26
|
+
# Caches a string value for the given attribute.
|
27
|
+
#
|
28
|
+
# @param attribute_name [Symbol] the name of the string attribute to cache.
|
29
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# cache_string :session_token
|
33
|
+
def cache_string(attribute_name, expires_in: nil)
|
34
|
+
define_method(attribute_name) do
|
35
|
+
cache_store.read(cache_key_for(attribute_name))
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method(:"#{attribute_name}=") do |value|
|
39
|
+
cache_store.write(cache_key_for(attribute_name), value, expires_in: expires_in)
|
40
|
+
end
|
41
|
+
|
42
|
+
attribute_name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Caches an integer value for the given attribute.
|
46
|
+
#
|
47
|
+
# @param attribute_name [Symbol] the name of the integer attribute to cache.
|
48
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# cache_integer :view_count
|
52
|
+
def cache_integer(attribute_name, expires_in: nil)
|
53
|
+
define_method(attribute_name) do
|
54
|
+
cache_store.read(cache_key_for(attribute_name)).to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
define_method(:"#{attribute_name}=") do |value|
|
58
|
+
cache_store.write(cache_key_for(attribute_name), value.to_i, expires_in: expires_in)
|
59
|
+
end
|
60
|
+
|
61
|
+
attribute_name
|
62
|
+
end
|
63
|
+
|
64
|
+
# Caches a decimal value for the given attribute.
|
65
|
+
#
|
66
|
+
# @param attribute_name [Symbol] the name of the decimal attribute to cache.
|
67
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# cache_decimal :account_balance
|
71
|
+
def cache_decimal(attribute_name, expires_in: nil)
|
72
|
+
define_method(attribute_name) do
|
73
|
+
cache_store.read(cache_key_for(attribute_name)).to_d
|
74
|
+
end
|
75
|
+
|
76
|
+
define_method(:"#{attribute_name}=") do |value|
|
77
|
+
cache_store.write(cache_key_for(attribute_name), value.to_d, expires_in: expires_in)
|
78
|
+
end
|
79
|
+
|
80
|
+
attribute_name
|
81
|
+
end
|
82
|
+
|
83
|
+
# Caches a datetime value for the given attribute.
|
84
|
+
#
|
85
|
+
# @param attribute_name [Symbol] the name of the datetime attribute to cache.
|
86
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
# cache_datetime :last_login
|
90
|
+
def cache_datetime(attribute_name, expires_in: nil)
|
91
|
+
define_method(attribute_name) do
|
92
|
+
cache_store.read(cache_key_for(attribute_name))&.to_time
|
93
|
+
end
|
94
|
+
|
95
|
+
define_method(:"#{attribute_name}=") do |value|
|
96
|
+
cache_store.write(cache_key_for(attribute_name), value&.to_time, expires_in: expires_in)
|
97
|
+
end
|
98
|
+
|
99
|
+
attribute_name
|
100
|
+
end
|
101
|
+
|
102
|
+
# Caches a flag (boolean) value for the given attribute.
|
103
|
+
#
|
104
|
+
# @param attribute_name [Symbol] the name of the flag attribute to cache.
|
105
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# cache_flag :is_active
|
109
|
+
def cache_flag(attribute_name, expires_in: nil)
|
110
|
+
define_method(attribute_name) do
|
111
|
+
cache_store.read(cache_key_for(attribute_name)).present?
|
112
|
+
end
|
113
|
+
|
114
|
+
define_method(:"#{attribute_name}=") do |value|
|
115
|
+
cache_store.write(cache_key_for(attribute_name), !!value, expires_in: expires_in)
|
116
|
+
end
|
117
|
+
|
118
|
+
attribute_name
|
119
|
+
end
|
120
|
+
|
121
|
+
# Caches a float value for the given attribute.
|
122
|
+
#
|
123
|
+
# @param attribute_name [Symbol] the name of the float attribute to cache.
|
124
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# cache_float :average_rating
|
128
|
+
def cache_float(attribute_name, expires_in: nil)
|
129
|
+
define_method(attribute_name) do
|
130
|
+
cache_store.read(cache_key_for(attribute_name)).to_f
|
131
|
+
end
|
132
|
+
|
133
|
+
define_method(:"#{attribute_name}=") do |value|
|
134
|
+
cache_store.write(cache_key_for(attribute_name), value.to_f, expires_in: expires_in)
|
135
|
+
end
|
136
|
+
|
137
|
+
attribute_name
|
138
|
+
end
|
139
|
+
|
140
|
+
# Caches an enum value for the given attribute, storing the value among defined options.
|
141
|
+
#
|
142
|
+
# @param attribute_name [Symbol] the name of the enum attribute to cache.
|
143
|
+
# @param options [Array] the list of acceptable values for the enum.
|
144
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
145
|
+
#
|
146
|
+
# @example
|
147
|
+
# cache_enum :status, %w[active inactive suspended]
|
148
|
+
def cache_enum(attribute_name, options, expires_in: nil)
|
149
|
+
define_method(attribute_name) do
|
150
|
+
value = cache_store.read(cache_key_for(attribute_name))
|
151
|
+
options.include?(value) ? value : options.first # Default to first option if invalid
|
152
|
+
end
|
153
|
+
|
154
|
+
define_method(:"#{attribute_name}=") do |value|
|
155
|
+
raise ArgumentError, "Invalid value for #{attribute_name}" unless options.include?(value)
|
156
|
+
|
157
|
+
cache_store.write(cache_key_for(attribute_name), value, expires_in: expires_in)
|
158
|
+
end
|
159
|
+
|
160
|
+
attribute_name
|
161
|
+
end
|
162
|
+
|
163
|
+
# Caches a JSON value for the given attribute.
|
164
|
+
#
|
165
|
+
# @param attribute_name [Symbol] the name of the JSON attribute to cache.
|
166
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
167
|
+
#
|
168
|
+
# @example
|
169
|
+
# cache_json :user_preferences
|
170
|
+
def cache_json(attribute_name, expires_in: nil)
|
171
|
+
define_method(attribute_name) do
|
172
|
+
JSON.parse(cache_store.read(cache_key_for(attribute_name)) || nil.to_json, symbolize_names: true)
|
173
|
+
end
|
174
|
+
|
175
|
+
define_method(:"#{attribute_name}=") do |value|
|
176
|
+
cache_store.write(cache_key_for(attribute_name), value.to_json, expires_in: expires_in)
|
177
|
+
end
|
178
|
+
|
179
|
+
attribute_name
|
180
|
+
end
|
181
|
+
|
182
|
+
# Caches a list of values for the given attribute, maintaining order and enforcing a limit.
|
183
|
+
#
|
184
|
+
# @param attribute_name [Symbol] the name of the list attribute to cache.
|
185
|
+
# @param limit [Integer, nil] optional maximum number of items in the list.
|
186
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
187
|
+
#
|
188
|
+
# @example
|
189
|
+
# cache_list :recent_posts, limit: 5
|
190
|
+
def cache_list(attribute_name, limit: nil, expires_in: nil)
|
191
|
+
define_method(attribute_name) do
|
192
|
+
cache_store.read(cache_key_for(attribute_name)) || []
|
193
|
+
end
|
194
|
+
|
195
|
+
define_method(:"add_to_#{attribute_name}") do |*values|
|
196
|
+
list = send(attribute_name)
|
197
|
+
values.each do |value|
|
198
|
+
list << value
|
199
|
+
end
|
200
|
+
list.shift if limit && list.size > limit # Remove oldest item if limit is exceeded
|
201
|
+
cache_store.write(cache_key_for(attribute_name), list, expires_in: expires_in)
|
202
|
+
end
|
203
|
+
|
204
|
+
define_method(:"remove_from_#{attribute_name}") do |*values|
|
205
|
+
list = send(attribute_name)
|
206
|
+
values.each do |value|
|
207
|
+
list.delete(value)
|
208
|
+
end
|
209
|
+
cache_store.write(cache_key_for(attribute_name), list, expires_in: expires_in)
|
210
|
+
end
|
211
|
+
|
212
|
+
attribute_name
|
213
|
+
end
|
214
|
+
|
215
|
+
# Caches a unique list of values for the given attribute, maintaining uniqueness and enforcing a limit.
|
216
|
+
#
|
217
|
+
# @param attribute_name [Symbol] the name of the unique list attribute to cache.
|
218
|
+
# @param limit [Integer, nil] optional maximum number of items in the list.
|
219
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
220
|
+
#
|
221
|
+
# @example
|
222
|
+
# cache_unique_list :favorite_articles, limit: 10
|
223
|
+
def cache_unique_list(attribute_name, limit: nil, expires_in: nil)
|
224
|
+
define_method(attribute_name) do
|
225
|
+
cache_store.read(cache_key_for(attribute_name)) || []
|
226
|
+
end
|
227
|
+
|
228
|
+
define_method(:"add_to_#{attribute_name}") do |*values|
|
229
|
+
unique_list = Set.new(send(attribute_name))
|
230
|
+
values.each do |value|
|
231
|
+
unique_list.add(value)
|
232
|
+
if limit && unique_list.size > limit
|
233
|
+
oldest_value = unique_list.to_a.shift # Remove the oldest item
|
234
|
+
unique_list.delete(oldest_value)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
cache_store.write(cache_key_for(attribute_name), unique_list.to_a, expires_in: expires_in)
|
238
|
+
end
|
239
|
+
|
240
|
+
define_method(:"remove_from_#{attribute_name}") do |*values|
|
241
|
+
unique_list = send(attribute_name)
|
242
|
+
values.each do |value|
|
243
|
+
unique_list.delete(value)
|
244
|
+
end
|
245
|
+
cache_store.write(cache_key_for(attribute_name), unique_list.to_a, expires_in: expires_in)
|
246
|
+
end
|
247
|
+
|
248
|
+
attribute_name
|
249
|
+
end
|
250
|
+
|
251
|
+
# Caches a set of unique values for the given attribute, maintaining uniqueness and enforcing a limit.
|
252
|
+
#
|
253
|
+
# @param attribute_name [Symbol] the name of the set attribute to cache.
|
254
|
+
# @param limit [Integer, nil] optional maximum number of items in the set.
|
255
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
256
|
+
#
|
257
|
+
# @example
|
258
|
+
# cache_set :tags, limit: 5
|
259
|
+
def cache_set(attribute_name, limit: nil, expires_in: nil)
|
260
|
+
define_method(attribute_name) do
|
261
|
+
Set.new(cache_store.read(cache_key_for(attribute_name)) || [])
|
262
|
+
end
|
263
|
+
|
264
|
+
define_method(:"add_to_#{attribute_name}") do |*values|
|
265
|
+
set = send(attribute_name)
|
266
|
+
values.each do |value|
|
267
|
+
set.add(value)
|
268
|
+
if limit && set.size > limit
|
269
|
+
oldest_value = set.to_a.shift # Remove the oldest item
|
270
|
+
set.delete(oldest_value)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
cache_store.write(cache_key_for(attribute_name), set.to_a, expires_in: expires_in)
|
274
|
+
end
|
275
|
+
|
276
|
+
define_method(:"remove_from_#{attribute_name}") do |*values|
|
277
|
+
set = send(attribute_name)
|
278
|
+
values.each do |value|
|
279
|
+
set.delete(value)
|
280
|
+
end
|
281
|
+
cache_store.write(cache_key_for(attribute_name), set.to_a, expires_in: expires_in)
|
282
|
+
end
|
283
|
+
|
284
|
+
attribute_name
|
285
|
+
end
|
286
|
+
|
287
|
+
# Caches an ordered set of values for the given attribute, maintaining order and enforcing a limit.
|
288
|
+
#
|
289
|
+
# @param attribute_name [Symbol] the name of the ordered set attribute to cache.
|
290
|
+
# @param limit [Integer, nil] optional maximum number of items in the ordered set.
|
291
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
292
|
+
#
|
293
|
+
# @example
|
294
|
+
# cache_ordered_set :recent_views, limit: 10
|
295
|
+
def cache_ordered_set(attribute_name, limit: nil, expires_in: nil)
|
296
|
+
define_method(attribute_name) do
|
297
|
+
Set.new(cache_store.read(cache_key_for(attribute_name)) || [])
|
298
|
+
end
|
299
|
+
|
300
|
+
define_method(:"add_to_#{attribute_name}") do |*values|
|
301
|
+
ordered_set = send(attribute_name)
|
302
|
+
values.each do |value|
|
303
|
+
ordered_set.delete(value)
|
304
|
+
ordered_set.add(value)
|
305
|
+
if limit && ordered_set.size > limit
|
306
|
+
oldest_value = ordered_set.to_a.shift # Remove the oldest item
|
307
|
+
ordered_set.delete(oldest_value)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
cache_store.write(cache_key_for(attribute_name), ordered_set, expires_in: expires_in)
|
311
|
+
end
|
312
|
+
|
313
|
+
define_method(:"remove_from_#{attribute_name}") do |*values|
|
314
|
+
ordered_set = send(attribute_name)
|
315
|
+
values.each do |value|
|
316
|
+
ordered_set.delete(value)
|
317
|
+
end
|
318
|
+
cache_store.write(cache_key_for(attribute_name), ordered_set, expires_in: expires_in)
|
319
|
+
end
|
320
|
+
|
321
|
+
attribute_name
|
322
|
+
end
|
323
|
+
|
324
|
+
##
|
325
|
+
# Caches a limited number of available "slots" for the given attribute.
|
326
|
+
# Slots represent a count of available resources, such as seats or reservations,
|
327
|
+
# which can be reserved and released. This method generates several helper
|
328
|
+
# methods to manage the slots, including checking availability, reserving a slot,
|
329
|
+
# releasing a slot, and resetting the slots.
|
330
|
+
#
|
331
|
+
# @param attribute_name [Symbol] the name of the slot attribute to cache.
|
332
|
+
# @param available [Integer] the maximum number of available slots.
|
333
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
334
|
+
#
|
335
|
+
# @example
|
336
|
+
# cache_slots :seats, available: 10
|
337
|
+
#
|
338
|
+
# This will generate the following methods:
|
339
|
+
# - `seats`: retrieves the current number of taken slots.
|
340
|
+
# - `available_seats?`: checks if there are any available slots left.
|
341
|
+
# - `reserve_seats!`: reserves a slot if available, incrementing the taken count.
|
342
|
+
# - `release_seats!`: releases a slot, decrementing the taken count.
|
343
|
+
# - `reset_seats!`: resets the count of taken slots to zero.
|
344
|
+
#
|
345
|
+
# @return [Symbol] the name of the attribute.
|
346
|
+
def cache_slots(attribute_name, available:, expires_in: nil)
|
347
|
+
define_method(attribute_name) do
|
348
|
+
cache_store.read(cache_key_for(attribute_name)).to_i
|
349
|
+
end
|
350
|
+
|
351
|
+
define_method(:"available_#{attribute_name}?") do
|
352
|
+
taken = send(attribute_name)
|
353
|
+
taken < available
|
354
|
+
end
|
355
|
+
|
356
|
+
define_method(:"reserve_#{attribute_name}!") do
|
357
|
+
taken = send(attribute_name)
|
358
|
+
if send(:"available_#{attribute_name}?")
|
359
|
+
cache_store.write(cache_key_for(attribute_name), taken + 1, expires_in: expires_in)
|
360
|
+
taken + 1
|
361
|
+
else
|
362
|
+
taken
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
define_method(:"release_#{attribute_name}!") do
|
367
|
+
taken = send(attribute_name)
|
368
|
+
if taken.positive?
|
369
|
+
cache_store.write(cache_key_for(attribute_name), taken - 1, expires_in: expires_in)
|
370
|
+
taken - 1
|
371
|
+
else
|
372
|
+
taken
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
define_method(:"reset_#{attribute_name}!") do
|
377
|
+
taken = send(attribute_name)
|
378
|
+
if taken.positive?
|
379
|
+
cache_store.write(cache_key_for(attribute_name), 0, expires_in: expires_in)
|
380
|
+
else
|
381
|
+
taken
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
attribute_name
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
# Caches a single slot for the given attribute.
|
390
|
+
# A single slot represents a binary (available/taken) resource that can be reserved
|
391
|
+
# or released, functioning similarly to {#cache_slots} with a fixed availability of 1.
|
392
|
+
#
|
393
|
+
# @param attribute_name [Symbol] the name of the slot attribute to cache.
|
394
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
395
|
+
#
|
396
|
+
# @example
|
397
|
+
# cache_slot :parking_space
|
398
|
+
#
|
399
|
+
# This will generate the following methods:
|
400
|
+
# - `parking_space`: retrieves the current state (0 or 1).
|
401
|
+
# - `available_parking_space?`: checks if the slot is available.
|
402
|
+
# - `reserve_parking_space!`: reserves the slot if available.
|
403
|
+
# - `release_parking_space!`: releases the slot.
|
404
|
+
# - `reset_parking_space!`: resets the slot to zero (unreserved).
|
405
|
+
#
|
406
|
+
# @return [Symbol] the name of the attribute.
|
407
|
+
def cache_slot(attribute_name, expires_in: nil)
|
408
|
+
cache_slots(attribute_name, available: 1, expires_in: expires_in)
|
409
|
+
attribute_name
|
410
|
+
end
|
411
|
+
|
412
|
+
# Caches a counter value for the given attribute.
|
413
|
+
#
|
414
|
+
# @param attribute_name [Symbol] the name of the counter attribute to cache.
|
415
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
416
|
+
#
|
417
|
+
# @example
|
418
|
+
# cache_counter :login_count
|
419
|
+
def cache_counter(attribute_name, expires_in: nil)
|
420
|
+
define_method(attribute_name) do
|
421
|
+
cache_store.read(cache_key_for(attribute_name)).to_i
|
422
|
+
end
|
423
|
+
|
424
|
+
define_method(:"increment_#{attribute_name}") do
|
425
|
+
new_value = send(attribute_name) + 1
|
426
|
+
cache_store.write(cache_key_for(attribute_name), new_value, expires_in: expires_in)
|
427
|
+
end
|
428
|
+
|
429
|
+
define_method(:"reset_#{attribute_name}") do
|
430
|
+
cache_store.write(cache_key_for(attribute_name), 0, expires_in: expires_in)
|
431
|
+
end
|
432
|
+
|
433
|
+
attribute_name
|
434
|
+
end
|
435
|
+
|
436
|
+
# Caches a limiter value for the given attribute, enforcing a limit.
|
437
|
+
#
|
438
|
+
# @param attribute_name [Symbol] the name of the limiter attribute to cache.
|
439
|
+
# @param limit [Integer] the maximum allowed count.
|
440
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
441
|
+
#
|
442
|
+
# @example
|
443
|
+
# cache_limiter :api_requests, limit: 100
|
444
|
+
def cache_limiter(attribute_name, limit:, expires_in: nil)
|
445
|
+
define_method(attribute_name) do
|
446
|
+
cache_store.read(cache_key_for(attribute_name)).to_i
|
447
|
+
end
|
448
|
+
|
449
|
+
define_method(:"increment_#{attribute_name}") do
|
450
|
+
current_value = send(attribute_name)
|
451
|
+
new_value = current_value + 1
|
452
|
+
|
453
|
+
if new_value <= limit
|
454
|
+
cache_store.write(cache_key_for(attribute_name), new_value, expires_in: expires_in)
|
455
|
+
true # Increment successful
|
456
|
+
else
|
457
|
+
false # Increment failed due to limit
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
define_method(:"reset_#{attribute_name}") do
|
462
|
+
cache_store.write(cache_key_for(attribute_name), 0, expires_in: expires_in)
|
463
|
+
end
|
464
|
+
|
465
|
+
attribute_name
|
466
|
+
end
|
467
|
+
|
468
|
+
# Caches a hash for the given attribute.
|
469
|
+
#
|
470
|
+
# @param attribute_name [Symbol] the name of the hash attribute to cache.
|
471
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
472
|
+
#
|
473
|
+
# @example
|
474
|
+
# cache_hash :user_settings
|
475
|
+
def cache_hash(attribute_name, expires_in: nil)
|
476
|
+
define_method(attribute_name) do
|
477
|
+
JSON.parse(cache_store.read(cache_key_for(attribute_name)) || "{}", symbolize_names: true)
|
478
|
+
end
|
479
|
+
|
480
|
+
define_method(:"#{attribute_name}=") do |value|
|
481
|
+
cache_store.write(cache_key_for(attribute_name), value.to_json, expires_in: expires_in)
|
482
|
+
end
|
483
|
+
|
484
|
+
attribute_name
|
485
|
+
end
|
486
|
+
|
487
|
+
# Caches a boolean value for the given attribute.
|
488
|
+
#
|
489
|
+
# @param attribute_name [Symbol] the name of the boolean attribute to cache.
|
490
|
+
# @param expires_in [ActiveSupport::Duration, nil] optional expiration time for the cache entry.
|
491
|
+
#
|
492
|
+
# @example
|
493
|
+
# cache_boolean :is_verified
|
494
|
+
def cache_boolean(attribute_name, expires_in: nil)
|
495
|
+
define_method(attribute_name) do
|
496
|
+
cache_store.read(cache_key_for(attribute_name)).present?
|
497
|
+
end
|
498
|
+
|
499
|
+
define_method(:"#{attribute_name}=") do |value|
|
500
|
+
cache_store.write(cache_key_for(attribute_name), !!value, expires_in: expires_in)
|
501
|
+
end
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
private
|
506
|
+
|
507
|
+
# Generates a cache key for the given attribute.
|
508
|
+
#
|
509
|
+
# @param attribute_name [Symbol] the name of the attribute.
|
510
|
+
# @return [String] the generated cache key.
|
511
|
+
def cache_key_for(attribute_name)
|
512
|
+
to_global_id(attribute_name: attribute_name)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activemodel-caching
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emmanuel Cousin
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-11-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: base64
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bigdecimal
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.1.2
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.1.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: globalid
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.2'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.2'
|
83
|
+
description: ActiveModel::Caching is a versatile gem for managing structured, temporary
|
84
|
+
data using a caching backend, typically Rails cache for Rails applications. This
|
85
|
+
gem provides an easy-to-use API for storing, retrieving, and manipulating data structures
|
86
|
+
like scalars, lists, and JSON, making it simple to handle transient data without
|
87
|
+
adding extra dependencies.
|
88
|
+
email:
|
89
|
+
- emmanuel@hey.com
|
90
|
+
executables: []
|
91
|
+
extensions: []
|
92
|
+
extra_rdoc_files: []
|
93
|
+
files:
|
94
|
+
- ".rubocop.yml"
|
95
|
+
- CHANGELOG.md
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- lib/active_model/caching.rb
|
100
|
+
- lib/active_model/caching/version.rb
|
101
|
+
- lib/activemodel/caching.rb
|
102
|
+
- sig/activemodel/caching.rbs
|
103
|
+
homepage: https://github.com/EmCousin/activemodel-caching
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata:
|
107
|
+
allowed_push_host: https://rubygems.org
|
108
|
+
homepage_uri: https://github.com/EmCousin/activemodel-caching
|
109
|
+
source_code_uri: https://github.com/EmCousin/activemodel-caching
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 3.0.0
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubygems_version: 3.5.20
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: ActiveModel::Caching is a flexible gem for managing temporary data structures
|
129
|
+
(scalars, lists, JSON) using a caching backend, typically Rails cache but adaptable
|
130
|
+
to other solutions. It offers a simple, Rails-friendly API for efficient, transient
|
131
|
+
data handling.
|
132
|
+
test_files: []
|