memo_wise 0.4.0 → 1.3.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: 634bee32d07ccd97dc7670c67521a4091ebc0df0deae1479cc054aaa7a246e6c
4
- data.tar.gz: 59523369b3373389481388962039486b4f0f5cf002d24091ad2244048f100bbc
3
+ metadata.gz: 4ff51bfee4fcc634d50588005e79c4a74702fd0d8deb8ab23dd917134cedf42f
4
+ data.tar.gz: 1cb14df9bcef27a3442b48cf1aee370835bbf9070b1f44bb3d8ec8266158bdab
5
5
  SHA512:
6
- metadata.gz: 3896bf8b001ad52afda6b14c5170d902d85a88c9faaa60d6c9097a01045e0ef90a379c130834b2a2fcbec356b51346f975fc96493dcb3d3c04943200eb7274a3
7
- data.tar.gz: 72718456d986ed21d593a6a99117ee74a0a385f94ba0b1a00fadb38cbad977202cea5d8e32325bdaa2e5eb273808a52ca9d62997cd927a6a98ffd31174bdcc6f
6
+ metadata.gz: 36a3f525833af24f076568ed9bdb43a06e90441788f2f8e8d26c0303ec7e0697a56c8401a3e24c06300cb3f2df199768ae00d8cf95cc90808fb4d9940ab9a931
7
+ data.tar.gz: f60569c63d693272856137c179b826a598c63a5762fedd76faf630b26fd3301d6ca37756f99cb7940501d5bcfd4c375094176a617610c35fc8788b67580159ce
data/.dokaz ADDED
@@ -0,0 +1,2 @@
1
+ README.md
2
+ --require ./spec/dokaz_helpers.rb
@@ -1,4 +1,4 @@
1
1
  **Before merging:**
2
2
 
