did_you_mean-1.2.1/ 0000755 0000041 0000041 00000000000 13263136567 014213 5 ustar www-data www-data did_you_mean-1.2.1/Rakefile 0000644 0000041 0000041 00000002220 13263136567 015654 0 ustar www-data www-data require 'bundler/gem_tasks'
require 'rake/testtask'
Rake::TestTask.new do |task|
task.libs << "test"
task.test_files = Dir['test/**/*_test.rb'].reject {|path| /(experimental)/ =~ path }
task.verbose = true
task.warning = true
end
Rake::TestTask.new("test:experimental") do |task|
task.libs << "test"
task.pattern = 'test/experimental/**/*_test.rb'
task.verbose = true
task.warning = true
task.ruby_opts << "-rdid_you_mean/experimental"
end
task default: %i(test test:experimental)
namespace :test do
namespace :accuracy do
desc "Download Wiktionary's Simple English data and save it as a dictionary"
task :prepare do
sh 'ruby evaluation/dictionary_generator.rb'
end
end
desc "Calculate accuracy of the gems' spell checker"
task :accuracy do
if !File.exist?("evaluation/dictionary.yml")
puts 'Generating dictionary for evaluation:'
Rake::Task["test:accuracy:prepare"].execute
puts "\n"
end
sh 'ruby evaluation/calculator.rb'
end
end
namespace :benchmark do
desc "Measure memory usage by the did_you_mean gem"
task :memory do
sh 'ruby benchmark/memory_usage.rb'
end
end
did_you_mean-1.2.1/Gemfile 0000644 0000041 0000041 00000000253 13263136567 015506 0 ustar www-data www-data source 'https://rubygems.org'
# Specify your gem's dependencies in did_you_mean.gemspec
gemspec
gem 'benchmark-ips'
gem 'memory_profiler'
gem 'jaro_winkler', '>= 1.4.0'
did_you_mean-1.2.1/doc/ 0000755 0000041 0000041 00000000000 13263136567 014760 5 ustar www-data www-data did_you_mean-1.2.1/doc/CHANGELOG.md.erb 0000644 0000041 0000041 00000000346 13263136567 017343 0 ustar www-data www-data <% releases.each do |release| %>
## [<%= release.name %>](https://github.com/<%= repository %>/tree/<%= release.tag_name %>)
_released at <%= release.published_at %>_
<%= release.body.gsub(/\r\n/, "\n") %>
<% end %>
did_you_mean-1.2.1/doc/changelog_generator.rb 0000644 0000041 0000041 00000001562 13263136567 021306 0 ustar www-data www-data require 'octokit'
require 'reverse_markdown'
require 'erb'
class ChangeLogGenerator
attr :repository, :template_path, :changelog_path
def initialize(repository, template_path: "CHANGELOG.md.erb", changelog_path: "CHANGELOG.md")
@repository = repository
@template_path = template_path
@changelog_path = changelog_path
end
def generate_and_save!
changelog_in_md = ERB.new(template).result(binding)
changelog_in_html = Octokit.markdown(changelog_in_md, context: repository, mode: "gfm")
File.open(changelog_path, 'w') do |file|
file.write ReverseMarkdown.convert(changelog_in_html, github_flavored: true)
end
end
private
def template
open("#{__dir__}/#{template_path}").read
end
def releases
@releases ||= Octokit.releases(repository)
end
end
ChangeLogGenerator.new("yuki24/did_you_mean").generate_and_save!
did_you_mean-1.2.1/.ruby-version 0000644 0000041 0000041 00000000006 13263136567 016654 0 ustar www-data www-data 2.5.1
did_you_mean-1.2.1/LICENSE.txt 0000644 0000041 0000041 00000002064 13263136567 016040 0 ustar www-data www-data Copyright (c) 2014-2016 Yuki Nishijima
MIT License
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.
did_you_mean-1.2.1/.travis.yml 0000644 0000041 0000041 00000000336 13263136567 016326 0 ustar www-data www-data language: ruby
script: bundle exec rake
cache: bundler
sudo: false
before_install:
- gem install bundler
after_success:
- bundle exec rake test:accuracy
- bundle exec rake benchmark:memory
rvm:
- 2.5.1
did_you_mean-1.2.1/benchmark/ 0000755 0000041 0000041 00000000000 13263136567 016145 5 ustar www-data www-data did_you_mean-1.2.1/benchmark/memory_usage.rb 0000644 0000041 0000041 00000000632 13263136567 021167 0 ustar www-data www-data # frozen-string-literal: true
require 'memory_profiler'
require 'did_you_mean'
# public def foo; end
# error = (self.fooo rescue $!)
# executable = -> { error.to_s }
METHODS = ''.methods
INPUT = 'start_with?'
collection = DidYouMean::SpellChecker.new(dictionary: METHODS)
executable = proc { collection.correct(INPUT) }
GC.disable
MemoryProfiler.report { 100.times(&executable) }.pretty_print
did_you_mean-1.2.1/benchmark/jaro_winkler/ 0000755 0000041 0000041 00000000000 13263136567 020633 5 ustar www-data www-data did_you_mean-1.2.1/benchmark/jaro_winkler/memory_usage.rb 0000644 0000041 0000041 00000000352 13263136567 023654 0 ustar www-data www-data require 'memory_profiler'
require 'did_you_mean/jaro_winkler'
str1, str2 = "user_signed_in?", "user_logged_in?"
report = MemoryProfiler.report do
80.times do
DidYouMean::Jaro.distance str1, str2
end
end
report.pretty_print
did_you_mean-1.2.1/benchmark/jaro_winkler/speed.rb 0000644 0000041 0000041 00000000444 13263136567 022262 0 ustar www-data www-data require 'benchmark/ips'
require 'did_you_mean'
Benchmark.ips do |x|
x.report "before" do
DidYouMean::Jaro.before_distance "user_signed_in?", "user_logged_in?"
end
x.report "after" do
DidYouMean::Jaro.after_distance "user_signed_in?", "user_logged_in?"
end
x.compare!
end
did_you_mean-1.2.1/benchmark/levenshtein/ 0000755 0000041 0000041 00000000000 13263136567 020471 5 ustar www-data www-data did_you_mean-1.2.1/benchmark/levenshtein/memory_usage.rb 0000644 0000041 0000041 00000000360 13263136567 023511 0 ustar www-data www-data require 'memory_profiler'
require 'did_you_mean/levenshtein'
str1, str2 = "user_signed_in?", "user_logged_in?"
report = MemoryProfiler.report do
80.times do
DidYouMean::Levenshtein.distance str1, str2
end
end
report.pretty_print
did_you_mean-1.2.1/benchmark/levenshtein/speed.rb 0000644 0000041 0000041 00000000530 13263136567 022114 0 ustar www-data www-data require 'benchmark/ips'
require 'did_you_mean'
require 'did_you_mean/levenshtein'
STR1, STR2 = "user_signed_in?", "user_logged_in?"
Benchmark.ips do |x|
x.report "enumerable" do
DidYouMean::Levenshtein.before_distance STR1, STR2
end
x.report "while" do
DidYouMean::Levenshtein.after_distance STR1, STR2
end
x.compare!
end
did_you_mean-1.2.1/lib/ 0000755 0000041 0000041 00000000000 13263136567 014761 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/ 0000755 0000041 0000041 00000000000 13263136567 017415 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/verbose.rb 0000644 0000041 0000041 00000000204 13263136567 021403 0 ustar www-data www-data require 'did_you_mean'
require 'did_you_mean/formatters/verbose_formatter'
DidYouMean.formatter = DidYouMean::VerboseFormatter.new
did_you_mean-1.2.1/lib/did_you_mean/core_ext/ 0000755 0000041 0000041 00000000000 13263136567 021225 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/core_ext/name_error.rb 0000644 0000041 0000041 00000000763 13263136567 023711 0 ustar www-data www-data module DidYouMean
module Correctable
def original_message
method(:to_s).super_method.call
end
def to_s
msg = super.dup
if !cause.respond_to?(:corrections) || cause.corrections.empty?
msg << DidYouMean.formatter.message_for(corrections)
end
msg
rescue
super
end
def corrections
spell_checker.corrections
end
def spell_checker
@spell_checker ||= SPELL_CHECKERS[self.class.to_s].new(self)
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checker.rb 0000644 0000041 0000041 00000002454 13263136567 022552 0 ustar www-data www-data # frozen-string-literal: true
require "did_you_mean/levenshtein"
require "did_you_mean/jaro_winkler"
module DidYouMean
class SpellChecker
def initialize(dictionary: )
@dictionary = dictionary
end
def correct(input)
input = normalize(input)
threshold = input.length > 3 ? 0.834 : 0.77
words = @dictionary.select {|word| JaroWinkler.distance(normalize(word), input) >= threshold }
words.reject! {|word| input == word.to_s }
words.sort_by! {|word| JaroWinkler.distance(word.to_s, input) }
words.reverse!
# Correct mistypes
threshold = (input.length * 0.25).ceil
corrections = words.select {|c| Levenshtein.distance(normalize(c), input) <= threshold }
# Correct misspells
if corrections.empty?
corrections = words.select do |word|
word = normalize(word)
length = input.length < word.length ? input.length : word.length
Levenshtein.distance(word, input) < length
end.first(1)
end
corrections
end
private
def normalize(str_or_symbol) #:nodoc:
str = if str_or_symbol.is_a?(String)
str_or_symbol.dup
else
str_or_symbol.to_s
end
str.downcase!
str.tr!("@", "")
str
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/experimental/ 0000755 0000041 0000041 00000000000 13263136567 022112 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/experimental/initializer_name_correction.rb 0000644 0000041 0000041 00000000724 13263136567 030214 0 ustar www-data www-data # frozen-string-literal: true
require 'did_you_mean/levenshtein'
module DidYouMean
module Experimental
module InitializerNameCorrection
def method_added(name)
super
distance = Levenshtein.distance(name.to_s, 'initialize')
if distance != 0 && distance <= 2
warn "warning: #{name} might be misspelled, perhaps you meant initialize?"
end
end
end
::Class.prepend(InitializerNameCorrection)
end
end
did_you_mean-1.2.1/lib/did_you_mean/experimental/ivar_name_correction.rb 0000644 0000041 0000041 00000004016 13263136567 026630 0 ustar www-data www-data # frozen-string-literal: true
require 'did_you_mean'
module DidYouMean
module Experimental #:nodoc:
class IvarNameCheckerBuilder #:nodoc:
attr_reader :original_checker
def initialize(original_checker) #:nodoc:
@original_checker = original_checker
end
def new(no_method_error) #:nodoc:
IvarNameChecker.new(no_method_error, original_checker: @original_checker)
end
end
class IvarNameChecker #:nodoc:
REPLS = {
"(irb)" => -> { Readline::HISTORY.to_a.last }
}
TRACE = TracePoint.trace(:raise) do |tp|
e = tp.raised_exception
if SPELL_CHECKERS.include?(e.class.to_s) && !e.instance_variable_defined?(:@frame_binding)
e.instance_variable_set(:@frame_binding, tp.binding)
end
end
attr_reader :original_checker
def initialize(no_method_error, original_checker: )
@original_checker = original_checker.new(no_method_error)
@location = no_method_error.backtrace_locations.first
@ivar_names = no_method_error.frame_binding.receiver.instance_variables
end
def corrections
original_checker.corrections + ivar_name_corrections
end
def ivar_name_corrections
@ivar_name_corrections ||= SpellChecker.new(dictionary: @ivar_names).correct(receiver_name.to_s)
end
private
def receiver_name
return unless @original_checker.receiver.nil?
abs_path = @location.absolute_path
lineno = @location.lineno
/@(\w+)*\.#{@original_checker.method_name}/ =~ line(abs_path, lineno).to_s && $1
end
def line(abs_path, lineno)
if REPLS[abs_path]
REPLS[abs_path].call
elsif File.exist?(abs_path)
File.open(abs_path) do |file|
file.detect { file.lineno == lineno }
end
end
end
end
end
NameError.send(:attr, :frame_binding)
SPELL_CHECKERS['NoMethodError'] = Experimental::IvarNameCheckerBuilder.new(SPELL_CHECKERS['NoMethodError'])
end
did_you_mean-1.2.1/lib/did_you_mean/jaro_winkler.rb 0000644 0000041 0000041 00000003451 13263136567 022433 0 ustar www-data www-data module DidYouMean
module Jaro
module_function
def distance(str1, str2)
str1, str2 = str2, str1 if str1.length > str2.length
length1, length2 = str1.length, str2.length
m = 0.0
t = 0.0
range = (length2 / 2).floor - 1
range = 0 if range < 0
flags1 = 0
flags2 = 0
# Avoid duplicating enumerable objects
str1_codepoints = str1.codepoints
str2_codepoints = str2.codepoints
i = 0
while i < length1
last = i + range
j = (i >= range) ? i - range : 0
while j <= last
if flags2[j] == 0 && str1_codepoints[i] == str2_codepoints[j]
flags2 |= (1 << j)
flags1 |= (1 << i)
m += 1
break
end
j += 1
end
i += 1
end
k = i = 0
while i < length1
if flags1[i] != 0
j = index = k
k = while j < length2
index = j
break(j + 1) if flags2[j] != 0
j += 1
end
t += 1 if str1_codepoints[i] != str2_codepoints[index]
end
i += 1
end
t = (t / 2).floor
m == 0 ? 0 : (m / length1 + m / length2 + (m - t) / m) / 3
end
end
module JaroWinkler
WEIGHT = 0.1
THRESHOLD = 0.7
module_function
def distance(str1, str2)
jaro_distance = Jaro.distance(str1, str2)
if jaro_distance > THRESHOLD
codepoints2 = str2.codepoints
prefix_bonus = 0
i = 0
str1.each_codepoint do |char1|
char1 == codepoints2[i] && i < 4 ? prefix_bonus += 1 : break
i += 1
end
jaro_distance + (prefix_bonus * WEIGHT * (1 - jaro_distance))
else
jaro_distance
end
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/ 0000755 0000041 0000041 00000000000 13263136567 022403 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/null_checker.rb 0000644 0000041 0000041 00000000150 13263136567 025362 0 ustar www-data www-data module DidYouMean
class NullChecker
def initialize(*); end
def corrections; [] end
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/name_error_checkers/ 0000755 0000041 0000041 00000000000 13263136567 026403 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb 0000644 0000041 0000041 00000002332 13263136567 032521 0 ustar www-data www-data # frozen-string-literal: true
require 'delegate'
require "did_you_mean/spell_checker"
module DidYouMean
class ClassNameChecker
attr_reader :class_name
def initialize(exception)
@class_name, @receiver, @original_message = exception.name, exception.receiver, exception.original_message
end
def corrections
@corrections ||= SpellChecker.new(dictionary: class_names)
.correct(class_name)
.map(&:full_name)
.reject {|qualified_name| @original_message.include?(qualified_name) }
end
def class_names
scopes.flat_map do |scope|
scope.constants.map do |c|
ClassName.new(c, scope == Object ? "" : "#{scope}::")
end
end
end
def scopes
@scopes ||= @receiver.to_s.split("::").inject([Object]) do |_scopes, scope|
_scopes << _scopes.last.const_get(scope)
end.uniq
end
class ClassName < SimpleDelegator
attr :namespace
def initialize(name, namespace = '')
super(name)
@namespace = namespace
end
def full_name
self.class.new("#{namespace}#{__getobj__}")
end
end
private_constant :ClassName
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb 0000644 0000041 0000041 00000002023 13263136567 033176 0 ustar www-data www-data # frozen-string-literal: true
require "did_you_mean/spell_checker"
module DidYouMean
class VariableNameChecker
attr_reader :name, :method_names, :lvar_names, :ivar_names, :cvar_names
NAMES_TO_EXCLUDE = { 'foo' => [:fork] }
NAMES_TO_EXCLUDE.default = []
RB_PREDEFINED_OBJECTS = [:false, :true, :nil]
def initialize(exception)
@name = exception.name.to_s.tr("@", "")
@lvar_names = exception.respond_to?(:local_variables) ? exception.local_variables : []
receiver = exception.receiver
@method_names = receiver.methods + receiver.private_methods
@ivar_names = receiver.instance_variables
@cvar_names = receiver.class.class_variables
@cvar_names += receiver.class_variables if receiver.kind_of?(Module)
end
def corrections
@corrections ||= SpellChecker
.new(dictionary: (RB_PREDEFINED_OBJECTS + lvar_names + method_names + ivar_names + cvar_names))
.correct(name) - NAMES_TO_EXCLUDE[@name]
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/name_error_checkers.rb 0000644 0000041 0000041 00000001135 13263136567 026730 0 ustar www-data www-data require 'did_you_mean/spell_checkers/name_error_checkers/class_name_checker'
require 'did_you_mean/spell_checkers/name_error_checkers/variable_name_checker'
module DidYouMean
class << (NameErrorCheckers = Object.new)
def new(exception)
case exception.original_message
when /uninitialized constant/
ClassNameChecker
when /undefined local variable or method/,
/undefined method/,
/uninitialized class variable/,
/no member '.*' in struct/
VariableNameChecker
else
NullChecker
end.new(exception)
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/key_error_checker.rb 0000644 0000041 0000041 00000000466 13263136567 026423 0 ustar www-data www-data require "did_you_mean/spell_checker"
module DidYouMean
class KeyErrorChecker
def initialize(key_error)
@key = key_error.key
@keys = key_error.receiver.keys
end
def corrections
@corrections ||= SpellChecker.new(dictionary: @keys).correct(@key).map(&:inspect)
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/spell_checkers/method_name_checker.rb 0000644 0000041 0000041 00000001422 13263136567 026673 0 ustar www-data www-data require "did_you_mean/spell_checker"
module DidYouMean
class MethodNameChecker
attr_reader :method_name, :receiver
NAMES_TO_EXCLUDE = { NilClass => nil.methods }
NAMES_TO_EXCLUDE.default = []
def initialize(exception)
@method_name = exception.name
@receiver = exception.receiver
@private_call = exception.respond_to?(:private_call?) ? exception.private_call? : false
end
def corrections
@corrections ||= SpellChecker.new(dictionary: method_names).correct(method_name) - NAMES_TO_EXCLUDE[@receiver.class]
end
def method_names
method_names = receiver.methods + receiver.singleton_methods
method_names += receiver.private_methods if @private_call
method_names.uniq!
method_names
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/version.rb 0000644 0000041 0000041 00000000052 13263136567 021424 0 ustar www-data www-data module DidYouMean
VERSION = "1.2.1"
end
did_you_mean-1.2.1/lib/did_you_mean/formatters/ 0000755 0000041 0000041 00000000000 13263136567 021603 5 ustar www-data www-data did_you_mean-1.2.1/lib/did_you_mean/formatters/verbose_formatter.rb 0000644 0000041 0000041 00000000437 13263136567 025664 0 ustar www-data www-data # frozen-string-literal: true
module DidYouMean
class VerboseFormatter
def message_for(corrections)
return "" if corrections.empty?
output = "\n\n Did you mean? ".dup
output << corrections.join("\n ")
output << "\n "
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/formatters/plain_formatter.rb 0000644 0000041 0000041 00000000327 13263136567 025320 0 ustar www-data www-data # frozen-string-literal: true
module DidYouMean
class PlainFormatter
def message_for(corrections)
corrections.empty? ? "" : "\nDid you mean? #{corrections.join("\n ")}"
end
end
end
did_you_mean-1.2.1/lib/did_you_mean/experimental.rb 0000644 0000041 0000041 00000000171 13263136567 022436 0 ustar www-data www-data require 'did_you_mean/experimental/initializer_name_correction'
require 'did_you_mean/experimental/ivar_name_correction'
did_you_mean-1.2.1/lib/did_you_mean/verbose_formatter.rb 0000644 0000041 0000041 00000000471 13263136567 023474 0 ustar www-data www-data require 'did_you_mean'
require 'did_you_mean/formatters/verbose_formatter'
DidYouMean.formatter = DidYouMean::VerboseFormatter.new
warn '`require "did_you_mean/verbose_formatter"\' has been deprecated and will be removed' \
" in the next major Ruby version. Please require 'did_you_mean/verbose' instead."
did_you_mean-1.2.1/lib/did_you_mean/levenshtein.rb 0000644 0000041 0000041 00000002541 13263136567 022270 0 ustar www-data www-data module DidYouMean
module Levenshtein # :nodoc:
# This code is based directly on the Text gem implementation
# Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
#
# Returns a value representing the "cost" of transforming str1 into str2
def distance(str1, str2)
n = str1.length
m = str2.length
return m if n.zero?
return n if m.zero?
d = (0..m).to_a
x = nil
# to avoid duplicating an enumerable object, create it outside of the loop
str2_codepoints = str2.codepoints
str1.each_codepoint.with_index(1) do |char1, i|
j = 0
while j < m
cost = (char1 == str2_codepoints[j]) ? 0 : 1
x = min3(
d[j+1] + 1, # insertion
i + 1, # deletion
d[j] + cost # substitution
)
d[j] = i
i = x
j += 1
end
d[m] = x
end
x
end
module_function :distance
private
# detects the minimum value out of three arguments. This method is
# faster than `[a, b, c].min` and puts less GC pressure.
# See https://github.com/yuki24/did_you_mean/pull/1 for a performance
# benchmark.
def min3(a, b, c)
if a < b && a < c
a
elsif b < c
b
else
c
end
end
module_function :min3
end
end
did_you_mean-1.2.1/lib/did_you_mean.rb 0000644 0000041 0000041 00000003036 13263136567 017744 0 ustar www-data www-data require "did_you_mean/version"
require "did_you_mean/core_ext/name_error"
require "did_you_mean/spell_checker"
require 'did_you_mean/spell_checkers/name_error_checkers'
require 'did_you_mean/spell_checkers/method_name_checker'
require 'did_you_mean/spell_checkers/key_error_checker'
require 'did_you_mean/spell_checkers/null_checker'
require "did_you_mean/formatters/plain_formatter"
module DidYouMean
class DeprecatedIgnoredCallers < Array
%i(
+
<<
[]=
insert
unshift
push
).each do |method_name|
eval <<-RUBY, nil, __FILE__, __LINE__ + 1
def #{method_name}(*)
warn "IGNORED_CALLERS has been deprecated and has no effect."
super
end
RUBY
end
end
IGNORED_CALLERS = DeprecatedIgnoredCallers.new
SPELL_CHECKERS = Hash.new(NullChecker)
SPELL_CHECKERS.merge!({
"NameError" => NameErrorCheckers,
"NoMethodError" => MethodNameChecker,
"KeyError" => KeyErrorChecker
})
NameError.prepend DidYouMean::Correctable
KeyError.prepend DidYouMean::Correctable
def self.formatter
@@formatter
end
def self.formatter=(formatter)
@@formatter = formatter
end
self.formatter = PlainFormatter.new
# Deprecated formatter
class Formatter #:nodoc:
def initialize(corrections = [])
@corrections = corrections
end
def to_s
return "" if @corrections.empty?
output = "\nDid you mean? ".dup
output << @corrections.join("\n ")
end
end
deprecate_constant :Formatter
end
did_you_mean-1.2.1/did_you_mean.gemspec 0000644 0000041 0000041 00000001665 13263136567 020224 0 ustar www-data www-data # coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'did_you_mean/version'
Gem::Specification.new do |spec|
spec.name = "did_you_mean"
spec.version = DidYouMean::VERSION
spec.authors = ["Yuki Nishijima"]
spec.email = ["mail@yukinishijima.net"]
spec.summary = '"Did you mean?" experience in Ruby'
spec.description = 'The gem that has been saving people from typos since 2014.'
spec.homepage = "https://github.com/yuki24/did_you_mean"
spec.license = "MIT"
spec.files = `git ls-files`.split($/).reject{|path| path.start_with?('evaluation/') }
spec.test_files = spec.files.grep(%r{^(test)/})
spec.require_paths = ["lib"]
spec.required_ruby_version = '>= 2.5.0'
spec.add_development_dependency "bundler"
spec.add_development_dependency "rake"
spec.add_development_dependency "minitest"
end
did_you_mean-1.2.1/test/ 0000755 0000041 0000041 00000000000 13263136567 015172 5 ustar www-data www-data did_you_mean-1.2.1/test/deprecated_formatter_test.rb 0000644 0000041 0000041 00000000523 13263136567 022741 0 ustar www-data www-data require 'test_helper'
class DeprecatedFormatterTest < Minitest::Test
def test_deprecated_formatter
assert_output nil, /test\/deprecated_formatter_test.rb:6: warning: constant DidYouMean::Formatter is deprecated/ do
assert_equal "\nDid you mean? does_exist", ::DidYouMean::Formatter.new(['does_exist']).to_s
end
end
end
did_you_mean-1.2.1/test/core_ext/ 0000755 0000041 0000041 00000000000 13263136567 017002 5 ustar www-data www-data did_you_mean-1.2.1/test/core_ext/name_error_extension_test.rb 0000644 0000041 0000041 00000002232 13263136567 024612 0 ustar www-data www-data require 'test_helper'
class NameErrorExtensionTest < Minitest::Test
SPELL_CHECKERS = DidYouMean::SPELL_CHECKERS
class TestSpellChecker
def initialize(*); end
def corrections; ["does_exist"]; end
end
def setup
@org, SPELL_CHECKERS['NameError'] = SPELL_CHECKERS['NameError'], TestSpellChecker
@error = assert_raises(NameError){ doesnt_exist }
end
def teardown
SPELL_CHECKERS['NameError'] = @org
end
def test_message
message = <<~MESSAGE.chomp
undefined local variable or method `doesnt_exist' for #{to_s}
Did you mean? does_exist
MESSAGE
assert_equal message, @error.to_s
assert_equal message, @error.message
end
def test_to_s_does_not_make_disruptive_changes_to_error_message
error = assert_raises(NameError) do
raise NameError, "uninitialized constant Object"
end
error.to_s
assert_equal 1, error.to_s.scan("Did you mean?").count
end
end
class DeprecatedIgnoreCallersTest < Minitest::Test
def test_ignore
assert_output nil, "IGNORED_CALLERS has been deprecated and has no effect.\n" do
DidYouMean::IGNORED_CALLERS << /( |`)do_not_correct_typo'/
end
end
end
did_you_mean-1.2.1/test/edit_distance/ 0000755 0000041 0000041 00000000000 13263136567 017771 5 ustar www-data www-data did_you_mean-1.2.1/test/edit_distance/jaro_winkler_test.rb 0000644 0000041 0000041 00000002574 13263136567 024053 0 ustar www-data www-data require 'test_helper'
# These tests were originally written by Jian Weihang (簡煒航) as part of his work
# on the jaro_winkler gem. The original code could be found here:
# https://github.com/tonytonyjan/jaro_winkler/blob/9bd12421/spec/jaro_winkler_spec.rb
#
# Copyright (c) 2014 Jian Weihang
class JaroWinklerTest < Minitest::Test
def test_jaro_winkler_distance
assert_distance 0.9667, 'henka', 'henkan'
assert_distance 1.0, 'al', 'al'
assert_distance 0.9611, 'martha', 'marhta'
assert_distance 0.8324, 'jones', 'johnson'
assert_distance 0.9167, 'abcvwxyz', 'zabcvwxy'
assert_distance 0.9583, 'abcvwxyz', 'cabvwxyz'
assert_distance 0.84, 'dwayne', 'duane'
assert_distance 0.8133, 'dixon', 'dicksonx'
assert_distance 0.0, 'fvie', 'ten'
assert_distance 0.9067, 'does_exist', 'doesnt_exist'
assert_distance 1.0, 'x', 'x'
end
def test_jarowinkler_distance_with_utf8_strings
assert_distance 0.9818, '變形金剛4:絕跡重生', '變形金剛4: 絕跡重生'
assert_distance 0.8222, '連勝文', '連勝丼'
assert_distance 0.8222, '馬英九', '馬英丸'
assert_distance 0.6667, '良い', 'いい'
end
private
def assert_distance(score, str1, str2)
assert_equal score, DidYouMean::JaroWinkler.distance(str1, str2).round(4)
end
end
did_you_mean-1.2.1/test/experimental/ 0000755 0000041 0000041 00000000000 13263136567 017667 5 ustar www-data www-data did_you_mean-1.2.1/test/experimental/method_name_checker_test.rb 0000644 0000041 0000041 00000000561 13263136567 025221 0 ustar www-data www-data require 'test_helper'
class ExperimentalMethodNameCorrectionTest < Minitest::Test
def test_corrects_incorrect_ivar_name
@number = 1
@nubmer = nil
error = assert_raises(NoMethodError) { @nubmer.zero? }
remove_instance_variable :@nubmer
assert_correction :@number, error.corrections
assert_match "Did you mean? @number", error.to_s
end
end
did_you_mean-1.2.1/test/experimental/initializer_name_correction_test.rb 0000644 0000041 0000041 00000000635 13263136567 027031 0 ustar www-data www-data require 'test_helper'
class InitializerNameCorrectionTest < Minitest::Test
def test_corrects_wrong_initializer_name
assert_output nil, "warning: intialize might be misspelled, perhaps you meant initialize?\n" do
Class.new { def intialize; end }
end
end
def test_does_not_correct_correct_initializer_name
assert_output nil, "" do
Class.new { def initialize; end }
end
end
end
did_you_mean-1.2.1/test/fixtures/ 0000755 0000041 0000041 00000000000 13263136567 017043 5 ustar www-data www-data did_you_mean-1.2.1/test/fixtures/book.rb 0000644 0000041 0000041 00000000043 13263136567 020317 0 ustar www-data www-data class Book
class Cover
end
end
did_you_mean-1.2.1/test/test_helper.rb 0000644 0000041 0000041 00000000551 13263136567 020036 0 ustar www-data www-data require 'minitest/autorun'
require 'minitest/pride'
require 'did_you_mean'
puts "DidYouMean version: #{DidYouMean::VERSION}"
module DidYouMean::TestHelper
def assert_correction(expected, array)
assert_equal Array(expected), array, "Expected #{array.inspect} to only include #{expected.inspect}"
end
end
MiniTest::Test.include(DidYouMean::TestHelper)
did_you_mean-1.2.1/test/spell_checker_test.rb 0000644 0000041 0000041 00000005716 13263136567 021372 0 ustar www-data www-data require 'test_helper'
class SpellCheckerTest < Minitest::Test
def test_spell_checker_corrects_mistypes
assert_spell 'foo', input: 'doo', dictionary: ['foo', 'fork']
assert_spell 'email', input: 'meail', dictionary: ['email', 'fail', 'eval']
assert_spell 'fail', input: 'fial', dictionary: ['email', 'fail', 'eval']
assert_spell 'fail', input: 'afil', dictionary: ['email', 'fail', 'eval']
assert_spell 'eval', input: 'eavl', dictionary: ['email', 'fail', 'eval']
assert_spell 'eval', input: 'veal', dictionary: ['email', 'fail', 'eval']
assert_spell 'sub!', input: 'suv!', dictionary: ['sub', 'gsub', 'sub!']
assert_spell 'sub', input: 'suv', dictionary: ['sub', 'gsub', 'sub!']
assert_spell %w(gsub! gsub), input: 'gsuv!', dictionary: %w(sub gsub gsub!)
assert_spell %w(sub! sub gsub!), input: 'ssub!', dictionary: %w(sub sub! gsub gsub!)
group_methods = %w(groups group_url groups_url group_path)
assert_spell 'groups', input: 'group', dictionary: group_methods
group_classes = %w(
GroupMembership
GroupMembershipPolicy
GroupMembershipDecorator
GroupMembershipSerializer
GroupHelper
Group
GroupMailer
NullGroupMembership
)
assert_spell 'GroupMembership', dictionary: group_classes, input: 'GroupMemberhip'
assert_spell 'GroupMembershipDecorator', dictionary: group_classes, input: 'GroupMemberhipDecorator'
names = %w(first_name_change first_name_changed? first_name_will_change!)
assert_spell names, input: 'first_name_change!', dictionary: names
assert_empty DidYouMean::SpellChecker.new(dictionary: ['proc']).correct('product_path')
assert_empty DidYouMean::SpellChecker.new(dictionary: ['fork']).correct('fooo')
end
def test_spell_checker_corrects_misspells
assert_spell 'descendants', input: 'dependents', dictionary: ['descendants']
assert_spell 'drag_to', input: 'drag', dictionary: ['drag_to']
assert_spell 'set_result_count', input: 'set_result', dictionary: ['set_result_count']
end
def test_spell_checker_sorts_results_by_simiarity
expected = %w(
name12345
name1234
name123
)
actual = DidYouMean::SpellChecker.new(dictionary: %w(
name12
name123
name1234
name12345
name123456
)).correct('name123456')
assert_equal expected, actual
end
def test_spell_checker_excludes_input_from_dictionary
assert_empty DidYouMean::SpellChecker.new(dictionary: ['input']).correct('input')
assert_empty DidYouMean::SpellChecker.new(dictionary: [:input]).correct('input')
assert_empty DidYouMean::SpellChecker.new(dictionary: ['input']).correct(:input)
end
private
def assert_spell(expected, input: , dictionary: )
corrections = DidYouMean::SpellChecker.new(dictionary: dictionary).correct(input)
assert_equal Array(expected), corrections, "Expected to suggest #{expected}, but got #{corrections.inspect}"
end
end
did_you_mean-1.2.1/test/spell_checking/ 0000755 0000041 0000041 00000000000 13263136567 020144 5 ustar www-data www-data did_you_mean-1.2.1/test/spell_checking/key_name_check_test.rb 0000644 0000041 0000041 00000002633 13263136567 024461 0 ustar www-data www-data require "test_helper"
class KeyNameCheckTest < Minitest::Test
def test_corrects_hash_key_name_with_fetch
hash = { "foo" => 1, bar: 2 }
error = assert_raises(KeyError) { hash.fetch(:bax) }
assert_correction ":bar", error.corrections
assert_match "Did you mean? :bar", error.to_s
error = assert_raises(KeyError) { hash.fetch("fooo") }
assert_correction %("foo"), error.corrections
assert_match %(Did you mean? "foo"), error.to_s
end
def test_corrects_hash_key_name_with_fetch_values
hash = { "foo" => 1, bar: 2 }
error = assert_raises(KeyError) { hash.fetch_values("foo", :bar, :bax) }
assert_correction ":bar", error.corrections
assert_match "Did you mean? :bar", error.to_s
error = assert_raises(KeyError) { hash.fetch_values("foo", :bar, "fooo") }
assert_correction %("foo"), error.corrections
assert_match %(Did you mean? "foo"), error.to_s
end
def test_corrects_sprintf_key_name
error = assert_raises(KeyError) { sprintf("%d", {fooo: 1}) }
assert_correction ":fooo", error.corrections
assert_match "Did you mean? :fooo", error.to_s
end
def test_corrects_env_key_name
ENV["FOO"] = "1"
ENV["BAR"] = "2"
error = assert_raises(KeyError) { ENV.fetch("BAX") }
assert_correction %("BAR"), error.corrections
assert_match %(Did you mean? "BAR"), error.to_s
ensure
ENV.delete("FOO")
ENV.delete("BAR")
end
end
did_you_mean-1.2.1/test/spell_checking/variable_name_check_test.rb 0000644 0000041 0000041 00000006144 13263136567 025457 0 ustar www-data www-data require 'test_helper'
class VariableNameCheckTest < Minitest::Test
class User
def initialize
@email_address = 'email_address@address.net'
@first_name = nil
@last_name = nil
end
def first_name; end
def to_s
"#{@first_name} #{@last_name} <#{email_address}>"
end
private
def cia_codename; "Alexa" end
end
module UserModule
def from_module; end
end
def setup
@user = User.new.extend(UserModule)
end
def test_corrections_include_instance_method
error = assert_raises(NameError) do
@user.instance_eval { flrst_name }
end
@user.instance_eval do
remove_instance_variable :@first_name
remove_instance_variable :@last_name
end
assert_correction :first_name, error.corrections
assert_match "Did you mean? first_name", error.to_s
end
def test_corrections_include_method_from_module
error = assert_raises(NameError) do
@user.instance_eval { fr0m_module }
end
assert_correction :from_module, error.corrections
assert_match "Did you mean? from_module", error.to_s
end
def test_corrections_include_local_variable_name
person = person = nil
error = (eprson rescue $!) # Do not use @assert_raises here as it changes a scope.
assert_correction :person, error.corrections
assert_match "Did you mean? person", error.to_s
end
def test_corrections_include_ruby_predefined_objects
some_var = nil
false_error = assert_raises(NameError) do
some_var = fals
end
true_error = assert_raises(NameError) do
some_var = treu
end
nil_error = assert_raises(NameError) do
some_var = nol
end
assert_correction :false, false_error.corrections
assert_match "Did you mean? false", false_error.to_s
assert_correction :true, true_error.corrections
assert_match "Did you mean? true", true_error.to_s
assert_correction :nil, nil_error.corrections
assert_match "Did you mean? nil", nil_error.to_s
end
def test_corrections_include_instance_variable_name
error = assert_raises(NameError){ @user.to_s }
assert_correction :@email_address, error.corrections
assert_match "Did you mean? @email_address", error.to_s
end
def test_corrections_include_private_method
error = assert_raises(NameError) do
@user.instance_eval { cia_code_name }
end
assert_correction :cia_codename, error.corrections
assert_match "Did you mean? cia_codename", error.to_s
end
@@does_exist = true
def test_corrections_include_class_variable_name
error = assert_raises(NameError){ @@doesnt_exist }
assert_correction :@@does_exist, error.corrections
assert_match "Did you mean? @@does_exist", error.to_s
end
def test_struct_name_error
value = Struct.new(:does_exist).new
error = assert_raises(NameError){ value[:doesnt_exist] }
assert_correction [:does_exist, :does_exist=], error.corrections
assert_match "Did you mean? does_exist", error.to_s
end
def test_exclude_typical_incorrect_suggestions
error = assert_raises(NameError){ foo }
assert_empty error.corrections
end
end
did_you_mean-1.2.1/test/spell_checking/class_name_check_test.rb 0000644 0000041 0000041 00000004060 13263136567 024772 0 ustar www-data www-data require 'test_helper'
module ACRONYM
end
class Project
def self.bo0k
Bo0k
end
end
class Book
class TableOfContents; end
def tableof_contents
TableofContents
end
class Page
def tableof_contents
TableofContents
end
def self.tableof_contents
TableofContents
end
end
end
class ClassNameCheckTest < Minitest::Test
def test_corrections
error = assert_raises(NameError) { ::Bo0k }
assert_correction "Book", error.corrections
end
def test_corrections_include_case_specific_class_name
error = assert_raises(NameError) { ::Acronym }
assert_correction "ACRONYM", error.corrections
end
def test_corrections_include_top_level_class_name
error = assert_raises(NameError) { Project.bo0k }
assert_correction "Book", error.corrections
end
def test_names_in_corrections_have_namespaces
error = assert_raises(NameError) { ::Book::TableofContents }
assert_correction "Book::TableOfContents", error.corrections
end
def test_corrections_candidates_for_names_in_upper_level_scopes
error = assert_raises(NameError) { Book::Page.tableof_contents }
assert_correction "Book::TableOfContents", error.corrections
end
def test_corrections_should_work_from_within_instance_method
error = assert_raises(NameError) { ::Book.new.tableof_contents }
assert_correction "Book::TableOfContents", error.corrections
end
def test_corrections_should_work_from_within_instance_method_on_nested_class
error = assert_raises(NameError) { ::Book::Page.new.tableof_contents }
assert_correction "Book::TableOfContents", error.corrections
end
def test_does_not_suggest_user_input
error = assert_raises(NameError) { ::Book::Cover }
# This is a weird require, but in a multi-threaded condition, a constant may
# be loaded between when a NameError occurred and when the spell checker
# attemps to find a possible suggestion. The manual require here simulates
# a race condition a single test.
require_relative '../fixtures/book'
assert_empty error.corrections
end
end
did_you_mean-1.2.1/test/spell_checking/uncorrectable_name_check_test.rb 0000644 0000041 0000041 00000000473 13263136567 026521 0 ustar www-data www-data require 'test_helper'
class UncorrectableNameCheckTest < Minitest::Test
class FirstNameError < NameError; end
def setup
@error = assert_raises(FirstNameError) do
raise FirstNameError, "Other name error"
end
end
def test_message
assert_equal "Other name error", @error.message
end
end
did_you_mean-1.2.1/test/spell_checking/method_name_check_test.rb 0000644 0000041 0000041 00000005725 13263136567 025156 0 ustar www-data www-data require 'test_helper'
class MethodNameCheckTest < Minitest::Test
class User
def friends; end
def first_name; end
def descendants; end
def call_incorrect_private_method
raiae NoMethodError
end
def raise_no_method_error
self.firstname
rescue NoMethodError => e
raise e, e.message, e.backtrace
end
protected
def the_protected_method; end
private
def friend; end
def the_private_method; end
class << self
def load; end
end
end
module UserModule
def from_module; end
end
def setup
@user = User.new.extend(UserModule)
end
def test_corrections_include_instance_method
error = assert_raises(NoMethodError){ @user.flrst_name }
assert_correction :first_name, error.corrections
assert_match "Did you mean? first_name", error.to_s
end
def test_corrections_include_private_method
error = assert_raises(NoMethodError){ @user.friend }
assert_correction :friends, error.corrections
assert_match "Did you mean? friends", error.to_s
end
def test_corrections_include_method_from_module
error = assert_raises(NoMethodError){ @user.fr0m_module }
assert_correction :from_module, error.corrections
assert_match "Did you mean? from_module", error.to_s
end
def test_corrections_include_class_method
error = assert_raises(NoMethodError){ User.l0ad }
assert_correction :load, error.corrections
assert_match "Did you mean? load", error.to_s
end
def test_private_methods_should_not_be_suggested
error = assert_raises(NoMethodError){ User.new.the_protected_method }
refute_includes error.corrections, :the_protected_method
error = assert_raises(NoMethodError){ User.new.the_private_method }
refute_includes error.corrections, :the_private_method
end
def test_corrections_when_private_method_is_called_with_args
error = assert_raises(NoMethodError){ @user.call_incorrect_private_method }
assert_correction :raise, error.corrections
assert_match "Did you mean? raise", error.to_s
end
def test_exclude_methods_on_nil
error = assert_raises(NoMethodError){ nil.map }
assert_empty error.corrections
end
def test_does_not_exclude_custom_methods_on_nil
def nil.empty?
end
error = assert_raises(NoMethodError){ nil.empty }
assert_correction :empty?, error.corrections
ensure
NilClass.class_eval { undef empty? }
end
def test_does_not_append_suggestions_twice
error = assert_raises NoMethodError do
begin
@user.firstname
rescue NoMethodError => e
raise e, e.message, e.backtrace
end
end
assert_equal 1, error.to_s.scan(/Did you mean/).count
end
def test_does_not_append_suggestions_three_times
error = assert_raises NoMethodError do
begin
@user.raise_no_method_error
rescue NoMethodError => e
raise e, e.message, e.backtrace
end
end
assert_equal 1, error.to_s.scan(/Did you mean/).count
end
end
did_you_mean-1.2.1/test/verbose_formatter_test.rb 0000644 0000041 0000041 00000000755 13263136567 022315 0 ustar www-data www-data require 'test_helper'
class VerboseFormatterTest < Minitest::Test
def setup
require 'did_you_mean/verbose'
does_exist = does_exist = nil
@error = assert_raises(NameError){ doesnt_exist }
end
def teardown
DidYouMean.formatter = DidYouMean::PlainFormatter.new
end
def test_message
assert_equal <<~MESSAGE.chomp, @error.message
undefined local variable or method `doesnt_exist' for #{to_s}
Did you mean? does_exist
MESSAGE
end
end
did_you_mean-1.2.1/.gitignore 0000644 0000041 0000041 00000000275 13263136567 016207 0 ustar www-data www-data *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
test/tmp
test/version_tmp
tmp
log
evaluation/dictionary.yml
benchmark/results
did_you_mean-1.2.1/CHANGELOG.md 0000644 0000041 0000041 00000036263 13263136567 016036 0 ustar www-data www-data ## [v1.1.2](https://github.com/yuki24/did_you_mean/tree/v1.1.2)
_released at 2017-09-24 07:28:48 UTC_
**This version is compatible with Ruby 2.4 and older**
#### Bug Fixes
- Fixed a bug where `did_you_mean` shows duplicate suggestions when the exception is raised multiple times ([#84](https://github.com/yuki24/did_you_mean/pull/84), [c2e4008](https://github.com/yuki24/did_you_mean/commit/c2e40083cef604c00ccd10efc6d7a5036ad9eb5b))
## [v1.1.1](https://github.com/yuki24/did_you_mean/tree/v1.1.1)
_released at 2017-09-24 07:24:02 UTC_
### This version has been yanked from Rubygems.org and is not available.
## [v1.1.0](https://github.com/yuki24/did_you_mean/tree/v1.1.0)
_released at 2016-12-19 23:19:06 UTC_
The version `1.1.0` only has support for Ruby 2.4.0 and later. Also, all patch releasess under `1.1.*` will only be compatible with Ruby 2.4.0 and later as well. Versions under `1.0.*` will still be maintained until Ruby 2.3 is deprecated. Any other versions below `1.0` will no longer be maintained.
#### New Features
- Suggest a method name on a NameError from the `Struct#[]` or `Struct#[]=` method ([#73](https://github.com/yuki24/did_you_mean/pull/73)):
```ruby
Struct.new(:foo).new[:fooo]
# => NameError: no member 'fooo' in struct
# Did you mean? foo
# foo=
```
- Added a public interface for the gem's spell checker:
```ruby
DidYouMean::SpellChecker.new(dictionary: ['email', 'fail', 'eval']).correct('meail')
# => ['email']
```
- Methods defined on `nil` by default are no longer suggested. Note that methods, defined after the gem is loaded, will still be suggested (e.g. ActiveSupport).
#### Bug Fixes
- Fixed a bug where private method names were added to the dictionary when an argument was passed in to a public method. Use the `NoMethodError#private_call?` method instead ([0a1b761](https://github.com/yuki24/did_you_mean/commit/0a1b7612252055e583a373b473932f789381ca0f))
## [v1.0.3](https://github.com/yuki24/did_you_mean/tree/v1.0.3)
_released at 2017-09-24 07:22:07 UTC_
**This version is compatible with Ruby 2.3 and older**
#### Bug Fixes
- Fixed a bug where `did_you_mean` shows duplicate suggestions when the exception is raised multiple times ([#84](https://github.com/yuki24/did_you_mean/pull/84), [c2e4008](https://github.com/yuki24/did_you_mean/commit/c2e40083cef604c00ccd10efc6d7a5036ad9eb5b))
## [v1.0.2](https://github.com/yuki24/did_you_mean/tree/v1.0.2)
_released at 2016-11-20 18:03:07 UTC_
**This version is compatible with Ruby 2.3 and older**
#### Features
- Experimental features are officially available through `require 'did_you_mean/experimental'`
#### Deprecations
- `require 'did_you_mean/extra_features'` is now deprecated in favor of `require 'did_you_mean/experimental'`
#### Internal Changes
- Replaced the `DidYouMean::SpellCheckable` module with the `DidYouMean::SpellChecker` class. This is a slower implementation but close to the model explained in [this talk](https://speakerdeck.com/yuki24/saving-people-from-typos), more reusable and possibly makes it easier to expose the class as a public interface.
## [v1.0.1](https://github.com/yuki24/did_you_mean/tree/v1.0.1)
_released at 2016-05-15 05:17:22 UTC_
#### Bug Fixes
- Fixed a bug where the gem suggests what is actually typed by the user: [1c52c88](https://github.com/yuki24/did_you_mean/commit/1c52c887c62b0921e799f94bcc4a846dc7cbc057)
- Fixed features that didn't work on JRuby 9.1.0.0: [dc48dde](https://github.com/yuki24/did_you_mean/commit/dc48dde1b2a8f05aab1fcf897e1cb3075a206f53), [4de23f8](https://github.com/yuki24/did_you_mean/commit/4de23f880502c80c5f321371d39c08bb0fa34040), [00e3059](https://github.com/yuki24/did_you_mean/commit/00e305971060d150fae4817b5e895d6478b37579). The local variable name correction is still disabled. Also see: [jruby/jruby#3480](https://github.com/jruby/jruby/issues/3480)
## [v1.0.0](https://github.com/yuki24/did_you_mean/tree/v1.0.0)
_released at 2015-12-25 05:13:04 UTC_
#### Features
- Introduced a [verbose formatter](https://github.com/yuki24/did_you_mean#verbose-formatter)
- Introduced an easy way to enabling [experimental features](https://github.com/yuki24/did_you_mean#experimental-features)
#### Bug Fixes
- Fixed a bug where the Jaro-Winkler implementation returns the wrong distance when 2 identical strings are given. fixes [#58](https://github.com/yuki24/did_you_mean/pull/58)
#### Internal Changes
- Slightly changed the spell checking algorithm. Take a look at [e2f5b24](https://github.com/yuki24/did_you_mean/commit/e2f5b2437f967565e4830eab6077f73ae166e0a7) for more details. fixes [#60](https://github.com/yuki24/did_you_mean/issues/60)
## [v1.0.0.rc1](https://github.com/yuki24/did_you_mean/tree/v1.0.0.rc1)
_released at 2015-12-25 05:02:25 UTC_
#### Internal Chagens
- No longer uses `TracePoint` API by default. fixes [#55](https://github.com/yuki24/did_you_mean/issues/55) and [#56](https://github.com/yuki24/did_you_mean/issues/56)
## [v1.0.0.beta3](https://github.com/yuki24/did_you_mean/tree/v1.0.0.beta3)
_released at 2015-12-25 04:56:13 UTC_
#### Internal Changes
- Use the `frozen-string-literal` pragma rather than calling `.freeze` everywhere
- Use the `NameError#receiver` method in `DidYouMean:: ClassNameChecker` to know the namespace where the constant call is made
- Refactored the `SpellCheckerTest`
## [v1.0.0.beta2](https://github.com/yuki24/did_you_mean/tree/v1.0.0.beta2)
_released at 2015-12-25 04:50:36 UTC_
#### Bug Fixes
- Fixed a bug where the gem doesn't install properly on Ruby 2.3.0dev
## [v1.0.0.beta1](https://github.com/yuki24/did_you_mean/tree/v1.0.0.beta1)
_released at 2015-12-25 05:27:53 UTC_
#### Breaking Changes
- Dropped support for MRIs older than 2.3, JRuby and Rubinus
#### Internal Changes
- The C extension has been removed since the `NameError#receiver` method has become part of the MRI 2.3
- The interception gem has been removed from the dependencies
- Removed code that was needed to support multiple Ruby implementations
## [v0.10.0](https://github.com/yuki24/did_you_mean/tree/v0.10.0)
_released at 2015-08-21 06:44:11 UTC_
#### Features
- Now it corrects an instance variable name if the ivar name is mistyped and `NoMethodError` is raised:
```ruby
@number = 1
@nubmer.zero?
# => NoMethodError: undefined method `zero?' for nil:NilClass
#
# Did you mean? @number
#
```
- Support for JRuby 9.0.0.0
- Prefix-based correction ( [@tjohn](https://github.com/tjohn), [#50](https://github.com/yuki24/did_you_mean/issues/50 "Match start of method name"), [#49](https://github.com/yuki24/did_you_mean/issues/49 "Use Jaro distance instead of Jaro-Winkler distance"))
- Correction search is about 75% faster than 0.9.10
#### Breaking Changes
- The ActiveRecord integration has been removed
## [v0.9.10](https://github.com/yuki24/did_you_mean/tree/v0.9.10)
_released at 2015-05-14 03:04:47 UTC_
#### Bug Fixes
- Fixed a bug where a duplicate "did you mean?" message was appended each time `#to_s` is called ( [@danfinnie](https://github.com/danfinnie), [#51](https://github.com/yuki24/did_you_mean/issues/51 "Duplicate output for constants in separate gem"))
## [v0.9.9](https://github.com/yuki24/did_you_mean/tree/v0.9.9)
_released at 2015-05-13 03:48:19 UTC_
#### Features
- Order word suggestions based on Levenshtein distance ( [@tleish](https://github.com/tleish), [#31](https://github.com/yuki24/did_you_mean/pull/31))
#### Internal Changes
- Reduce memory allocation by about 40%
- Speed up Levenshtein distance calculation by about 40%
- The Java extension has been replaced with a pure JRuby implementation
## [v0.9.8](https://github.com/yuki24/did_you_mean/tree/v0.9.8)
_released at 2015-04-12 01:55:27 UTC_
#### Internal Changes
- Speed up Levenshtein by 50% and reduce 97% of memory usage
## [v0.9.7](https://github.com/yuki24/did_you_mean/tree/v0.9.7)
_released at 2015-04-02 04:20:26 UTC_
#### Bug Fixes
- Fixed an issue where _did\_you\_mean_ doesn't install on JRuby properly.
## [v0.9.6](https://github.com/yuki24/did_you_mean/tree/v0.9.6)
_released at 2015-01-24 23:19:27 UTC_
#### Bug Fixes
- Fixed a bug where did\_you\_mean incorrectly suggests protected methods when it just isn't callable ( [@glittershark](https://github.com/glittershark), [#34](https://github.com/yuki24/did_you_mean/issues/34 "Did\_you\_mean incorrectly called when attempting to call protected/private method"))
## [v0.9.5](https://github.com/yuki24/did_you_mean/tree/v0.9.5)
_released at 2015-01-07 12:41:23 UTC_
#### Bug Fixes
- Whitelist `#safe_constantize` method from `ActiveSupport::Inflector` to avoid significant performance slowdown ( [@tleish](https://github.com/tleish), [#19](https://github.com/yuki24/did_you_mean/issues/19 "Significant Slowdown when Using Debugger"), [#20](https://github.com/yuki24/did_you_mean/pull/20 "Whitelisting safe\_constantize (ActiveSupport::Inflector) method"))
## [v0.9.4](https://github.com/yuki24/did_you_mean/tree/v0.9.4)
_released at 2014-11-19 20:00:00 UTC_
#### Bug Fixes
- Fixed a bug where no suggestions will be made on JRuby
## [v0.9.3](https://github.com/yuki24/did_you_mean/tree/v0.9.3)
_released at 2014-11-18 03:50:11 UTC_
**This version has been yanked from rubygems.org as it doesn't work with jRuby at all. Please upgrade to 0.9.4 or higher as soon as possible.**
#### Internal Changes
- Replaced the crazy C extension with a so much better one (thanks to [@nobu](https://github.com/nobu)!)
## [v0.9.2](https://github.com/yuki24/did_you_mean/tree/v0.9.2)
_released at 2014-11-17 15:32:33 UTC_
#### Bug Fixes
- Fixed a bug where did\_you\_mean doesn't compile on Ruby 2.1.2/2.1.5 ( [#16](https://github.com/yuki24/did_you_mean/issues/16 "Gem building failed on Debian 6.0.10 x86\_64"))
## [v0.9.1](https://github.com/yuki24/did_you_mean/tree/v0.9.1)
_released at 2014-11-16 18:54:24 UTC_
**This version has been yanked from rubygems.org as it doesn't compile on Ruby 2.1.2 and 2.1.5. Please upgrade to 0.9.4 or higher as soon as possible.**
#### Internal Changes
- Shrink the gem size by removing unneeded ruby header files.
- Now it forces everyone to upgrade the gem when they upgrade Ruby to a new version. This avoids introducing a bug like [#14](https://github.com/yuki24/did_you_mean/issues/14 "Compatibility with `letter\_opener` gem").
## [v0.9.0](https://github.com/yuki24/did_you_mean/tree/v0.9.0)
_released at 2014-11-09 01:26:31 UTC_
#### Features
- did\_you\_mean now suggests instance variable names if `@` is missing ( [#12](https://github.com/yuki24/did_you_mean/issues/12 "Suggest instance- and class-vars"), [39d1e2b](https://github.com/yuki24/did_you_mean/commit/39d1e2bd66d6ff8acbc4dd5da922fc7e5fcefb20))
```ruby
@full_name = "Yuki Nishijima"
first_name, last_name = full_name.split(" ")
# => NameError: undefined local variable or method `full_name' for main:Object
#
# Did you mean? @full_name
#
```
#### Bug Fixes
- Fixed a bug where did\_you\_mean changes some behaviours of Ruby 2.1.3/2.1.4 installed on Max OS X ( [#14](https://github.com/yuki24/did_you_mean/issues/14 "Compatibility with `letter\_opener` gem"), [44c451f](https://github.com/yuki24/did_you_mean/commit/44c451f8c38b11763ba28ddf1ceb9696707ccea0), [9ebde21](https://github.com/yuki24/did_you_mean/commit/9ebde211e92eac8494e704f627c62fea7fdbee16))
- Fixed a bug where sometimes `NoMethodError` suggests duplicate method names ( [9865cc5](https://github.com/yuki24/did_you_mean/commit/9865cc5a9ce926dd9ad4c20d575b710e5f257a4b))
## [v0.8.0](https://github.com/yuki24/did_you_mean/tree/v0.8.0)
_released at 2014-10-27 02:03:13 UTC_
**This version has been yanked from rubygems.org as it has a serious bug with Ruby 2.1.3 and 2.1.4 installed on Max OS X. Please upgrade to 0.9.4 or higher as soon as possible.**
#### Features
- JRuby support!
#### Bug Fixes
- Fixed a bug where did\_you\_mean unexpectedly disables [better\_errors](https://github.com/charliesome/better_errors)'s REPL
- Replaced [binding\_of\_caller](https://github.com/banister/binding_of_caller) dependency with [interception](https://github.com/ConradIrwin/interception)
- Fixed the wrong implementation of Levenshtein algorithm ( [#2](https://github.com/yuki24/did_you_mean/pull/2 "Fix bug of DidYouMean::Levenshtein#min3."), [@fortissimo1997](https://github.com/fortissimo1997))
## [v0.7.0](https://github.com/yuki24/did_you_mean/tree/v0.7.0)
_released at 2014-09-26 03:37:18 UTC_
**This version has been yanked from rubygems.org as it has a serious bug with Ruby 2.1.3 and 2.1.4 installed on Max OS X. Please upgrade to 0.9.4 or higher as soon as possible.**
#### Features
- Added support for Ruby 2.1.3, 2.2.0-preview1 and ruby-head
- Added support for ActiveRecord 4.2.0.beta1
- Word searching is now about 40% faster than v0.6.0
- Removed `text` gem dependency
- Better output on pry and Rspec
#### Internal Changes
- A lot of internal refactoring
## [v0.6.0](https://github.com/yuki24/did_you_mean/tree/v0.6.0)
_released at 2014-05-18 00:23:24 UTC_
**This version has been yanked from rubygems.org as it has a serious bug with Ruby 2.1.3 and 2.1.4 installed on Max OS X. Please upgrade to 0.9.4 or higher as soon as possible.**
#### Features
- Added basic support for constants. Now you'll see class name suggestions when you misspelled a class names/module names:
```ruby
> Ocject
# => NameError: uninitialized constant Ocject
#
# Did you mean? Object
#
```
#### Bug Fixes
- Fixed a bug where did\_you\_mean segfaults on Ruby head(2.2.0dev)
## [v0.5.0](https://github.com/yuki24/did_you_mean/tree/v0.5.0)
_released at 2014-05-10 17:59:54 UTC_
#### Features
- Added support for Ruby 2.1.2
## [v0.4.0](https://github.com/yuki24/did_you_mean/tree/v0.4.0)
_released at 2014-04-20 02:10:31 UTC_
#### Features
- did\_you\_mean now suggests a similar attribute name when you misspelled it.
```ruby
User.new(flrst_name: "wrong flrst name")
# => ActiveRecord::UnknownAttributeError: unknown attribute: flrst_name
#
# Did you mean? first_name: string
#
```
#### Bug Fixes
- Fixed a bug where did\_you\_mean doesn't work with `ActiveRecord::UnknownAttributeError`
## [v0.3.1](https://github.com/yuki24/did_you_mean/tree/v0.3.1)
_released at 2014-03-20 23:16:20 UTC_
#### Features
- Changed output for readability.
- Made the spell checking algorithm slight better to find the correct method.
## [v0.3.0](https://github.com/yuki24/did_you_mean/tree/v0.3.0)
_released at 2014-03-20 23:13:13 UTC_
#### Features
- Added support for Ruby 2.1.1 and 2.2.0(head).
## [v0.2.0](https://github.com/yuki24/did_you_mean/tree/v0.2.0)
_released on 2014-03-20 23:12:13 UTC_
#### Features
- did\_you\_mean no longer makes Ruby slow.
#### Breaking Changes
- dropped support for JRuby and Rubbinious.
## [v0.1.0: First Release](https://github.com/yuki24/did_you_mean/tree/v0.1.0)
_released on 2014-03-20 23:11:14 UTC_
- Now you will have "did you mean?" experience in Ruby!
- but still very experimental since this gem makes Ruby a lot slower.
did_you_mean-1.2.1/README.md 0000644 0000041 0000041 00000006247 13263136567 015503 0 ustar www-data www-data # did_you_mean [](https://rubygems.org/gems/did_you_mean) [](https://travis-ci.org/yuki24/did_you_mean)
## Installation
Ruby 2.3 and later ships with this gem and it will automatically be `require`d when a Ruby process starts up. No special setup is required.
## Examples
### NameError
#### Correcting a Misspelled Method Name
```ruby
methosd
# => NameError: undefined local variable or method `methosd' for main:Object
# Did you mean? methods
# method
```
#### Correcting a Misspelled Class Name
```ruby
OBject
# => NameError: uninitialized constant OBject
# Did you mean? Object
```
#### Suggesting an Instance Variable Name
```ruby
@full_name = "Yuki Nishijima"
first_name, last_name = full_name.split(" ")
# => NameError: undefined local variable or method `full_name' for main:Object
# Did you mean? @full_name
```
#### Correcting a Class Variable Name
```ruby
@@full_name = "Yuki Nishijima"
@@full_anme
# => NameError: uninitialized class variable @@full_anme in Object
# Did you mean? @@full_name
```
### NoMethodError
```ruby
full_name = "Yuki Nishijima"
full_name.starts_with?("Y")
# => NoMethodError: undefined method `starts_with?' for "Yuki Nishijima":String
# Did you mean? start_with?
```
### KeyError
```ruby
hash = {foo: 1, bar: 2, baz: 3}
hash.fetch(:fooo)
# => KeyError: key not found: :fooo
# Did you mean? :foo
```
## Experimental Features
Aside from the basic features above, the `did_you_mean` gem comes with experimental features. They can be enabled by calling `require 'did_you_mean/experimental'`.
Note that **these experimental features should never be enabled in production as they would impact Ruby's performance and use some unstable Ruby APIs.**
### Correcting an Instance Variable When It's Incorrectly Spelled
```ruby
require 'did_you_mean/experimental'
@full_name = "Yuki Nishijima"
@full_anme.split(" ")
# => NoMethodError: undefined method `split' for nil:NilClass
# Did you mean? @full_name
```
### Displaying a Warning When `initialize` is Incorrectly Spelled
```ruby
require 'did_you_mean/experimental'
class Person
def intialize
...
end
end
# => warning: intialize might be misspelled, perhaps you meant initialize?
```
## Verbose Formatter
This verbose formatter changes the error message format to take more lines/spaces so it'll be slightly easier to read the suggestions. This formatter can totally be used in any environment including production.
```ruby
OBject
# => NameError: uninitialized constant OBject
# Did you mean? Object
require 'did_you_mean/verbose'
OBject
# => NameError: uninitialized constant OBject
#
# Did you mean? Object
#
```
## Contributing
1. Fork it (http://github.com/yuki24/did_you_mean/fork)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Make sure all tests pass (`bundle exec rake`)
5. Push to the branch (`git push origin my-new-feature`)
6. Create new Pull Request
## License
Copyright (c) 2014-16 Yuki Nishijima. See MIT-LICENSE for further details.