michie 0.1.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 +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/Rakefile +6 -0
- data/lib/michie/version.rb +3 -0
- data/lib/michie.rb +80 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1f6ef1a836a13383202e735dd16366a9e171183bf8405f25bfb812d68560155b
|
4
|
+
data.tar.gz: c343ea5eb201adaea073ac2406a87d0a570b2adc7c93d93c8ace4473ae9e118c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0162b216d0e66f5ea0ef7d4925f51063bc7cd73b48e02444fa9bdd42e9a29db045d7dc5a84c7a0c4a8dd4495fcbd03c471eed860d0628778f83d6afbf6011e95
|
7
|
+
data.tar.gz: 79eeae6b562a5973534ce7d80aaa1f2385e72311c67a87ebe1abc30ef201b32a08864296dc8060f6de1d451f7b2c263a24e0768aa96784f9ee4d8197033cc51b
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
michie (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (11.1.3)
|
10
|
+
coderay (1.1.3)
|
11
|
+
diff-lcs (1.5.0)
|
12
|
+
method_source (1.0.0)
|
13
|
+
pry (0.13.1)
|
14
|
+
coderay (~> 1.1)
|
15
|
+
method_source (~> 1.0)
|
16
|
+
pry-byebug (3.9.0)
|
17
|
+
byebug (~> 11.0)
|
18
|
+
pry (~> 0.13.0)
|
19
|
+
rake (10.5.0)
|
20
|
+
rspec (3.11.0)
|
21
|
+
rspec-core (~> 3.11.0)
|
22
|
+
rspec-expectations (~> 3.11.0)
|
23
|
+
rspec-mocks (~> 3.11.0)
|
24
|
+
rspec-core (3.11.0)
|
25
|
+
rspec-support (~> 3.11.0)
|
26
|
+
rspec-expectations (3.11.0)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.11.0)
|
29
|
+
rspec-mocks (3.11.1)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.11.0)
|
32
|
+
rspec-support (3.11.0)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
michie!
|
39
|
+
pry-byebug
|
40
|
+
rake (~> 10.0)
|
41
|
+
rspec (~> 3.0)
|
42
|
+
|
43
|
+
BUNDLED WITH
|
44
|
+
2.0.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Chris Salzberg
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Michie
|
2
|
+
|
3
|
+
Michie (pronounced /ˈmɪki/, like “Mickey”) memoizes methods defined in a block.
|
4
|
+
Unlike other meomization libraries, Michie encapsulates its memoization in
|
5
|
+
a single module which it prepends over the original method.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Simply extend a class with `Michie` and define methods in a block passed to the
|
10
|
+
`memoize` method:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
class BillingApi
|
14
|
+
extend Michie
|
15
|
+
|
16
|
+
memoize do
|
17
|
+
def fetch_aggregate_data
|
18
|
+
# returns all data from remote server
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
api = BillingApi.new
|
24
|
+
api.fetch_aggregate_data
|
25
|
+
#=> (calls original #fetch_aggregate_data method, fetching and returning data from server)
|
26
|
+
|
27
|
+
api.fetch_aggregate_data
|
28
|
+
#=> returns value memoized by Michie
|
29
|
+
```
|
30
|
+
|
31
|
+
Unlike other memoization libraries which use `alias_method` to reference the
|
32
|
+
original method, leaving artifacts in the including class, memoization in
|
33
|
+
Michie is encapsulated in a module.
|
34
|
+
|
35
|
+
This module is dynamically created and prepended by `Michie#memoize`:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
BillingApi.ancestors
|
39
|
+
#=> [<#Michie::Memoizer (methods: fetch_aggregate_data)>, BillingApi, Object, PP::ObjectMixin, Kernel, BasicObject]
|
40
|
+
|
41
|
+
memoizer = BillingApi.ancestors[0]
|
42
|
+
memoizer.instance_methods(false)
|
43
|
+
#=> [:fetch_aggregate_data]
|
44
|
+
```
|
45
|
+
|
46
|
+
The memoizer method uses an instance variable to memoize the value returned
|
47
|
+
from the original method, and calls the original method with `super`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
def fetch_aggregate_data
|
51
|
+
return @__michie_fetch_aggregate_data if defined?(@__michie_fetch_aggregate_data)
|
52
|
+
|
53
|
+
result = super
|
54
|
+
@__michie_fetch_aggregate_data = result
|
55
|
+
result
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
By default, Michie prepends `__michie_` to the method name to generate the
|
60
|
+
instance variable name. This can be changed by passing a `prefix` option to
|
61
|
+
`memoize` (see specs for details).
|
62
|
+
|
63
|
+
Since Michie uses the presence of an instance variable to signal memoization,
|
64
|
+
`false` and `nil` values can be memoized (unlike techniques which use `||=`).
|
65
|
+
Michie also respects method visibility, so you can use it to memoize your
|
66
|
+
private and protected methods.
|
67
|
+
|
68
|
+
Passing `eager: true` to `memoize` will eagerly call all methods defined in the
|
69
|
+
`memoize` block as soon as an instance is created:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
class BillingApi
|
73
|
+
extend Michie
|
74
|
+
|
75
|
+
memoize(eager: true) do
|
76
|
+
def fetch_aggregate_data
|
77
|
+
# returns all data from remote server
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
api = BillingApi.new
|
83
|
+
# `fetch_agregate_data` has already been memoized
|
84
|
+
```
|
85
|
+
|
86
|
+
Beware that Michie does not memoize methods that take arguments; this is
|
87
|
+
a design decision to make the code as simple and readable as possible.
|
88
|
+
|
89
|
+
## Reference
|
90
|
+
|
91
|
+
The gem is named after [Donald
|
92
|
+
Michie](https://en.wikipedia.org/wiki/Donald_Michie) (1923-2007), the British AI
|
93
|
+
researcher who invented memoization. Not to be confused with a [Rust memoization
|
94
|
+
library](https://docs.rs/michie/latest/michie/) with the same name.
|
95
|
+
|
96
|
+
## License
|
97
|
+
|
98
|
+
The gem is available as open source under the terms of the [MIT
|
99
|
+
License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/michie.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
require "michie/version"
|
3
|
+
|
4
|
+
module Michie
|
5
|
+
DEFAULT_IVAR_PREFIX = "__michie"
|
6
|
+
|
7
|
+
def memoize(eager: false, prefix: DEFAULT_IVAR_PREFIX, &block)
|
8
|
+
methods_before = instance_methods(false) + private_instance_methods(false)
|
9
|
+
block.call
|
10
|
+
methods_to_memoize = instance_methods(false) + private_instance_methods(false) - methods_before
|
11
|
+
|
12
|
+
memoizer = eager ? EagerMemoizer : Memoizer
|
13
|
+
|
14
|
+
prepend(memoizer.new(methods_to_memoize, prefix))
|
15
|
+
end
|
16
|
+
|
17
|
+
class Memoizer < Module
|
18
|
+
def initialize(methods_to_memoize, prefix)
|
19
|
+
methods_to_memoize.each do |method_name|
|
20
|
+
define_memoized_method(method_name, prefix)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_memoized_method(method_name, prefix)
|
25
|
+
ivar = Helpers.ivar_name(method_name, prefix)
|
26
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
27
|
+
def #{method_name}
|
28
|
+
return #{ivar} if defined?(#{ivar})
|
29
|
+
|
30
|
+
result = super
|
31
|
+
#{ivar} = result
|
32
|
+
result
|
33
|
+
end
|
34
|
+
EOM
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
"<##{self.class} (methods: #{instance_methods(false).join(", ")})>"
|
39
|
+
end
|
40
|
+
|
41
|
+
def prepended(klass)
|
42
|
+
memoized_methods = instance_methods(false)
|
43
|
+
(klass.protected_instance_methods(false) & memoized_methods).each do |method_name|
|
44
|
+
protected method_name
|
45
|
+
end
|
46
|
+
(klass.private_instance_methods(false) & memoized_methods).each do |method_name|
|
47
|
+
private method_name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class EagerMemoizer < Memoizer
|
53
|
+
def prepended(klass)
|
54
|
+
klass.include(Initializer)
|
55
|
+
end
|
56
|
+
|
57
|
+
module Initializer
|
58
|
+
def initialize(*, &block)
|
59
|
+
super
|
60
|
+
|
61
|
+
self.class.ancestors.grep(EagerMemoizer).each do |mod|
|
62
|
+
mod.instance_methods(false).each { |m| send(m) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module Helpers
|
69
|
+
def ivar_name(method_name, memoization_prefix)
|
70
|
+
string = "#{memoization_prefix}_#{method_name.to_s}"
|
71
|
+
|
72
|
+
if string.end_with?("?", "!")
|
73
|
+
string = string.dup
|
74
|
+
string.sub!(/\?\Z/, "_query") || string.sub!(/!\Z/, "_bang")
|
75
|
+
end
|
76
|
+
"@#{string}"
|
77
|
+
end
|
78
|
+
module_function :ivar_name
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: michie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Salzberg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
description: Michie allows you to memoize methods simply by defining them in a block.
|
42
|
+
email:
|
43
|
+
- chris@dejimata.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- Gemfile.lock
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- lib/michie.rb
|
54
|
+
- lib/michie/version.rb
|
55
|
+
homepage: https://github.com/shioyama/michie
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata:
|
59
|
+
homepage_uri: https://github.com/shioyama/michie
|
60
|
+
source_code_uri: https://github.com/shioyama/michie
|
61
|
+
changelog_uri: https://github.com/shioyama/blob/master/CHANGELOG.md
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.0.6
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Memoization done right.
|
81
|
+
test_files: []
|