3
- - [ ] Copy the latest benchmark results into the `README.md` and update this PR
4
- - [ ] If this change merits an update to `CHANGELOG.md`, add an entry following Keep a Changelog [guidelines](https://keepachangelog.com/en/1.0.0/) with [semantic versioning](https://semver.org/)
3
+ - [ ] Copy the table printed at the end of the latest benchmark results into the `README.md` and update this PR
4
+ - [ ] If this change merits an update to `CHANGELOG.md`, add an entry following Keep a Changelog [guidelines](https://keepachangelog.com/en/1.0.0/) with [semantic versioning](https://semver.org/)
@@ -0,0 +1,20 @@
1
+ version: 2
2
+
3
+ updates:
4
+ # Maintain dependencies for Ruby's Bundler
5
+ - package-ecosystem: bundler
6
+ directory: "/"
7
+ schedule:
8
+ interval: daily
9
+
10
+ # Maintain dependencies for Ruby's Bundler (for Benchmarks!)
11
+ - package-ecosystem: bundler
12
+ directory: "/benchmarks"
13
+ schedule:
14
+ interval: daily
15
+
16
+ # Maintain dependencies for GitHub Actions
17
+ - package-ecosystem: "github-actions"
18
+ directory: "/"
19
+ schedule:
20
+ interval: "daily"
@@ -15,26 +15,34 @@ jobs:
15
15
  ruby: [jruby, 2.4, 2.5, 2.6, 2.7, 3.0]
16
16
  runs-on: ubuntu-latest
17
17
  steps:
18
- - uses: actions/checkout@main
18
+ - uses: actions/checkout@v2
19
+
20
+ # Conditionally configure bundler via environment variables as advised
21
+ # * https://github.com/ruby/setup-ruby#bundle-config
22
+ - name: Set bundler environment variables
23
+ run: |
24
+ echo "BUNDLE_WITHOUT=checks:docs" >> $GITHUB_ENV
25
+ if: matrix.ruby != 3.0
26
+
27
+ # Use 'bundler-cache: true' instead of actions/cache as advised:
28
+ # * https://github.com/actions/cache/blob/main/examples.md#ruby---bundler
19
29
  - uses: ruby/setup-ruby@v1
20
30
  with:
21
31
  ruby-version: ${{ matrix.ruby }}
22
- - uses: actions/cache@v1
23
- with:
24
- path: vendor/bundle
25
- key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
26
- restore-keys: |
27
- ${{ runner.os }}-gems-
28
- - run: bundle config path vendor/bundle
29
- - run: bundle config set without 'checks:docs'
30
- if: matrix.ruby != 3.0
31
- - run: bundle install --jobs 4 --retry 3
32
+ bundler-cache: true
33
+
32
34
  - run: bundle exec rspec
35
+
33
36
  - run: bundle exec rubocop
34
37
  if: matrix.ruby == 3.0
35
- - run: bundle exec yard doctest
36
- if: matrix.ruby == 3.0
38
+
37
39
  - run: |
40
+ bundle exec yard doctest
41
+ bundle exec dokaz
42
+ if: matrix.ruby == 3.0
43
+
44
+ - name: Run benchmarks on Ruby 2.7 or 3.0
45
+ run: |
38
46
  BUNDLE_GEMFILE=benchmarks/Gemfile bundle install --jobs 4 --retry 3
39
47
  BUNDLE_GEMFILE=benchmarks/Gemfile bundle exec ruby benchmarks/benchmarks.rb
40
48
  if: matrix.ruby == '2.7' || matrix.ruby == '3.0'
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  /.bundle/
2
2
  /benchmarks/.bundle/
3
+ /benchmarks/Gemfile.lock
3
4
  /.yardoc
4
5
  /_yardoc/
5
6
  /coverage/
data/.rubocop.yml CHANGED
@@ -1,2 +1,17 @@
1
1
  inherit_gem:
2
2
  panolint: rubocop.yml
3
+
4
+ Layout/LineLength:
5
+ Max: 120
6
+
7
+ Metrics/ModuleLength:
8
+ Enabled: false
9
+
10
+ Metrics/PerceivedComplexity:
11
+ Enabled: false
12
+
13
+ Style/DocumentDynamicEvalDefinition:
14
+ Enabled: false
15
+
16
+ Style/MultipleComparison:
17
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -6,7 +6,54 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
- (nothing yet!)
9
+
10
+ - Nothing yet!
11
+
12
+ ## [1.3.0] - 2021-11-22
13
+
14
+ - Fix thread-safety issue in concurrent calls to zero-arg method in unmemoized
15
+ state which resulted in a `nil` value being accidentally returned in one thread
16
+ - Fix bugs related to child classes inheriting from parent classes that use
17
+ `MemoWise`
18
+
19
+ ## [1.2.0] - 2021-11-10
20
+
21
+ ### Updated
22
+ - Improved performance of all methods by using an outer Array instead of a Hash
23
+ - Improved performance for multi-argument methods and simplify internal data
24
+ structures
25
+
26
+ ### Fixed
27
+ - Removed use of #hash due to potential of hash collisions
28
+ - Updated internal local variable names to avoid name collisions with method
29
+ arguments
30
+
31
+ ### Breaking Changes
32
+ - None
33
+
34
+ ## [1.1.0] - 2021-07-29
35
+ ### Updated
36
+ - Improved performance across the board by:
37
+ - removing `Hash#fetch`
38
+ - using `Array#hash`
39
+ - avoiding multi-layer hash lookups for multi-argument methods
40
+ - optimizing for truthy results
41
+ - Add `dry-core` to benchmarks in README
42
+
43
+ ### Fixed
44
+ - Fixed usage on module singleton classes
45
+ - Fixed usage on module which would be extended by other classes
46
+
47
+ ### Breaking Changes
48
+ - None
49
+
50
+ ## [1.0.0] - 2021-06-24
51
+ ### Added
52
+ - Support for `.preset_memo_wise` on class methods
53
+ - Support for `.reset_memo_wise` on class methods
54
+
55
+ ### Updated
56
+ - Improved performance for common cases by reducing array allocations
10
57
 
11
58
  ## [0.4.0] - 2021-04-30
12
59
  ### Added
@@ -54,7 +101,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
54
101
  - Panolint
55
102
  - Dependabot setup
56
103
 
57
- [Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v0.4.0...HEAD
104
+ [Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v1.3.0...HEAD
105
+ [1.3.0]: https://github.com/panorama-ed/memo_wise/compare/v1.2.0...v1.3.0
106
+ [1.2.0]: https://github.com/panorama-ed/memo_wise/compare/v1.1.0...v1.2.0
107
+ [1.1.0]: https://github.com/panorama-ed/memo_wise/compare/v1.0.0...v1.1.0
108
+ [1.0.0]: https://github.com/panorama-ed/memo_wise/compare/v0.4.0...v1.0.0
58
109
  [0.4.0]: https://github.com/panorama-ed/memo_wise/compare/v0.3.0...v0.4.0
59
110
  [0.3.0]: https://github.com/panorama-ed/memo_wise/compare/v0.2.0...v0.3.0
60
111
  [0.2.0]: https://github.com/panorama-ed/memo_wise/compare/v0.1.2...v0.2.0
data/Gemfile CHANGED
@@ -19,6 +19,7 @@ end
19
19
 
20
20
  # Excluded from CI except on latest MRI Ruby, to reduce compatibility burden
21
21
  group :docs do
22
+ gem "dokaz"
22
23
  gem "redcarpet", "~> 3.5"
23
24
  gem "yard", "~> 0.9"
24
25
  gem "yard-doctest", "~> 0.1"
data/Gemfile.lock CHANGED
@@ -1,6 +1,6 @@
1
1
  GIT
2
2
  remote: https://github.com/panorama-ed/panolint.git
3
- revision: e2f76aa2482f02e68826eb8643a41998a09fc7ab
3
+ revision: c709ebcc5fd9593db959df4a92c12cae1d1fb9af
4
4
  branch: main
5
5
  specs:
6
6
  panolint (0.1.3)
@@ -14,35 +14,41 @@ GIT
14
14
  PATH
15
15
  remote: .
16
16
  specs:
17
- memo_wise (0.4.0)
17
+ memo_wise (1.3.0)
18
18
 
19
19
  GEM
20
20
  remote: https://rubygems.org/
21
21
  specs:
22
- activesupport (5.2.5)
22
+ activesupport (5.2.6)
23
23
  concurrent-ruby (~> 1.0, >= 1.0.2)
24
24
  i18n (>= 0.7, < 2)
25
25
  minitest (~> 5.1)
26
26
  tzinfo (~> 1.1)
27
+ ansi (1.5.0)
27
28
  ast (2.4.2)
28
- brakeman (5.0.0)
29
- codecov (0.5.2)
29
+ brakeman (5.1.1)
30
+ codecov (0.6.0)
30
31
  simplecov (>= 0.15, < 0.22)
31
- concurrent-ruby (1.1.8)
32
+ concurrent-ruby (1.1.9)
32
33
  diff-lcs (1.4.4)
33
34
  docile (1.3.5)
35
+ dokaz (0.0.4)
36
+ ansi
37
+ rouge
38
+ slop (~> 3)
34
39
  i18n (1.8.10)
35
40
  concurrent-ruby (~> 1.0)
36
41
  minitest (5.14.4)
37
42
  parallel (1.20.1)
38
- parser (3.0.1.0)
43
+ parser (3.0.2.0)
39
44
  ast (~> 2.4.1)
40
45
  rack (2.2.3)
41
46
  rainbow (3.0.0)
42
- rake (13.0.3)
47
+ rake (13.0.6)
43
48
  redcarpet (3.5.1)
44
49
  regexp_parser (2.1.1)
45
50
  rexml (3.2.5)
51
+ rouge (3.26.0)
46
52
  rspec (3.10.0)
47
53
  rspec-core (~> 3.10.0)
48
54
  rspec-expectations (~> 3.10.0)
@@ -84,6 +90,7 @@ GEM
84
90
  docile (~> 1.1)
85
91
  simplecov-html (~> 0.11)
86
92
  simplecov-html (0.12.3)
93
+ slop (3.6.0)
87
94
  thread_safe (0.3.6)
88
95
  tzinfo (1.2.9)
89
96
  thread_safe (~> 0.1)
@@ -99,6 +106,7 @@ PLATFORMS
99
106
 
100
107
  DEPENDENCIES
101
108
  codecov
109
+ dokaz
102
110
  memo_wise!
103
111
  panolint!
104
112
  rake
@@ -109,4 +117,4 @@ DEPENDENCIES
109
117
  yard-doctest (~> 0.1)
110
118
 
111
119
  BUNDLED WITH
112
- 2.2.3
120
+ 2.2.28
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2020 Panorama Education
3
+ Copyright (c) 2020-2021 Panorama Education
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -20,6 +20,7 @@
20
20
  * Support for [resetting](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#reset_memo_wise-instance_method) and [presetting](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#preset_memo_wise-instance_method) memoized values
21
21
  * Support for memoization on frozen objects
22
22
  * Support for memoization of class and module methods
23
+ * Support for inheritance of memoized class and instance methods
23
24
  * Full [documentation](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise) and [test coverage](https://codecov.io/gh/panorama-ed/memo_wise)!
24
25
 
25
26
  ## Installation
@@ -50,11 +51,21 @@ methods:
50
51
  ```ruby
51
52
  class Example
52
53
  prepend MemoWise
54
+
53
55
  def slow_value(x)
54
56
  sleep x
55
57
  x
56
58
  end
57
59
  memo_wise :slow_value
60
+
61
+ private
62
+
63
+ # maintains privacy of the memoized method
64
+ def private_slow_method(x)
65
+ sleep x
66
+ x
67
+ end
68
+ memo_wise :private_slow_method
58
69
  end
59
70
 
60
71
  ex = Example.new
@@ -71,29 +82,68 @@ ex.slow_value(3) # => 4 # Returns immediately because the result is memoized
71
82
  ex.reset_memo_wise # Resets all memoized results for all methods on ex
72
83
  ```
73
84
 
74
- Methods which take implicit or explicit block arguments cannot be memoized.
85
+ The same three methods are exposed for class methods as well:
75
86
 
76
- For more usage details, see our detailed [documentation](#documentation).
87
+ ```ruby
88
+ class Example
89
+ prepend MemoWise
77
90
 
78
- ## Benchmarks
91
+ def self.class_slow_value(x)
92
+ sleep x
93
+ x
94
+ end
95
+ memo_wise self: :class_slow_value
96
+ end
97
+
98
+ Example.class_slow_value(2) # => 2 # Sleeps for 2 seconds before returning
99
+ Example.class_slow_value(2) # => 2 # Returns immediately because the result is memoized
100
+
101
+ Example.reset_memo_wise(:class_slow_value) # Resets all memoized results for class_slow_value
79
102
 
80
- Benchmarks measure memoized value retrieval time using
81
- [`benchmark-ips`](https://github.com/evanphx/benchmark-ips). All benchmarks are
82
- run on Ruby 3.0.0, except as indicated below for specific gems. Benchmarks are
83
- run in GitHub Actions and updated in every PR that changes code.
103
+ Example.preset_memo_wise(:class_slow_value, 3) { 4 } # Store 4 as the result for slow_value(3)
104
+ Example.class_slow_value(3) # => 4 # Returns immediately because the result is memoized
105
+ Example.reset_memo_wise # Resets all memoized results for all methods on class
106
+ ```
107
+
108
+ **NOTE:** Methods which take implicit or explicit block arguments cannot be
109
+ memoized.
84
110
 
85
- |Method arguments|**`memo_wise` (0.1.0)**|`memery` (1.3.0)|`memoist`\* (0.16.2)|`memoized`\* (1.0.2)|`memoizer`\* (1.0.3)|
86
- |--|--|--|--|--|--|
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|
111
+ For more usage details, see our detailed [documentation](#documentation).
112
+
113
+ ## Benchmarks
94
114
 
95
- _\*Indicates a benchmark run on Ruby 2.7.2 because the gem raises errors in Ruby
96
- 3.0.0 due to its incorrect handling of keyword arguments._
115
+ Benchmarks are run in GitHub Actions, and the tables below are updated with every code change. **Values >1.00x represent how much _slower_ each gem’s memoized value retrieval is than the latest commit of `MemoWise`**, according to [`benchmark-ips`](https://github.com/evanphx/benchmark-ips) (2.9.1).
116
+
117
+ Results using Ruby 3.0.2:
118
+
119
+ |Method arguments|`Dry::Core`\* (0.7.1)|`Memery` (1.4.0)|
120
+ |--|--|--|
121
+ |`()` (none)|1.42x|17.84x|
122
+ |`(a)`|2.48x|11.48x|
123
+ |`(a, b)`|0.46x|2.05x|
124
+ |`(a:)`|2.18x|20.50x|
125
+ |`(a:, b:)`|0.48x|4.22x|
126
+ |`(a, b:)`|0.48x|4.08x|
127
+ |`(a, *args)`|0.90x|1.97x|
128
+ |`(a:, **kwargs)`|0.80x|3.02x|
129
+ |`(a, *args, b:, **kwargs)`|0.61x|1.54x|
130
+
131
+ \* `Dry::Core`
132
+ [may cause incorrect behavior caused by hash collisions](https://github.com/dry-rb/dry-core/issues/63).
133
+
134
+ Results using Ruby 2.7.4 (because these gems raise errors in Ruby 3.x):
135
+
136
+ |Method arguments|`DDMemoize` (1.0.0)|`Memoist` (0.16.2)|`Memoized` (1.0.2)|`Memoizer` (1.0.3)|
137
+ |--|--|--|--|--|
138
+ |`()` (none)|33.90x|3.44x|1.56x|4.03x|
139
+ |`(a)`|24.56x|17.02x|12.94x|14.91x|
140
+ |`(a, b)`|3.14x|2.35x|1.84x|2.03x|
141
+ |`(a:)`|34.42x|28.14x|23.83x|25.26x|
142
+ |`(a:, b:)`|5.13x|4.28x|3.77x|3.97x|
143
+ |`(a, b:)`|4.83x|4.08x|3.50x|3.66x|
144
+ |`(a, *args)`|3.03x|2.25x|1.95x|1.95x|
145
+ |`(a:, **kwargs)`|2.90x|2.44x|2.14x|2.24x|
146
+ |`(a, *args, b:, **kwargs)`|2.05x|1.77x|1.65x|1.64x|
97
147
 
98
148
  You can run benchmarks yourself with:
99
149
 
@@ -106,18 +156,6 @@ $ bundle exec ruby benchmarks.rb
106
156
  If your results differ from what's posted here,
107
157
  [let us know](https://github.com/panorama-ed/memo_wise/issues/new)!
108
158
 
109
- ## Development
110
-
111
- After checking out the repo, run `bin/setup` to install dependencies. Then, run
112
- `rake spec` to run the tests. You can also run `bin/console` for an interactive
113
- prompt that will allow you to experiment.
114
-
115
- To install this gem onto your local machine, run `bundle exec rake install`. To
116
- release a new version, update the version number in `version.rb`, and then run
117
- `bundle exec rake release`, which will create a git tag for the version, push
118
- git commits and tags, and push the `.gem` file to
119
- [rubygems.org](https://rubygems.org).
120
-
121
159
  ## Documentation
122
160
 
123
161
  ### Documentation is Automatically Generated
@@ -140,6 +178,14 @@ code examples in our YARD documentation. To run `doctest` locally:
140
178
  bundle exec yard doctest
141
179
  ```
142
180
 
181
+ We use [dokaz](https://github.com/zverok/dokaz) to test all code examples in
182
+ this README.md file, and all other non-code documentation. To run `dokaz`
183
+ locally:
184
+
185
+ ```bash
186
+ bundle exec dokaz
187
+ ```
188
+
143
189
  ### A Note on Testing
144
190
 
145
191
  When testing memoized *module* methods, note that some testing setups will
@@ -159,6 +205,20 @@ memoization between your tests with something like:
159
205
  after(:each) { helper.reset_memo_wise }
160
206
  ```
161
207
 
208
+ ## Further Reading
209
+
210
+ We presented at RubyConf 2021:
211
+
212
+ - Achieving Fast Method Metaprogramming: Lessons from `MemoWise`
213
+ ([slides](https://docs.google.com/presentation/d/1XgERQ0YHplwJKM3wNQwZn584d_9szYZp2WsDEXoY_7Y/edit?usp=sharing) /
214
+ [benchmarks](https://gist.github.com/JacobEvelyn/17b7b000e50151c30eaea928f1fcdc11))
215
+
216
+ And we've written more about `MemoWise` in a series of blog posts:
217
+
218
+ - [Introducing: MemoWise](https://medium.com/building-panorama-education/introducing-memowise-51a5f0523489)
219
+ - [Optimizing MemoWise Performance](https://ja.cob.land/optimizing-memowise-performance)
220
+ - [Esosteric Ruby in MemoWise](https://jemma.dev/blog/esoteric-ruby-in-memowise)
221
+
162
222
  ## Logo
163
223
 
164
224
  `MemoWise`'s logo was created by [Luci Cooke](https://www.lucicooke.com/). The
@@ -180,7 +240,7 @@ To make a new release of `MemoWise` to
180
240
  dependencies (e.g. `rake`) as follows:
181
241
 
182
242
  ```shell
183
- bundle config set --local with 'release'
243
+ bundle config --local with 'release'
184
244
  bundle install
185
245
  ```
186
246
 
@@ -188,6 +248,7 @@ Then carry out these steps:
188
248
 
189
249
  1. Update `CHANGELOG.md`:
190
250
  - Add an entry for the upcoming version _x.y.z_
251
+ - Add a link for this version's comparison to the bottom of `CHANGELOG.md`
191
252
  - Move content from _Unreleased_ to the upcoming version _x.y.z_
192
253
  - Commit with title `Update CHANGELOG.md for x.y.z`
193
254
 
data/benchmarks/Gemfile CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- ruby ">= 2.7.2"
5
+ ruby ">= 2.7.4"
6
6
 
7
- gem "benchmark-ips", "2.8.4"
7
+ gem "benchmark-ips", "2.9.1"
8
8
 
9
9
  if RUBY_VERSION > "3"
10
- gem "memery", "1.3.0"
10
+ gem "dry-core", "0.7.1"
11
+ gem "memery", "1.4.0"
11
12
  else
13
+ gem "ddmemoize", "1.0.0"
12
14
  gem "memoist", "0.16.2"
13
15
  gem "memoized", "1.0.2"
14
16
  gem "memoizer", "1.0.3"