memo_wise 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1b98d394deb079784021cd44f5c7b41abdbf1cce2b0ce14b0a3957b12bfdabe
4
- data.tar.gz: 45b6796cf708b5401a56edfd68156587f92826a71d833b7fe3bec24985d81ac9
3
+ metadata.gz: 634bee32d07ccd97dc7670c67521a4091ebc0df0deae1479cc054aaa7a246e6c
4
+ data.tar.gz: 59523369b3373389481388962039486b4f0f5cf002d24091ad2244048f100bbc
5
5
  SHA512:
6
- metadata.gz: 9ba145822aef3d07b2385daffaef15c22f5ffd4604e68d1fbad5d2bdf682c7723bb9a109cdda69d26efd6e5c05a21c1f0310ae70a1dbfd0f18a28cbe92d13d6f
7
- data.tar.gz: 210c34c42f11e9d761c0fda0512daf62a533ec0fe2f19609a6cf0e052a1f2e2fdf22a55711764865616a319e387bff83fc5a6505973a4d89483ef383af008d13
6
+ metadata.gz: 3896bf8b001ad52afda6b14c5170d902d85a88c9faaa60d6c9097a01045e0ef90a379c130834b2a2fcbec356b51346f975fc96493dcb3d3c04943200eb7274a3
7
+ data.tar.gz: 72718456d986ed21d593a6a99117ee74a0a385f94ba0b1a00fadb38cbad977202cea5d8e32325bdaa2e5eb273808a52ca9d62997cd927a6a98ffd31174bdcc6f
data/CHANGELOG.md CHANGED
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
  (nothing yet!)
10
10
 
11
+ ## [0.4.0] - 2021-04-30
12
+ ### Added
13
+ - Documentation of confusing module test behavior
14
+ - Support using MemoWise in classes with keyword arguments in the initializer
15
+ - Support Marshal dump/load of classes using MemoWise
16
+
11
17
  ## [0.3.0] - 2021-02-11
12
18
  ### Added
13
19
  - Changelog and tags
@@ -48,7 +54,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
48
54
  - Panolint
49
55
  - Dependabot setup
50
56
 
