memoizable 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,11 @@
1
+ Contributing
2
+ ------------
3
+
4
+ * 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.
5
+ * Fork the project.
6
+ * Make your feature addition or bug fix.
7
+ * Follow this [style guide](https://github.com/dkubb/styleguide).
8
+ * 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.
9
+ * 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)
10
+ * Run "rake ci". This must pass and not show any regressions in the metrics for the code to be merged.
11
+ * Send me a pull request. Bonus points for topic branches.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Enrique Comba Riepenhausen
1
+ Copyright (c) 2013 Dan Kubb, Erik Michaels-Ober
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # memoizable
2
+
3
+ Memoize method return values
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/memoizable.png)][gem]
6
+ [![Build Status](https://secure.travis-ci.org/dkubb/memoizable.png?branch=master)][travis]
7
+ [![Dependency Status](https://gemnasium.com/dkubb/memoizable.png)][gemnasium]
8
+ [![Code Climate](https://codeclimate.com/github/dkubb/memoizable.png)][codeclimate]
9
+ [![Coverage Status](https://coveralls.io/repos/dkubb/memoizable/badge.png?branch=master)][coveralls]
10
+
11
+ [gem]: https://rubygems.org/gems/memoizable
12
+ [travis]: https://travis-ci.org/dkubb/memoizable
13
+ [gemnasium]: https://gemnasium.com/dkubb/memoizable
14
+ [codeclimate]: https://codeclimate.com/github/dkubb/memoizable
15
+ [coveralls]: https://coveralls.io/r/dkubb/memoizable
16
+
17
+ ## Contributing
18
+
19
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
20
+
21
+ ## Copyright
22
+
23
+ Copyright © 2013 Dan Kubb, Erik Michaels-Ober. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,48 +1,8 @@
1
- require 'rubygems'
2
- require 'rake'
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
3
 
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "memoizable"
8
- gem.summary = %Q{Memoize method calls}
9
- gem.description = %Q{Memoizes calls to method to boost the performance of recursive calls}
10
- gem.email = "ecomba@nexwerk.com"
11
- gem.homepage = "http://github.com/ecomba/memoizable"
12
- gem.authors = ["Enrique Comba Riepenhausen"]
13
- gem.add_development_dependency "rspec"
14
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
- end
16
- rescue LoadError
17
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
- end
19
-
20
- require 'spec/rake/spectask'
21
- Spec::Rake::SpecTask.new(:spec) do |spec|
22
- spec.libs << 'lib' << 'spec'
23
- spec.spec_files = FileList['spec/**/*_spec.rb']
24
- end
25
-
26
- Spec::Rake::SpecTask.new(:rcov) do |spec|
27
- spec.libs << 'lib' << 'spec'
28
- spec.pattern = 'spec/**/*_spec.rb'
29
- spec.rcov = true
30
- end
31
-
32
- task :spec => :check_dependencies
4
+ Bundler::GemHelper.install_tasks
5
+ RSpec::Core::RakeTask.new(:spec)
33
6
 
7
+ task :test => :spec
34
8
  task :default => :spec
35
-
36
- require 'rake/rdoctask'
37
- Rake::RDocTask.new do |rdoc|
38
- if File.exist?('VERSION')
39
- version = File.read('VERSION')
40
- else
41
- version = ""
42
- end
43
-
44
- rdoc.rdoc_dir = 'rdoc'
45
- rdoc.title = "memoizable #{version}"
46
- rdoc.rdoc_files.include('README*')
47
- rdoc.rdoc_files.include('lib/**/*.rb')
48
- end
@@ -0,0 +1,47 @@
1
+ module Memoizable
2
+
3
+ # Methods mixed in to memoizable instances
4
+ module InstanceMethods
5
+
6
+ # Freeze the object
7
+ #
8
+ # @example
9
+ # object.freeze # object is now frozen
10
+ #
11
+ # @return [Object]
12
+ #
13
+ # @api public
14
+ def freeze
15
+ memoized_method_cache # initialize method cache
16
+ super
17
+ end
18
+
19
+ # Sets a memoized value for a method
20
+ #
21
+ # @example
22
+ # object.memoize(hash: 12345)
23
+ #
24
+ # @param [Hash{Symbol => Object}] data
25
+ # the data to memoize
26
+ #
27
+ # @return [self]
28
+ #
29
+ # @api public
30
+ def memoize(data)
31
+ memoized_method_cache.set(data)
32
+ self
33
+ end
34
+
35
+ private
36
+
37
+ # The memoized method results
38
+ #
39
+ # @return [Hash]
40
+ #
41
+ # @api private
42
+ def memoized_method_cache
43
+ @_memoized_method_cache ||= Memory.new(self.class.freezer)
44
+ end
45
+
46
+ end # InstanceMethods
47
+ end # Memoizable
@@ -0,0 +1,88 @@
1
+ module Memoizable
2
+
3
+ # Storage for memoized methods
4
+ class Memory
5
+
6
+ def initialize(freezer)
7
+ @memory = ThreadSafe::Cache.new
8
+ @freezer = freezer
9
+ end
10
+
11
+ # Get the value from memory
12
+ #
13
+ # @param [Symbol] name
14
+ #
15
+ # @return [Object]
16
+ #
17
+ # @api public
18
+ def [](name)
19
+ @memory.fetch(name) do
20
+ raise NameError, "No method #{name.inspect} was memoized"
21
+ end
22
+ end
23
+
24
+ # Store the value in memory
25
+ #
26
+ # @param [Symbol] name
27
+ # @param [Object] value
28
+ #
29
+ # @return [undefined]
30
+ #
31
+ # @api public
32
+ def []=(name, value)
33
+ if @memory.key?(name)
34
+ raise ArgumentError, "The method #{name} is already memoized"
35
+ end
36
+ @memory[name] = freeze_value(value)
37
+ end
38
+
39
+ # Fetch the value from memory, or store it if it does not exist
40
+ #
41
+ # @param [Symbol] name
42
+ #
43
+ # @yieldreturn [Object]
44
+ # the value to memoize
45
+ #
46
+ # @api public
47
+ def fetch(name)
48
+ @memory.fetch(name) { self[name] = yield }
49
+ end
50
+
51
+ # Set the memory
52
+ #
53
+ # @param [Hash]
54
+ #
55
+ # @return [Memory]
56
+ #
57
+ # @api public
58
+ def set(data)
59
+ data.each { |name, value| self[name] = value }
60
+ self
61
+ end
62
+
63
+ # Test if the name has a value in memory
64
+ #
65
+ # @param [Symbol] name
66
+ #
67
+ # @return [Boolean]
68
+ #
69
+ # @api public
70
+ def key?(name)
71
+ @memory.key?(name)
72
+ end
73
+
74
+ private
75
+
76
+ # Freeze the value
77
+ #
78
+ # @param [Object] value
79
+ #
80
+ # @return [Object]
81
+ #
82
+ # @api private
83
+ def freeze_value(value)
84
+ @freezer.call(value)
85
+ end
86
+
87
+ end # Memory
88
+ end # Memoizable
@@ -0,0 +1,133 @@
1
+ module Memoizable
2
+
3
+ # Build the memoized method
4
+ class MethodBuilder
5
+
6
+ # Raised when the method arity is invalid
7
+ class InvalidArityError < ArgumentError
8
+
9
+ # Initialize an invalid arity exception
10
+ #
11
+ # @param [Module] descendant
12
+ # @param [Symbol] method
13
+ # @param [Integer] arity
14
+ #
15
+ # @api private
16
+ def initialize(descendant, method, arity)
17
+ super("Cannot memoize #{descendant}##{method}, it's arity is #{arity}")
18
+ end
19
+
20
+ end # InvalidArityError
21
+
22
+ # The original method before memoization
23
+ #
24
+ # @return [UnboundMethod]
25
+ #
26
+ # @api public
27
+ attr_reader :original_method
28
+
29
+ # Initialize an object to build a memoized method
30
+ #
31
+ # @param [Module] descendant
32
+ # @param [Symbol] method_name
33
+ #
34
+ # @return [undefined]
35
+ #
36
+ # @api private
37
+ def initialize(descendant, method_name)
38
+ @descendant = descendant
39
+ @method_name = method_name
40
+ @original_visibility = visibility
41
+ @original_method = @descendant.instance_method(@method_name)
42
+ assert_zero_arity
43
+ end
44
+
45
+ # Build a new memoized method
46
+ #
47
+ # @example
48
+ # method_builder.call # => creates new method
49
+ #
50
+ # @return [MethodBuilder]
51
+ #
52
+ # @api public
53
+ def call
54
+ remove_original_method
55
+ create_memoized_method
56
+ set_method_visibility
57
+ self
58
+ end
59
+
60
+ private
61
+
62
+ # Assert the method arity is zero
63
+ #
64
+ # @return [undefined]
65
+ #
66
+ # @raise [InvalidArityError]
67
+ #
68
+ # @api private
69
+ def assert_zero_arity
70
+ arity = @original_method.arity
71
+ if arity.nonzero?
72
+ raise InvalidArityError.new(@descendant, @method_name, arity)
73
+ end
74
+ end
75
+
76
+ # Remove the original method
77
+ #
78
+ # @return [undefined]
79
+ #
80
+ # @api private
81
+ def remove_original_method
82
+ descendant_exec(@method_name) { |name| undef_method(name) }
83
+ end
84
+
85
+ # Create a new memoized method
86
+ #
87
+ # @return [undefined]
88
+ #
89
+ # @api private
90
+ def create_memoized_method
91
+ descendant_exec(@method_name, @original_method) do |name, method|
92
+ define_method(name) do ||
93
+ memoized_method_cache.fetch(name, &method.bind(self))
94
+ end
95
+ end
96
+ end
97
+
98
+ # Set the memoized method visibility to match the original method
99
+ #
100
+ # @return [undefined]
101
+ #
102
+ # @api private
103
+ def set_method_visibility
104
+ descendant_exec(@method_name, @original_visibility) do |name, visibility|
105
+ send(visibility, name)
106
+ end
107
+ end
108
+
109
+ # Get the visibility of the original method
110
+ #
111
+ # @return [Symbol]
112
+ #
113
+ # @api private
114
+ def visibility
115
+ if @descendant.private_method_defined?(@method_name) then :private
116
+ elsif @descendant.protected_method_defined?(@method_name) then :protected
117
+ else :public
118
+ end
119
+ end
120
+
121
+ # Helper method to execute code within the descendant scope
122
+ #
123
+ # @param [Array] args
124
+ #
125
+ # @return [undefined]
126
+ #
127
+ # @api private
128
+ def descendant_exec(*args, &block)
129
+ @descendant.instance_exec(*args, &block)
130
+ end
131
+
132
+ end # MethodBuilder
133
+ end # Memoizable
@@ -0,0 +1,110 @@
1
+ module Memoizable
2
+
3
+ # Methods mixed in to memoizable singleton classes
4
+ module ModuleMethods
5
+
6
+ # Return default deep freezer
7
+ #
8
+ # @return [#call]
9
+ #
10
+ # @api private
11
+ #
12
+ def freezer
13
+ Freezer
14
+ end
15
+
16
+ # Memoize a list of methods
17
+ #
18
+ # @example
19
+ # memoize :hash
20
+ #
21
+ # @param [Array<Symbol>] methods
22
+ # a list of methods to memoize
23
+ #
24
+ # @return [self]
25
+ #
26
+ # @api public
27
+ def memoize(*methods)
28
+ methods.each(&method(:memoize_method))
29
+ self
30
+ end
31
+
32
+ # Test if an instance method is memoized
33
+ #
34
+ # @example
35
+ # class Foo
36
+ # include Memoizable
37
+ #
38
+ # def bar
39
+ # end
40
+ # memoize :bar
41
+ # end
42
+ #
43
+ # Foo.memoized?(:bar) # true
44
+ # Foo.memoized?(:baz) # false
45
+ #
46
+ # @param [Symbol] name
47
+ #
48
+ # @return [Boolean]
49
+ # true if method is memoized, false if not
50
+ #
51
+ # @api private
52
+ def memoized?(name)
53
+ memoized_methods.key?(name)
54
+ end
55
+
56
+ # Return unmemoized instance method
57
+ #
58
+ # @example
59
+ #
60
+ # class Foo
61
+ # include Memoizable
62
+ #
63
+ # def bar
64
+ # end
65
+ # memoize :bar
66
+ # end
67
+ #
68
+ # Foo.unmemoized_instance_method(:bar)
69
+ #
70
+ # @param [Symbol] name
71
+ #
72
+ # @return [UnboundMethod]
73
+ # the memoized method
74
+ #
75
+ # @raise [NameError]
76
+ # raised if the method is unknown
77
+ #
78
+ # @api public
79
+ def unmemoized_instance_method(name)
80
+ memoized_methods[name].original_method
81
+ end
82
+
83
+ private
84
+
85
+ # Memoize the named method
86
+ #
87
+ # @param [Symbol] method_name
88
+ # a method name to memoize
89
+ # @param [#call] freezer
90
+ # a freezer for memoized values
91
+ #
92
+ # @return [undefined]
93
+ #
94
+ # @api private
95
+ def memoize_method(method_name)
96
+ memoized_methods[method_name] = MethodBuilder.new(self, method_name).call
97
+ end
98
+
99
+ # Return method builder registry
100
+ #
101
+ # @return [Hash<Symbol, MethodBuilder>]
102
+ #
103
+ # @api private
104
+ #
105
+ def memoized_methods
106
+ @_memoized_methods ||= Memory.new(freezer)
107
+ end
108
+
109
+ end # ModuleMethods
110
+ end # Memoizable
@@ -0,0 +1,6 @@
1
+ module Memoizable
2
+
3
+ # Gem version
4
+ VERSION = '0.2.0'.freeze
5
+
6
+ end # Memoizable
data/lib/memoizable.rb CHANGED
@@ -1,17 +1,30 @@
1
+ require 'thread_safe'
2
+
3
+ require 'memoizable/instance_methods'
4
+ require 'memoizable/method_builder'
5
+ require 'memoizable/module_methods'
6
+ require 'memoizable/memory'
7
+ require 'memoizable/version'
8
+
9
+ # Allow methods to be memoized
1
10
  module Memoizable
2
- CACHE = Hash.new
3
- module ClassMethods
4
- def memoize(name)
5
- original = "__original__#{name}"
6
- alias_method original, name
7
- define_method(name) do |*args|
8
- CACHE[self.to_s.unpack("a*")<<name.to_s.unpack("a*")<<args] ||=
9
- send(original, *args)
10
- end
11
+
12
+ # Default freezer
13
+ Freezer = lambda { |object| object.freeze }.freeze
14
+
15
+ # Hook called when module is included
16
+ #
17
+ # @param [Module] descendant
18
+ # the module or class including Memoizable
19
+ #
20
+ # @return [self]
21
+ #
22
+ # @api private
23
+ def self.included(descendant)
24
+ descendant.module_eval do
25
+ extend ModuleMethods
26
+ include InstanceMethods
11
27
  end
12
28
  end
13
-
14
- def self.included(receiver)
15
- receiver.extend ClassMethods
16
- end
17
- end
29
+
30
+ end # Memoizable
data/memoizable.gemspec CHANGED
@@ -1,56 +1,23 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
1
+ require File.expand_path('../lib/memoizable/version', __FILE__)
5
2
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{memoizable}
8
- s.version = "0.1.0"
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'memoizable'
5
+ gem.version = Memoizable::VERSION.dup
6
+ gem.authors = ['Dan Kubb']
7
+ gem.email = 'dan.kubb@gmail.com'
8
+ gem.description = 'Memoize method return values'
9
+ gem.summary = gem.description
10
+ gem.homepage = 'https://github.com/dkubb/memoizable'
11
+ gem.license = 'MIT'
9
12
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Enrique Comba Riepenhausen"]
12
- s.date = %q{2009-12-21}
13
- s.description = %q{Memoizes calls to method to boost the performance of recursive calls}
14
- s.email = %q{ecomba@nexwerk.com}
15
- s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.rdoc",
24
- "Rakefile",
25
- "VERSION.yml",
26
- "lib/memoizable.rb",
27
- "memoizable.gemspec",
28
- "spec/fibonacci_sample_spec.rb",
29
- "spec/memoizable_spec.rb",
30
- "spec/spec_helper.rb"
31
- ]
32
- s.homepage = %q{http://github.com/ecomba/memoizable}
33
- s.rdoc_options = ["--charset=UTF-8"]
34
- s.require_paths = ["lib"]
35
- s.rubygems_version = %q{1.3.5}
36
- s.summary = %q{Memoize method calls}
37
- s.test_files = [
38
- "spec/fibonacci_sample_spec.rb",
39
- "spec/memoizable_spec.rb",
40
- "spec/spec_helper.rb"
41
- ]
13
+ gem.require_paths = %w[lib]
14
+ gem.files = %w[CONTRIBUTING.md LICENSE.md README.md Rakefile memoizable.gemspec]
15
+ gem.files += Dir.glob('lib/**/*.rb')
16
+ gem.files += Dir.glob('spec/**/*')
17
+ gem.test_files = Dir.glob('spec/**/*')
18
+ gem.extra_rdoc_files = %w[LICENSE.md README.md CONTRIBUTING.md]
42
19
 
43
- if s.respond_to? :specification_version then
44
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
- s.specification_version = 3
20
+ gem.add_runtime_dependency('thread_safe', '~> 0.1.3')
46
21
 
47
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
48
- s.add_development_dependency(%q<rspec>, [">= 0"])
49
- else
50
- s.add_dependency(%q<rspec>, [">= 0"])
51
- end
52
- else
53
- s.add_dependency(%q<rspec>, [">= 0"])
54
- end
22
+ gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
55
23
  end
56
-
@@ -0,0 +1,32 @@
1
+ module MemoizableSpecs
2
+ class Object
3
+ include Memoizable
4
+
5
+ def required_arguments(foo)
6
+ end
7
+
8
+ def optional_arguments(foo = nil)
9
+ end
10
+
11
+ def test
12
+ 'test'
13
+ end
14
+
15
+ def public_method
16
+ caller
17
+ end
18
+
19
+ protected
20
+
21
+ def protected_method
22
+ caller
23
+ end
24
+
25
+ private
26
+
27
+ def private_method
28
+ caller
29
+ end
30
+
31
+ end # class Object
32
+ end # module MemoizableSpecs
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+ require File.expand_path('../fixtures/classes', __FILE__)
3
+
4
+ shared_examples_for 'memoizes method' do
5
+ it 'memoizes the instance method' do
6
+ subject
7
+ instance = object.new
8
+ expect(instance.send(method)).to be(instance.send(method))
9
+ end
10
+
11
+ it 'creates a method that returns a same value' do
12
+ subject
13
+ instance = object.new
14
+ expect(instance.send(method)).to be(instance.send(method))
15
+ end
16
+
17
+ it 'creates a method with an arity of 0' do
18
+ subject
19
+ expect(object.new.method(method).arity).to be_zero
20
+ end
21
+
22
+ context 'when the initializer calls the memoized method' do
23
+ before do
24
+ method = self.method
25
+ object.send(:define_method, :initialize) { send(method) }
26
+ end
27
+
28
+ it 'allows the memoized method to be called within the initializer' do
29
+ subject
30
+ expect { object.new }.to_not raise_error
31
+ end
32
+ end
33
+ end
34
+
35
+ shared_examples_for 'a command method' do
36
+ it 'returns self' do
37
+ should equal(object)
38
+ end
39
+ end
40
+
41
+ describe Memoizable::ModuleMethods, '#memoize' do
42
+ subject { object.memoize(method) }
43
+
44
+ let(:object) do
45
+ stub_const 'TestClass', Class.new(MemoizableSpecs::Object) {
46
+ def some_state
47
+ Object.new
48
+ end
49
+ }
50
+ end
51
+
52
+ context 'on method with required arguments' do
53
+ let(:method) { :required_arguments }
54
+
55
+ it 'should raise error' do
56
+ expect { subject }.to raise_error(
57
+ Memoizable::MethodBuilder::InvalidArityError,
58
+ "Cannot memoize TestClass#required_arguments, it's arity is 1"
59
+ )
60
+ end
61
+ end
62
+
63
+ context 'on method with optional arguments' do
64
+ let(:method) { :optional_arguments }
65
+
66
+ it 'should raise error' do
67
+ expect { subject }.to raise_error(
68
+ Memoizable::MethodBuilder::InvalidArityError,
69
+ "Cannot memoize TestClass#optional_arguments, it's arity is -1"
70
+ )
71
+ end
72
+ end
73
+
74
+ context 'memoized method that returns generated values' do
75
+ let(:method) { :some_state }
76
+
77
+ it_should_behave_like 'a command method'
78
+ it_should_behave_like 'memoizes method'
79
+
80
+ it 'creates a method that returns a frozen value' do
81
+ subject
82
+ expect(object.new.send(method)).to be_frozen
83
+ end
84
+ end
85
+
86
+ context 'public method' do
87
+ let(:method) { :public_method }
88
+
89
+ it_should_behave_like 'a command method'
90
+ it_should_behave_like 'memoizes method'
91
+
92
+ it 'is still a public method' do
93
+ should be_public_method_defined(method)
94
+ end
95
+
96
+ it 'creates a method that returns a frozen value' do
97
+ subject
98
+ expect(object.new.send(method)).to be_frozen
99
+ end
100
+ end
101
+
102
+ context 'protected method' do
103
+ let(:method) { :protected_method }
104
+
105
+ it_should_behave_like 'a command method'
106
+ it_should_behave_like 'memoizes method'
107
+
108
+ it 'is still a protected method' do
109
+ should be_protected_method_defined(method)
110
+ end
111
+
112
+ it 'creates a method that returns a frozen value' do
113
+ subject
114
+ expect(object.new.send(method)).to be_frozen
115
+ end
116
+ end
117
+
118
+ context 'private method' do
119
+ let(:method) { :private_method }
120
+
121
+ it_should_behave_like 'a command method'
122
+ it_should_behave_like 'memoizes method'
123
+
124
+ it 'is still a private method' do
125
+ should be_private_method_defined(method)
126
+ end
127
+
128
+ it 'creates a method that returns a frozen value' do
129
+ subject
130
+ expect(object.new.send(method)).to be_frozen
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Memoizable::ModuleMethods, '#memoized?' do
4
+ let(:object) do
5
+ Class.new do
6
+ include Memoizable
7
+ def foo
8
+ end
9
+ memoize :foo
10
+ end
11
+ end
12
+
13
+ subject { object.memoized?(name) }
14
+
15
+ context 'with memoized method' do
16
+ let(:name) { :foo }
17
+
18
+ it { should be(true) }
19
+ end
20
+
21
+ context 'with non memoized method' do
22
+ let(:name) { :bar }
23
+
24
+ it { should be(false) }
25
+ end
26
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,25 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+ SimpleCov.start do
9
+ command_name 'spec'
10
+
11
+ add_filter 'config'
12
+ add_filter 'spec'
13
+ add_filter 'vendor'
14
+
15
+ minimum_coverage 89.8
16
+ end
17
+
3
18
  require 'memoizable'
4
- require 'spec'
5
- require 'spec/autorun'
19
+ require 'rspec'
6
20
 
7
- Spec::Runner.configure do |config|
8
-
21
+ RSpec.configure do |config|
22
+ config.expect_with :rspec do |expect_with|
23
+ expect_with.syntax = :expect
24
+ end
9
25
  end
metadata CHANGED
@@ -1,77 +1,106 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: memoizable
3
- version: !ruby/object:Gem::Version
4
- version: 0.1.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
5
6
  platform: ruby
6
- authors:
7
- - Enrique Comba Riepenhausen
7
+ authors:
8
+ - Dan Kubb
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
-
12
- date: 2009-12-21 00:00:00 +00:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: rspec
12
+ date: 2013-11-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thread_safe
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.3'
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.3.5
17
41
  type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
24
- version:
25
- description: Memoizes calls to method to boost the performance of recursive calls
26
- email: ecomba@nexwerk.com
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '1.3'
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: 1.3.5
52
+ description: Memoize method return values
53
+ email: dan.kubb@gmail.com
27
54
  executables: []
28
-
29
55
  extensions: []
30
-
31
- extra_rdoc_files:
32
- - LICENSE
33
- - README.rdoc
34
- files:
35
- - .document
36
- - .gitignore
37
- - LICENSE
38
- - README.rdoc
56
+ extra_rdoc_files:
57
+ - LICENSE.md
58
+ - README.md
59
+ - CONTRIBUTING.md
60
+ files:
61
+ - CONTRIBUTING.md
62
+ - LICENSE.md
63
+ - README.md
39
64
  - Rakefile
40
- - VERSION.yml
41
- - lib/memoizable.rb
42
65
  - memoizable.gemspec
43
- - spec/fibonacci_sample_spec.rb
44
- - spec/memoizable_spec.rb
66
+ - lib/memoizable/instance_methods.rb
67
+ - lib/memoizable/memory.rb
68
+ - lib/memoizable/method_builder.rb
69
+ - lib/memoizable/module_methods.rb
70
+ - lib/memoizable/version.rb
71
+ - lib/memoizable.rb
72
+ - spec/fixtures/classes.rb
73
+ - spec/memoize_spec.rb
74
+ - spec/memoized_predicate_spec.rb
45
75
  - spec/spec_helper.rb
46
- has_rdoc: true
47
- homepage: http://github.com/ecomba/memoizable
48
- licenses: []
49
-
76
+ homepage: https://github.com/dkubb/memoizable
77
+ licenses:
78
+ - MIT
50
79
  post_install_message:
51
- rdoc_options:
52
- - --charset=UTF-8
53
- require_paths:
80
+ rdoc_options: []
81
+ require_paths:
54
82
  - lib
55
- required_ruby_version: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: "0"
60
- version:
61
- required_rubygems_version: !ruby/object:Gem::Requirement
62
- requirements:
63
- - - ">="
64
- - !ruby/object:Gem::Version
65
- version: "0"
66
- version:
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
67
95
  requirements: []
68
-
69
96
  rubyforge_project:
70
- rubygems_version: 1.3.5
97
+ rubygems_version: 1.8.23
71
98
  signing_key:
72
99
  specification_version: 3
73
- summary: Memoize method calls
74
- test_files:
75
- - spec/fibonacci_sample_spec.rb
76
- - spec/memoizable_spec.rb
100
+ summary: Memoize method return values
101
+ test_files:
102
+ - spec/fixtures/classes.rb
103
+ - spec/memoize_spec.rb
104
+ - spec/memoized_predicate_spec.rb
77
105
  - spec/spec_helper.rb
106
+ has_rdoc:
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- coverage
4
- rdoc
5
- pkg
data/README.rdoc DELETED
@@ -1,64 +0,0 @@
1
- = Memoizable
2
-
3
- == Introduction
4
-
5
- When doing recursion sometimes we are faced with the problem of performance
6
- versus the beauty of the code we are implementing, specially when, due to
7
- the recursive nature of the method we have written we seem to be computing
8
- over and over the same.
9
-
10
- The concept of memoization comes from the idea of capturing a certain
11
- method call and saving it's result to an internal cache, so that, if this
12
- particular method (with the same parameters) is called, it will return the
13
- previously computed result.
14
-
15
- I was inspired in writing this little module after reading an article
16
- on James Edward Grey II'2 blog (http://blog.grayproductions.net/articles/caching_and_memoization)
17
-
18
- == Example
19
-
20
- Imagine we want to compute the Fibonacci sequence:
21
-
22
- class Fibonacci
23
- def fib(num)
24
- return num if num < 2
25
- fib(num -1) + fib(num - 2)
26
- end
27
- end
28
-
29
- As you can see immediately is that this will result in poor performance the
30
- higher the number in the sequence we request as the algorithm will compute
31
- over and over same method calls.
32
-
33
- You could add some cache functionallity into your method and class, although
34
- you would add new behaviour that the class and method shouldn't really have
35
- as they should be dealing exclusively with the logic they are supposed to
36
- execute; in this case the computation of the Fibonacci sequence.
37
-
38
- Here is how the class would look like when we Memonize it:
39
-
40
- class Fibonacci
41
- include Memoizable
42
-
43
- def fib(num)
44
- return num if num < 2
45
- fib(num -1) + fib(num - 2)
46
- end
47
-
48
- memoize :fib
49
- end
50
-
51
- == Note on Patches/Pull Requests
52
-
53
- * Fork the project.
54
- * Make your feature addition or bug fix.
55
- * Add tests for it. This is important so I don't break it in a
56
- future version unintentionally.
57
- * Commit, do not mess with rakefile, version, or history.
58
- (if you want to have your own version, that is fine but
59
- bump version in a commit by itself I can ignore when I pull)
60
- * Send me a pull request. Bonus points for topic branches.
61
-
62
- == Copyright
63
-
64
- Copyright (c) 2009 Enrique Comba Riepenhausen. See LICENSE for details.
data/VERSION.yml DELETED
@@ -1,4 +0,0 @@
1
- ---
2
- :major: 0
3
- :minor: 1
4
- :patch: 0
@@ -1,26 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- require 'benchmark'
4
-
5
- describe "Fibonacci" do
6
- class Fibonacci
7
- def fib(number)
8
- return number if number < 2
9
- fib(number -1) + fib(number - 2)
10
- end
11
- end
12
-
13
- context "context" do
14
- it "should run faster" do
15
- fibo = Fibonacci.new
16
- bm = Benchmark.measure { fibo.fib(30) }
17
- class Fibonacci; include Memoizable; memoize :fib; end;
18
- fibo2 = Fibonacci.new
19
- bm2 = Benchmark.measure { fibo2.fib(30) }
20
-
21
- # This test is flakey, I know... How to test the real speed?
22
- bm.to_a[5].should > bm2.to_a[5]
23
- bm2.to_a[5].should < 0.01
24
- end
25
- end
26
- end
@@ -1,61 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "Memoizable" do
4
- before(:all) do
5
- class Foo; def a; return "blaah" end; end;
6
- end
7
-
8
- context "Module structure" do
9
- it "should have a ClassMethods module" do
10
- Memoizable::ClassMethods.class.should be(Module)
11
- end
12
-
13
- it "should contain a memonize method" do
14
- Memoizable::ClassMethods.public_method_defined?(:memoize).should be_true
15
- end
16
-
17
- context ": Cache" do
18
- it "should be present" do
19
- Memoizable::CACHE.should_not be_nil
20
- end
21
-
22
- it "should be a hash" do
23
- Memoizable::CACHE.should be_instance_of(Hash)
24
- end
25
- end
26
- end
27
-
28
- context "Class Inclusion" do
29
- it "should extend a given class with the memoize method" do
30
- class Foo; include Memoizable; end;
31
- Foo.respond_to?(:memoize).should be_true
32
- end
33
-
34
- it "should alias the original method" do
35
- Foo.memoize :a
36
- foo = Foo.new
37
- foo.respond_to?("__original__a").should be_true
38
- end
39
-
40
- it "should modify the method" do
41
- method_a = Foo.instance_method(:a)
42
- Foo.memoize :a
43
- method_a.should_not == Foo.instance_method(:a)
44
- end
45
- end
46
-
47
- context "Method calls" do
48
- it "should return the same values" do
49
- foo = Foo.new
50
- first_return = foo.a
51
- Foo.memoize :a
52
- first_return.should == foo.a
53
- end
54
-
55
- it "should put the method call into the cache" do
56
- Foo.memoize :a
57
- foo = Foo.new
58
- Memoizable::CACHE.size.should > 0
59
- end
60
- end
61
- end