cacheable 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +218 -0
- data/cache-adapters.md +28 -0
- data/lib/cacheable.rb +42 -0
- data/lib/cacheable/cache_adapter.rb +37 -0
- data/lib/cacheable/cache_adapters.rb +21 -0
- data/lib/cacheable/cache_adapters/memory_adapter.rb +42 -0
- data/lib/cacheable/method_generator.rb +78 -0
- data/lib/cacheable/version.rb +16 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dcf3283fac4f544317c0b3db775ef6a55686b12f
|
4
|
+
data.tar.gz: 9d214b09f7c242a7879343034b5a9edeedc85bee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0065e91bae9e147ac4c134ecc7b862d576b3e4968fdf13f4360ee81a4c804608b3e0f3148164a764b2cd8d6ab0bc323ef93c412a9410ba3f6ecb015d881dbc0
|
7
|
+
data.tar.gz: a671db83562e5655161a7e92efc41f99ca7f597d0987e869dc64f6a3500e4c89582d4f545c648b77a4d1915b143432fec6db7a8c07b1a9bc1da0835174218099
|
data/README.md
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
# Cacheable
|
2
|
+
|
3
|
+
By [Splitwise](https://www.splitwise.com)
|
4
|
+
|
5
|
+
Cacheable is a gem which intends to adds method caching in an [aspect-oriented programming (AOP)](https://en.wikipedia.org/wiki/Aspect-oriented_programming) fashion in Ruby. Its core goals are:
|
6
|
+
|
7
|
+
* ease of use (method annotation)
|
8
|
+
* flexibility (simple adaptablability for any cache backend)
|
9
|
+
* portability (plain Ruby for use with any framework)
|
10
|
+
|
11
|
+
While Rails is not a requirement, Cacheable was built inside a mature Rails app and later extracted. This first release will seemlyless work in Rails and only includes an adapter for an in-memory cache backed by a simple Hash. This may be enough for you needs but it is more likely that additional cache adapters will need to be written.
|
12
|
+
|
13
|
+
See more about [Cache Adapters](cache-adapters.md).
|
14
|
+
|
15
|
+
## Getting Started
|
16
|
+
|
17
|
+
Add it to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'cacheable'
|
21
|
+
```
|
22
|
+
|
23
|
+
Set your cache adapter
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# If you're in a Rails app place the following in config/initializers/cacheable.rb
|
27
|
+
Cacheable.cache_adapter = Rails.cache
|
28
|
+
|
29
|
+
# Otherwise you can specify the name of the adapter anywhere before you use it
|
30
|
+
Cacheable.cache_adapter = :memory
|
31
|
+
```
|
32
|
+
|
33
|
+
### Simple Implementation Example
|
34
|
+
|
35
|
+
Cacheable is designed to work seemlessly with your already existings codebase. Consider the following contrived class:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class SimpleExample
|
39
|
+
def expensive_calculation
|
40
|
+
puts 'beginning expensive method'
|
41
|
+
…
|
42
|
+
return 'my_result'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
To cache this method and it's result, simply add the following:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require 'cacheable' # this may not be necessary depending on your autoloading system
|
51
|
+
|
52
|
+
class SimpleExample
|
53
|
+
include Cacheable
|
54
|
+
|
55
|
+
cacheable :expensive_calculation
|
56
|
+
|
57
|
+
def expensive_calculation
|
58
|
+
puts 'beginning expensive method'
|
59
|
+
…
|
60
|
+
return 'my_result'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
**Thats it!** There's some complex Ruby magic going on under the hood but to the end user you can simply call `expensive_calculation` and the result will be retreived from the cache, if available, or generated and placed into the cache. To confirm it is working, fire up an IRB console try the following:
|
66
|
+
|
67
|
+
```irb
|
68
|
+
> s = SimpleExample.new
|
69
|
+
> s.expensive_calculation
|
70
|
+
beginning expensive method
|
71
|
+
=> "my_result"
|
72
|
+
> s.expensive_calculation
|
73
|
+
=> "my_result"
|
74
|
+
|
75
|
+
# Notice that the `puts` was not output the 2nd time the method was invoked.
|
76
|
+
```
|
77
|
+
|
78
|
+
### Additional Methods
|
79
|
+
|
80
|
+
Cacheable also adds two useful methods to your class.
|
81
|
+
|
82
|
+
#### Skip the Cache via `#{method}_without_cache`
|
83
|
+
|
84
|
+
The cache can intentionally be skipped by appending `_without_cache` to the method name. This invocation with neither check the cache nor populate it as if you called the original method and never used Cacheable.
|
85
|
+
|
86
|
+
```irb
|
87
|
+
> s = SimpleExample.new
|
88
|
+
> s.expensive_calculation_without_cache
|
89
|
+
beginning expensive method
|
90
|
+
=> "my_result"
|
91
|
+
> s.expensive_calculation_without_cache
|
92
|
+
beginning expensive method
|
93
|
+
=> "my_result"
|
94
|
+
```
|
95
|
+
|
96
|
+
#### Remove the Value via `clear_#{method}_cache`
|
97
|
+
|
98
|
+
The cache can be cleared at any time by calling `clear_#{your_method_name}_cache`.
|
99
|
+
|
100
|
+
```irb
|
101
|
+
> s = SimpleExample.new
|
102
|
+
> s.expensive_calculation
|
103
|
+
beginning expensive method
|
104
|
+
=> "my_result"
|
105
|
+
> s.expensive_calculation
|
106
|
+
=> "my_result"
|
107
|
+
|
108
|
+
> s.clear_expensive_calculation_cache
|
109
|
+
=> true
|
110
|
+
> s.expensive_calculation
|
111
|
+
beginning expensive method
|
112
|
+
=> "my_result"
|
113
|
+
```
|
114
|
+
|
115
|
+
## Additional Configuration
|
116
|
+
|
117
|
+
### Cache Invalidation
|
118
|
+
|
119
|
+
#### Default
|
120
|
+
|
121
|
+
One of the hardest things to do correctly is cache invalidation. Cacheable handles this in a variety of ways. By default Cacheable will construct key a key in the format `[cache_key || class_name, method_name]`.
|
122
|
+
|
123
|
+
If the object responds to `cache_key` its return value will be the first element in the array. `ActiveRecord` provides [`cache_key`](https://api.rubyonrails.org/classes/ActiveRecord/Integration.html#method-i-cache_key) but it can be added to any Ruby object or overwritten. If the object does not respond to it, the name of the class will be used instead. The second element will be the name of the method as a symbol.
|
124
|
+
|
125
|
+
It is up to the cache adapter what to do with this array. For example, Rails will turn `[SomeClass, :some_method]` into `"SomeClass/some_method"`. For more information see the documentation on [Cache Adapters](cache-adapters.md)
|
126
|
+
|
127
|
+
#### Set Your Own
|
128
|
+
|
129
|
+
If (re)defining `cache_key` does not provide enough flexibility you can pass a proc to the `key_format:` option of `cacheable`.
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class CustomKeyExample
|
133
|
+
include Cacheable
|
134
|
+
|
135
|
+
cacheable :my_method, key_format: -> (target, method_name, method_args) do
|
136
|
+
args = method_args.collect { |argument| "#{argument.class}::#{argument}" }.join
|
137
|
+
"#{method_name} called on #{target} with #{args}"
|
138
|
+
end
|
139
|
+
|
140
|
+
def my_method(arg1)
|
141
|
+
…
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
* `target` is the object the method is being called on (`#<CustomKeyExample:0x0…0>`)
|
147
|
+
* `method_name` is the name of the method being cached (`:my_method`)
|
148
|
+
* `method_args` is an array of arguments being passed to the method (`[arg1]`)
|
149
|
+
|
150
|
+
So if we called `CustomKeyExample.new.my_method(123)` we would get the cache key
|
151
|
+
|
152
|
+
`"my_method called on #<CustomKeyExample:0x0…0> with Integer::123"`.
|
153
|
+
|
154
|
+
### Conditional Cacheing
|
155
|
+
|
156
|
+
You can control if a method should be cached by supplying a proc to the `unless:` option which will get the same arguments as `key_format:`. Alternatively this method can be defined on the class and a symbol of the name of the method can be passed. **Note**: When using a symbol, the first argument will not be passed but will be available in the method as `self`. The following example will not cache the value if the first argument to the method is `false`.
|
157
|
+
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class ConditionalCachingExample
|
161
|
+
include Cacheable
|
162
|
+
|
163
|
+
cacheable :maybe_cache, unless: :should_not_cache?
|
164
|
+
|
165
|
+
def maybe_cache(cache)
|
166
|
+
…
|
167
|
+
end
|
168
|
+
|
169
|
+
def should_not_cache?(_method_name, method_args)
|
170
|
+
method_args.first == false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
### Cache Options
|
176
|
+
|
177
|
+
If your cache backend supports options you can pass them as the `cache_options:` option. This will be passed though untouched to the cache's `fetch` method.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
cacheable :with_options, cache_options: {expires_in: 3_600}
|
181
|
+
```
|
182
|
+
|
183
|
+
### Flexible Options
|
184
|
+
|
185
|
+
You can use the same options with multiple cache methods or limit them only to specific methods:
|
186
|
+
|
187
|
+
```
|
188
|
+
cacheable :these, :methods, :share, :options, key_format: key_proc, unless: unless_proc
|
189
|
+
cacheable :this_method_has_its_own_options, unless: unless_proc2
|
190
|
+
```
|
191
|
+
|
192
|
+
### Class Method Cacheing
|
193
|
+
|
194
|
+
You can cache class methods just as easily as a Ruby class is just an instance of `Class`. You simply need to `include Cacheable` within the `class << self` block. Methods can be defined in this block or outside using the `def self.` syntax.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
class StaticMethodExample
|
198
|
+
class << self
|
199
|
+
include Cacheable
|
200
|
+
|
201
|
+
cacheable :class_method, :self_class_method
|
202
|
+
|
203
|
+
def class_method
|
204
|
+
puts 'class_method called'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.self_class_method
|
209
|
+
puts 'self_class_method called'
|
210
|
+
end
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
214
|
+
### Contributors (alphabetical by last name)
|
215
|
+
|
216
|
+
* [Jess Hottenstein](https://github.com/jhottenstein)
|
217
|
+
* [Ryan Laughlin](https://github.com/rofreg)
|
218
|
+
* [Aaron Rosenberg](https://github.com/agrberg)
|
data/cache-adapters.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Cache Adapters
|
2
|
+
|
3
|
+
A cache adapter is an object that Cacheable can use as an interface to your system's cache. Cacheable will work out of the box using the object returned by `Rails.cache` as a cache adapter.
|
4
|
+
|
5
|
+
The other adapter provided with the library is the [Memory Adapter](lib/cacheable/cache_adapters/memory_adapter.rb). Is a simple memoizing cache used in testing. It is little more than an object that conforms to the protocol and is backed by a Ruby Hash. When writting a new cache adapter it can be used as a template.
|
6
|
+
|
7
|
+
### Protocol
|
8
|
+
|
9
|
+
There are only two methods the cache adapter protocol requires.
|
10
|
+
|
11
|
+
#### `fetch(key, cache_options) { block }`
|
12
|
+
|
13
|
+
`fetch` takes a key and options for the cache implementation. If the key is found in the cache the associated value will be returned. If it is not found, the block will be run and the result of the block will be returned and placed in the cache.
|
14
|
+
|
15
|
+
**Note**: Unless manually defined by [setting your own key format proc](README.md#set-your-own), `key` will be an *Array*. It is the cache adapter's responsibility to turn this into whatever value your cache backend requires for keys.
|
16
|
+
|
17
|
+
#### `delete(key)`
|
18
|
+
|
19
|
+
`delete` takes a key and removes it's associated value in the cache. While not currently dependend on by Cacheable, it appears the standard is to return `true` if the value was present and removed and `false` if not present to begin with.
|
20
|
+
|
21
|
+
#### Additional useful methods
|
22
|
+
|
23
|
+
These are additional methods that are very useful to have on a cache adapter but are not depended on by Cacheable. They can be found in the Memory Adapter but they are only used to aid in testing.
|
24
|
+
|
25
|
+
* **`read(key)`** read the value for the given key out of the cache or `nil` if the key is not present
|
26
|
+
* **`write(key, value)`** write a value to the cache under the key
|
27
|
+
* **`exist?(key)`** `true` if the key exists in the cache, `false` otherwise
|
28
|
+
* **`clear`** reset the entire cache
|
data/lib/cacheable.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2017-2018 Splitwise Inc.
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
#++
|
25
|
+
|
26
|
+
require 'cacheable/cache_adapter'
|
27
|
+
require 'cacheable/cache_adapters'
|
28
|
+
require 'cacheable/method_generator'
|
29
|
+
require 'cacheable/version'
|
30
|
+
|
31
|
+
module Cacheable
|
32
|
+
extend CacheAdapter
|
33
|
+
|
34
|
+
def self.included(base)
|
35
|
+
base.extend(Cacheable::MethodGenerator)
|
36
|
+
|
37
|
+
interceptor_name = base.send(:method_interceptor_module_name)
|
38
|
+
remove_const(interceptor_name) if const_defined?(interceptor_name)
|
39
|
+
|
40
|
+
base.prepend const_set(interceptor_name, Module.new)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cacheable
|
4
|
+
module CacheAdapter
|
5
|
+
CACHE_ADAPTER_METHODS = %i[fetch delete].freeze
|
6
|
+
DEFAULT_ADAPTER = :memory
|
7
|
+
|
8
|
+
def self.extended(base)
|
9
|
+
base.instance_variable_set(:@_cache_adapter, nil)
|
10
|
+
base.cache_adapter = DEFAULT_ADAPTER
|
11
|
+
end
|
12
|
+
|
13
|
+
def cache_adapter
|
14
|
+
@_cache_adapter
|
15
|
+
end
|
16
|
+
|
17
|
+
def cache_adapter=(name_or_adapter)
|
18
|
+
@_cache_adapter = interprete_adapter(name_or_adapter)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def interprete_adapter(name_or_adapter)
|
24
|
+
return name_or_adapter if cache_adapter?(name_or_adapter)
|
25
|
+
|
26
|
+
unless [Symbol, String].include?(name_or_adapter.class)
|
27
|
+
raise ArgumentError, 'Must pass the name of a known adapter or an instance'
|
28
|
+
end
|
29
|
+
|
30
|
+
Cacheable::CacheAdapters.lookup(name_or_adapter).new
|
31
|
+
end
|
32
|
+
|
33
|
+
def cache_adapter?(adapter_instance)
|
34
|
+
CACHE_ADAPTER_METHODS.all? { |method| adapter_instance.respond_to?(method) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cacheable/cache_adapters/memory_adapter'
|
4
|
+
|
5
|
+
module Cacheable
|
6
|
+
module CacheAdapters
|
7
|
+
ADAPTER = 'Adapter'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def lookup(adapter_name)
|
11
|
+
const_get(class_name_for(adapter_name.to_s) + ADAPTER)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def class_name_for(string)
|
17
|
+
string.split('_').map { |name_part| "#{name_part[0].upcase}#{name_part[1..-1].downcase}" }.join
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module CacheAdapters
|
3
|
+
class MemoryAdapter
|
4
|
+
def initialize
|
5
|
+
clear
|
6
|
+
end
|
7
|
+
|
8
|
+
def read(key)
|
9
|
+
cache[key]
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(key, value)
|
13
|
+
cache[key] = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def exist?(key)
|
17
|
+
cache.key?(key)
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch(key, _options = {})
|
21
|
+
return read(key) if exist?(key)
|
22
|
+
|
23
|
+
write(key, Proc.new.call)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(key)
|
27
|
+
return false unless exist?(key)
|
28
|
+
|
29
|
+
cache.delete key
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear
|
34
|
+
@cache = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :cache
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'english'
|
3
|
+
|
4
|
+
module Cacheable
|
5
|
+
module MethodGenerator
|
6
|
+
def cacheable(*original_method_names, **opts)
|
7
|
+
original_method_names.each do |original_method_name|
|
8
|
+
create_cacheable_methods(original_method_name, opts)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def method_interceptor_module_name
|
15
|
+
class_name = name || to_s.gsub(/[^a-zA-Z_0-9]/, '')
|
16
|
+
"#{class_name}Cacher"
|
17
|
+
end
|
18
|
+
|
19
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
20
|
+
def create_cacheable_methods(original_method_name, opts = {})
|
21
|
+
method_names = create_method_names(original_method_name)
|
22
|
+
key_format_proc = opts[:key_format] || default_key_format
|
23
|
+
|
24
|
+
const_get(method_interceptor_module_name).class_eval do
|
25
|
+
define_method(method_names[:key_format_method_name]) do |*args|
|
26
|
+
key_format_proc.call(self, original_method_name, args)
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method(method_names[:clear_cache_method_name]) do |*args|
|
30
|
+
Cacheable.cache_adapter.delete(__send__(method_names[:key_format_method_name], *args))
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method(method_names[:without_cache_method_name]) do |*args|
|
34
|
+
original_method = method(original_method_name).super_method
|
35
|
+
original_method.call(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method(method_names[:with_cache_method_name]) do |*args|
|
39
|
+
Cacheable.cache_adapter.fetch(__send__(method_names[:key_format_method_name], *args), opts[:cache_options]) do
|
40
|
+
__send__(method_names[:without_cache_method_name], *args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method(original_method_name) do |*args|
|
45
|
+
unless_proc = opts[:unless].is_a?(Symbol) ? opts[:unless].to_proc : opts[:unless]
|
46
|
+
|
47
|
+
if unless_proc&.call(self, original_method_name, args)
|
48
|
+
__send__(method_names[:without_cache_method_name], *args)
|
49
|
+
else
|
50
|
+
__send__(method_names[:with_cache_method_name], *args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
56
|
+
|
57
|
+
def default_key_format
|
58
|
+
proc do |target, method_name, _method_args|
|
59
|
+
# By default, we omit the _method_args from the cache key because there is no acceptable default behavior
|
60
|
+
class_name = (target.is_a?(Module) ? target.name : target.class.name)
|
61
|
+
cache_key = target.respond_to?(:cache_key) ? target.cache_key : class_name
|
62
|
+
[cache_key, method_name].compact
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_method_names(original_method_name)
|
67
|
+
method_name_without_punctuation = original_method_name.to_s.sub(/([?!=])$/, '')
|
68
|
+
punctuation = $LAST_PAREN_MATCH
|
69
|
+
|
70
|
+
{
|
71
|
+
with_cache_method_name: "#{method_name_without_punctuation}_with_cache#{punctuation}",
|
72
|
+
without_cache_method_name: "#{method_name_without_punctuation}_without_cache#{punctuation}",
|
73
|
+
key_format_method_name: "#{method_name_without_punctuation}_key_format#{punctuation}",
|
74
|
+
clear_cache_method_name: "clear_#{method_name_without_punctuation}_cache#{punctuation}"
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cacheable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jess Hottenstein
|
8
|
+
- Ryan Laughlin
|
9
|
+
- Aaron Rosenberg
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2018-07-31 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Add caching simply without modifying your existing code. Inlcudes configurable
|
16
|
+
options for simple cache invalidation. See README on github for more information.
|
17
|
+
email: support@splitwise.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- README.md
|
23
|
+
- cache-adapters.md
|
24
|
+
- lib/cacheable.rb
|
25
|
+
- lib/cacheable/cache_adapter.rb
|
26
|
+
- lib/cacheable/cache_adapters.rb
|
27
|
+
- lib/cacheable/cache_adapters/memory_adapter.rb
|
28
|
+
- lib/cacheable/method_generator.rb
|
29
|
+
- lib/cacheable/version.rb
|
30
|
+
homepage: https://github.com/splitwise/cacheable
|
31
|
+
licenses:
|
32
|
+
- MIT
|
33
|
+
metadata: {}
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 2.0.0
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubyforge_project:
|
50
|
+
rubygems_version: 2.6.13
|
51
|
+
signing_key:
|
52
|
+
specification_version: 4
|
53
|
+
summary: Add caching to any Ruby method in a aspect orientated programming approach.
|
54
|
+
test_files: []
|
55
|
+
has_rdoc:
|