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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91c69224d38dc7b680a40c666d20e36ba65383235e70cec68b8b54b5e42f61ef
4
- data.tar.gz: 8340fb04f65f8f57a5ee177d6affcb3183d892f65fae04153fb5c2e74d3ee2cf
3
+ metadata.gz: b422cc5336406f390272a02aea253b991470519a786844cb72b4cc0c05ef3b56
4
+ data.tar.gz: 12c2329a8539305d85dcb99b30604e55aa04b0f75153fc6a173ae73875f423c8
5
5
  SHA512:
6
- metadata.gz: 1acd168dd8d4bc989ac51ca5c37eb1229cf86c8378aac3ef0fef716e4ba81bb50e85975712ac4ea1997ded3c58db43647ffe342f186d6f3a12d7bfc87e646293
7
- data.tar.gz: 219204fb9a75cd005dcd3102740ba81a67302b5b15609bfceb4c0fe0484698c5a0f1467ce737f7d30067ad85a7b2d9be68df4d489d56d11d6f6789536c7b0533
6
+ metadata.gz: 94b69a032837511e10b368ab6db8212bf6b48c8b835c5f76732484b05dea8910af17712797a42fa334ee0529973f8c61d69ea052c1949619411bc1ae7c0897fa
7
+ data.tar.gz: fd17f734226f87487223034bf07afae259ed54ca5b216bfc11322b38adcdf1dd3898bf0f238ab8dcb846492c7f93baa8e51d909584bd1ec1e8b07774d4cc3693
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
3
  --require spec_helper
4
+ --warnings
@@ -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
@@ -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
- [Unreleased]: https://github.com/tycooon/memery/compare/v1.1.0...HEAD
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
@@ -2,5 +2,3 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
  gemspec
5
-
6
- gem "rubocop-config-umbrellio", github: "umbrellio/code-style"
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: :spec
10
+ task default: %i[lint spec]
11
+
12
+ task :benchmark do
13
+ require_relative "./benchmark"
14
+ end
@@ -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 "```"
@@ -3,26 +3,39 @@
3
3
  require "memery/version"
4
4
 
5
5
  module Memery
6
- def self.included(base)
7
- base.extend(ClassMethods)
8
- base.include(InstanceMethods)
9
- end
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
- def self.method_visibility(klass, method_name)
12
- case
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&.[](key)
62
+ store = (@_memery_memoized_values ||= {})[key] ||= {}
50
63
 
51
- return store[args] if store&.key?(args)
64
+ if store.key?(args) && (ttl.nil? || Memery.monotonic_clock <= store[args][:time] + ttl)
65
+ return store[args][:result]
66
+ end
52
67
 
53
- @_memery_memoized_values ||= {}
54
- @_memery_memoized_values[key] ||= {}
55
- @_memery_memoized_values[key][args] = super(*args)
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Memery
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.0"
5
5
  end
@@ -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.1.0
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-08-05 00:00:00.000000000 Z
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