short_circu_it 0.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/lib/short_circu_it.rb +92 -0
- data/lib/short_circu_it/errors.rb +5 -0
- data/lib/short_circu_it/memoization_store.rb +91 -0
- data/lib/short_circu_it/version.rb +5 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fcdddba43f8cac401f3a4d6998089348572453118ac95231bdf25e669b7a8316
|
4
|
+
data.tar.gz: 1077aedbd2bebdc80cf08583e0b2e0ba549cd4b7ff3876b37364494d7e4a550c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '0388a2743f8cf78f1146fab8843340eb087b0e55c687a525ea543bed09e8db366d162d962937e7fcb80922664aa33f067914ebadc768e414b3c8159bb00ca787'
|
7
|
+
data.tar.gz: 819206d14daa6a4c218262a48754cb270eaa375ab4128b90b332139436f77eb1bd52c8f1a4768325b40838a1a7179a3dcafde440c69d8f0e6f096ff28ccc8f0f
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Allen Rettberg
|
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,39 @@
|
|
1
|
+
# ShortCircuIt
|
2
|
+
|
3
|
+
[](https://semaphoreci.com/freshly/spicerack)
|
4
|
+
[](https://codeclimate.com/github/Freshly/spicerack/maintainability)
|
5
|
+
[](https://codeclimate.com/github/Freshly/spicerack/test_coverage)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'short_circu_it'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install short_circu_it
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
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.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Freshly/spicerack.
|
36
|
+
|
37
|
+
## License
|
38
|
+
|
39
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "short_circu_it/version"
|
4
|
+
require "short_circu_it/memoization_store"
|
5
|
+
require "active_support/core_ext/module"
|
6
|
+
require "active_support/core_ext/array"
|
7
|
+
require "active_support/concern"
|
8
|
+
require "around_the_world"
|
9
|
+
|
10
|
+
module ShortCircuIt
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
include AroundTheWorld
|
13
|
+
|
14
|
+
PREPEND_MODULE_NAME = "MemoizedMethods"
|
15
|
+
|
16
|
+
included do
|
17
|
+
delegate :memoization_observers, to: :class
|
18
|
+
delegate :clear_memoization, :clear_all_memoization, to: :memoization_store
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# @return [ShortCircuIt::MemoizationStore]
|
24
|
+
def memoization_store
|
25
|
+
@memoization_store ||= MemoizationStore.new(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
class_methods do
|
29
|
+
attr_internal_reader :memoization_observers
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
# @example
|
34
|
+
# def expensive_method
|
35
|
+
# puts "doing some really expensive operation here!"
|
36
|
+
# "these datas are yuuge"
|
37
|
+
# end
|
38
|
+
# memoize :expensive_method
|
39
|
+
#
|
40
|
+
# expensive_method
|
41
|
+
# doing some really expensive operation here!
|
42
|
+
# => "these datas are yuuge"
|
43
|
+
# expensive_method
|
44
|
+
# => "these datas are yuuge"
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# def some_association
|
48
|
+
# SomeAssociation.find(some_association_id)
|
49
|
+
# end
|
50
|
+
# memoize :some_association, observes: :some_association_id
|
51
|
+
#
|
52
|
+
# some_association_id = 1
|
53
|
+
# some_association
|
54
|
+
# * database stuff *
|
55
|
+
# => #<SomeAssociation:1234 id: 1>
|
56
|
+
#
|
57
|
+
# some_association
|
58
|
+
# * no database stuff *
|
59
|
+
# => #<SomeAssociation:1234 id: 1>
|
60
|
+
#
|
61
|
+
# some_association_id = 2
|
62
|
+
# some_association
|
63
|
+
# * database stuff *
|
64
|
+
# => #<SomeAssociation:2468 id: 2>
|
65
|
+
#
|
66
|
+
# @param method_name [Symbol] The name of the method to be memoized
|
67
|
+
# @param :observes [Symbol, Array<Symbol>] A method or array of methods to be observed to determine memoization
|
68
|
+
# cache validity. If any of the observed values change, the cached
|
69
|
+
# value will be invalidated. By default, the object will observe itself.
|
70
|
+
def memoize(method_name, observes: :itself)
|
71
|
+
add_memoized_observers(method_name.to_sym, observes)
|
72
|
+
|
73
|
+
around_method method_name.to_sym, PREPEND_MODULE_NAME do |*args|
|
74
|
+
memoization_store.memoize(method_name.to_sym, args.hash) do
|
75
|
+
super(*args)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
attr_internal_writer :memoization_observers
|
83
|
+
|
84
|
+
def add_memoized_observers(method_name, observers)
|
85
|
+
# TODO: Raise an error if method has already been memoized? A warning maybe?
|
86
|
+
self.memoization_observers ||= {}
|
87
|
+
self.memoization_observers = memoization_observers.
|
88
|
+
merge(method_name.to_sym => Array.wrap(observers).freeze).
|
89
|
+
freeze
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module"
|
4
|
+
|
5
|
+
module ShortCircuIt
|
6
|
+
class MemoizationStore
|
7
|
+
delegate :memoization_observers, to: :owner
|
8
|
+
|
9
|
+
# @param owner [*] The object being memoized
|
10
|
+
def initialize(owner)
|
11
|
+
@owner = owner
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param method_name [Symbol] The name of the method being memoized.
|
15
|
+
# @param argument_hash [Integer] The hash value of the arguments passed to the method.
|
16
|
+
# @yield [] Yields to a given block with no arguments. Memoizes the value returned by the block.
|
17
|
+
# @return [*] The value returned either from the memoization cache if present, or yielded block if not.
|
18
|
+
def memoize(method_name, argument_hash)
|
19
|
+
return memoized_value(method_name, argument_hash) if memoized?(method_name, argument_hash)
|
20
|
+
|
21
|
+
clear_memoization(method_name) unless current_memoization_for_method?(method_name)
|
22
|
+
|
23
|
+
yield.tap do |returned_value|
|
24
|
+
current_memoization_for_method(method_name)[argument_hash] = returned_value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Clears all cached values for the given method
|
29
|
+
#
|
30
|
+
# @param method_name [Symbol] The name of a memoized method
|
31
|
+
# @return [Boolean] True if a value was cleared, false if not
|
32
|
+
def clear_memoization(method_name)
|
33
|
+
!!memoized_hash.delete(method_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Clears all memoized values on the object
|
37
|
+
def clear_all_memoization
|
38
|
+
memoized_hash.clear
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"#<#{self.class} memoized: #{memoized_hash.keys.inspect}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def memoized_hash
|
48
|
+
@memoized_hash ||= {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def memoization_for_method(method_name)
|
52
|
+
memoized_hash[method_name] ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param method_name [Symbol] The name of the method to memoize
|
56
|
+
# @param argument_hash [Integer] The hash value of the arguments passed to the method
|
57
|
+
# @return [Boolean] True if the method has a current memoized value with the given arguments
|
58
|
+
def memoized?(method_name, argument_hash)
|
59
|
+
current_memoization_for_method?(method_name) && current_memoization_for_method(method_name).key?(argument_hash)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param method_name [String] The name of the method to memoize
|
63
|
+
# @param argument_hash [Integer] The hash value of the arguments passed to the method
|
64
|
+
# @return [*] The value that has been memoized for the method/argument combination
|
65
|
+
def memoized_value(method_name, argument_hash)
|
66
|
+
raise NotMemoizedError unless memoized?(method_name, argument_hash)
|
67
|
+
|
68
|
+
current_memoization_for_method(method_name)[argument_hash]
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param method_name [Symbol] The name of a memoized method
|
72
|
+
# @return [Hash] A hash of memoized values for the current state of the observed objects for the given method.
|
73
|
+
def current_memoization_for_method(method_name)
|
74
|
+
memoization_for_method(method_name)[state_hash(method_name)] ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param method_name [Symbol] The name of a memoized method
|
78
|
+
# @return [Boolean] True if there are any memoized values for the current state of the observed objects.
|
79
|
+
def current_memoization_for_method?(method_name)
|
80
|
+
memoization_for_method(method_name).key?(state_hash(method_name))
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param method_name [Symbol] The name of a memoized method
|
84
|
+
# @return [Integer] The hash value of all observed objects for the given method.
|
85
|
+
def state_hash(method_name)
|
86
|
+
memoization_observers[method_name].map { |observed_method| owner.public_send(observed_method) }.hash
|
87
|
+
end
|
88
|
+
|
89
|
+
attr_reader :owner
|
90
|
+
end
|
91
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: short_circu_it
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Allen Rettberg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.1.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.1.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: around_the_world
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.0
|
41
|
+
description: Memoize methods safely with parameter and dependency observation
|
42
|
+
email:
|
43
|
+
- allen.rettberg@freshly.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE.txt
|
49
|
+
- README.md
|
50
|
+
- lib/short_circu_it.rb
|
51
|
+
- lib/short_circu_it/errors.rb
|
52
|
+
- lib/short_circu_it/memoization_store.rb
|
53
|
+
- lib/short_circu_it/version.rb
|
54
|
+
homepage: https://www.freshly.com/
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 2.7.6
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Effortless memoization
|
78
|
+
test_files: []
|