memoizable-0.4.2/0000755000076400007640000000000012325376437012711 5ustar pravipravimemoizable-0.4.2/spec/0000755000076400007640000000000012325376437013643 5ustar pravipravimemoizable-0.4.2/spec/spec_helper.rb0000644000076400007640000000120512325376437016457 0ustar pravipravi# encoding: utf-8 require 'simplecov' require 'coveralls' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter ] SimpleCov.start do command_name 'spec' add_filter 'config' add_filter 'spec' add_filter 'vendor' minimum_coverage 100 end require 'memoizable' require 'rspec' # Require spec support files and shared behavior Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each do |file| require file.chomp('.rb') end RSpec.configure do |config| config.expect_with :rspec do |expect_with| expect_with.syntax = :expect end end memoizable-0.4.2/spec/shared/0000755000076400007640000000000012325376437015111 5ustar pravipravimemoizable-0.4.2/spec/shared/command_method_behavior.rb0000644000076400007640000000016712325376437022277 0ustar pravipravi# encoding: utf-8 shared_examples_for 'a command method' do it 'returns self' do should equal(object) end end memoizable-0.4.2/spec/shared/call_super_shared_spec.rb0000644000076400007640000000134412325376437022131 0ustar pravipravi# encoding: utf-8 shared_examples 'it calls super' do |method| around do |example| # Restore original method after each example original = "original_#{method}" superclass.class_eval do alias_method original, method example.call undef_method method alias_method method, original end end it "delegates to the superclass ##{method} method" do # This is the most succinct approach I could think of to test whether the # superclass method is called. All of the built-in rspec helpers did not # seem to work for this. called = false superclass.class_eval { define_method(method) { |_| called = true } } expect { subject }.to change { called }.from(false).to(true) end end memoizable-0.4.2/spec/integration/0000755000076400007640000000000012325376437016166 5ustar pravipravimemoizable-0.4.2/spec/integration/serializable_spec.rb0000644000076400007640000000133112325376437022171 0ustar pravipravi# encoding: utf-8 require 'spec_helper' class Serializable include Memoizable def random_number rand(10000) end memoize :random_number end describe 'A serializable object' do let(:serializable) do Serializable.new end before do serializable.random_number # Call the memoized method to trigger lazy memoization end it 'is serializable with Marshal' do expect { Marshal.dump(serializable) }.not_to raise_error end it 'is deserializable with Marshal' do serialized = Marshal.dump(serializable) deserialized = Marshal.load(serialized) expect(deserialized).to be_an_instance_of(Serializable) expect(deserialized.random_number).to eql(serializable.random_number) end end memoizable-0.4.2/spec/unit/0000755000076400007640000000000012325376437014622 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/0000755000076400007640000000000012325376437016746 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/fixtures/0000755000076400007640000000000012325376437020617 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/fixtures/classes.rb0000644000076400007640000000072712325376437022607 0ustar pravipravi# encoding: utf-8 module Fixture class Object include Memoizable def required_arguments(foo) end def optional_arguments(foo = nil) end def test 'test' end def zero_arity caller end def one_arity(arg) end def public_method caller end protected def protected_method caller end private def private_method caller end end # class Object end # module Fixture memoizable-0.4.2/spec/unit/memoizable/module_methods/0000755000076400007640000000000012325376437021756 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/module_methods/memoize_spec.rb0000644000076400007640000000605112325376437024764 0ustar pravipravi# encoding: utf-8 require 'spec_helper' require File.expand_path('../../fixtures/classes', __FILE__) shared_examples_for 'memoizes method' do it 'memoizes the instance method' do subject instance = object.new expect(instance.send(method)).to be(instance.send(method)) end it 'creates a zero arity method', :unless => RUBY_VERSION == '1.8.7' do subject expect(object.new.method(method).arity).to be_zero end context 'when the initializer calls the memoized method' do before do method = self.method object.send(:define_method, :initialize) { send(method) } end it 'allows the memoized method to be called within the initializer' do subject expect { object.new }.to_not raise_error end end end describe Memoizable::ModuleMethods, '#memoize' do subject { object.memoize(method) } let(:object) do stub_const 'TestClass', Class.new(Fixture::Object) { def some_state Object.new end } end context 'on method with required arguments' do let(:method) { :required_arguments } it 'should raise error' do expect { subject }.to raise_error( Memoizable::MethodBuilder::InvalidArityError, 'Cannot memoize TestClass#required_arguments, its arity is 1' ) end end context 'on method with optional arguments' do let(:method) { :optional_arguments } it 'should raise error' do expect { subject }.to raise_error( Memoizable::MethodBuilder::InvalidArityError, 'Cannot memoize TestClass#optional_arguments, its arity is -1' ) end end context 'memoized method that returns generated values' do let(:method) { :some_state } it_should_behave_like 'a command method' it_should_behave_like 'memoizes method' it 'creates a method that returns a frozen value' do subject expect(object.new.send(method)).to be_frozen end end context 'public method' do let(:method) { :public_method } it_should_behave_like 'a command method' it_should_behave_like 'memoizes method' it 'is still a public method' do should be_public_method_defined(method) end it 'creates a method that returns a frozen value' do subject expect(object.new.send(method)).to be_frozen end end context 'protected method' do let(:method) { :protected_method } it_should_behave_like 'a command method' it_should_behave_like 'memoizes method' it 'is still a protected method' do should be_protected_method_defined(method) end it 'creates a method that returns a frozen value' do subject expect(object.new.send(method)).to be_frozen end end context 'private method' do let(:method) { :private_method } it_should_behave_like 'a command method' it_should_behave_like 'memoizes method' it 'is still a private method' do should be_private_method_defined(method) end it 'creates a method that returns a frozen value' do subject expect(object.new.send(method)).to be_frozen end end end memoizable-0.4.2/spec/unit/memoizable/module_methods/unmemoized_instance_method_spec.rb0000644000076400007640000000153212325376437030716 0ustar pravipravi# encoding: utf-8 require 'spec_helper' describe Memoizable::ModuleMethods, '#unmemoized_instance_method' do subject { object.unmemoized_instance_method(name) } let(:object) do Class.new do include Memoizable def initialize @foo = 0 end def foo @foo += 1 end memoize :foo end end context 'when the method was memoized' do let(:name) { :foo } it { should be_instance_of(UnboundMethod) } it 'returns the original method' do # original method is not memoized method = subject.bind(object.new) expect(method.call).to_not be(method.call) end end context 'when the method was not memoized' do let(:name) { :bar } it 'raises an exception' do expect { subject }.to raise_error(NameError, 'No method bar is memoized') end end end memoizable-0.4.2/spec/unit/memoizable/module_methods/memoized_predicate_spec.rb0000644000076400007640000000070012325376437027143 0ustar pravipravi# encoding: utf-8 require 'spec_helper' describe Memoizable::ModuleMethods, '#memoized?' do let(:object) do Class.new do include Memoizable def foo end memoize :foo end end subject { object.memoized?(name) } context 'with memoized method' do let(:name) { :foo } it { should be(true) } end context 'with non memoized method' do let(:name) { :bar } it { should be(false) } end end memoizable-0.4.2/spec/unit/memoizable/module_methods/included_spec.rb0000644000076400007640000000121312325376437025101 0ustar pravipravi# encoding: utf-8 require 'spec_helper' describe Memoizable::ModuleMethods, '#included' do subject { descendant.instance_exec(object) { |mod| include mod } } let(:object) { Module.new.extend(described_class) } let(:descendant) { Class.new } let(:superclass) { Module } before do # Prevent Module.included from being called through inheritance allow(Memoizable).to receive(:included) end it_behaves_like 'it calls super', :included it 'includes Memoizable into the descendant' do subject expect(descendant.included_modules).to include(Memoizable) end end memoizable-0.4.2/spec/unit/memoizable/class_methods/0000755000076400007640000000000012325376437021576 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/class_methods/included_spec.rb0000644000076400007640000000070312325376437024724 0ustar pravipravi# encoding: utf-8 require 'spec_helper' describe Memoizable, '.included' do subject { object.class_eval { include Memoizable } } let(:object) { Class.new } let(:superclass) { Module } it_behaves_like 'it calls super', :included it 'extends the descendant with module methods' do subject extended_modules = class << object; included_modules end expect(extended_modules).to include(Memoizable::ModuleMethods) end end memoizable-0.4.2/spec/unit/memoizable/instance_methods/0000755000076400007640000000000012325376437022275 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/instance_methods/memoize_spec.rb0000644000076400007640000000171112325376437025301 0ustar pravipravi# encoding: utf-8 require 'spec_helper' require File.expand_path('../../fixtures/classes', __FILE__) describe Memoizable::InstanceMethods, '#memoize' do subject { object.memoize(method => value) } let(:described_class) { Class.new(Fixture::Object) } let(:object) { described_class.new } let(:method) { :test } before do described_class.memoize(method) end context 'when the method is not memoized' do let(:value) { String.new } it 'sets the memoized value for the method to the value' do subject expect(object.send(method)).to be(value) end it_should_behave_like 'a command method' end context 'when the method is already memoized' do let(:value) { double } let(:original) { nil } before do object.memoize(method => original) end it 'raises an exception' do expect { subject }.to raise_error(ArgumentError) end end end memoizable-0.4.2/spec/unit/memoizable/instance_methods/freeze_spec.rb0000644000076400007640000000114212325376437025112 0ustar pravipravi# encoding: utf-8 require 'spec_helper' require File.expand_path('../../fixtures/classes', __FILE__) describe Memoizable::InstanceMethods, '#freeze' do subject { object.freeze } let(:described_class) { Class.new(Fixture::Object) } before do described_class.memoize(:test) end let(:object) { described_class.allocate } it_should_behave_like 'a command method' it 'freezes the object' do expect { subject }.to change(object, :frozen?).from(false).to(true) end it 'allows methods not yet called to be memoized' do subject expect(object.test).to be(object.test) end end memoizable-0.4.2/spec/unit/memoizable/method_builder/0000755000076400007640000000000012325376437021734 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/method_builder/call_spec.rb0000644000076400007640000000452312325376437024212 0ustar pravipravi# encoding: utf-8 require 'spec_helper' require File.expand_path('../../fixtures/classes', __FILE__) describe Memoizable::MethodBuilder, '#call' do subject { object.call } let(:object) { described_class.new(descendant, method_name, freezer) } let(:freezer) { lambda { |object| object.freeze } } let(:instance) { descendant.new } let(:descendant) do Class.new do include Memoizable def public_method __method__.to_s end def protected_method __method__.to_s end protected :protected_method def private_method __method__.to_s end private :private_method end end shared_examples_for 'Memoizable::MethodBuilder#call' do it_should_behave_like 'a command method' it 'creates a method that is memoized' do subject expect(instance.send(method_name)).to be(instance.send(method_name)) end it 'creates a method that returns the expected value' do subject expect(instance.send(method_name)).to eql(method_name.to_s) end it 'creates a method that returns a frozen value' do subject expect(descendant.new.send(method_name)).to be_frozen end it 'creates a method that does not accept a block' do subject expect { descendant.new.send(method_name) {} }.to raise_error( described_class::BlockNotAllowedError, "Cannot pass a block to #{descendant}##{method_name}, it is memoized" ) end end context 'public method' do let(:method_name) { :public_method } it_should_behave_like 'Memoizable::MethodBuilder#call' it 'creates a public memoized method' do subject expect(descendant).to be_public_method_defined(method_name) end end context 'protected method' do let(:method_name) { :protected_method } it_should_behave_like 'Memoizable::MethodBuilder#call' it 'creates a protected memoized method' do subject expect(descendant).to be_protected_method_defined(method_name) end end context 'private method' do let(:method_name) { :private_method } it_should_behave_like 'Memoizable::MethodBuilder#call' it 'creates a private memoized method' do subject expect(descendant).to be_private_method_defined(method_name) end end end memoizable-0.4.2/spec/unit/memoizable/method_builder/original_method_spec.rb0000644000076400007640000000134412325376437026441 0ustar pravipravi# encoding: utf-8 require 'spec_helper' describe Memoizable::MethodBuilder, '#original_method' do subject { object.original_method } let(:object) { described_class.new(descendant, method_name, freezer) } let(:method_name) { :foo } let(:freezer) { lambda { |object| object.freeze } } let(:descendant) do Class.new do def initialize @foo = 0 end def foo @foo += 1 end end end it { should be_instance_of(UnboundMethod) } it 'returns the original method' do # original method is not memoized method = subject.bind(descendant.new) expect(method.call).to_not be(method.call) end end memoizable-0.4.2/spec/unit/memoizable/method_builder/class_methods/0000755000076400007640000000000012325376437024564 5ustar pravipravimemoizable-0.4.2/spec/unit/memoizable/method_builder/class_methods/new_spec.rb0000644000076400007640000000167212325376437026722 0ustar pravipravi# encoding: utf-8 require 'spec_helper' require File.expand_path('../../../fixtures/classes', __FILE__) describe Memoizable::MethodBuilder, '.new' do subject { described_class.new(descendant, method_name, freezer) } let(:descendant) { Fixture::Object } let(:freezer) { lambda { |object| object.freeze } } context 'with a zero arity method' do let(:method_name) { :zero_arity } it { should be_instance_of(described_class) } it 'sets the original method' do # original method is not memoized method = subject.original_method.bind(descendant.new) expect(method.call).to_not be(method.call) end end context 'with a one arity method' do let(:method_name) { :one_arity } it 'raises an exception' do expect { subject }.to raise_error( described_class::InvalidArityError, 'Cannot memoize Fixture::Object#one_arity, its arity is 1' ) end end end memoizable-0.4.2/spec/unit/memoizable/memory_spec.rb0000644000076400007640000000130012325376437021607 0ustar pravipravirequire 'spec_helper' describe Memoizable::Memory do let(:memory) { Memoizable::Memory.new } context "serialization" do let(:deserialized) { Marshal.load(Marshal.dump(memory)) } it 'is serializable with Marshal' do expect { Marshal.dump(memory) }.not_to raise_error end it 'is deserializable with Marshal' do expect(deserialized).to be_an_instance_of(Memoizable::Memory) end it 'mantains the same class of cache when deserialized' do original_cache = memory.instance_variable_get(:@memory) deserialized_cache = deserialized.instance_variable_get(:@memory) expect(deserialized_cache.class).to eql(original_cache.class) end end end memoizable-0.4.2/LICENSE.md0000644000076400007640000000206012325376437014313 0ustar pravipraviCopyright (c) 2013 Dan Kubb, Erik Michaels-Ober 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. memoizable-0.4.2/memoizable.gemspec0000644000076400007640000000162612325376437016407 0ustar pravipravi# encoding: utf-8 require File.expand_path('../lib/memoizable/version', __FILE__) Gem::Specification.new do |gem| gem.name = 'memoizable' gem.version = Memoizable::VERSION.dup gem.authors = ['Dan Kubb', 'Erik Michaels-Ober'] gem.email = ['dan.kubb@gmail.com', 'sferik@gmail.com'] gem.description = 'Memoize method return values' gem.summary = gem.description gem.homepage = 'https://github.com/dkubb/memoizable' gem.license = 'MIT' gem.require_paths = %w[lib] gem.files = %w[CONTRIBUTING.md LICENSE.md README.md Rakefile memoizable.gemspec] gem.files += Dir.glob('{lib,spec}/**/*.rb') gem.test_files = Dir.glob('spec/{unit,integration}/**/*.rb') gem.extra_rdoc_files = Dir.glob('**/*.md') gem.add_runtime_dependency('thread_safe', '~> 0.3', '>= 0.3.1') gem.add_development_dependency('bundler', '~> 1.5', '>= 1.5.3') end memoizable-0.4.2/CONTRIBUTING.md0000644000076400007640000000202412325376437015140 0ustar pravipraviContributing ------------ * If you want your code merged into the mainline, please discuss the proposed changes with me before doing any work on it. This library is still in early development, and the direction it is going may not always be clear. Some features may not be appropriate yet, may need to be deferred until later when the foundation for them is laid, or may be more applicable in a plugin. * Fork the project. * Make your feature addition or bug fix. * Follow this [style guide](https://github.com/dkubb/styleguide). * Add specs for it. This is important so I don't break it in a future version unintentionally. Tests must cover all branches within the code, and code must be fully covered. * Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Run "rake ci". This must pass and not show any regressions in the metrics for the code to be merged. * Send me a pull request. Bonus points for topic branches. memoizable-0.4.2/checksums.yaml.gz0000444000076400007640000000041512325376437016177 0ustar pravipravi23Se;N@ D"7=',)d7YK{:"GI QN1dl9ܓyד۹ }09B`Z^2iK2ҿ6qrj!J3sap^)uj84Ap)-pCf-ͬBLG9huxGPFRIҮ Gk\eΨ2 cFbI`{;8y ~cmemoizable-0.4.2/README.md0000644000076400007640000000677612325376437014210 0ustar pravipravi# Memoizable [![Gem Version](http://img.shields.io/gem/v/memoizable.svg)][gem] [![Build Status](http://img.shields.io/travis/dkubb/memoizable.svg)][travis] [![Dependency Status](http://img.shields.io/gemnasium/dkubb/memoizable.svg)][gemnasium] [![Code Climate](http://img.shields.io/codeclimate/github/dkubb/memoizable.svg)][codeclimate] [![Coverage Status](http://img.shields.io/coveralls/dkubb/memoizable.svg)][coveralls] [gem]: https://rubygems.org/gems/memoizable [travis]: https://travis-ci.org/dkubb/memoizable [gemnasium]: https://gemnasium.com/dkubb/memoizable [codeclimate]: https://codeclimate.com/github/dkubb/memoizable [coveralls]: https://coveralls.io/r/dkubb/memoizable Memoize method return values ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for details. ## Rationale Memoization is an optimization that saves the return value of a method so it doesn't need to be re-computed every time that method is called. For example, perhaps you've written a method like this: ```ruby class Planet # This is the equation for the area of a sphere. If it's true for a # particular instance of a planet, then that planet is spherical. def spherical? 4 * Math::PI * radius ** 2 == area end end ``` This code will re-compute whether a particular planet is spherical every time the method is called. If the method is called more than once, it may be more efficient to save the computed value in an instance variable, like so: ```ruby class Planet def spherical? @spherical ||= 4 * Math::PI * radius ** 2 == area end end ``` One problem with this approach is that, if the return value is `false`, the value will still be computed each time the method is called. It also becomes unweildy for methods that grow to be longer than one line. These problems can be solved by mixing-in the `Memoizable` module and memoizing the method. ```ruby require 'memoizable' class Planet include Memoizable def spherical? 4 * Math::PI * radius ** 2 == area end memoize :spherical? end ``` ## Warning The example above assumes that the radius and area of a planet will not change over time. This seems like a reasonable assumption but such an assumption is not safe in every domain. If it was possible for one of the attributes to change between method calls, memoizing that value could produce the wrong result. Please keep this in mind when considering which methods to memoize. Supported Ruby Versions ----------------------- This library aims to support and is [tested against][travis] the following Ruby implementations: * Ruby 1.8.7 * Ruby 1.9.2 * Ruby 1.9.3 * Ruby 2.0.0 * Ruby 2.1.0 * [JRuby][] * [Rubinius][] * [Ruby Enterprise Edition][ree] [jruby]: http://jruby.org/ [rubinius]: http://rubini.us/ [ree]: http://www.rubyenterpriseedition.com/ If something doesn't work on one of these versions, it's a bug. This library may inadvertently work (or seem to work) on other Ruby versions or implementations, however support will only be provided for the implementations listed above. If you would like this library to support another Ruby version or implementation, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Copyright Copyright © 2013 Dan Kubb, Erik Michaels-Ober. See LICENSE for details. memoizable-0.4.2/metadata.yml0000644000076400007640000000730712325376437015223 0ustar pravipravi--- !ruby/object:Gem::Specification name: memoizable version: !ruby/object:Gem::Version version: 0.4.2 platform: ruby authors: - Dan Kubb - Erik Michaels-Ober autorequire: bindir: bin cert_chain: [] date: 2014-03-27 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: thread_safe requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.3' - - '>=' - !ruby/object:Gem::Version version: 0.3.1 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.3' - - '>=' - !ruby/object:Gem::Version version: 0.3.1 - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.5' - - '>=' - !ruby/object:Gem::Version version: 1.5.3 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.5' - - '>=' - !ruby/object:Gem::Version version: 1.5.3 description: Memoize method return values email: - dan.kubb@gmail.com - sferik@gmail.com executables: [] extensions: [] extra_rdoc_files: - CONTRIBUTING.md - LICENSE.md - README.md files: - CONTRIBUTING.md - LICENSE.md - README.md - Rakefile - lib/memoizable.rb - lib/memoizable/instance_methods.rb - lib/memoizable/memory.rb - lib/memoizable/method_builder.rb - lib/memoizable/module_methods.rb - lib/memoizable/version.rb - memoizable.gemspec - spec/integration/serializable_spec.rb - spec/shared/call_super_shared_spec.rb - spec/shared/command_method_behavior.rb - spec/spec_helper.rb - spec/unit/memoizable/class_methods/included_spec.rb - spec/unit/memoizable/fixtures/classes.rb - spec/unit/memoizable/instance_methods/freeze_spec.rb - spec/unit/memoizable/instance_methods/memoize_spec.rb - spec/unit/memoizable/memory_spec.rb - spec/unit/memoizable/method_builder/call_spec.rb - spec/unit/memoizable/method_builder/class_methods/new_spec.rb - spec/unit/memoizable/method_builder/original_method_spec.rb - spec/unit/memoizable/module_methods/included_spec.rb - spec/unit/memoizable/module_methods/memoize_spec.rb - spec/unit/memoizable/module_methods/memoized_predicate_spec.rb - spec/unit/memoizable/module_methods/unmemoized_instance_method_spec.rb homepage: https://github.com/dkubb/memoizable licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: Memoize method return values test_files: - spec/unit/memoizable/class_methods/included_spec.rb - spec/unit/memoizable/fixtures/classes.rb - spec/unit/memoizable/instance_methods/freeze_spec.rb - spec/unit/memoizable/instance_methods/memoize_spec.rb - spec/unit/memoizable/memory_spec.rb - spec/unit/memoizable/method_builder/call_spec.rb - spec/unit/memoizable/method_builder/class_methods/new_spec.rb - spec/unit/memoizable/method_builder/original_method_spec.rb - spec/unit/memoizable/module_methods/included_spec.rb - spec/unit/memoizable/module_methods/memoize_spec.rb - spec/unit/memoizable/module_methods/memoized_predicate_spec.rb - spec/unit/memoizable/module_methods/unmemoized_instance_method_spec.rb - spec/integration/serializable_spec.rb has_rdoc: memoizable-0.4.2/lib/0000755000076400007640000000000012325376437013457 5ustar pravipravimemoizable-0.4.2/lib/memoizable.rb0000644000076400007640000000125012325376437016126 0ustar pravipravi# encoding: utf-8 require 'monitor' require 'thread_safe' require 'memoizable/instance_methods' require 'memoizable/method_builder' require 'memoizable/module_methods' require 'memoizable/memory' require 'memoizable/version' # Allow methods to be memoized module Memoizable include InstanceMethods # Default freezer Freezer = lambda { |object| object.freeze }.freeze # Hook called when module is included # # @param [Module] descendant # the module or class including Memoizable # # @return [self] # # @api private def self.included(descendant) super descendant.extend(ModuleMethods) end private_class_method :included end # Memoizable memoizable-0.4.2/lib/memoizable/0000755000076400007640000000000012325376437015603 5ustar pravipravimemoizable-0.4.2/lib/memoizable/method_builder.rb0000644000076400007640000000675612325376437021134 0ustar pravipravi# encoding: utf-8 module Memoizable # Build the memoized method class MethodBuilder # Raised when the method arity is invalid class InvalidArityError < ArgumentError # Initialize an invalid arity exception # # @param [Module] descendant # @param [Symbol] method # @param [Integer] arity # # @api private def initialize(descendant, method, arity) super("Cannot memoize #{descendant}##{method}, its arity is #{arity}") end end # InvalidArityError # Raised when a block is passed to a memoized method class BlockNotAllowedError < ArgumentError # Initialize a block not allowed exception # # @param [Module] descendant # @param [Symbol] method # # @api private def initialize(descendant, method) super("Cannot pass a block to #{descendant}##{method}, it is memoized") end end # BlockNotAllowedError # The original method before memoization # # @return [UnboundMethod] # # @api public attr_reader :original_method # Initialize an object to build a memoized method # # @param [Module] descendant # @param [Symbol] method_name # @param [#call] freezer # # @return [undefined] # # @api private def initialize(descendant, method_name, freezer) @descendant = descendant @method_name = method_name @freezer = freezer @original_visibility = visibility @original_method = @descendant.instance_method(@method_name) assert_arity(@original_method.arity) end # Build a new memoized method # # @example # method_builder.call # => creates new method # # @return [MethodBuilder] # # @api public def call remove_original_method create_memoized_method set_method_visibility self end private # Assert the method arity is zero # # @param [Integer] arity # # @return [undefined] # # @raise [InvalidArityError] # # @api private def assert_arity(arity) if arity.nonzero? fail InvalidArityError.new(@descendant, @method_name, arity) end end # Remove the original method # # @return [undefined] # # @api private def remove_original_method name = @method_name @descendant.module_eval { undef_method(name) } end # Create a new memoized method # # @return [undefined] # # @api private def create_memoized_method name, method, freezer = @method_name, @original_method, @freezer @descendant.module_eval do define_method(name) do |&block| fail BlockNotAllowedError.new(self.class, name) if block memoized_method_cache.fetch(name) do freezer.call(method.bind(self).call) end end end end # Set the memoized method visibility to match the original method # # @return [undefined] # # @api private def set_method_visibility @descendant.send(@original_visibility, @method_name) end # Get the visibility of the original method # # @return [Symbol] # # @api private def visibility if @descendant.private_method_defined?(@method_name) then :private elsif @descendant.protected_method_defined?(@method_name) then :protected else :public end end end # MethodBuilder end # Memoizable memoizable-0.4.2/lib/memoizable/memory.rb0000644000076400007640000000416612325376437017447 0ustar pravipravi# encoding: utf-8 module Memoizable # Storage for memoized methods class Memory # Initialize the memory storage for memoized methods # # @param [ThreadSafe::Cache] memory # # @return [undefined] # # @api private def initialize @memory = ThreadSafe::Cache.new @monitor = Monitor.new freeze end # Get the value from memory # # @param [Symbol] name # # @return [Object] # # @api public def [](name) @memory.fetch(name) do fail NameError, "No method #{name} is memoized" end end # Store the value in memory # # @param [Symbol] name # @param [Object] value # # @return [undefined] # # @api public def []=(name, value) memoized = true @memory.compute_if_absent(name) do memoized = false value end fail ArgumentError, "The method #{name} is already memoized" if memoized end # Fetch the value from memory, or store it if it does not exist # # @param [Symbol] name # # @yieldreturn [Object] # the value to memoize # # @api public def fetch(name) @memory.fetch(name) do # check for the key @monitor.synchronize do # acquire a lock if the key is not found @memory.fetch(name) do # recheck under lock self[name] = yield # set the value end end end end # Test if the name has a value in memory # # @param [Symbol] name # # @return [Boolean] # # @api public def key?(name) @memory.key?(name) end # A hook that allows Marshal to dump the object # # @return [Hash] # A hash used to populate the internal memory # # @api public def marshal_dump @memory.marshal_dump end # A hook that allows Marshal to load the object # # @param [Hash] hash # A hash used to populate the internal memory # # @return [undefined] # # @api public def marshal_load(hash) initialize @memory.marshal_load(hash) end end # Memory end # Memoizable memoizable-0.4.2/lib/memoizable/version.rb0000644000076400007640000000014312325376437017613 0ustar pravipravi# encoding: utf-8 module Memoizable # Gem version VERSION = '0.4.2'.freeze end # Memoizable memoizable-0.4.2/lib/memoizable/module_methods.rb0000644000076400007640000000450512325376437021144 0ustar pravipravi# encoding: utf-8 module Memoizable # Methods mixed in to memoizable singleton classes module ModuleMethods # Return default deep freezer # # @return [#call] # # @api private def freezer Freezer end # Memoize a list of methods # # @example # memoize :hash # # @param [Array] methods # a list of methods to memoize # # @return [self] # # @api public def memoize(*methods) methods.each(&method(:memoize_method)) self end # Test if an instance method is memoized # # @example # class Foo # include Memoizable # # def bar # end # memoize :bar # end # # Foo.memoized?(:bar) # true # Foo.memoized?(:baz) # false # # @param [Symbol] name # # @return [Boolean] # true if method is memoized, false if not # # @api private def memoized?(name) memoized_methods.key?(name) end # Return unmemoized instance method # # @example # # class Foo # include Memoizable # # def bar # end # memoize :bar # end # # Foo.unmemoized_instance_method(:bar) # # @param [Symbol] name # # @return [UnboundMethod] # the memoized method # # @raise [NameError] # raised if the method is unknown # # @api public def unmemoized_instance_method(name) memoized_methods[name].original_method end private # Hook called when module is included # # @param [Module] descendant # the module including ModuleMethods # # @return [self] # # @api private def included(descendant) super descendant.module_eval { include Memoizable } end # Memoize the named method # # @param [Symbol] method_name # a method name to memoize # # @return [undefined] # # @api private def memoize_method(method_name) memoized_methods[method_name] = MethodBuilder.new( self, method_name, freezer ).call end # Return method builder registry # # @return [Hash] # # @api private def memoized_methods @_memoized_methods ||= Memory.new end end # ModuleMethods end # Memoizable memoizable-0.4.2/lib/memoizable/instance_methods.rb0000644000076400007640000000161012325376437021455 0ustar pravipravi# encoding: utf-8 module Memoizable # Methods mixed in to memoizable instances module InstanceMethods # Freeze the object # # @example # object.freeze # object is now frozen # # @return [Object] # # @api public def freeze memoized_method_cache # initialize method cache super end # Sets a memoized value for a method # # @example # object.memoize(hash: 12345) # # @param [Hash{Symbol => Object}] data # the data to memoize # # @return [self] # # @api public def memoize(data) data.each { |name, value| memoized_method_cache[name] = value } self end private # The memoized method results # # @return [Hash] # # @api private def memoized_method_cache @_memoized_method_cache ||= Memory.new end end # InstanceMethods end # Memoizable memoizable-0.4.2/Rakefile0000644000076400007640000000026612325376437014362 0ustar pravipravi# encoding: utf-8 require 'bundler' require 'rspec/core/rake_task' Bundler::GemHelper.install_tasks RSpec::Core::RakeTask.new(:spec) task :test => :spec task :default => :spec