memery 1.0.0 → 1.4.1
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 +4 -4
- data/.github/workflows/ci.yml +31 -0
- data/.gitignore +0 -1
- data/.rspec +1 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +41 -3
- data/Gemfile +0 -2
- data/Gemfile.lock +116 -0
- data/README.md +80 -3
- data/Rakefile +7 -1
- data/benchmark.rb +62 -0
- data/lib/memery.rb +82 -29
- data/lib/memery/version.rb +1 -1
- data/memery.gemspec +8 -1
- metadata +68 -11
- data/.travis.yml +0 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa73eb81311be05525651697942d76ae7b171ffe70d0f1b5851e5f41bf74cebd
|
|
4
|
+
data.tar.gz: d5993503224c4c1e60e84c97343d373c0e59eebfe4f735302423b6f88a1d2d33
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb9610aeab7ea46e94e411d5200f295910f01687f240b7ea21c0867c33a6e57a111649104199d0808dda3e0db2b36f944a3fd40c808eda26e78c3bb7ab5c3c6d
|
|
7
|
+
data.tar.gz: 3ced0dec017d683e82b698866a79072f97ad723b6df0a3e6029ac41037ff634343abbaec3f2fc5740799308108c3a1cc3ff7e36c15f4796121b287aaaf120858
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
|
|
9
|
+
# We want to run on external PRs, but not on our own internal PRs as they'll be run on push event
|
|
10
|
+
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'tycooon/memery'
|
|
11
|
+
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
ruby: ["2.5", "2.6", "2.7", "3.0"]
|
|
16
|
+
|
|
17
|
+
name: ${{ matrix.ruby }}
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v2
|
|
21
|
+
|
|
22
|
+
- uses: ruby/setup-ruby@v1
|
|
23
|
+
with:
|
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
|
25
|
+
bundler-cache: true
|
|
26
|
+
|
|
27
|
+
- run: bundle exec rake
|
|
28
|
+
|
|
29
|
+
- uses: coverallsapp/github-action@v1.1.2
|
|
30
|
+
with:
|
|
31
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,24 +1,62 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.4.0] - 2021-03-15
|
|
4
|
+
### Fixed
|
|
5
|
+
- Fix compatibility with `ActiveSupport::Concern`. ([@tycooon] and [@AlexWayfer]) [#26]
|
|
6
|
+
|
|
7
|
+
## [1.3.0] - 2020-02-10
|
|
8
|
+
### Added
|
|
9
|
+
- Allow memoization after including module with Memery. ([@AlexWayfer]) [#23]
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Make `memoize` return the method name to allow chaining. ([@JelF]) [#22]
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Fix warnings in Ruby 2.7. ([@AlexWayfer]) [#19], [#25]
|
|
16
|
+
|
|
17
|
+
## [1.2.0] - 2019-10-19
|
|
18
|
+
### Added
|
|
19
|
+
- Add `:ttl` option for `memoize` method ([@AlexWayfer]) [#11]
|
|
20
|
+
- Add benchmark script ([@AlexWayfer]) [#14]
|
|
21
|
+
- Add `.memoized?` method ([@AlexWayfer]) [#17]
|
|
22
|
+
|
|
23
|
+
## [1.1.0] - 2019-08-05
|
|
24
|
+
### Fixed
|
|
25
|
+
- Optimize speed and memory for cached values returns. ([@AlexWayfer]) [#10]
|
|
26
|
+
|
|
3
27
|
## [1.0.0] - 2018-08-31
|
|
4
28
|
### Added
|
|
5
|
-
- Add `:condition` option for `.memoize` method ([@AlexWayfer]) [#7]
|
|
29
|
+
- Add `:condition` option for `.memoize` method. ([@AlexWayfer]) [#7]
|
|
6
30
|
|
|
7
31
|
## [0.6.0] - 2018-04-20
|
|
8
32
|
### Added
|
|
9
33
|
- Readme example for memoizing class methods. ([@AlexWayfer]) [#3]
|
|
10
34
|
- Memery raises `ArgumentError` if method is not defined when you call `memoize`.
|
|
11
35
|
|
|
12
|
-
## [0.5.0] - 2017-
|
|
36
|
+
## [0.5.0] - 2017-06-12
|
|
13
37
|
- Initial public version.
|
|
14
38
|
|
|
15
39
|
[0.5.0]: https://github.com/tycooon/memery/tree/v0.5.0
|
|
16
40
|
[0.6.0]: https://github.com/tycooon/memery/compare/v0.5.0...v0.6.0
|
|
17
41
|
[1.0.0]: https://github.com/tycooon/memery/compare/v0.6.0...v1.0.0
|
|
18
|
-
[
|
|
42
|
+
[1.1.0]: https://github.com/tycooon/memery/compare/v1.0.0...v1.1.0
|
|
43
|
+
[1.2.0]: https://github.com/tycooon/memery/compare/v1.1.0...v1.2.0
|
|
44
|
+
[1.3.0]: https://github.com/tycooon/memery/compare/v1.2.0...v1.3.0
|
|
45
|
+
[1.4.0]: https://github.com/tycooon/memery/compare/v1.3.0...v1.4.0
|
|
46
|
+
[Unreleased]: https://github.com/tycooon/memery/compare/v1.4.0...HEAD
|
|
19
47
|
|
|
20
48
|
[@tycooon]: https://github.com/tycooon
|
|
21
49
|
[@AlexWayfer]: https://github.com/AlexWayfer
|
|
50
|
+
[@JelF]: https://github.com/JelF
|
|
22
51
|
|
|
23
52
|
[#3]: https://github.com/tycooon/memery/pull/3
|
|
24
53
|
[#7]: https://github.com/tycooon/memery/pull/7
|
|
54
|
+
[#10]: https://github.com/tycooon/memery/pull/10
|
|
55
|
+
[#11]: https://github.com/tycooon/memery/pull/11
|
|
56
|
+
[#14]: https://github.com/tycooon/memery/pull/14
|
|
57
|
+
[#17]: https://github.com/tycooon/memery/pull/17
|
|
58
|
+
[#19]: https://github.com/tycooon/memery/pull/19
|
|
59
|
+
[#22]: https://github.com/tycooon/memery/pull/22
|
|
60
|
+
[#23]: https://github.com/tycooon/memery/pull/23
|
|
61
|
+
[#25]: https://github.com/tycooon/memery/pull/25
|
|
62
|
+
[#26]: https://github.com/tycooon/memery/pull/26
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
memery (1.4.0)
|
|
5
|
+
ruby2_keywords (~> 0.0.2)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
activesupport (6.1.3.2)
|
|
11
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
12
|
+
i18n (>= 1.6, < 2)
|
|
13
|
+
minitest (>= 5.1)
|
|
14
|
+
tzinfo (~> 2.0)
|
|
15
|
+
zeitwerk (~> 2.3)
|
|
16
|
+
ast (2.4.2)
|
|
17
|
+
benchmark-ips (2.9.1)
|
|
18
|
+
benchmark-memory (0.1.2)
|
|
19
|
+
memory_profiler (~> 0.9)
|
|
20
|
+
coderay (1.1.3)
|
|
21
|
+
concurrent-ruby (1.1.9)
|
|
22
|
+
diff-lcs (1.4.4)
|
|
23
|
+
docile (1.4.0)
|
|
24
|
+
i18n (1.8.10)
|
|
25
|
+
concurrent-ruby (~> 1.0)
|
|
26
|
+
memory_profiler (0.9.14)
|
|
27
|
+
method_source (1.0.0)
|
|
28
|
+
minitest (5.14.4)
|
|
29
|
+
parallel (1.20.1)
|
|
30
|
+
parser (3.0.1.1)
|
|
31
|
+
ast (~> 2.4.1)
|
|
32
|
+
pry (0.14.1)
|
|
33
|
+
coderay (~> 1.1)
|
|
34
|
+
method_source (~> 1.0)
|
|
35
|
+
rack (2.2.3)
|
|
36
|
+
rainbow (3.0.0)
|
|
37
|
+
rake (13.0.3)
|
|
38
|
+
regexp_parser (2.1.1)
|
|
39
|
+
rexml (3.2.5)
|
|
40
|
+
rspec (3.10.0)
|
|
41
|
+
rspec-core (~> 3.10.0)
|
|
42
|
+
rspec-expectations (~> 3.10.0)
|
|
43
|
+
rspec-mocks (~> 3.10.0)
|
|
44
|
+
rspec-core (3.10.1)
|
|
45
|
+
rspec-support (~> 3.10.0)
|
|
46
|
+
rspec-expectations (3.10.1)
|
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
48
|
+
rspec-support (~> 3.10.0)
|
|
49
|
+
rspec-mocks (3.10.2)
|
|
50
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
51
|
+
rspec-support (~> 3.10.0)
|
|
52
|
+
rspec-support (3.10.2)
|
|
53
|
+
rubocop (1.11.0)
|
|
54
|
+
parallel (~> 1.10)
|
|
55
|
+
parser (>= 3.0.0.0)
|
|
56
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
57
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
58
|
+
rexml
|
|
59
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
|
60
|
+
ruby-progressbar (~> 1.7)
|
|
61
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
62
|
+
rubocop-ast (1.7.0)
|
|
63
|
+
parser (>= 3.0.1.1)
|
|
64
|
+
rubocop-config-umbrellio (1.11.0.51)
|
|
65
|
+
rubocop (= 1.11.0)
|
|
66
|
+
rubocop-performance (= 1.10.0)
|
|
67
|
+
rubocop-rails (= 2.9.1)
|
|
68
|
+
rubocop-rake (= 0.5.1)
|
|
69
|
+
rubocop-rspec (= 2.2.0)
|
|
70
|
+
rubocop-sequel (= 0.2.0)
|
|
71
|
+
rubocop-performance (1.10.0)
|
|
72
|
+
rubocop (>= 0.90.0, < 2.0)
|
|
73
|
+
rubocop-ast (>= 0.4.0)
|
|
74
|
+
rubocop-rails (2.9.1)
|
|
75
|
+
activesupport (>= 4.2.0)
|
|
76
|
+
rack (>= 1.1)
|
|
77
|
+
rubocop (>= 0.90.0, < 2.0)
|
|
78
|
+
rubocop-rake (0.5.1)
|
|
79
|
+
rubocop
|
|
80
|
+
rubocop-rspec (2.2.0)
|
|
81
|
+
rubocop (~> 1.0)
|
|
82
|
+
rubocop-ast (>= 1.1.0)
|
|
83
|
+
rubocop-sequel (0.2.0)
|
|
84
|
+
rubocop (~> 1.0)
|
|
85
|
+
ruby-progressbar (1.11.0)
|
|
86
|
+
ruby2_keywords (0.0.4)
|
|
87
|
+
simplecov (0.21.2)
|
|
88
|
+
docile (~> 1.1)
|
|
89
|
+
simplecov-html (~> 0.11)
|
|
90
|
+
simplecov_json_formatter (~> 0.1)
|
|
91
|
+
simplecov-html (0.12.3)
|
|
92
|
+
simplecov-lcov (0.8.0)
|
|
93
|
+
simplecov_json_formatter (0.1.3)
|
|
94
|
+
tzinfo (2.0.4)
|
|
95
|
+
concurrent-ruby (~> 1.0)
|
|
96
|
+
unicode-display_width (2.0.0)
|
|
97
|
+
zeitwerk (2.4.2)
|
|
98
|
+
|
|
99
|
+
PLATFORMS
|
|
100
|
+
ruby
|
|
101
|
+
|
|
102
|
+
DEPENDENCIES
|
|
103
|
+
activesupport
|
|
104
|
+
benchmark-ips
|
|
105
|
+
benchmark-memory
|
|
106
|
+
bundler
|
|
107
|
+
memery!
|
|
108
|
+
pry
|
|
109
|
+
rake
|
|
110
|
+
rspec
|
|
111
|
+
rubocop-config-umbrellio
|
|
112
|
+
simplecov
|
|
113
|
+
simplecov-lcov
|
|
114
|
+
|
|
115
|
+
BUNDLED WITH
|
|
116
|
+
2.2.17
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ def user
|
|
|
8
8
|
end
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
However, this approach
|
|
11
|
+
However, this approach doesn't work if calculated result can be `nil` or `false` or in case the method is using arguments. You will also require extra `begin`/`end` lines in case your method requires multiple lines:
|
|
12
12
|
|
|
13
13
|
```ruby
|
|
14
14
|
def user
|
|
@@ -31,7 +31,8 @@ end
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Installation
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
Simply add `gem "memery"` to your Gemfile.
|
|
35
36
|
|
|
36
37
|
## Usage
|
|
37
38
|
|
|
@@ -61,6 +62,25 @@ a.call { 1 } # => 42
|
|
|
61
62
|
# Will print because passing a block disables memoization
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
Methods with arguments are supported and the memoization will be done based on arguments using an internal hash. So this will work as expected:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
class A
|
|
69
|
+
include Memery
|
|
70
|
+
|
|
71
|
+
memoize def call(arg1, arg2)
|
|
72
|
+
puts "calculating"
|
|
73
|
+
arg1 + arg2
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
a = A.new
|
|
78
|
+
a.call(1, 5) # => 6
|
|
79
|
+
a.call(2, 15) # => 17
|
|
80
|
+
a.call(1, 5) # => 6
|
|
81
|
+
# Text will be printed only twice, once per unique argument list.
|
|
82
|
+
```
|
|
83
|
+
|
|
64
84
|
For class methods:
|
|
65
85
|
|
|
66
86
|
```ruby
|
|
@@ -116,8 +136,62 @@ a.call # => 42
|
|
|
116
136
|
# with `true` result of condition block.
|
|
117
137
|
```
|
|
118
138
|
|
|
139
|
+
For memoization with time-to-live:
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
class A
|
|
143
|
+
include Memery
|
|
144
|
+
|
|
145
|
+
def call
|
|
146
|
+
puts "calculating"
|
|
147
|
+
42
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
memoize :call, ttl: 3 # seconds
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
a = A.new
|
|
154
|
+
a.call # => 42
|
|
155
|
+
# calculating
|
|
156
|
+
a.call # => 42
|
|
157
|
+
a.call # => 42
|
|
158
|
+
# Text will be printed again only after 3 seconds of time-to-live.
|
|
159
|
+
# 3 seconds later...
|
|
160
|
+
a.call # => 42
|
|
161
|
+
# calculating
|
|
162
|
+
a.call # => 42
|
|
163
|
+
a.call # => 42
|
|
164
|
+
# another 3 seconds later...
|
|
165
|
+
a.call # => 42
|
|
166
|
+
# calculating
|
|
167
|
+
a.call # => 42
|
|
168
|
+
a.call # => 42
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Check if method is memoized:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
class A
|
|
175
|
+
include Memery
|
|
176
|
+
|
|
177
|
+
memoize def call
|
|
178
|
+
puts "calculating"
|
|
179
|
+
42
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def execute
|
|
183
|
+
puts "non-memoized"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
a = A.new
|
|
188
|
+
|
|
189
|
+
a.memoized?(:call) # => true
|
|
190
|
+
a.memoized?(:execute) # => false
|
|
191
|
+
```
|
|
192
|
+
|
|
119
193
|
## Difference with other gems
|
|
120
|
-
Memery is very similar to [Memoist](https://github.com/matthewrudy/memoist). The difference is that it doesn't override methods, instead it uses Ruby 2 `Module.prepend` feature. This approach is cleaner and it allows subclasses' methods to work properly: if you redefine a memoized method in a subclass, it's not memoized by default, but you can memoize it normally (without using awkward `identifier: ` argument) and it will just work:
|
|
194
|
+
Memery is very similar to [Memoist](https://github.com/matthewrudy/memoist). The difference is that it doesn't override methods, instead it uses Ruby 2 `Module.prepend` feature. This approach is cleaner (for example you are able to inspect the original method body using `method(:x).super_method.source`) and it allows subclasses' methods to work properly: if you redefine a memoized method in a subclass, it's not memoized by default, but you can memoize it normally (without using awkward `identifier: ` argument) and it will just work:
|
|
121
195
|
|
|
122
196
|
```ruby
|
|
123
197
|
class A
|
|
@@ -172,10 +246,13 @@ a.users {}
|
|
|
172
246
|
However, this solution is kind of hacky.
|
|
173
247
|
|
|
174
248
|
## Contributing
|
|
249
|
+
|
|
175
250
|
Bug reports and pull requests are welcome on GitHub at https://github.com/tycooon/memery.
|
|
176
251
|
|
|
177
252
|
## License
|
|
253
|
+
|
|
178
254
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
179
255
|
|
|
180
256
|
## Author
|
|
257
|
+
|
|
181
258
|
Created by Yuri Smirnov.
|
data/Rakefile
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
4
|
require "rspec/core/rake_task"
|
|
5
|
+
require "rubocop/rake_task"
|
|
5
6
|
|
|
6
7
|
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
+
RuboCop::RakeTask.new(:lint)
|
|
7
9
|
|
|
8
|
-
task default:
|
|
10
|
+
task default: %i[lint spec]
|
|
11
|
+
|
|
12
|
+
task :benchmark do
|
|
13
|
+
require_relative "./benchmark"
|
|
14
|
+
end
|
data/benchmark.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
Bundler.setup
|
|
5
|
+
|
|
6
|
+
require "benchmark"
|
|
7
|
+
require "benchmark/ips"
|
|
8
|
+
require "benchmark/memory"
|
|
9
|
+
|
|
10
|
+
puts "```ruby"
|
|
11
|
+
puts File.read(__FILE__)
|
|
12
|
+
puts "```"
|
|
13
|
+
puts
|
|
14
|
+
puts "### Output"
|
|
15
|
+
puts
|
|
16
|
+
puts "```"
|
|
17
|
+
|
|
18
|
+
require_relative "lib/memery"
|
|
19
|
+
|
|
20
|
+
class Foo
|
|
21
|
+
class << self
|
|
22
|
+
include Memery
|
|
23
|
+
|
|
24
|
+
def base_find(char)
|
|
25
|
+
("a".."k").find { |letter| letter == char }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
memoize def find_z
|
|
29
|
+
base_find("z")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
memoize def find_new(char)
|
|
33
|
+
base_find(char)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_no_args
|
|
39
|
+
Foo.find_z
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_with_args
|
|
43
|
+
Foo.find_new("d")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Benchmark.ips do |x|
|
|
47
|
+
x.report("test_no_args") { test_no_args }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Benchmark.memory do |x|
|
|
51
|
+
x.report("test_no_args") { 100.times { test_no_args } }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Benchmark.ips do |x|
|
|
55
|
+
x.report("test_with_args") { test_with_args }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Benchmark.memory do |x|
|
|
59
|
+
x.report("test_with_args") { 100.times { test_with_args } }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
puts "```"
|
data/lib/memery.rb
CHANGED
|
@@ -1,64 +1,117 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "ruby2_keywords"
|
|
4
|
+
|
|
3
5
|
require "memery/version"
|
|
4
6
|
|
|
5
7
|
module Memery
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
class << self
|
|
9
|
+
def monotonic_clock
|
|
10
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
OUR_BLOCK = lambda do
|
|
15
|
+
extend(ClassMethods)
|
|
16
|
+
include(InstanceMethods)
|
|
17
|
+
extend ModuleMethods if instance_of?(Module)
|
|
9
18
|
end
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
private_constant :OUR_BLOCK
|
|
21
|
+
|
|
22
|
+
module ModuleMethods
|
|
23
|
+
def included(base = nil, &block)
|
|
24
|
+
if base.nil? && block
|
|
25
|
+
super do
|
|
26
|
+
instance_exec(&block)
|
|
27
|
+
instance_exec(&OUR_BLOCK)
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
base.instance_exec(&OUR_BLOCK)
|
|
31
|
+
end
|
|
19
32
|
end
|
|
20
33
|
end
|
|
21
34
|
|
|
35
|
+
extend ModuleMethods
|
|
36
|
+
|
|
22
37
|
module ClassMethods
|
|
23
|
-
def memoize(method_name, condition: nil)
|
|
38
|
+
def memoize(method_name, condition: nil, ttl: nil)
|
|
24
39
|
prepend_memery_module!
|
|
25
|
-
define_memoized_method!(method_name, condition: condition)
|
|
40
|
+
define_memoized_method!(method_name, condition: condition, ttl: ttl)
|
|
41
|
+
method_name
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def memoized?(method_name)
|
|
45
|
+
return false unless defined?(@_memery_module)
|
|
46
|
+
|
|
47
|
+
@_memery_module.method_defined?(method_name) ||
|
|
48
|
+
@_memery_module.private_method_defined?(method_name)
|
|
26
49
|
end
|
|
27
50
|
|
|
28
51
|
private
|
|
29
52
|
|
|
30
53
|
def prepend_memery_module!
|
|
31
54
|
return if defined?(@_memery_module)
|
|
32
|
-
@_memery_module = Module.new
|
|
33
|
-
prepend
|
|
55
|
+
@_memery_module = Module.new { extend MemoizationModule }
|
|
56
|
+
prepend(@_memery_module)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def define_memoized_method!(method_name, **options)
|
|
60
|
+
@_memery_module.define_memoized_method!(self, method_name, **options)
|
|
34
61
|
end
|
|
35
62
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
module MemoizationModule
|
|
64
|
+
Cache = Struct.new(:result, :time) do
|
|
65
|
+
def fresh?(ttl)
|
|
66
|
+
return true if ttl.nil?
|
|
67
|
+
Memery.monotonic_clock <= time + ttl
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def define_memoized_method!(klass, method_name, condition: nil, ttl: nil)
|
|
72
|
+
method_key = "#{method_name}_#{object_id}"
|
|
73
|
+
|
|
74
|
+
original_visibility = method_visibility(klass, method_name)
|
|
75
|
+
original_arity = klass.instance_method(method_name).arity
|
|
40
76
|
|
|
41
|
-
@_memery_module.module_eval do
|
|
42
77
|
define_method(method_name) do |*args, &block|
|
|
43
78
|
if block || (condition && !instance_exec(&condition))
|
|
44
79
|
return super(*args, &block)
|
|
45
80
|
end
|
|
46
81
|
|
|
47
|
-
@_memery_memoized_values ||= {}
|
|
82
|
+
cache_store = (@_memery_memoized_values ||= {})
|
|
83
|
+
cache_key = original_arity.zero? ? method_key : [method_key, *args]
|
|
84
|
+
cache = cache_store[cache_key]
|
|
48
85
|
|
|
49
|
-
|
|
50
|
-
store = @_memery_memoized_values[key] ||= {}
|
|
86
|
+
return cache.result if cache&.fresh?(ttl)
|
|
51
87
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
88
|
+
result = super(*args)
|
|
89
|
+
new_cache = Cache.new(result, Memery.monotonic_clock)
|
|
90
|
+
cache_store[cache_key] = new_cache
|
|
91
|
+
|
|
92
|
+
result
|
|
57
93
|
end
|
|
58
94
|
|
|
59
|
-
|
|
95
|
+
ruby2_keywords(method_name)
|
|
96
|
+
send(original_visibility, method_name)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def method_visibility(klass, method_name)
|
|
102
|
+
if klass.private_method_defined?(method_name)
|
|
103
|
+
:private
|
|
104
|
+
elsif klass.protected_method_defined?(method_name)
|
|
105
|
+
:protected
|
|
106
|
+
elsif klass.public_method_defined?(method_name)
|
|
107
|
+
:public
|
|
108
|
+
else
|
|
109
|
+
raise ArgumentError, "Method #{method_name} is not defined on #{klass}"
|
|
110
|
+
end
|
|
60
111
|
end
|
|
61
112
|
end
|
|
113
|
+
|
|
114
|
+
private_constant :MemoizationModule
|
|
62
115
|
end
|
|
63
116
|
|
|
64
117
|
module InstanceMethods
|
data/lib/memery/version.rb
CHANGED
data/memery.gemspec
CHANGED
|
@@ -5,6 +5,8 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
5
5
|
require "memery/version"
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |spec|
|
|
8
|
+
spec.required_ruby_version = ">= 2.5.0"
|
|
9
|
+
|
|
8
10
|
spec.name = "memery"
|
|
9
11
|
spec.version = Memery::VERSION
|
|
10
12
|
spec.authors = ["Yuri Smirnov"]
|
|
@@ -20,11 +22,16 @@ Gem::Specification.new do |spec|
|
|
|
20
22
|
end
|
|
21
23
|
spec.require_paths = ["lib"]
|
|
22
24
|
|
|
25
|
+
spec.add_runtime_dependency "ruby2_keywords", "~> 0.0.2"
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency "activesupport"
|
|
28
|
+
spec.add_development_dependency "benchmark-ips"
|
|
29
|
+
spec.add_development_dependency "benchmark-memory"
|
|
23
30
|
spec.add_development_dependency "bundler"
|
|
24
|
-
spec.add_development_dependency "coveralls"
|
|
25
31
|
spec.add_development_dependency "pry"
|
|
26
32
|
spec.add_development_dependency "rake"
|
|
27
33
|
spec.add_development_dependency "rspec"
|
|
28
34
|
spec.add_development_dependency "rubocop-config-umbrellio"
|
|
29
35
|
spec.add_development_dependency "simplecov"
|
|
36
|
+
spec.add_development_dependency "simplecov-lcov"
|
|
30
37
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,59 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: memery
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuri Smirnov
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-06-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: ruby2_keywords
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.0.2
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.0.2
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: benchmark-ips
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: benchmark-memory
|
|
15
57
|
requirement: !ruby/object:Gem::Requirement
|
|
16
58
|
requirements:
|
|
17
59
|
- - ">="
|
|
@@ -25,7 +67,7 @@ dependencies:
|
|
|
25
67
|
- !ruby/object:Gem::Version
|
|
26
68
|
version: '0'
|
|
27
69
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
70
|
+
name: bundler
|
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
|
30
72
|
requirements:
|
|
31
73
|
- - ">="
|
|
@@ -108,6 +150,20 @@ dependencies:
|
|
|
108
150
|
- - ">="
|
|
109
151
|
- !ruby/object:Gem::Version
|
|
110
152
|
version: '0'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: simplecov-lcov
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
111
167
|
description: Memery is a gem for memoization.
|
|
112
168
|
email:
|
|
113
169
|
- tycooon@yandex.ru
|
|
@@ -116,15 +172,17 @@ extensions: []
|
|
|
116
172
|
extra_rdoc_files: []
|
|
117
173
|
files:
|
|
118
174
|
- ".editorconfig"
|
|
175
|
+
- ".github/workflows/ci.yml"
|
|
119
176
|
- ".gitignore"
|
|
120
177
|
- ".rspec"
|
|
121
178
|
- ".rubocop.yml"
|
|
122
|
-
- ".travis.yml"
|
|
123
179
|
- CHANGELOG.md
|
|
124
180
|
- Gemfile
|
|
181
|
+
- Gemfile.lock
|
|
125
182
|
- LICENSE.txt
|
|
126
183
|
- README.md
|
|
127
184
|
- Rakefile
|
|
185
|
+
- benchmark.rb
|
|
128
186
|
- lib/memery.rb
|
|
129
187
|
- lib/memery/version.rb
|
|
130
188
|
- memery.gemspec
|
|
@@ -132,7 +190,7 @@ homepage: https://github.com/tycooon/memery
|
|
|
132
190
|
licenses:
|
|
133
191
|
- MIT
|
|
134
192
|
metadata: {}
|
|
135
|
-
post_install_message:
|
|
193
|
+
post_install_message:
|
|
136
194
|
rdoc_options: []
|
|
137
195
|
require_paths:
|
|
138
196
|
- lib
|
|
@@ -140,16 +198,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
140
198
|
requirements:
|
|
141
199
|
- - ">="
|
|
142
200
|
- !ruby/object:Gem::Version
|
|
143
|
-
version:
|
|
201
|
+
version: 2.5.0
|
|
144
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
203
|
requirements:
|
|
146
204
|
- - ">="
|
|
147
205
|
- !ruby/object:Gem::Version
|
|
148
206
|
version: '0'
|
|
149
207
|
requirements: []
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
signing_key:
|
|
208
|
+
rubygems_version: 3.2.17
|
|
209
|
+
signing_key:
|
|
153
210
|
specification_version: 4
|
|
154
211
|
summary: A gem for memoization.
|
|
155
212
|
test_files: []
|
data/.travis.yml
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
language: ruby
|
|
2
|
-
|
|
3
|
-
sudo: false
|
|
4
|
-
|
|
5
|
-
rvm:
|
|
6
|
-
- 2.2
|
|
7
|
-
- 2.3
|
|
8
|
-
- 2.4
|
|
9
|
-
- 2.5
|
|
10
|
-
- ruby-head
|
|
11
|
-
|
|
12
|
-
before_install: gem install bundler
|
|
13
|
-
|
|
14
|
-
env: SUITE="rspec"
|
|
15
|
-
|
|
16
|
-
script: bundle exec $SUITE
|
|
17
|
-
|
|
18
|
-
matrix:
|
|
19
|
-
fast_finish: true
|
|
20
|
-
# Only run RuboCop on the latest Ruby
|
|
21
|
-
include:
|
|
22
|
-
- rvm: 2.5
|
|
23
|
-
env: SUITE="rubocop"
|
|
24
|
-
allow_failures:
|
|
25
|
-
- rvm: ruby-head
|