memo_wise 0.3.0 → 0.4.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: 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