lite-memoize 1.0.0 → 1.0.1

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: ce750474bdced13fe1092158da39d598a7da547a2e6c8a756f2c9fbb20a42937
4
- data.tar.gz: 57abad4fb181dadc17c22c0db81f8d7a1c02783bcdb25bd5906b98984285465d
3
+ metadata.gz: 2a2e51485362684ce1520b7eca9484106b5b83c9bc6336b2215dcffa6d56cfed
4
+ data.tar.gz: 43eb2cd5cb4330f12a503f3b66594b6a0c076bb1a0a991a0735bef1c11fd3cb6
5
5
  SHA512:
6
- metadata.gz: 758011939ac4b1f398c3f5a290a0c09be7adec6a9c9480b294e5d5a8872ceccb7bdd9e301b5ac98110a0bde6d0ec4cd1599fe0b54fe62c390c25d1f171e2a413
7
- data.tar.gz: 48cf101236f639ab626563f24213b47bb28799aaca4a157dd32611a2b2135ab54ca7040375ddb2ce82146c217636064cc492c0f40f853fba94757a9b4d3e186e
6
+ metadata.gz: b082b4804dbcedb965b006b0077375195316720d7397b5ed0140b5ae0de8847706d3503e8ca9f33f7c6ce064a0d79c2ff6f25a57a06e3a458ee2506e0f623436
7
+ data.tar.gz: 0dc03f302a930483a8ca51f114e80b6ed36506153bf1840f0ddcfb221404acd1f307dc77ae1dcf8f4a0a60970513d382954180cb70e49928d7727a41dcdcf48c
data/.rubocop.yml CHANGED
@@ -11,13 +11,19 @@ Layout/EmptyLinesAroundBlockBody:
11
11
  Exclude:
12
12
  - 'spec/**/**/*'
13
13
  Layout/EmptyLinesAroundClassBody:
14
- EnforcedStyle: empty_lines
14
+ EnforcedStyle: empty_lines_except_namespace
15
15
  Layout/EmptyLinesAroundModuleBody:
16
16
  EnforcedStyle: empty_lines_except_namespace
17
17
  Metrics/BlockLength:
18
18
  Exclude:
19
19
  - 'spec/**/**/*'
20
20
  - '*.gemspec'
21
+ Metrics/ModuleLength:
22
+ Enabled: false
23
+ Naming/FileName:
24
+ Enabled: false
25
+ Naming/MemoizedInstanceVariableName:
26
+ Enabled: false
21
27
  Naming/UncommunicativeMethodParamName:
22
28
  Enabled: false
23
29
  Style/Documentation:
data/.travis.yml CHANGED
@@ -1,20 +1,24 @@
1
- ---
2
1
  sudo: false
3
2
  language: ruby
4
3
  cache: bundler
5
4
  rvm:
6
- - 2.5
7
- - 2.6
8
- - ruby-head
5
+ - 2.5
6
+ - 2.6
7
+ - ruby-head
9
8
  matrix:
10
9
  fast_finish: true
11
10
  allow_failures:
12
- - rvm: ruby-head
11
+ - rvm: ruby-head
13
12
  before_install:
14
- - gem update --system
15
- - gem install bundler
13
+ - gem update --system
14
+ - gem install bundler
16
15
  install:
17
- - bundle install --jobs=3 --retry=3
16
+ - bundle install --jobs=3 --retry=3
18
17
  script:
19
- - bundle exec rspec
20
- # - bundle exec fasterer
18
+ - bundle exec rspec
19
+ - bundle exec rubocop
20
+ # - bundle exec fasterer
21
+ notifications:
22
+ email: false
23
+ slack:
24
+ secure: AqhUTKwgANtYDGb/t49g7Ibvm9B7zgIOQXJSTRC4NXjWSE8xCJ7uYEH6EwmuBPK4/QfK81TgzSC74cQKmzQojpZnTjtHtdNzUbjJ07EKvlaywdRiFNlqs7WpKVs4MdqUd9UpX8f9KAKqZqEsVPmCNuFqApJ66wJiz0ab4tXKqcC8XDhpbzXSPlwhPXXwBUx5gBQZy/sOSWVhSZCJvLO6RwuShzUbA4wHBtMYuAfu5nhHdkK7Jq48aMy973Nh3zvPRTR2dt+25/DPWoDlXtZ6p6JvuTkuzkqsFm13pMH3maxTwZOESFbGD/eFAmeOtJkZuDAfWhajfMh1ESjr4ONRC7UFCXEMZFmAbd24xS57zF5C2de5eM8UFN3O9SEHa4VU3fYFsDOJPFeJoadeyrmkmpnlO4u/EvFH1qrB2wbLurNB7daI30BvQ0UflKE6hBW7TyeswP0o3g5y4fwJORCmG7symRiy9BDkj3LFaANQ7zz1LRabZliiD5ZiNLMiXQ8GLtQ6um1F91ZtX60qj0Qo7z16UJRdWJTkmJmneneJYoP/F5fgKmVRactX+24dEojKUFV9EsF6qOf2W9q5PiOjJ7Ja8O6ubpx3XrDWbZpq1zgJ3DWw7J40TxWzPHAHJRdg1fQ90AmyUMpwJ/4HoRuGD1w3VLeoK9+d9Nrx23FJX4Q=
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.0.1] - 2019-07-18
10
+ ### Added
11
+ - Added alias memoization
12
+ - Added benchmarks
13
+ ### Changed
14
+ - Improved speed of key generation and lookup for instance
15
+ - Changed refresh arg to reload
16
+ - Renamed shared to mixin
17
+
9
18
  ## [1.0.0] - 2019-06-24
10
19
  ### Added