51
- [Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v0.3.0...HEAD
57
+ [Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v0.4.0...HEAD
58
+ [0.4.0]: https://github.com/panorama-ed/memo_wise/compare/v0.3.0...v0.4.0
52
59
  [0.3.0]: https://github.com/panorama-ed/memo_wise/compare/v0.2.0...v0.3.0
53
60
  [0.2.0]: https://github.com/panorama-ed/memo_wise/compare/v0.1.2...v0.2.0
54
61
  [0.1.2]: https://github.com/panorama-ed/memo_wise/compare/v0.1.1...v0.1.2
data/Gemfile CHANGED
@@ -14,7 +14,7 @@ end
14
14
  # Excluded from CI except on latest MRI Ruby, to reduce compatibility burden
15
15
  group :checks do
16
16
  gem "codecov"
17
- gem "panolint", github: "panorama-ed/panolint"
17
+ gem "panolint", github: "panorama-ed/panolint", branch: "main"
18
18
  end
19
19
 
20
20
  # Excluded from CI except on latest MRI Ruby, to reduce compatibility burden
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  GIT
2
2
  remote: https://github.com/panorama-ed/panolint.git
3
- revision: ca5edaa1f6985e2812998a937229f972c07bce56
3
+ revision: e2f76aa2482f02e68826eb8643a41998a09fc7ab
4
+ branch: main
4
5
  specs:
5
- panolint (0.1.2)
6
+ panolint (0.1.3)
6
7
  brakeman (>= 4.8, < 6.0)
7
8
  rubocop (>= 0.83, < 2.0)
8
9
  rubocop-performance (~> 1.5)
@@ -13,35 +14,35 @@ GIT
13
14
  PATH
14
15
  remote: .
15
16
  specs:
16
- memo_wise (0.3.0)
17
+ memo_wise (0.4.0)
17
18
 
18
19
  GEM
19
20
  remote: https://rubygems.org/
20
21
  specs:
21
- activesupport (5.2.4.4)
22
+ activesupport (5.2.5)
22
23
  concurrent-ruby (~> 1.0, >= 1.0.2)
23
24
  i18n (>= 0.7, < 2)
24
25
  minitest (~> 5.1)
25
26
  tzinfo (~> 1.1)
26
27
  ast (2.4.2)
27
28
  brakeman (5.0.0)
28
- codecov (0.4.3)
29
+ codecov (0.5.2)
29
30
  simplecov (>= 0.15, < 0.22)
30
31
  concurrent-ruby (1.1.8)
31
32
  diff-lcs (1.4.4)
32
33
  docile (1.3.5)
33
- i18n (1.8.8)
34
+ i18n (1.8.10)
34
35
  concurrent-ruby (~> 1.0)
35
- minitest (5.14.3)
36
+ minitest (5.14.4)
36
37
  parallel (1.20.1)
37
- parser (3.0.0.0)
38
+ parser (3.0.1.0)
38
39
  ast (~> 2.4.1)
39
40
  rack (2.2.3)
40
41
  rainbow (3.0.0)
41
42
  rake (13.0.3)
42
43
  redcarpet (3.5.1)
43
- regexp_parser (2.0.3)
44
- rexml (3.2.4)
44
+ regexp_parser (2.1.1)
45
+ rexml (3.2.5)
45
46
  rspec (3.10.0)
46
47
  rspec-core (~> 3.10.0)
47
48
  rspec-expectations (~> 3.10.0)
@@ -55,7 +56,7 @@ GEM
55
56
  diff-lcs (>= 1.2.0, < 2.0)
56
57
  rspec-support (~> 3.10.0)
57
58
  rspec-support (3.10.0)
58
- rubocop (1.9.1)
59
+ rubocop (1.12.1)
59
60
  parallel (~> 1.10)
60
61
  parser (>= 3.0.0.0)
61
62
  rainbow (>= 2.2.2, < 4.0)
@@ -66,7 +67,7 @@ GEM
66
67
  unicode-display_width (>= 1.4.0, < 3.0)
67
68
  rubocop-ast (1.4.1)
68
69
  parser (>= 2.7.1.5)
69
- rubocop-performance (1.9.2)
70
+ rubocop-performance (1.10.2)
70
71
  rubocop (>= 0.90.0, < 2.0)
71
72
  rubocop-ast (>= 0.4.0)
72
73
  rubocop-rails (2.9.1)
data/README.md CHANGED
@@ -12,7 +12,6 @@
12
12
  [![Gem Version](https://img.shields.io/gem/v/memo_wise.svg)](https://rubygems.org/gems/memo_wise)
13
13
  [![Gem Downloads](https://img.shields.io/gem/dt/memo_wise.svg)](https://rubygems.org/gems/memo_wise)
14
14
 
15
-
16
15
  ## Why `MemoWise`?
17
16
 
18
17
  `MemoWise` is **the wise choice for Ruby memoization**, featuring:
@@ -85,13 +84,13 @@ run in GitHub Actions and updated in every PR that changes code.
85
84
 
86
85
  |Method arguments|**`memo_wise` (0.1.0)**|`memery` (1.3.0)|`memoist`\* (0.16.2)|`memoized`\* (1.0.2)|`memoizer`\* (1.0.3)|
87
86
  |--|--|--|--|--|--|
88
- |`()` (none)|**baseline**|14.69x slower|2.59x slower|1.15x slower|2.91x slower|
89
- |`(a, b)`|**baseline**|1.93x slower|2.20x slower|1.79x slower|1.96x slower|
90
- |`(a:, b:)`|**baseline**|3.01x slower|2.41x slower|2.18x slower|2.28x slower|
91
- |`(a, b:)`|**baseline**|1.49x slower|1.75x slower|1.51x slower|1.60x slower|
92
- |`(a, *args)`|**baseline**|1.92x slower|2.23x slower|1.94x slower|1.98x slower|
93
- |`(a:, **kwargs)`|**baseline**|3.08x slower|2.48x slower|2.17x slower|2.28x slower|
94
- |`(a, *args, b:, **kwargs)`|**baseline**|1.55x slower|1.73x slower|1.65x slower|1.67x slower|
87
+ |`()` (none)|**baseline**|13.17x slower|2.85x slower|1.30x slower|3.05x slower|
88
+ |`(a, b)`|**baseline**|1.93x slower|2.20x slower|1.97x slower|1.86x slower|
89
+ |`(a:, b:)`|**baseline**|3.05x slower|2.34x slower|2.27x slower|2.14x slower|
90
+ |`(a, b:)`|**baseline**|1.50x slower|1.63x slower|1.56x slower|1.48x slower|
91
+ |`(a, *args)`|**baseline**|1.91x slower|2.13x slower|1.90x slower|1.88x slower|
92
+ |`(a:, **kwargs)`|**baseline**|3.08x slower|2.37x slower|2.24x slower|2.09x slower|
93
+ |`(a, *args, b:, **kwargs)`|**baseline**|1.72x slower|1.61x slower|1.56x slower|1.67x slower|
95
94
 
96
95
  _\*Indicates a benchmark run on Ruby 2.7.2 because the gem raises errors in Ruby
97
96
  3.0.0 due to its incorrect handling of keyword arguments._
@@ -141,6 +140,25 @@ code examples in our YARD documentation. To run `doctest` locally:
141
140
  bundle exec yard doctest
142
141
  ```
143
142
 
143
+ ### A Note on Testing
144
+
145
+ When testing memoized *module* methods, note that some testing setups will
146
+ reuse the same instance (which `include`s/`extend`s/`prepend`s the module)
147
+ across tests, which can result in confusing test failures when this differs from
148
+ how you use the code in production.
149
+
150
+ For example, Rails view helpers are modules that are commonly tested with a
151
+ [shared `view` instance](https://github.com/rails/rails/blob/291a3d2ef29a3842d1156ada7526f4ee60dd2b59/actionview/lib/action_view/test_case.rb#L203-L214). Rails initializes a new view instance for each web request so any view helper
152
+ methods would only be memoized for the duration of that web request, but in
153
+ tests (such as when using
154
+ [`rspec-rails`'s `helper`](https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/example/helper_example_group.rb#L22-L27)),
155
+ the memoization may persist across tests. In this case, simply reset the
156
+ memoization between your tests with something like:
157
+
158
+ ```ruby
159
+ after(:each) { helper.reset_memo_wise }
160
+ ```
161
+
144
162
  ## Logo
145
163
 
146
164
  `MemoWise`'s logo was created by [Luci Cooke](https://www.lucicooke.com/). The
data/lib/memo_wise.rb CHANGED
@@ -39,10 +39,40 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
39
39
  # [Values](https://github.com/tcrayford/Values)
40
40
  # [gem](https://rubygems.org/gems/values).
41
41
  #
42
- def initialize(*)
43
- MemoWise.create_memo_wise_state!(self)
44
- super
45
- end
42
+ # To support syntax differences with keyword and positional arguments starting
43
+ # with ruby 2.7, we have to set up the initializer with some slightly
44
+ # different syntax for the different versions. This variance in syntax is not
45
+ # included in coverage reports since the branch chosen will never differ
46
+ # within a single ruby version. This means it is impossible for us to get
47
+ # 100% coverage of this line within a single CI run.
48
+ #
49
+ # See
50
+ # [this article](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/)
51
+ # for more information.
52
+ #
53
+ # :nocov:
54
+ all_args = RUBY_VERSION < "2.7" ? "*" : "..."
55
+ # :nocov:
56
+ class_eval <<-END_OF_METHOD, __FILE__, __LINE__ + 1
57
+ # On Ruby 2.7 or greater:
58
+ #
59
+ # def initialize(...)
60
+ # MemoWise.create_memo_wise_state!(self)
61
+ # super
62
+ # end
63
+ #
64
+ # On Ruby 2.6 or lower:
65
+ #
66
+ # def initialize(*)
67
+ # MemoWise.create_memo_wise_state!(self)
68
+ # super
69
+ # end
70
+
71
+ def initialize(#{all_args})
72
+ MemoWise.create_memo_wise_state!(self)
73
+ super
74
+ end
75
+ END_OF_METHOD
46
76
 
47
77
  # @private
48
78
  #
@@ -182,10 +212,7 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
182
212
  # @return [Object] the passed-in obj
183
213
  def self.create_memo_wise_state!(obj)
184
214
  unless obj.instance_variables.include?(:@_memo_wise)
185
- obj.instance_variable_set(
186
- :@_memo_wise,
187
- Hash.new { |h, k| h[k] = {} }
188
- )
215
+ obj.instance_variable_set(:@_memo_wise, {})
189
216
  end
190
217
 
191
218
  obj
@@ -269,7 +296,7 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
269
296
  if method.arity.zero?
270
297
  klass.module_eval <<-END_OF_METHOD, __FILE__, __LINE__ + 1
271
298
  # def foo
272
- # @_memo_wise.fetch(:foo}) do
299
+ # @_memo_wise.fetch(:foo) do
273
300
  # @_memo_wise[:foo] = _memo_wise_original_foo
274
301
  # end
275
302
  # end
@@ -303,14 +330,18 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
303
330
  # because Ruby always copies argument arrays when splatted.
304
331
  klass.module_eval <<-END_OF_METHOD, __FILE__, __LINE__ + 1
305
332
  # def foo(*args, **kwargs)
306
- # hash = @_memo_wise[:foo]
333
+ # hash = @_memo_wise.fetch(:foo) do
334
+ # @_memo_wise[:foo] = {}
335
+ # end
307
336
  # hash.fetch([args, kwargs].freeze) do
308
337
  # hash[[args, kwargs].freeze] = _memo_wise_original_foo(*args, **kwargs)
309
338
  # end
310
339
  # end
311
340
 
312
341
  def #{method_name}#{args_str}
313
- hash = @_memo_wise[:#{method_name}]
342
+ hash = @_memo_wise.fetch(:#{method_name}) do
343
+ @_memo_wise[:#{method_name}] = {}
344
+ end
314
345
  hash.fetch(#{fetch_key}) do
315
346
  hash[#{fetch_key}] = #{original_memo_wised_name}#{args_str}
316
347
  end
@@ -373,7 +404,7 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
373
404
  # valid for the given method.
374
405
  #
375
406
  # @param method_name [Symbol]
376
- # Name of a method previously setup with `#memo_wise`.
407
+ # Name of a method previously set up with `#memo_wise`.
377
408
  #
378
409
  # @param args [Array]
379
410
  # (Optional) If the method takes positional args, these are the values of
@@ -424,7 +455,10 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
424
455
  if method(method_name).arity.zero?
425
456
  @_memo_wise[method_name] = yield
426
457
  else
427
- @_memo_wise[method_name][fetch_key(method_name, *args, **kwargs)] = yield
458
+ hash = @_memo_wise.fetch(method_name) do
459
+ @_memo_wise[method_name] = {}
460
+ end
461
+ hash[fetch_key(method_name, *args, **kwargs)] = yield
428
462
  end
429
463
  end
430
464
 
@@ -449,7 +483,7 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
449
483
  # - Resets all memoized results of calling *all methods*.
450
484
  #
451
485
  # @param method_name [Symbol, nil]
452
- # (Optional) Name of a method previously setup with `#memo_wise`. If not
486
+ # (Optional) Name of a method previously set up with `#memo_wise`. If not
453
487
  # given, will reset *all* memoized results for *all* methods.
454
488
  #
455
489
  # @param args [Array]
@@ -521,7 +555,7 @@ module MemoWise # rubocop:disable Metrics/ModuleLength
521
555
  if args.empty? && kwargs.empty?
522
556
  @_memo_wise.delete(method_name)
523
557
  else
524
- @_memo_wise[method_name].delete(fetch_key(method_name, *args, **kwargs))
558
+ @_memo_wise[method_name]&.delete(fetch_key(method_name, *args, **kwargs))
525
559
  end
526
560
  end
527
561
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MemoWise
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memo_wise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Panorama Education
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2021-02-11 00:00:00.000000000 Z
14
+ date: 2021-04-30 00:00:00.000000000 Z
15
15
  dependencies: []
16
16
  description:
17
17
  email:
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  requirements: []
72
- rubygems_version: 3.2.3
72
+ rubygems_version: 3.1.4
73
73
  signing_key:
74
74
  specification_version: 4
75
75
  summary: The wise choice for Ruby memoization