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 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MemoizationStore
4
+ class NotMemoizedError < StandardError; end
5
+ 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShortCircuIt
4
+ VERSION = "0.3.0"
5
+ 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: []