Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constants can be close engough too #20

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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