Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 42 additions & 14 deletions lib/close_enough/extensions.rb
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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)
Expand Down
26 changes: 24 additions & 2 deletions spec/close_enough_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down