Skip to content

Instantly share code, notes, and snippets.

@sneakin
Forked from sneakin/rubyfu_speling.rb
Created April 7, 2009 16:45
Show Gist options
  • Save sneakin/91326 to your computer and use it in GitHub Desktop.
Save sneakin/91326 to your computer and use it in GitHub Desktop.
rubyfu_speling.rb – Overrides #method_missing and #const_missing to resolve mispeled methods and constants.
# rubyfu_speling.rb
# Overrides #method_missing and #const_missing to resolve
# mispeled methods and constants.
#
# Copyright (C) 2009 Nolan Eakins
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
require 'active_support'
require 'amatch'
module Speling
mattr_accessor :raise_on_error, :quiet_warnings
class << self
def closest_match_with_pair_distance(needle, collection)
return if collection.blank?
PairMatcher.new(needle).score(collection)
end
def closest_match_with_hamming(needle, collection)
return if collection.blank?
HammingMatcher.new(needle).score(collection)
end
alias closest_match closest_match_with_hamming
def warn(calling, wanted)
raise NameError.new(wanted.to_s) if raise_on_error ||
(defined?(RAILS_ENV) && RAILS_ENV == 'production')
unless quiet_warnings
$stderr.puts "SPELING ERROR!!!!\tCalling #{calling} for #{wanted}"
end
end
end
class Matcher
attr_reader :needle, :algor
def initialize(needle)
@needle = needle
end
def score(collection)
scores = collection.
collect { |m| [m, algor.match(m)] }.
sort { |a, b| a[1] <=> b[1] }
pick_score(scores)
end
end
class HammingMatcher < Matcher
def algor
@algor ||= Amatch::Hamming.new(needle.to_s)
end
def pick_score(scores)
last = scores.first
# $stderr.puts "Scoring #{needle.inspect} with #{last.inspect} in #{scores.inspect}"
return last[0] if last[1] < 4
end
end
class PairMatcher < Matcher
def algor
@algor ||= Amatch::PairDistance.new(needle.to_s)
end
def pick_score(scores)
last = scores.last
return last[0] if last[1] > 0.5
end
end
module MethodMissing
def self.included(base)
base.class_eval do
unless defined?(method_missing_without_speling)
alias_method_chain :method_missing, :speling
end
end
end
def method_missing_with_speling(mid, *args, &block)
ex = nil
begin
# $stderr.puts "#{mid.inspect} #{self.inspect}"
return method_missing_without_speling(mid, *args, &block)
rescue
ex = $!
end
if (meth = Speling.closest_match(mid.to_s, methods))
Speling.warn(meth, mid)
return method(meth).call(*args, &block)
end
raise ex
rescue
raise ex
end
end
module ConstMissing
def self.included(base)
base.class_eval do
unless defined?(const_missing_without_speling)
alias_method_chain :const_missing, :speling
end
end
end
def const_missing_with_speling(const)
ex = nil
begin
return const_missing_without_speling(const)
rescue NameError
ex = $!
end
if (c = Speling.closest_match(const, constants))
Speling.warn(c, const)
return const_get(c)
end
raise ex
rescue NameError
raise ex
end
end
end
class Module
include Speling::ConstMissing
end
class Object
include Speling::MethodMissing
end
require 'spec'
require 'rubyfu_speling'
describe Speling do
Speling.quiet_warnings = true
HelloWorld = "Hello world "
it "works with constants" do
HleloWorld.should == HelloWorld
end
it "works with methods" do
HelloWolrd.stri.upcsae.should == HelloWorld.strip.upcase
end
describe 'speling error', :shared => true do
it "raises an error on a method mispelling" do
lambda { HelloWorld.stri.upcsae }.should raise_error(NameError)
end
it "raises an error on a constant mispelling" do
lambda { HelloWrold }.should raise_error(NameError)
end
end
context 'raise_on_error = true' do
before(:each) { Speling.raise_on_error = true }
it_should_behave_like 'speling error'
end
context 'RAILS_ENV = production' do
before(:each) { RAILS_ENV = 'production' }
it_should_behave_like 'speling error'
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment