From c769d8ec9b1ba116b90aa8df3fb4c4d3443cbfad Mon Sep 17 00:00:00 2001 From: Dan Kubb Date: Sun, 15 Dec 2013 23:47:38 -0800 Subject: [PATCH 1/3] Add memoization for methods based on immutable state [Fix #9] --- config/flog.yml | 2 +- lib/adamantium.rb | 5 ++++- spec/unit/adamantium/hash_spec.rb | 13 +++++++++++++ spec/unit/adamantium/inspect_spec.rb | 13 +++++++++++++ spec/unit/adamantium/to_s_spec.rb | 13 +++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 spec/unit/adamantium/hash_spec.rb create mode 100644 spec/unit/adamantium/inspect_spec.rb create mode 100644 spec/unit/adamantium/to_s_spec.rb diff --git a/config/flog.yml b/config/flog.yml index 05e12f8..ce963b5 100644 --- a/config/flog.yml +++ b/config/flog.yml @@ -1,2 +1,2 @@ --- -threshold: 15.0 +threshold: 16.3 diff --git a/lib/adamantium.rb b/lib/adamantium.rb index 4aac1a3..cf34ac9 100644 --- a/lib/adamantium.rb +++ b/lib/adamantium.rb @@ -47,7 +47,10 @@ def self.included(descendant) descendant.class_eval do include Memoizable extend ModuleMethods - extend ClassMethods if instance_of?(Class) + if instance_of?(Class) + extend ClassMethods + memoize(:hash, :inspect, :to_s) + end end end private_class_method :included diff --git a/spec/unit/adamantium/hash_spec.rb b/spec/unit/adamantium/hash_spec.rb new file mode 100644 index 0000000..b3ee3cd --- /dev/null +++ b/spec/unit/adamantium/hash_spec.rb @@ -0,0 +1,13 @@ +# encoding: utf-8 + +require 'spec_helper' +require File.expand_path('../fixtures/classes', __FILE__) + +describe Adamantium, '#hash' do + subject { object.hash } + + let(:object) { described_class.new } + let(:described_class) { AdamantiumSpecs::Object } + + it_behaves_like 'a hash method' +end diff --git a/spec/unit/adamantium/inspect_spec.rb b/spec/unit/adamantium/inspect_spec.rb new file mode 100644 index 0000000..83e073a --- /dev/null +++ b/spec/unit/adamantium/inspect_spec.rb @@ -0,0 +1,13 @@ +# encoding: utf-8 + +require 'spec_helper' +require File.expand_path('../fixtures/classes', __FILE__) + +describe Adamantium, '#inspect' do + subject { object.inspect } + + let(:object) { described_class.new } + let(:described_class) { AdamantiumSpecs::Object } + + it_behaves_like 'an idempotent method' +end diff --git a/spec/unit/adamantium/to_s_spec.rb b/spec/unit/adamantium/to_s_spec.rb new file mode 100644 index 0000000..987334e --- /dev/null +++ b/spec/unit/adamantium/to_s_spec.rb @@ -0,0 +1,13 @@ +# encoding: utf-8 + +require 'spec_helper' +require File.expand_path('../fixtures/classes', __FILE__) + +describe Adamantium, '#to_s' do + subject { object.to_s } + + let(:object) { described_class.new } + let(:described_class) { AdamantiumSpecs::Object } + + it_behaves_like 'an idempotent method' +end From 4fe41445073854b71550a8ed2e08f0922dda352a Mon Sep 17 00:00:00 2001 From: Dan Kubb Date: Sun, 15 Dec 2013 23:54:52 -0800 Subject: [PATCH 2/3] Add README instructions for usage with equalizer --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 5cbffe2..22084b2 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,29 @@ class FlatExample end ``` +## Usage with equalizer + +Adamanitum may be used with [equalizer](https://www.github.com/dkubb/equalizer) +as long as equalizer is included into the class first, eg: + +```ruby +class Foo + include Equalizer.new(:foo) + include Adamantium +end +``` + +Another, less common form is to include all the modules in a single line. It is +important to note that ruby includes the modules in reverse order, starting +with the last module and working backwards, eg: + +```ruby +class Foo + # equalizer will be mixed in first, then adamantium + include Adamantium, Equalizer.new(:foo) +end +``` + ## Credits * Dan Kubb ([dkubb](https://github.com/dkubb)) From af31be45c0e41f486161885e2c8556b5f3b5b26f Mon Sep 17 00:00:00 2001 From: Dan Kubb Date: Mon, 11 Aug 2014 00:49:11 -0700 Subject: [PATCH 3/3] Add memoization to methods when they are added --- lib/adamantium.rb | 5 +---- lib/adamantium/module_methods.rb | 15 +++++++++++++++ spec/unit/adamantium/hash_spec.rb | 14 ++++++++++++-- spec/unit/adamantium/inspect_spec.rb | 11 +++++++++-- spec/unit/adamantium/to_s_spec.rb | 11 +++++++++-- 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/lib/adamantium.rb b/lib/adamantium.rb index cf34ac9..4aac1a3 100644 --- a/lib/adamantium.rb +++ b/lib/adamantium.rb @@ -47,10 +47,7 @@ def self.included(descendant) descendant.class_eval do include Memoizable extend ModuleMethods - if instance_of?(Class) - extend ClassMethods - memoize(:hash, :inspect, :to_s) - end + extend ClassMethods if instance_of?(Class) end end private_class_method :included diff --git a/lib/adamantium/module_methods.rb b/lib/adamantium/module_methods.rb index eedfe07..4e85cf9 100644 --- a/lib/adamantium/module_methods.rb +++ b/lib/adamantium/module_methods.rb @@ -4,6 +4,8 @@ module Adamantium # Methods mixed in to adamantium modules module ModuleMethods + MEMOIZE_METHODS = [:hash, :inspect, :to_s].freeze + RECURSION_GUARD = IceNine::RecursionGuard::ObjectSet.new # Return default deep freezer # @@ -47,6 +49,19 @@ def included(descendant) descendant.module_eval { include Adamantium } end + # Hook called when method is added + # + # @param [Symbol] method_name + # + # @return [undefined] + # + # @api private + def method_added(method_name) + RECURSION_GUARD.guard(self) do + memoize(method_name) if MEMOIZE_METHODS.include?(method_name) + end + end + # Memoize the named method # # @param [Symbol] method_name diff --git a/spec/unit/adamantium/hash_spec.rb b/spec/unit/adamantium/hash_spec.rb index b3ee3cd..8f82840 100644 --- a/spec/unit/adamantium/hash_spec.rb +++ b/spec/unit/adamantium/hash_spec.rb @@ -6,8 +6,18 @@ describe Adamantium, '#hash' do subject { object.hash } - let(:object) { described_class.new } - let(:described_class) { AdamantiumSpecs::Object } + let(:object) { described_class.new } + + let(:described_class) do + Class.new(AdamantiumSpecs::Object) do + FIXNUM_MAX = 2**(0.size * 8 - 2) - 1 + FIXNUM_MIN = -2**(0.size * 8 - 2) + + def hash + Random.rand(FIXNUM_MIN..FIXNUM_MAX) + end + end + end it_behaves_like 'a hash method' end diff --git a/spec/unit/adamantium/inspect_spec.rb b/spec/unit/adamantium/inspect_spec.rb index 83e073a..20d8066 100644 --- a/spec/unit/adamantium/inspect_spec.rb +++ b/spec/unit/adamantium/inspect_spec.rb @@ -6,8 +6,15 @@ describe Adamantium, '#inspect' do subject { object.inspect } - let(:object) { described_class.new } - let(:described_class) { AdamantiumSpecs::Object } + let(:object) { described_class.new } + + let(:described_class) do + Class.new(AdamantiumSpecs::Object) do + def inspect + rand.to_s + end + end + end it_behaves_like 'an idempotent method' end diff --git a/spec/unit/adamantium/to_s_spec.rb b/spec/unit/adamantium/to_s_spec.rb index 987334e..fe97d91 100644 --- a/spec/unit/adamantium/to_s_spec.rb +++ b/spec/unit/adamantium/to_s_spec.rb @@ -6,8 +6,15 @@ describe Adamantium, '#to_s' do subject { object.to_s } - let(:object) { described_class.new } - let(:described_class) { AdamantiumSpecs::Object } + let(:object) { described_class.new } + + let(:described_class) do + Class.new(AdamantiumSpecs::Object) do + def to_s + rand.to_s + end + end + end it_behaves_like 'an idempotent method' end