memery 1.1.0 → 1.2.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 +4 -4
- data/.rspec +1 -0
- data/.travis.yml +0 -8
- data/CHANGELOG.md +11 -1
- data/Gemfile +0 -2
- data/README.md +73 -0
- data/Rakefile +7 -1
- data/benchmark.rb +46 -0
- data/lib/memery.rb +35 -20
- data/lib/memery/version.rb +1 -1
- data/memery.gemspec +3 -0
- metadata +45 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b422cc5336406f390272a02aea253b991470519a786844cb72b4cc0c05ef3b56
|
4
|
+
data.tar.gz: 12c2329a8539305d85dcb99b30604e55aa04b0f75153fc6a173ae73875f423c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94b69a032837511e10b368ab6db8212bf6b48c8b835c5f76732484b05dea8910af17712797a42fa334ee0529973f8c61d69ea052c1949619411bc1ae7c0897fa
|
7
|
+
data.tar.gz: fd17f734226f87487223034bf07afae259ed54ca5b216bfc11322b38adcdf1dd3898bf0f238ab8dcb846492c7f93baa8e51d909584bd1ec1e8b07774d4cc3693
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
@@ -11,15 +11,7 @@ rvm:
|
|
11
11
|
|
12
12
|
before_install: gem install bundler
|
13
13
|
|
14
|
-
env: SUITE="rspec"
|
15
|
-
|
16
|
-
script: bundle exec $SUITE
|
17
|
-
|
18
14
|
matrix:
|
19
15
|
fast_finish: true
|
20
|
-
# Only run RuboCop on the latest Ruby
|
21
|
-
include:
|
22
|
-
- rvm: 2.6
|
23
|
-
env: SUITE="rubocop"
|
24
16
|
allow_failures:
|
25
17
|
- rvm: ruby-head
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.2.0] - 2019-10-19
|
4
|
+
### Added
|
5
|
+
- Add `:ttl` option for `memoize` method ([@AlexWayfer]) [#11]
|
6
|
+
- Add benchmark script ([@AlexWayfer]) [#14]
|
7
|
+
- Add `.memoized?` method ([@AlexWayfer]) [#17]
|
8
|
+
|
3
9
|
## [1.1.0] - 2019-08-05
|
4
10
|
### Fixed
|
5
11
|
- Optimize speed and memory for cached values returns. ([@AlexWayfer]) [#10]
|
@@ -20,7 +26,8 @@
|
|
20
26
|
[0.6.0]: https://github.com/tycooon/memery/compare/v0.5.0...v0.6.0
|
21
27
|
[1.0.0]: https://github.com/tycooon/memery/compare/v0.6.0...v1.0.0
|
22
28
|
[1.1.0]: https://github.com/tycooon/memery/compare/v1.0.0...v1.1.0
|
23
|
-
[
|
29
|
+
[1.2.0]: https://github.com/tycooon/memery/compare/v1.1.0...v1.2.0
|
30
|
+
[Unreleased]: https://github.com/tycooon/memery/compare/v1.2.0...HEAD
|
24
31
|
|
25
32
|
[@tycooon]: https://github.com/tycooon
|
26
33
|
[@AlexWayfer]: https://github.com/AlexWayfer
|
@@ -28,3 +35,6 @@
|
|
28
35
|
[#3]: https://github.com/tycooon/memery/pull/3
|
29
36
|
[#7]: https://github.com/tycooon/memery/pull/7
|
30
37
|
[#10]: https://github.com/tycooon/memery/pull/10
|
38
|
+
[#11]: https://github.com/tycooon/memery/pull/11
|
39
|
+
[#14]: https://github.com/tycooon/memery/pull/14
|
40
|
+
[#17]: https://github.com/tycooon/memery/pull/17
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -62,6 +62,25 @@ a.call { 1 } # => 42
|
|
62
62
|
# Will print because passing a block disables memoization
|
63
63
|
```
|
64
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
|
+
|
65
84
|
For class methods:
|
66
85
|
|
67
86
|
```ruby
|
@@ -117,6 +136,60 @@ a.call # => 42
|
|
117
136
|
# with `true` result of condition block.
|
118
137
|
```
|
119
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
|
+
|
120
193
|
## Difference with other gems
|
121
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:
|
122
195
|
|
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,46 @@
|
|
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_new(char)
|
29
|
+
base_find(char)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_memery
|
35
|
+
Foo.find_new("d")
|
36
|
+
end
|
37
|
+
|
38
|
+
Benchmark.ips do |x|
|
39
|
+
x.report("test_memery") { test_memery }
|
40
|
+
end
|
41
|
+
|
42
|
+
Benchmark.memory do |x|
|
43
|
+
x.report("test_memery") { 100.times { test_memery } }
|
44
|
+
end
|
45
|
+
|
46
|
+
puts "```"
|
data/lib/memery.rb
CHANGED
@@ -3,26 +3,39 @@
|
|
3
3
|
require "memery/version"
|
4
4
|
|
5
5
|
module Memery
|
6
|
-
|
7
|
-
base
|
8
|
-
|
9
|
-
|
6
|
+
class << self
|
7
|
+
def included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
base.include(InstanceMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_visibility(klass, method_name)
|
13
|
+
case
|
14
|
+
when klass.private_method_defined?(method_name)
|
15
|
+
:private
|
16
|
+
when klass.protected_method_defined?(method_name)
|
17
|
+
:protected
|
18
|
+
when klass.public_method_defined?(method_name)
|
19
|
+
:public
|
20
|
+
end
|
21
|
+
end
|
10
22
|
|
11
|
-
|
12
|
-
|
13
|
-
when klass.private_method_defined?(method_name)
|
14
|
-
:private
|
15
|
-
when klass.protected_method_defined?(method_name)
|
16
|
-
:protected
|
17
|
-
when klass.public_method_defined?(method_name)
|
18
|
-
:public
|
23
|
+
def monotonic_clock
|
24
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
19
25
|
end
|
20
26
|
end
|
21
27
|
|
22
28
|
module ClassMethods
|
23
|
-
def memoize(method_name, condition: nil)
|
29
|
+
def memoize(method_name, condition: nil, ttl: nil)
|
24
30
|
prepend_memery_module!
|
25
|
-
define_memoized_method!(method_name, condition: condition)
|
31
|
+
define_memoized_method!(method_name, condition: condition, ttl: ttl)
|
32
|
+
end
|
33
|
+
|
34
|
+
def memoized?(method_name)
|
35
|
+
return false unless defined?(@_memery_module)
|
36
|
+
|
37
|
+
@_memery_module.method_defined?(method_name) ||
|
38
|
+
@_memery_module.private_method_defined?(method_name)
|
26
39
|
end
|
27
40
|
|
28
41
|
private
|
@@ -33,7 +46,7 @@ module Memery
|
|
33
46
|
prepend @_memery_module
|
34
47
|
end
|
35
48
|
|
36
|
-
def define_memoized_method!(method_name, condition: nil)
|
49
|
+
def define_memoized_method!(method_name, condition: nil, ttl: nil)
|
37
50
|
mod_id = @_memery_module.object_id
|
38
51
|
visibility = Memery.method_visibility(self, method_name)
|
39
52
|
raise ArgumentError, "Method #{method_name} is not defined on #{self}" unless visibility
|
@@ -46,13 +59,15 @@ module Memery
|
|
46
59
|
|
47
60
|
key = "#{method_name}_#{mod_id}"
|
48
61
|
|
49
|
-
store = @_memery_memoized_values
|
62
|
+
store = (@_memery_memoized_values ||= {})[key] ||= {}
|
50
63
|
|
51
|
-
|
64
|
+
if store.key?(args) && (ttl.nil? || Memery.monotonic_clock <= store[args][:time] + ttl)
|
65
|
+
return store[args][:result]
|
66
|
+
end
|
52
67
|
|
53
|
-
|
54
|
-
@_memery_memoized_values[key]
|
55
|
-
|
68
|
+
result = super(*args)
|
69
|
+
@_memery_memoized_values[key][args] = { result: result, time: Memery.monotonic_clock }
|
70
|
+
result
|
56
71
|
end
|
57
72
|
|
58
73
|
send(visibility, method_name)
|
data/lib/memery/version.rb
CHANGED
data/memery.gemspec
CHANGED
@@ -20,10 +20,13 @@ Gem::Specification.new do |spec|
|
|
20
20
|
end
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
+
spec.add_development_dependency "benchmark-ips"
|
24
|
+
spec.add_development_dependency "benchmark-memory"
|
23
25
|
spec.add_development_dependency "bundler"
|
24
26
|
spec.add_development_dependency "coveralls"
|
25
27
|
spec.add_development_dependency "pry"
|
26
28
|
spec.add_development_dependency "rake"
|
27
29
|
spec.add_development_dependency "rspec"
|
30
|
+
spec.add_development_dependency "rubocop-config-umbrellio"
|
28
31
|
spec.add_development_dependency "simplecov"
|
29
32
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuri Smirnov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: benchmark-ips
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: benchmark-memory
|
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'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +108,20 @@ dependencies:
|
|
80
108
|
- - ">="
|
81
109
|
- !ruby/object:Gem::Version
|
82
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-config-umbrellio
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
83
125
|
- !ruby/object:Gem::Dependency
|
84
126
|
name: simplecov
|
85
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,6 +153,7 @@ files:
|
|
111
153
|
- LICENSE.txt
|
112
154
|
- README.md
|
113
155
|
- Rakefile
|
156
|
+
- benchmark.rb
|
114
157
|
- lib/memery.rb
|
115
158
|
- lib/memery/version.rb
|
116
159
|
- memery.gemspec
|