memoist-0.16.2/0000755000004100000410000000000013602271734013312 5ustar www-datawww-datamemoist-0.16.2/.travis.yml0000644000004100000410000000045013602271734015422 0ustar www-datawww-datasudo: false cache: bundler language: ruby rvm: - 1.9.3 - 2.0.0 - 2.1.10 - 2.2.7 - 2.3.4 - 2.4.1 - 2.5.1 - 2.6.0 - ruby-head - jruby-19mode - jruby-9.1.9.0 - jruby-head before_install: - gem install bundler --no-document -v '~> 1.13' before_script: - unset JRUBY_OPTS memoist-0.16.2/test/0000755000004100000410000000000013602271734014271 5ustar www-datawww-datamemoist-0.16.2/test/memoist_test.rb0000644000004100000410000003520213602271734017334 0ustar www-datawww-datarequire 'test_helper' require 'memoist' class MemoistTest < Minitest::Test class CallCounter def initialize @calls = {} end def call(method_name) @calls[method_name] ||= 0 @calls[method_name] += 1 end def count(method_name) @calls[method_name] ||= 0 end end class Person extend Memoist def initialize @counter = CallCounter.new end def name_calls @counter.count(:name) end def student_name_calls @counter.count(:student_name) end def name_query_calls @counter.count(:name?) end def is_developer_calls @counter.count(:is_developer?) end def age_calls @counter.count(:age) end def name @counter.call(:name) 'Josh' end def name? @counter.call(:name?) true end memoize :name? def update(_name) 'Joshua' end memoize :update def age @counter.call(:age) nil end memoize :name, :age def age? @counter.call(:age?) true end memoize 'age?' def sleep(hours = 8) @counter.call(:sleep) hours end memoize :sleep def sleep_calls @counter.count(:sleep) end def update_attributes(_options = {}) @counter.call(:update_attributes) true end memoize :update_attributes def update_attributes_calls @counter.count(:update_attributes) end protected def memoize_protected_test 'protected' end memoize :memoize_protected_test private def is_developer? @counter.call(:is_developer?) 'Yes' end memoize :is_developer? end class Student < Person def name @counter.call(:student_name) "Student #{super}" end memoize :name, identifier: :student end class Teacher < Person def seniority 'very_senior' end memoize :seniority end class Company attr_reader :name_calls def initialize @name_calls = 0 end def name @name_calls += 1 '37signals' end end module Rates extend Memoist attr_reader :sales_tax_calls def sales_tax(price) @sales_tax_calls ||= 0 @sales_tax_calls += 1 price * 0.1025 end memoize :sales_tax end class Calculator extend Memoist include Rates attr_reader :fib_calls def initialize @fib_calls = 0 end def fib(n) @fib_calls += 1 if n == 0 || n == 1 n else fib(n - 1) + fib(n - 2) end end memoize :fib def add_or_subtract(i, j, add) if add i + j else i - j end end memoize :add_or_subtract def counter @count ||= 0 @count += 1 end memoize :counter end class Book extend Memoist STATUSES = %w[new used].freeze CLASSIFICATION = %w[fiction nonfiction].freeze GENRES = %w[humor romance reference sci-fi classic philosophy].freeze attr_reader :title, :author def initialize(title, author) @title = title @author = author end def full_title "#{@title} by #{@author}" end memoize :full_title class << self extend Memoist def all_types STATUSES.product(CLASSIFICATION).product(GENRES).collect(&:flatten) end memoize :all_types end end class Abb extend Memoist def run(*_args) flush_cache if respond_to?(:flush_cache) execute end def execute some_method end def some_method # Override this end end class Bbb < Abb def some_method :foo end memoize :some_method end def setup @person = Person.new @calculator = Calculator.new @book = Book.new('My Life', "Brian 'Fudge' Turmuck") end def test_memoization assert_equal 'Josh', @person.name assert_equal 1, @person.name_calls 3.times { assert_equal 'Josh', @person.name } assert_equal 1, @person.name_calls end def test_memoize_with_optional_arguments assert_equal 4, @person.sleep(4) assert_equal 1, @person.sleep_calls 3.times { assert_equal 4, @person.sleep(4) } assert_equal 1, @person.sleep_calls 3.times { assert_equal 4, @person.sleep(4, :reload) } assert_equal 4, @person.sleep_calls end def test_memoize_with_options_hash assert_equal true, @person.update_attributes(age: 21, name: 'James') assert_equal 1, @person.update_attributes_calls 3.times { assert_equal true, @person.update_attributes(age: 21, name: 'James') } assert_equal 1, @person.update_attributes_calls 3.times { assert_equal true, @person.update_attributes({ age: 21, name: 'James' }, :reload) } assert_equal 4, @person.update_attributes_calls end def test_memoization_with_punctuation assert_equal true, @person.name? @person.memoize_all @person.unmemoize_all end def test_memoization_when_memoize_is_called_with_punctuated_string assert_equal true, @person.age? @person.memoize_all @person.unmemoize_all end def test_memoization_flush_with_punctuation assert_equal true, @person.name? @person.flush_cache(:name?) 3.times { assert_equal true, @person.name? } assert_equal 2, @person.name_query_calls end def test_memoization_with_nil_value assert_nil @person.age assert_equal 1, @person.age_calls 3.times { assert_nil @person.age } assert_equal 1, @person.age_calls end def test_reloadable assert_equal 1, @calculator.counter assert_equal 2, @calculator.counter(:reload) assert_equal 2, @calculator.counter assert_equal 3, @calculator.counter(true) assert_equal 3, @calculator.counter end def test_flush_cache assert_equal 1, @calculator.counter assert @calculator.instance_variable_get(:@_memoized_counter) @calculator.flush_cache(:counter) assert_equal false, @calculator.instance_variable_defined?(:@_memoized_counter) assert_equal 2, @calculator.counter end def test_class_flush_cache @book.memoize_all assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title Book.memoize_all assert_instance_of Array, Book.instance_variable_get(:@_memoized_all_types) Book.flush_cache assert_equal false, Book.instance_variable_defined?(:@_memoized_all_types) end def test_class_flush_cache_preserves_instances @book.memoize_all Book.memoize_all assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title Book.flush_cache assert_equal false, Book.instance_variable_defined?(:@_memoized_all_types) assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title end def test_flush_cache_in_child_class x = Bbb.new # This should not throw error x.run end def test_unmemoize_all assert_equal 1, @calculator.counter assert_equal true, @calculator.instance_variable_defined?(:@_memoized_counter) assert @calculator.instance_variable_get(:@_memoized_counter) @calculator.unmemoize_all assert_equal false, @calculator.instance_variable_defined?(:@_memoized_counter) assert_equal 2, @calculator.counter end def test_all_memoized_structs # Person memoize :age, :age?, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes # Student < Person memoize :name, :identifier => :student # Teacher < Person memoize :seniority expected = %w[age age? is_developer? memoize_protected_test name name? sleep update update_attributes] structs = Person.all_memoized_structs assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar # Same expected methods structs = Student.all_memoized_structs assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort assert_equal '@_memoized_student_name', structs.detect { |s| s.memoized_method == :name }.ivar expected = (expected << 'seniority').sort structs = Teacher.all_memoized_structs assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar end def test_unmemoize_all_subclasses # Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes # Student < Person memoize :name, :identifier => :student # Teacher < Person memoize :seniority teacher = Teacher.new assert_equal 'Josh', teacher.name assert_equal 'Josh', teacher.instance_variable_get(:@_memoized_name) assert_equal 'very_senior', teacher.seniority assert_equal 'very_senior', teacher.instance_variable_get(:@_memoized_seniority) teacher.unmemoize_all assert_equal false, teacher.instance_variable_defined?(:@_memoized_name) assert_equal false, teacher.instance_variable_defined?(:@_memoized_seniority) student = Student.new assert_equal 'Student Josh', student.name assert_equal 'Student Josh', student.instance_variable_get(:@_memoized_student_name) assert_equal false, student.instance_variable_defined?(:@_memoized_seniority) student.unmemoize_all assert_equal false, @calculator.instance_variable_defined?(:@_memoized_student_name) end def test_memoize_all @calculator.memoize_all assert_equal true, @calculator.instance_variable_defined?(:@_memoized_counter) end def test_memoize_all_subclasses # Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes # Student < Person memoize :name, :identifier => :student # Teacher < Person memoize :seniority teacher = Teacher.new teacher.memoize_all assert_equal 'very_senior', teacher.instance_variable_get(:@_memoized_seniority) assert_equal 'Josh', teacher.instance_variable_get(:@_memoized_name) student = Student.new student.memoize_all assert_equal 'Student Josh', student.instance_variable_get(:@_memoized_student_name) assert_equal 'Student Josh', student.name assert_equal false, student.instance_variable_defined?(:@_memoized_seniority) end def test_memoization_cache_is_different_for_each_instance assert_equal 1, @calculator.counter assert_equal 2, @calculator.counter(:reload) assert_equal 1, Calculator.new.counter end def test_memoization_class_variables @book.memoize_all assert_equal "My Life by Brian 'Fudge' Turmuck", @book.instance_variable_get(:@_memoized_full_title) assert_equal "My Life by Brian 'Fudge' Turmuck", @book.full_title Book.memoize_all assert_instance_of Array, Book.instance_variable_get(:@_memoized_all_types) assert_equal 24, Book.all_types.count end def test_memoized_is_not_affected_by_freeze @person.freeze assert_equal 'Josh', @person.name assert_equal 'Joshua', @person.update('Joshua') end def test_memoization_with_args assert_equal 55, @calculator.fib(10) assert_equal 11, @calculator.fib_calls end def test_reloadable_with_args assert_equal 55, @calculator.fib(10) assert_equal 11, @calculator.fib_calls assert_equal 55, @calculator.fib(10, :reload) assert_equal 12, @calculator.fib_calls assert_equal 55, @calculator.fib(10, true) assert_equal 13, @calculator.fib_calls end def test_memoization_with_boolean_arg assert_equal 4, @calculator.add_or_subtract(2, 2, true) assert_equal 2, @calculator.add_or_subtract(4, 2, false) end def test_object_memoization [Company.new, Company.new, Company.new].each do |company| company.extend Memoist company.memoize :name assert_equal '37signals', company.name assert_equal 1, company.name_calls assert_equal '37signals', company.name assert_equal 1, company.name_calls end end def test_memoized_module_methods assert_equal 1.025, @calculator.sales_tax(10) assert_equal 1, @calculator.sales_tax_calls assert_equal 1.025, @calculator.sales_tax(10) assert_equal 1, @calculator.sales_tax_calls assert_equal 2.5625, @calculator.sales_tax(25) assert_equal 2, @calculator.sales_tax_calls end def test_object_memoized_module_methods company = Company.new company.extend(Rates) assert_equal 1.025, company.sales_tax(10) assert_equal 1, company.sales_tax_calls assert_equal 1.025, company.sales_tax(10) assert_equal 1, company.sales_tax_calls assert_equal 2.5625, company.sales_tax(25) assert_equal 2, company.sales_tax_calls end def test_double_memoization_with_identifier # Person memoize :age, :is_developer?, :memoize_protected_test, :name, :name?, :sleep, :update, :update_attributes # Student < Person memoize :name, :identifier => :student # Teacher < Person memoize :seniority Person.memoize :name, identifier: :again p = Person.new assert_equal 'Josh', p.name assert p.instance_variable_get(:@_memoized_again_name) # HACK: tl;dr: Don't memoize classes in test that are used elsewhere. # Calling Person.memoize :name, :identifier => :again pollutes Person # and descendents since we cache the memoized method structures. # This populates those structs, verifies Person is polluted, resets the # structs, cleans up cached memoized_methods Student.all_memoized_structs Person.all_memoized_structs Teacher.all_memoized_structs assert Person.memoized_methods.any? { |m| m.ivar == '@_memoized_again_name' } [Student, Teacher, Person].each(&:clear_structs) assert Person.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' } assert_nil Student.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' } assert_nil Teacher.memoized_methods.reject! { |m| m.ivar == '@_memoized_again_name' } end def test_memoization_with_a_subclass student = Student.new student.name student.name assert_equal 1, student.student_name_calls assert_equal 1, student.name_calls end def test_memoization_is_chainable klass = Class.new do def foo 'bar' end end klass.extend Memoist chainable = klass.memoize :foo assert_equal :foo, chainable end def test_protected_method_memoization person = Person.new assert_raises(NoMethodError) { person.memoize_protected_test } assert_equal 'protected', person.send(:memoize_protected_test) end def test_private_method_memoization person = Person.new assert_raises(NoMethodError) { person.is_developer? } assert_equal 'Yes', person.send(:is_developer?) assert_equal 1, person.is_developer_calls assert_equal 'Yes', person.send(:is_developer?) assert_equal 1, person.is_developer_calls end end memoist-0.16.2/test/test_helper.rb0000644000004100000410000000014413602271734017133 0ustar www-datawww-datarequire 'minitest/autorun' $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') memoist-0.16.2/README.md0000644000004100000410000000632213602271734014574 0ustar www-datawww-dataMemoist ============= [![Build Status](https://travis-ci.org/matthewrudy/memoist.svg?branch=master)](https://travis-ci.org/matthewrudy/memoist) Memoist is an extraction of ActiveSupport::Memoizable. Since June 2011 ActiveSupport::Memoizable has been deprecated. But I love it, and so I plan to keep it alive. Usage ----- Just extend with the Memoist module ```ruby require 'memoist' class Person extend Memoist def social_security puts "execute!" decrypt_social_security end memoize :social_security end person = Person.new person.social_security # execute! # => (returns decrypt_social_security) person.social_security # => (returns the memoized value) ``` And person.social_security will only be calculated once. Every memoized function (which initially was not accepting any arguments) has a ```(reload)``` argument you can pass in to bypass and reset the memoization: ```ruby def some_method Time.now end memoize :some_method ``` Calling ```some_method``` will be memoized, but calling ```some_method(true)``` will rememoize each time. You can even memoize method that takes arguments. ```ruby class Person def taxes_due(income) income * 0.40 end memoize :taxes_due end ``` This will only be calculated once per value of income. You can also memoize class methods. ```ruby class Person class << self extend Memoist def with_overdue_taxes # ... end memoize :with_overdue_taxes end end ``` When a sub-class overrides one of its parent's methods and you need to memoize both. Then you can use the `:identifier` parameter in order to help _Memoist_ distinguish between the two. ```ruby class Clock extend Memoist def now "The time now is #{Time.now.hour} o'clock and #{Time.now.min} minutes" end memoize :now end class AccurateClock < Clock extend Memoist def now "#{super} and #{Time.now.sec} seconds" end memoize :now, :identifier => :accurate_clock end ``` Reload ------ Each memoized function comes with a way to flush the existing value. ```ruby person.social_security # returns the memoized value person.social_security(true) # bypasses the memoized value and rememoizes it ``` This also works with a memoized method with arguments ```ruby person.taxes_due(100_000) # returns the memoized value person.taxes_due(100_000, true) # bypasses the memoized value and rememoizes it ``` If you want to flush the entire memoization cache for an object ```ruby person.flush_cache # returns an array of flushed memoized methods, e.g. ["social_security", "some_method"] ``` Authors =========== Everyone who contributed to it in the rails repository. * Joshua Peek * Tarmo Tänav * Jeremy Kemper * Eugene Pimenov * Xavier Noria * Niels Ganser * Carl Lerche & Yehuda Katz * jeem * Jay Pignata * Damien Mathieu * José Valim * Matthew Rudy Jacobs Contributing ============ 1. Fork it ( https://github.com/matthewrudy/memoist/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request License ======= Released under the [MIT License](http://www.opensource.org/licenses/MIT), just as Ruby on Rails is. memoist-0.16.2/LICENSE.md0000644000004100000410000000205413602271734014717 0ustar www-datawww-dataCopyright (c) 2012-2013 Matthew Rudy Jacobs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. memoist-0.16.2/CHANGELOG.md0000644000004100000410000001565413602271734015136 0ustar www-datawww-data# Changelog ## [Unreleased](https://github.com/matthewrudy/memoist/tree/HEAD) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.16.1...HEAD) **Merged pull requests:** - Fix regression introduced by frozen symbol fix [\#86](https://github.com/matthewrudy/memoist/pull/86) ([sebjacobs](https://github.com/sebjacobs)) ## [v0.16.1](https://github.com/matthewrudy/memoist/tree/v0.16.1) (2019-11-08) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.16.0...v0.16.1) **Merged pull requests:** - Remove ruby 1.9.2 from travis build matrix [\#84](https://github.com/matthewrudy/memoist/pull/84) ([unasuke](https://github.com/unasuke)) - Make Memoist.escape\_punctuation compatible with MRI 2.7 [\#82](https://github.com/matthewrudy/memoist/pull/82) ([casperisfine](https://github.com/casperisfine)) - add 2.5.1 to travis [\#77](https://github.com/matthewrudy/memoist/pull/77) ([matthewrudy](https://github.com/matthewrudy)) - Remove ghit.me [\#74](https://github.com/matthewrudy/memoist/pull/74) ([matthewrudy](https://github.com/matthewrudy)) - Place sample code for execution in README.md [\#73](https://github.com/matthewrudy/memoist/pull/73) ([3nan3](https://github.com/3nan3)) - Require Ruby \>=1.9.2 [\#69](https://github.com/matthewrudy/memoist/pull/69) ([matthewrudy](https://github.com/matthewrudy)) ## [v0.16.0](https://github.com/matthewrudy/memoist/tree/v0.16.0) (2017-06-20) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.15.0...v0.16.0) **Merged pull requests:** - Fix undefined `memoized\_methods` error raised when a parent class has not call `memoize` [\#68](https://github.com/matthewrudy/memoist/pull/68) ([PikachuEXE](https://github.com/PikachuEXE)) - Add support for class-level cache flushing. [\#67](https://github.com/matthewrudy/memoist/pull/67) ([JoeMcB](https://github.com/JoeMcB)) - Add ruby 2.4 to travis \(bump 2.2 and 2.3 versions\) [\#64](https://github.com/matthewrudy/memoist/pull/64) ([jrafanie](https://github.com/jrafanie)) - Fix tests for Ruby \< 1.9.3 [\#56](https://github.com/matthewrudy/memoist/pull/56) ([matthewrudy](https://github.com/matthewrudy)) - Add return in comments for `flush\_cache`. [\#55](https://github.com/matthewrudy/memoist/pull/55) ([joshuapinter](https://github.com/joshuapinter)) - Update readme [\#53](https://github.com/matthewrudy/memoist/pull/53) ([biow0lf](https://github.com/biow0lf)) ## [v0.15.0](https://github.com/matthewrudy/memoist/tree/v0.15.0) (2016-08-23) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.14.0...v0.15.0) **Merged pull requests:** - Remove test warnings [\#52](https://github.com/matthewrudy/memoist/pull/52) ([matthewrudy](https://github.com/matthewrudy)) - Use SVG badge over PNG [\#44](https://github.com/matthewrudy/memoist/pull/44) ([olivierlacan](https://github.com/olivierlacan)) ## [v0.14.0](https://github.com/matthewrudy/memoist/tree/v0.14.0) (2015-12-15) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.13.0...v0.14.0) **Merged pull requests:** - Faster2: Cache the method, ivar, and arity and the ancestry memoized methods [\#38](https://github.com/matthewrudy/memoist/pull/38) ([jrafanie](https://github.com/jrafanie)) ## [v0.13.0](https://github.com/matthewrudy/memoist/tree/v0.13.0) (2015-11-26) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.12.0...v0.13.0) **Merged pull requests:** - Faster memoist with less object allocations [\#36](https://github.com/matthewrudy/memoist/pull/36) ([jrafanie](https://github.com/jrafanie)) - Be optimistic about bundler version [\#35](https://github.com/matthewrudy/memoist/pull/35) ([lotyrin](https://github.com/lotyrin)) - Add syntax highlighting for code blocks. [\#34](https://github.com/matthewrudy/memoist/pull/34) ([joshuapinter](https://github.com/joshuapinter)) ## [v0.12.0](https://github.com/matthewrudy/memoist/tree/v0.12.0) (2015-04-13) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.11.0...v0.12.0) **Merged pull requests:** - Fix forking link [\#30](https://github.com/matthewrudy/memoist/pull/30) ([brandondrew](https://github.com/brandondrew)) - Update README with :identifier info [\#29](https://github.com/matthewrudy/memoist/pull/29) ([fervic](https://github.com/fervic)) ## [v0.11.0](https://github.com/matthewrudy/memoist/tree/v0.11.0) (2014-10-10) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.10.0...v0.11.0) **Merged pull requests:** - Call abs on arity when extracting reload [\#27](https://github.com/matthewrudy/memoist/pull/27) ([bradylove](https://github.com/bradylove)) ## [v0.10.0](https://github.com/matthewrudy/memoist/tree/v0.10.0) (2014-08-13) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.9.3...v0.10.0) **Merged pull requests:** - Make memoize return a :symbol [\#24](https://github.com/matthewrudy/memoist/pull/24) ([matthewrudy](https://github.com/matthewrudy)) - Use Minitest [\#19](https://github.com/matthewrudy/memoist/pull/19) ([matthewrudy](https://github.com/matthewrudy)) ## [v0.9.3](https://github.com/matthewrudy/memoist/tree/v0.9.3) (2014-06-01) [Full Changelog](https://github.com/matthewrudy/memoist/compare/v0.9.2...v0.9.3) **Merged pull requests:** - Remove Array caching hack [\#17](https://github.com/matthewrudy/memoist/pull/17) ([matthewrudy](https://github.com/matthewrudy)) ## [v0.9.2](https://github.com/matthewrudy/memoist/tree/v0.9.2) (2014-04-16) [Full Changelog](https://github.com/matthewrudy/memoist/compare/0.9.0...v0.9.2) **Merged pull requests:** - Give double-memoize errors their own error class [\#15](https://github.com/matthewrudy/memoist/pull/15) ([zachhale](https://github.com/zachhale)) - Add tax-themed example for class method memoization fixes \#9 [\#10](https://github.com/matthewrudy/memoist/pull/10) ([fny](https://github.com/fny)) ## [0.9.0](https://github.com/matthewrudy/memoist/tree/0.9.0) (2013-03-20) [Full Changelog](https://github.com/matthewrudy/memoist/compare/0.2.0...0.9.0) **Merged pull requests:** - Update README.md to include memoization bypass description [\#6](https://github.com/matthewrudy/memoist/pull/6) ([andreychernih](https://github.com/andreychernih)) - Adds a note about the MIT License [\#4](https://github.com/matthewrudy/memoist/pull/4) ([matiaskorhonen](https://github.com/matiaskorhonen)) ## [0.2.0](https://github.com/matthewrudy/memoist/tree/0.2.0) (2012-08-15) [Full Changelog](https://github.com/matthewrudy/memoist/compare/0.1.0...0.2.0) **Merged pull requests:** - Improved performance of flush\_cache and prime\_cache when parameters are passed to them. [\#2](https://github.com/matthewrudy/memoist/pull/2) ([jrafanie](https://github.com/jrafanie)) ## [0.1.0](https://github.com/matthewrudy/memoist/tree/0.1.0) (2012-01-24) [Full Changelog](https://github.com/matthewrudy/memoist/compare/7a5352d6b6c4219f37f329d2422985961c749748...0.1.0) \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* memoist-0.16.2/.gitignore0000644000004100000410000000023213602271734015277 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp memoist-0.16.2/script/0000755000004100000410000000000013602271734014616 5ustar www-datawww-datamemoist-0.16.2/script/benchmark.rb0000644000004100000410000000146613602271734017104 0ustar www-datawww-data$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'benchmark/ips' require 'memoist' class Benchy extend Memoist def arity_0 'Hello World' end memoize :arity_0 def arity_1(name) "Hello #{name}" end memoize :arity_1 end OBJECT = Benchy.new puts "Benchmarking: #{Memoist::VERSION}" Benchmark.ips do |x| x.report('arity 0 - memoized') do |times| times.times do OBJECT.arity_0 end end # x.report("arity 0 - unmemoized") do |times| # times.times do # OBJECT._unmemoized_arity_0 # end # end x.report('arity 1 - memoized') do |times| times.times do OBJECT.arity_1(:World) end end # x.report("arity 1 - unmemoized") do |times| # times.times do # OBJECT._unmemoized_arity_1(:World) # end # end end memoist-0.16.2/Rakefile0000644000004100000410000000032313602271734014755 0ustar www-datawww-data# encoding: utf-8 require 'bundler/gem_tasks' require 'rake/testtask' Rake::TestTask.new do |t| t.libs << 'test' t.test_files = FileList['test/**/*_test.rb'] t.verbose = true end task default: ['test'] memoist-0.16.2/lib/0000755000004100000410000000000013602271734014060 5ustar www-datawww-datamemoist-0.16.2/lib/memoist/0000755000004100000410000000000013602271734015535 5ustar www-datawww-datamemoist-0.16.2/lib/memoist/version.rb0000644000004100000410000000011613602271734017545 0ustar www-datawww-data# frozen_string_literal: true module Memoist VERSION = '0.16.2'.freeze end memoist-0.16.2/lib/memoist.rb0000644000004100000410000001470413602271734016070 0ustar www-datawww-data# frozen_string_literal: true require 'memoist/version' module Memoist def self.extended(extender) Memoist.memoist_eval(extender) do unless singleton_class.method_defined?(:memoized_methods) def self.memoized_methods @_memoized_methods ||= [] end end end end def self.memoized_ivar_for(method_name, identifier = nil) "@#{memoized_prefix(identifier)}_#{escape_punctuation(method_name)}" end def self.unmemoized_method_for(method_name, identifier = nil) "#{unmemoized_prefix(identifier)}_#{method_name}".to_sym end def self.memoized_prefix(identifier = nil) if identifier "_memoized_#{identifier}" else '_memoized'.freeze end end def self.unmemoized_prefix(identifier = nil) if identifier "_unmemoized_#{identifier}" else '_unmemoized'.freeze end end def self.escape_punctuation(string) string = string.is_a?(String) ? string.dup : string.to_s.dup return string unless string.end_with?('?'.freeze, '!'.freeze) # A String can't end in both ? and ! if string.sub!(/\?\Z/, '_query'.freeze) else string.sub!(/!\Z/, '_bang'.freeze) end string end def self.memoist_eval(klass, *args, &block) if klass.respond_to?(:class_eval) klass.class_eval(*args, &block) else klass.singleton_class.class_eval(*args, &block) end end def self.extract_reload!(method, args) if args.length == method.arity.abs + 1 && (args.last == true || args.last == :reload) reload = args.pop end reload end module InstanceMethods def memoize_all prime_cache end def unmemoize_all flush_cache end def memoized_structs(names) ref_obj = self.class.respond_to?(:class_eval) ? singleton_class : self structs = ref_obj.all_memoized_structs return structs if names.empty? structs.select { |s| names.include?(s.memoized_method) } end def prime_cache(*method_names) memoized_structs(method_names).each do |struct| if struct.arity == 0 __send__(struct.memoized_method) else instance_variable_set(struct.ivar, {}) end end end def flush_cache(*method_names) memoized_structs(method_names).each do |struct| remove_instance_variable(struct.ivar) if instance_variable_defined?(struct.ivar) end end end MemoizedMethod = Struct.new(:memoized_method, :ivar, :arity) def all_memoized_structs @all_memoized_structs ||= begin structs = memoized_methods.dup # Collect the memoized_methods of ancestors in ancestor order # unless we already have it since self or parents could be overriding # an ancestor method. ancestors.grep(Memoist).each do |ancestor| ancestor.memoized_methods.each do |m| structs << m unless structs.any? { |am| am.memoized_method == m.memoized_method } end end structs end end def clear_structs @all_memoized_structs = nil end def memoize(*method_names) identifier = method_names.pop[:identifier] if method_names.last.is_a?(Hash) method_names.each do |method_name| unmemoized_method = Memoist.unmemoized_method_for(method_name, identifier) memoized_ivar = Memoist.memoized_ivar_for(method_name, identifier) Memoist.memoist_eval(self) do include InstanceMethods if method_defined?(unmemoized_method) warn "Already memoized #{method_name}" return end alias_method unmemoized_method, method_name mm = MemoizedMethod.new(method_name, memoized_ivar, instance_method(method_name).arity) memoized_methods << mm if mm.arity == 0 # define a method like this; # def mime_type(reload=true) # skip_cache = reload || !instance_variable_defined?("@_memoized_mime_type") # set_cache = skip_cache && !frozen? # # if skip_cache # value = _unmemoized_mime_type # else # value = @_memoized_mime_type # end # # if set_cache # @_memoized_mime_type = value # end # # value # end module_eval <<-EOS, __FILE__, __LINE__ + 1 def #{method_name}(reload = false) skip_cache = reload || !instance_variable_defined?("#{memoized_ivar}") set_cache = skip_cache && !frozen? if skip_cache value = #{unmemoized_method} else value = #{memoized_ivar} end if set_cache #{memoized_ivar} = value end value end EOS else # define a method like this; # def mime_type(*args) # reload = Memoist.extract_reload!(method(:_unmemoized_mime_type), args) # # skip_cache = reload || !memoized_with_args?(:mime_type, args) # set_cache = skip_cache && !frozen # # if skip_cache # value = _unmemoized_mime_type(*args) # else # value = @_memoized_mime_type[args] # end # # if set_cache # @_memoized_mime_type ||= {} # @_memoized_mime_type[args] = value # end # # value # end module_eval <<-EOS, __FILE__, __LINE__ + 1 def #{method_name}(*args) reload = Memoist.extract_reload!(method(#{unmemoized_method.inspect}), args) skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.has_key?(args)) set_cache = skip_cache && !frozen? if skip_cache value = #{unmemoized_method}(*args) else value = #{memoized_ivar}[args] end if set_cache #{memoized_ivar} ||= {} #{memoized_ivar}[args] = value end value end EOS end if private_method_defined?(unmemoized_method) private method_name elsif protected_method_defined?(unmemoized_method) protected method_name end end end # return a chainable method_name symbol if we can method_names.length == 1 ? method_names.first : method_names end end memoist-0.16.2/memoist.gemspec0000644000004100000410000000333713602271734016342 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'memoist/version' AUTHORS = [ ['Joshua Peek', 'josh@joshpeek.com'], ['Tarmo Tänav', 'tarmo@itech.ee'], ['Jeremy Kemper', 'jeremy@bitsweat.net'], ['Eugene Pimenov', 'libc@mac.com'], ['Xavier Noria', 'fxn@hashref.com'], ['Niels Ganser', 'niels@herimedia.co'], ['Carl Lerche & Yehuda Katz', 'wycats@gmail.com'], ['jeem', 'jeem@hughesorama.com'], ['Jay Pignata', 'john.pignata@gmail.com'], ['Damien Mathieu', '42@dmathieu.com'], ['José Valim', 'jose.valim@gmail.com'], ['Matthew Rudy Jacobs', 'matthewrudyjacobs@gmail.com'] ].freeze Gem::Specification.new do |spec| spec.name = 'memoist' spec.version = Memoist::VERSION spec.authors = AUTHORS.map { |name, _email| name } spec.email = AUTHORS.map { |_name, email| email } spec.summary = 'memoize methods invocation' spec.homepage = 'https://github.com/matthewrudy/memoist' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.required_ruby_version = '>= 1.9.2' spec.add_development_dependency 'benchmark-ips' spec.add_development_dependency 'bundler' if RUBY_VERSION < '1.9.3' spec.add_development_dependency 'rake', '~> 10.4' else spec.add_development_dependency 'rake' end spec.add_development_dependency 'minitest', '~> 5.10' end memoist-0.16.2/Gemfile0000644000004100000410000000013413602271734014603 0ustar www-datawww-datasource 'https://rubygems.org' # Specify your gem's dependencies in memoist.gemspec gemspec