short_circu_it 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://semaphoreci.com/api/v1/freshly/spicerack/branches/master/badge.svg)](https://semaphoreci.com/freshly/spicerack)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/7e089c2617c530a85b17/maintainability)](https://codeclimate.com/github/Freshly/spicerack/maintainability)
|
5
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/7e089c2617c530a85b17/test_coverage)](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: []
|