11
20
  - Initial project version
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lite-memoize (1.0.0)
4
+ lite-memoize (1.0.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -3,8 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/lite-memoize.svg)](http://badge.fury.io/rb/lite-memoize)
4
4
  [![Build Status](https://travis-ci.org/drexed/lite-memoize.svg?branch=master)](https://travis-ci.org/drexed/lite-memoize)
5
5
 
6
- Lite::Memoize provides an API for caching and memoizing locally expensive calculations including those with parameters.
7
- The flexible API allows you to memoize results using class or instance level cache.
6
+ Lite::Memoize provides an API for caching and memoizing locally expensive calculations including those with parameters. The flexible API allows you to memoize results using alias, class, instance, or mixin level cache.
8
7
 
9
8
  **NOTE:** If you are coming from `ActiveMemoize`, please read the [port](#port) section.
10
9
 
@@ -26,21 +25,63 @@ Or install it yourself as:
26
25
 
27
26
  ## Table of Contents
28
27
 
29
- * [Port](#port)
28
+ * [Alias](#alias)
30
29
  * [Klass](#klass)
31
30
  * [Instance](#instance)
31
+ * [Mixin](#mixin)
32
+ * [Benchmarks](#benchmarks)
33
+ * [Port](#port)
32
34
 
33
- ## Port
35
+ ## Alias
34
36
 
35
- `Lite::Memoize` is compatible port of [ActiveMemoize](https://github.com/drexed/active_memoize).
37
+ Alias level memoization is the fastest of the available methods, and provides a decent level
38
+ of control. It's the only one that can also be used to memoize class level methods. Method
39
+ arguments are automatically watched to cache dynamic values.
36
40
 
37
- Switching is as easy as renaming `ActiveMemoize::Klass` to `Lite::Memoize::Klass`
38
- and `ActiveMemoize::Instance` to `Lite::Memoize::Instance`.
41
+ You can only cache results without access to any information about the `store`.
42
+
43
+ ```ruby
44
+ class Movies
45
+ extend Lite::Memoize::Alias
46
+
47
+ class << self
48
+ extend Lite::Memoize::Alias
49
+
50
+ def random
51
+ HTTP.get('http://movies.com/any')
52
+ end
53
+
54
+ memoize :random
55
+ end
56
+
57
+ def random
58
+ HTTP.get('http://movies.com/any')
59
+ end
60
+
61
+ memoize :random
62
+
63
+ def search(title)
64
+ HTTP.get("http://movies.com?title=#{title}")
65
+ end
66
+
67
+ memoize :search, as: :find
68
+
69
+ end
70
+
71
+ # NOTE: To reload a method just append the reload argument key
72
+ Movies.random #=> Cached
73
+ Movies.random(reload: true) #=> New value
74
+
75
+ # NOTE: To flush the entire cache
76
+ Movies.clear_cache #=> New value
77
+ ```
39
78
 
40
79
  ## Klass
41
80
 
42
- Class level memoization is the quickest way to get up and running using your cache, but provides the least amount of flexibility.
43
- You can only cache results without access to any information about your cache.
81
+ Class level memoization is the quickest way to get up without polluting your class with new methods.
82
+ It's perfect for short lived or non-altering items like `activerecord` objects.
83
+
84
+ You can only cache results without access to any information about the `store`.
44
85
 
45
86
  ```ruby
46
87
  class Movies
@@ -63,7 +104,12 @@ end
63
104
 
64
105
  ## Instance
65
106
 
66
- Instance level memoization is a more involved way to setup your cache, but provides the most amount of flexibility.
107
+ Instance level memoization is the slowest of the available methods, but it provides
108
+ the most amount of flexibility and control. It's very useful for creating services or things
109
+ where control is paramount like clearing the cache or dumping it to JSON. Please read the spec
110
+ suite to see all available actions. Method arguments are automatically watched to cache dynamic
111
+ values.
112
+
67
113
  You can access almost all methods in the `instance.rb` file.
68
114
 
69
115
  ```ruby
@@ -73,16 +119,18 @@ class Movies
73
119
  @cache ||= Lite::Memoize::Instance.new
74
120
  end
75
121
 
122
+ # NOTE: This method gets all relevent info like name and args automatically
76
123
  def all
77
124
  cache.memoize { HTTP.get("http://movies.com/all") }
78
125
  end
79
126
 
80
- def random
81
- cache['random'] ||= HTTP.get('http://movies.com/any')
127
+ def random(type)
128
+ cache['random'] ||= HTTP.get("http://movies.com/any?type=#{type}")
82
129
  end
83
130
 
131
+ # NOTE: Arguments in the memoize method are optional
84
132
  def search(title)
85
- cache.memoize(as: :find, refresh: !cache.empty?) do
133
+ cache.memoize(as: :find, args: [title], reload: !cache.empty?) do
86
134
  HTTP.get("http://movies.com?title=#{title}")
87
135
  end
88
136
  end
@@ -90,6 +138,44 @@ class Movies
90
138
  end
91
139
  ```
92
140
 
141
+ ## Mixin
142
+
143
+ Mixin level memoization is the leanest of the available methods, and provides a decent level
144
+ of control. Useful when you want to keep your class light weight.
145
+
146
+ You can access all methods to the `Hash` class.
147
+
148
+ ```ruby
149
+ class Movies
150
+ include Lite::Memoize::Mixin
151
+
152
+ def all
153
+ memoize(:all) { HTTP.get("http://movies.com/all") }
154
+ end
155
+
156
+ # NOTE: Arguments in the memoize method are optional with the exception of method name
157
+ def search(title)
158
+ memoize(:find, args: [title], reload: false) do
159
+ HTTP.get("http://movies.com?title=#{title}")
160
+ end
161
+ end
162
+
163
+ end
164
+ ```
165
+
166
+ ## Benchmarks
167
+
168
+ The classes ranked from fastest to slowest are `Alias`, `Mixin`, `Klass`, and `Instance`.
169
+
170
+ View all how it compares to other libs by running the [benchmarks](https://github.com/drexed/lite-statistics/tree/master/benchmarks).
171
+
172
+ ## Port
173
+
174
+ `Lite::Memoize` is a compatible port of [ActiveMemoize](https://github.com/drexed/active_memoize).
175
+
176
+ Switching is as easy as renaming `ActiveMemoize::Klass` to `Lite::Memoize::Klass`
177
+ and `ActiveMemoize::Instance` to `Lite::Memoize::Instance`.
178
+
93
179
  ## Development
94
180
 
95
181
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ %w[lib benchmarks].each { |name| $LOAD_PATH.unshift(name) }
4
+
5
+ require 'benchmark/ips'
6
+ require 'lite/memoize'
7
+
8
+ class LiteMemoizeAliasCache
9
+
10
+ extend Lite::Memoize::Alias
11
+
12
+ def randomize
13
+ rand(1..99)
14
+ end
15
+
16
+ memoize :randomize
17
+
18
+ end
19
+
20
+ class LiteMemoizeInstanceCache
21
+
22
+ def cache
23
+ @cache ||= Lite::Memoize::Instance.new
24
+ end
25
+
26
+ def randomize
27
+ cache.memoize { rand(1..99) }
28
+ end
29
+
30
+ end
31
+
32
+ class LiteMemoizeKlassCache
33
+
34
+ extend Lite::Memoize::Klass
35
+
36
+ def randomize
37
+ rand(1..99)
38
+ end
39
+
40
+ memoize :randomize
41
+
42
+ end
43
+
44
+ class LiteMemoizeMixinCache
45
+
46
+ include Lite::Memoize::Mixin
47
+
48
+ def randomize
49
+ memoize(:randomize) { rand(1..99) }
50
+ end
51
+
52
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memery'
4
+ require_relative 'base'
5
+
6
+ class MemeryCache
7
+
8
+ include Memery
9
+
10
+ memoize def randomize
11
+ rand(1..99)
12
+ end
13
+
14
+ end
15
+
16
+ klass_a = LiteMemoizeInstanceCache.new
17
+ klass_b = LiteMemoizeKlassCache.new
18
+ klass_c = LiteMemoizeMixinCache.new
19
+ klass_d = LiteMemoizeAliasCache.new
20
+ klass_z = MemeryCache.new
21
+
22
+ Benchmark.ips do |x|
23
+ x.report('LM.instance') do
24
+ klass_a.randomize
25
+ end
26
+
27
+ x.report('LM.klass') do
28
+ klass_b.randomize
29
+ end
30
+
31
+ x.report('LM.mixin') do
32
+ klass_c.randomize
33
+ end
34
+
35
+ x.report('LM.alias') do
36
+ klass_d.randomize
37
+ end
38
+
39
+ x.report('Memery') do
40
+ klass_z.randomize
41
+ end
42
+
43
+ x.compare!
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memist'
4
+ require_relative 'base'
5
+
6
+ class MemistCache
7
+
8
+ def randomize
9
+ rand(1..99)
10
+ end
11
+
12
+ memoize :randomize
13
+
14
+ end
15
+
16
+ klass_a = LiteMemoizeInstanceCache.new
17
+ klass_b = LiteMemoizeKlassCache.new
18
+ klass_c = LiteMemoizeMixinCache.new
19
+ klass_d = LiteMemoizeAliasCache.new
20
+ klass_z = MemistCache.new
21
+
22
+ Benchmark.ips do |x|
23
+ x.report('LM.instance') do
24
+ klass_a.randomize
25
+ end
26
+
27
+ x.report('LM.klass') do
28
+ klass_b.randomize
29
+ end
30
+
31
+ x.report('LM.mixin') do
32
+ klass_c.randomize
33
+ end
34
+
35
+ x.report('LM.alias') do
36
+ klass_d.randomize
37
+ end
38
+
39
+ x.report('Memist') do
40
+ klass_z.randomize
41
+ end
42
+
43
+ x.compare!
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memo/it'
4
+ require_relative 'base'
5
+
6
+ class MemoitCache
7
+
8
+ def randomize
9
+ memo do
10
+ rand(1..99)
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ klass_a = LiteMemoizeInstanceCache.new
17
+ klass_b = LiteMemoizeKlassCache.new
18
+ klass_c = LiteMemoizeMixinCache.new
19
+ klass_d = LiteMemoizeAliasCache.new
20
+ klass_z = MemoitCache.new
21
+
22
+ Benchmark.ips do |x|
23
+ x.report('LM.instance') do
24
+ klass_a.randomize
25
+ end
26
+
27
+ x.report('LM.klass') do
28
+ klass_b.randomize
29
+ end
30
+
31
+ x.report('LM.mixin') do
32
+ klass_c.randomize
33
+ end
34
+
35
+ x.report('LM.alias') do
36
+ klass_d.randomize
37
+ end
38
+
39
+ x.report('Memoit') do
40
+ klass_z.randomize
41
+ end
42
+
43
+ x.compare!
44
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+ require_relative 'base'
5
+
6
+ class MemoistCache
7
+
8
+ extend Memoist
9
+
10
+ def randomize
11
+ rand(1..99)
12
+ end
13
+
14
+ memoize :randomize
15
+
16
+ end
17
+
18
+ klass_a = LiteMemoizeInstanceCache.new
19
+ klass_b = LiteMemoizeKlassCache.new
20
+ klass_c = LiteMemoizeMixinCache.new
21
+ klass_d = LiteMemoizeAliasCache.new
22
+ klass_z = MemoistCache.new
23
+
24
+ Benchmark.ips do |x|
25
+ x.report('LM.instance') do
26
+ klass_a.randomize
27
+ end
28
+
29
+ x.report('LM.klass') do
30
+ klass_b.randomize
31
+ end
32
+
33
+ x.report('LM.mixin') do
34
+ klass_c.randomize
35
+ end
36
+
37
+ x.report('LM.alias') do
38
+ klass_d.randomize
39
+ end
40
+
41
+ x.report('Memoist') do
42
+ klass_z.randomize
43
+ end
44
+
45
+ x.compare!
46
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist2'
4
+ require_relative 'base'
5
+
6
+ class Memoist2Cache
7
+
8
+ include Memoist2
9
+
10
+ def randomize
11
+ rand(1..99)
12
+ end
13
+
14
+ memoize :randomize
15
+
16
+ end
17
+
18
+ klass_a = LiteMemoizeInstanceCache.new
19
+ klass_b = LiteMemoizeKlassCache.new
20
+ klass_c = LiteMemoizeMixinCache.new
21
+ klass_d = LiteMemoizeAliasCache.new
22
+ klass_z = Memoist2Cache.new
23
+
24
+ Benchmark.ips do |x|
25
+ x.report('LM.instance') do
26
+ klass_a.randomize
27
+ end
28
+
29
+ x.report('LM.klass') do
30
+ klass_b.randomize
31
+ end
32
+
33
+ x.report('LM.mixin') do
34
+ klass_c.randomize
35
+ end
36
+
37
+ x.report('LM.alias') do
38
+ klass_d.randomize
39
+ end
40
+
41
+ x.report('Memoist2') do
42
+ klass_z.randomize
43
+ end
44
+
45
+ x.compare!
46
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoit'
4
+ require_relative 'base'
5
+
6
+ class MemoitCache
7
+
8
+ memoize def randomize
9
+ rand(1..99)
10
+ end
11
+
12
+ end
13
+
14
+ klass_a = LiteMemoizeInstanceCache.new
15
+ klass_b = LiteMemoizeKlassCache.new
16
+ klass_c = LiteMemoizeMixinCache.new
17
+ klass_d = LiteMemoizeAliasCache.new
18
+ klass_z = MemoitCache.new
19
+
20
+ Benchmark.ips do |x|
21
+ x.report('LM.instance') do
22
+ klass_a.randomize
23
+ end
24
+
25
+ x.report('LM.klass') do
26
+ klass_b.randomize
27
+ end
28
+
29
+ x.report('LM.mixin') do
30
+ klass_c.randomize
31
+ end
32
+
33
+ x.report('LM.alias') do
34
+ klass_d.randomize
35
+ end
36
+
37
+ x.report('Memoit') do
38
+ klass_z.randomize
39
+ end
40
+
41
+ x.compare!
42
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoizable'
4
+ require_relative 'base'
5
+
6
+ class MemoizableCache
7
+
8
+ include Memoizable
9
+
10
+ def randomize
11
+ rand(1..99)
12
+ end
13
+
14
+ memoize :randomize
15
+
16
+ end
17
+
18
+ klass_a = LiteMemoizeInstanceCache.new
19
+ klass_b = LiteMemoizeKlassCache.new
20
+ klass_c = LiteMemoizeMixinCache.new
21
+ klass_d = LiteMemoizeAliasCache.new
22
+ klass_z = MemoizableCache.new
23
+
24
+ Benchmark.ips do |x|
25
+ x.report('LM.instance') do
26
+ klass_a.randomize
27
+ end
28
+
29
+ x.report('LM.klass') do
30
+ klass_b.randomize
31
+ end
32
+
33
+ x.report('LM.mixin') do
34
+ klass_c.randomize
35
+ end
36
+
37
+ x.report('LM.alias') do
38
+ klass_d.randomize
39
+ end
40
+
41
+ x.report('Memoizable') do
42
+ klass_z.randomize
43
+ end
44
+
45
+ x.compare!
46
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoizer'
4
+ require_relative 'base'
5
+
6
+ class MemoizerCache
7
+
8
+ include Memoizer
9
+
10
+ def randomize
11
+ rand(1..99)
12
+ end
13
+
14
+ memoize :randomize
15
+
16
+ end
17
+
18
+ klass_a = LiteMemoizeInstanceCache.new
19
+ klass_b = LiteMemoizeKlassCache.new
20
+ klass_c = LiteMemoizeMixinCache.new
21
+ klass_d = LiteMemoizeAliasCache.new
22
+ klass_z = MemoizerCache.new
23
+
24
+ Benchmark.ips do |x|
25
+ x.report('LM.instance') do
26
+ klass_a.randomize
27
+ end
28
+
29
+ x.report('LM.klass') do
30
+ klass_b.randomize
31
+ end
32
+
33
+ x.report('LM.mixin') do
34
+ klass_c.randomize
35
+ end
36
+
37
+ x.report('LM.alias') do
38
+ klass_d.randomize
39
+ end
40
+
41
+ x.report('Memoizer') do
42
+ klass_z.randomize
43
+ end
44
+
45
+ x.compare!
46
+ end
data/lib/lite/memoize.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- %w[version shared klass instance].each do |name|
3
+ %w[version alias mixin instance klass].each do |name|
4
4
  require "lite/memoize/#{name}"
5
5
  end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Memoize
5
+ module Alias
6
+
7
+ MemoizedMethod = Struct.new(:memoized_method, :ivar, :arity)
8
+
9
+ module InstanceMethods
10
+
11
+ def memoized_structs(names)
12
+ ref_obj = self.class.respond_to?(:class_eval) ? singleton_class : self
13
+ structs = ref_obj.all_memoized_structs
14
+ return structs if names.empty?
15
+
16
+ structs.select { |s| names.include?(s.memoized_method) }
17
+ end
18
+
19
+ def memoize_all(*method_names)
20
+ memoized_structs(method_names).each do |struct|
21
+ if struct.arity.zero?
22
+ __send__(struct.memoized_method)
23
+ else
24
+ instance_variable_set(struct.ivar, {})
25
+ end
26
+ end
27
+ end
28
+
29
+ def clear_cache(*method_names)
30
+ memoized_structs(method_names).each do |struct|
31
+ next unless instance_variable_defined?(struct.ivar)
32
+
33
+ remove_instance_variable(struct.ivar)
34
+ end
35
+ end
36
+
37
+ alias flush_cache clear_cache
38
+
39
+ end
40
+
41
+ class << self
42
+
43
+ # rubocop:disable Lint/NestedMethodDefinition
44
+ def extended(extender)
45
+ Lite::Memoize::Alias.memoist_eval(extender) do
46
+ return if singleton_class.method_defined?(:memoized_methods)
47
+
48
+ def self.memoized_methods
49
+ @_memoized_methods ||= []
50
+ end
51
+ end
52
+ end
53
+ # rubocop:enable Lint/NestedMethodDefinition
54
+
55
+ def memoized_ivar_for(method_name, as = nil)
56
+ "@#{memoized_prefix(as)}_#{escape_punctuation(method_name)}"
57
+ end
58
+
59
+ def unmemoized_method_for(method_name, as = nil)
60
+ "#{unmemoized_prefix(as)}_#{method_name}".to_sym
61
+ end
62
+
63
+ def memoized_prefix(as = nil)
64
+ return '_memoized' unless as
65
+
66
+ "_memoized_#{as}"
67
+ end
68
+
69
+ def unmemoized_prefix(as = nil)
70
+ return '_unmemoized' unless as
71
+
72
+ "_unmemoized_#{as}"
73
+ end
74
+
75
+ def escape_punctuation(string)
76
+ string = string.is_a?(String) ? string.dup : string.to_s
77
+ return string unless string.end_with?('?', '!')
78
+
79
+ string.sub!(/\?\Z/, '_query') || string.sub!(/!\Z/, '_bang') || string
80
+ end
81
+
82
+ def memoist_eval(klass, *args, &block)
83
+ return klass.class_eval(*args, &block) if klass.respond_to?(:class_eval)
84
+
85
+ klass.singleton_class.class_eval(*args, &block)
86
+ end
87
+
88
+ end
89
+
90
+ def all_memoized_structs
91
+ @all_memoized_structs ||= begin
92
+ structs = memoized_methods.dup
93
+
94
+ ancestors.grep(Lite::Memoize::Alias).each do |ancestor|
95
+ ancestor.memoized_methods.each do |m|
96
+ next if structs.any? { |am| am.memoized_method == m.memoized_method }
97
+
98
+ structs << m
99
+ end
100
+ end
101
+
102
+ structs
103
+ end
104
+ end
105
+
106
+ def clear_structs
107
+ @all_memoized_structs = nil
108
+ end
109
+
110
+ # rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity
111
+ # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity
112
+ def memoize(*method_names)
113
+ as = method_names.pop[:as] if method_names.last.is_a?(Hash)
114
+
115
+ method_names.each do |method_name|
116
+ unmemoized_method = Lite::Memoize::Alias.unmemoized_method_for(method_name, as)
117
+ memoized_ivar = Lite::Memoize::Alias.memoized_ivar_for(method_name, as)
118
+
119
+ Lite::Memoize::Alias.memoist_eval(self) do
120
+ include InstanceMethods
121
+
122
+ # rubocop:disable Lint/NonLocalExitFromIterator
123
+ if method_defined?(unmemoized_method)
124
+ warn "Already memoized #{method_name}"
125
+ return
126
+ end
127
+ # rubocop:enable Lint/NonLocalExitFromIterator
128
+
129
+ alias_method unmemoized_method, method_name
130
+
131
+ mm = MemoizedMethod.new(method_name, memoized_ivar, instance_method(method_name).arity)
132
+ memoized_methods << mm
133
+
134
+ if mm.arity.zero?
135
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
136
+ def #{method_name}(reload: false)
137
+ skip_cache = reload || !instance_variable_defined?("#{memoized_ivar}")
138
+ set_cache = skip_cache && !frozen?
139
+
140
+ if skip_cache
141
+ value = #{unmemoized_method}
142
+ else
143
+ value = #{memoized_ivar}
144
+ end
145
+
146
+ if set_cache
147
+ #{memoized_ivar} = value
148
+ end
149
+
150
+ value
151
+ end
152
+ RUBY
153
+ else
154
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
155
+ def #{method_name}(*args, reload: false)
156
+ skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.key?(args))
157
+ set_cache = skip_cache && !frozen?
158
+
159
+ if skip_cache
160
+ value = #{unmemoized_method}(*args)
161
+ else
162
+ value = #{memoized_ivar}[args]
163
+ end
164
+
165
+ if set_cache
166
+ #{memoized_ivar} ||= {}
167
+ #{memoized_ivar}[args] = value
168
+ end
169
+
170
+ value
171
+ end
172
+ RUBY
173
+ end
174
+
175
+ if private_method_defined?(unmemoized_method)
176
+ private method_name
177
+ elsif protected_method_defined?(unmemoized_method)
178
+ protected method_name
179
+ end
180
+ end
181
+ end
182
+
183
+ method_names.size == 1 ? method_names.first : method_names
184
+ end
185
+ # rubocop:enable Metrics/MethodLength, Metrics/PerceivedComplexity
186
+ # rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity
187
+
188
+ end
189
+ end
190
+ end
@@ -4,59 +4,62 @@ module Lite
4
4
  module Memoize
5
5
  class Instance
6
6
 
7
- include Lite::Memoize::Shared
8
-
9
- CALLER_METHOD_REGEX ||= /`([^']*)'/.freeze
7
+ include Lite::Memoize::Mixin
10
8
 
11
9
  def initialize; end
12
10
 
13
11
  def [](key)
14
- cache[key]
12
+ store[key]
15
13
  end
16
14
 
17
15
  def []=(key, val)
18
- cache[key] = val
16
+ store[key] = val
19
17
  end
20
18
 
21
19
  def clear
22
- cache.clear
20
+ store.clear
23
21
  end
24
22
 
23
+ alias flush clear
24
+
25
25
  def delete(key)
26
- cache.delete(key)
26
+ store.delete(key)
27
27
  end
28
28
 
29
29
  # :nocov:
30
30
  def each
31
- cache.each { |key, val| yield(key, val) }
31
+ store.each { |key, val| yield(key, val) }
32
32
  end
33
33
  # :nocov:
34
34
 
35
35
  def empty?
36
- cache.empty?
36
+ store.empty?
37
37
  end
38
38
 
39
39
  alias blank? empty?
40
40
 
41
41
  def key?(key)
42
- cache.key?(key)
42
+ store.key?(key)
43
43
  end
44
44
 
45
45
  alias hit? key?
46
46
 
47
47
  def keys
48
- cache.keys
48
+ store.keys
49
49
  end
50
50
 
51
51
  def merge!(hash)
52
- cache.merge!(hash)
52
+ store.merge!(hash)
53
53
  end
54
54
 
55
- def memoize(as: nil, refresh: false, &block)
56
- key = key(as || caller_method, caller_locals(block))
57
- return cache[key] if !refresh && cache.key?(key)
55
+ def memoize(as: nil, args: nil, reload: false, &block)
56
+ key = caller_key(args || block, as: as)
58
57
 
59
- cache[key] = yield(block)
58
+ if reload
59
+ store[key] = yield
60
+ else
61
+ store[key] ||= yield
62
+ end
60
63
  end
61
64
 
62
65
  def present?
@@ -66,37 +69,23 @@ module Lite
66
69
  alias hits? present?
67
70
 
68
71
  def size
69
- cache.size
72
+ store.size
70
73
  end
71
74
 
72
75
  def slice!(*keys)
73
- keys.each { |key| cache.delete(key) }
74
- cache
76
+ keys.each { |key| store.delete(key) }
77
+ store
75
78
  end
76
79
 
77
80
  def to_hash
78
- cache
81
+ store
79
82
  end
80
83
 
81
84
  alias as_json to_hash
82
85
  alias hits to_hash
83
86
 
84
87
  def values
85
- cache.values
86
- end
87
-
88
- private
89
-
90
- def caller_locals(block)
91
- local_vars = block.binding.local_variables
92
- local_vars.flat_map { |name| [name, block.binding.local_variable_get(name)] }
93
- end
94
-
95
- def caller_method
96
- val = caller(2..2).first[CALLER_METHOD_REGEX, 1]
97
- return val unless val.include?('<top (required)>')
98
-
99
- caller(1..1).first[CALLER_METHOD_REGEX, 1]
88
+ store.values
100
89
  end
101
90
 
102
91
  end
@@ -4,14 +4,15 @@ module Lite
4
4
  module Memoize
5
5
  module Klass
6
6
 
7
- include Lite::Memoize::Shared
7
+ include Lite::Memoize::Mixin
8
8
 
9
9
  def memoize(method_name, as: nil)
10
10
  inner_method = instance_method(method_name)
11
11
 
12
12
  define_method(method_name) do |*args|
13
- key = self.class.key(as || method_name, args)
14
- self.class.cache[key] ||= inner_method.bind(self).call(*args)
13
+ key = self.class.caller_key(args, as: as || method_name)
14
+
15
+ self.class.store[key] ||= inner_method.bind(self).call(*args)
15
16
  end
16
17
  end
17
18
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Memoize
5
+ module Mixin
6
+
7
+ def store
8
+ @_memoized_methods ||= {}
9
+ end
10
+
11
+ def caller_key(block, as: nil)
12
+ name = as ? [as] : block.source_location
13
+ return name.concat(block) if block.is_a?(Array)
14
+
15
+ block.binding.local_variables.each_with_object(name) do |local_name, array|
16
+ array << local_name
17
+ array << block.binding.local_variable_get(local_name)
18
+ end
19
+ end
20
+
21
+ def memoize(method_name, args: nil, reload: false)
22
+ key = "#{method_name}:#{args}"
23
+
24
+ if reload
25
+ store[key] = yield
26
+ else
27
+ store[key] ||= yield
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -3,7 +3,7 @@
3
3
  module Lite
4
4
  module Memoize
5
5
 
6
- VERSION ||= '1.0.0'
6
+ VERSION ||= '1.0.1'
7
7
 
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lite-memoize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juan Gomez
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-24 00:00:00.000000000 Z
11
+ date: 2019-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -128,12 +128,22 @@ files:
128
128
  - README.md
129
129
  - Rakefile
130
130
  - _config.yml
131
+ - benchmarks/base.rb
132
+ - benchmarks/memery.rb
133
+ - benchmarks/memist.rb
134
+ - benchmarks/memo-it.rb
135
+ - benchmarks/memoist.rb
136
+ - benchmarks/memoist2.rb
137
+ - benchmarks/memoit.rb
138
+ - benchmarks/memoizable.rb
139
+ - benchmarks/memoizer.rb
131
140
  - bin/console
132
141
  - bin/setup
133
142
  - lib/lite/memoize.rb
143
+ - lib/lite/memoize/alias.rb
134
144
  - lib/lite/memoize/instance.rb
135
145
  - lib/lite/memoize/klass.rb
136
- - lib/lite/memoize/shared.rb
146
+ - lib/lite/memoize/mixin.rb
137
147
  - lib/lite/memoize/version.rb
138
148
  - lite-memoize.gemspec
139
149
  homepage: http://drexed.github.io/lite-memoize
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'digest'
4
-
5
- module Lite
6
- module Memoize
7
- module Shared
8
-
9
- def cache
10
- @cache ||= {}
11
- end
12
-
13
- def key(method_name, method_args)
14
- return method_name.to_s if method_args.empty?
15
-
16
- method_sha1 = Digest::SHA1.hexdigest(method_args.to_s)
17
- "#{method_name}:#{method_sha1}"
18
- end
19
-
20
- end
21
- end
22
- end