diff --git a/lib/close_enough/extensions.rb b/lib/close_enough/extensions.rb index a7a459c..dd06d15 100644 --- a/lib/close_enough/extensions.rb +++ b/lib/close_enough/extensions.rb @@ -1,4 +1,44 @@ +class Module + def const_missing(name) + + if const = CloseEnough.nearest(constants, name) + return const_get(const) + end + + namespaces = self.name.split('::') + namespaces.pop + + until namespaces.empty? + namespace = const_get(namespaces.join('::')) + if const = CloseEnough.nearest(namespace.constants, name) + return namespace.const_get(const) + end + end + + if const = CloseEnough.nearest(Object.constants, name) + return Object.const_get(const) + end + + raise NameError.new("uninitialized constant #{name}") + end +end + module CloseEnough + + def self.nearest(choices, name) + dl = DamerauLevenshtein + ms = choices.map(&:to_s).reject {|m| m =~ /to_.+/} + return false if ms.empty? + selected = ms.min_by {|possible| dl.distance(name.to_s, possible)} + + unless dl.distance(name.to_s, selected) < 3 + return false + else + warn "[CloseEnough] #{name.to_s} not found, using #{selected.to_s} instead" + return selected + end + end + module Extensions module ClassMethods @@ -7,23 +47,11 @@ module ClassMethods module InstanceMethods private - def nearest_method(name) - dl = DamerauLevenshtein - ms = methods.map(&:to_s).reject {|m| m =~ /to_.+/} - meth = ms.min_by {|possible| dl.distance(name.to_s, possible)} - unless dl.distance(name.to_s, meth) < 3 - return false - else - warn "[CloseEnough] #{name.to_s} not found, using #{meth.to_s} instead" - return meth - end - end - def method_missing(name, *args, &block) - meth = nearest_method(name) - + meth = CloseEnough.nearest(methods, name) meth ? send(meth, *args, &block) : super end + end def self.included(receiver) diff --git a/spec/close_enough_spec.rb b/spec/close_enough_spec.rb index 9e69a05..14edaac 100644 --- a/spec/close_enough_spec.rb +++ b/spec/close_enough_spec.rb @@ -1,5 +1,15 @@ require "spec_helper.rb" +module Foo + module Target + end + module Bar + def self.works! + Targer.should == Target + end + end +end + module CloseEnoughSpec describe "nearest_method should find the nearest method to the typo" do @@ -8,7 +18,7 @@ module CloseEnoughSpec end it "should return 'freeze' for 'frese'" do - @obj.send(:nearest_method, 'frese').should == "freeze" + CloseEnough.nearest(@obj.methods, 'frese').should == "freeze" end end @@ -44,6 +54,18 @@ module CloseEnoughSpec end end + describe "should return the closest const" do + + it "should return FalseClass when FolseClass is referenced" do + FolseClass.should be FalseClass + end + + it 'should works inside a module' do + Foo::Bar.works! + end + + end + describe "it should exxclude to_* methods" do it "should raise NoMethodError when to_sim is called" do @@ -62,7 +84,7 @@ module CloseEnoughSpec describe "warnings should be issued when the correct method is guessed" do it "should warn when guessing 'reverse' for 'reserve'" do str = "avocado" - str.should_receive(:warn).with("[CloseEnough] reserve not found, using reverse instead") + CloseEnough.should_receive(:warn).with("[CloseEnough] reserve not found, using reverse instead") str.reserve end end