did_you_mean 0.9.10-java → 0.10.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.travis.yml +13 -19
- data/CHANGELOG.md +229 -0
- data/Gemfile +4 -7
- data/README.md +5 -16
- data/Rakefile +35 -33
- data/benchmark/jaro_winkler/memory_usage.rb +12 -0
- data/benchmark/jaro_winkler/speed.rb +14 -0
- data/benchmark/levenshtein/memory_usage.rb +12 -0
- data/benchmark/levenshtein/speed.rb +17 -0
- data/benchmark/memory_usage.rb +31 -0
- data/did_you_mean.gemspec +9 -2
- data/evaluation/calculator.rb +122 -0
- data/evaluation/dictionary_generator.rb +36 -0
- data/evaluation/incorrect_words.yaml +1159 -0
- data/ext/did_you_mean/extconf.rb +1 -1
- data/ext/did_you_mean/{method_missing.c → method_receiver.c} +1 -1
- data/lib/did_you_mean.rb +22 -3
- data/lib/did_you_mean/core_ext/name_error.rb +30 -22
- data/lib/did_you_mean/core_ext/no_method_error.rb +13 -2
- data/lib/did_you_mean/core_ext/rubinius.rb +16 -0
- data/lib/did_you_mean/finders.rb +48 -17
- data/lib/did_you_mean/finders/method_finder.rb +62 -0
- data/lib/did_you_mean/finders/name_error_finders.rb +8 -11
- data/lib/did_you_mean/finders/name_error_finders/class_finder.rb +77 -0
- data/lib/did_you_mean/finders/name_error_finders/name_finder.rb +18 -0
- data/lib/did_you_mean/formatter.rb +20 -0
- data/lib/did_you_mean/jaro_winkler.rb +87 -0
- data/lib/did_you_mean/test_helper.rb +1 -1
- data/lib/did_you_mean/version.rb +1 -1
- data/test/core_ext/name_error_extension_test.rb +74 -0
- data/test/{no_method_error_extension_test.rb → core_ext/no_method_error_extension_test.rb} +1 -1
- data/test/correctable/class_name_test.rb +65 -0
- data/test/correctable/method_name_test.rb +84 -0
- data/test/{null_finder_test.rb → correctable/uncorrectable_name_test.rb} +3 -7
- data/test/correctable/variable_name_test.rb +79 -0
- data/test/edit_distance/jaro_winkler_test.rb +30 -0
- data/test/test_helper.rb +2 -20
- data/test/word_collection_test.rb +76 -12
- metadata +58 -56
- data/Appraisals +0 -23
- data/gemfiles/activerecord_32.gemfile +0 -21
- data/gemfiles/activerecord_40.gemfile +0 -21
- data/gemfiles/activerecord_41.gemfile +0 -21
- data/gemfiles/activerecord_42.gemfile +0 -21
- data/gemfiles/activerecord_edge.gemfile +0 -25
- data/lib/did_you_mean/finders/name_error_finders/similar_class_finder.rb +0 -50
- data/lib/did_you_mean/finders/name_error_finders/similar_name_finder.rb +0 -61
- data/lib/did_you_mean/finders/similar_attribute_finder.rb +0 -26
- data/lib/did_you_mean/finders/similar_method_finder.rb +0 -34
- data/lib/did_you_mean/word_collection.rb +0 -30
- data/test/all_test.rb +0 -18
- data/test/name_error_extension_test.rb +0 -73
- data/test/similar_attribute_finder_test.rb +0 -17
- data/test/similar_class_finder_test.rb +0 -85
- data/test/similar_method_finder_test.rb +0 -60
- data/test/similar_name_finder_test.rb +0 -62
@@ -1,25 +0,0 @@
|
|
1
|
-
# This file was generated by Appraisal
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
git "git://github.com/rails/rails.git" do
|
6
|
-
gem "activerecord", :require => "activerecord"
|
7
|
-
end
|
8
|
-
|
9
|
-
gem "arel", :github => "rails/arel"
|
10
|
-
|
11
|
-
platforms :ruby do
|
12
|
-
gem "sqlite3"
|
13
|
-
end
|
14
|
-
|
15
|
-
platforms :jruby do
|
16
|
-
gem "activerecord-jdbcsqlite3-adapter"
|
17
|
-
end
|
18
|
-
|
19
|
-
platforms :rbx do
|
20
|
-
gem "rubysl", "~> 2.0"
|
21
|
-
gem "racc"
|
22
|
-
gem "rubinius-developer_tools"
|
23
|
-
end
|
24
|
-
|
25
|
-
gemspec :path => "../"
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module DidYouMean
|
2
|
-
class SimilarClassFinder
|
3
|
-
include BaseFinder
|
4
|
-
attr_reader :class_name, :original_message
|
5
|
-
|
6
|
-
def initialize(exception)
|
7
|
-
@class_name, @original_message = exception.name, exception.original_message
|
8
|
-
end
|
9
|
-
|
10
|
-
def words
|
11
|
-
scopes.flat_map do |scope|
|
12
|
-
scope.constants.map {|name| ConstantName.new(name, scope) }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def name_from_message
|
17
|
-
class_name || /([A-Z]\w*$)/.match(original_message)[0]
|
18
|
-
end
|
19
|
-
alias target_word name_from_message
|
20
|
-
|
21
|
-
def similar_words
|
22
|
-
super.map(&:full_name)
|
23
|
-
end
|
24
|
-
|
25
|
-
def scopes
|
26
|
-
@scopes ||= scope_base.size.times.map do |count|
|
27
|
-
eval(scope_base[0..(- count)].join("::"))
|
28
|
-
end.reverse << Object
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def scope_base
|
34
|
-
@scope_base ||= (/(([A-Z]\w*::)*)([A-Z]\w*)$/ =~ original_message ? $1 : "").split("::")
|
35
|
-
end
|
36
|
-
|
37
|
-
class ConstantName < String
|
38
|
-
attr_reader :scope
|
39
|
-
|
40
|
-
def initialize(str, scope)
|
41
|
-
super(str.to_s)
|
42
|
-
@scope = scope.to_s
|
43
|
-
end
|
44
|
-
|
45
|
-
def full_name
|
46
|
-
scope == "Object" ? to_s : "#{scope}::#{to_s}"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
module DidYouMean
|
2
|
-
class SimilarNameFinder
|
3
|
-
include BaseFinder
|
4
|
-
attr_reader :name, :_methods, :_local_variables, :_instance_variables
|
5
|
-
|
6
|
-
def initialize(exception)
|
7
|
-
@name = exception.name
|
8
|
-
@_methods = exception.frame_binding.eval("methods")
|
9
|
-
@_local_variables = exception.frame_binding.eval("local_variables")
|
10
|
-
@_instance_variables = exception.frame_binding.eval("instance_variables").map do |name|
|
11
|
-
name.to_s.tr("@", "")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def words
|
16
|
-
local_variable_names + method_names + instance_variable_names
|
17
|
-
end
|
18
|
-
|
19
|
-
alias target_word name
|
20
|
-
|
21
|
-
def local_variable_names
|
22
|
-
_local_variables.map {|word| LocalVariableName.new(word.to_s) }
|
23
|
-
end
|
24
|
-
|
25
|
-
def similar_local_variables
|
26
|
-
similar_words.select{|word| word.is_a?(LocalVariableName) }
|
27
|
-
end
|
28
|
-
|
29
|
-
def method_names
|
30
|
-
_methods.map {|word| MethodName.new(word.to_s) }
|
31
|
-
end
|
32
|
-
|
33
|
-
def similar_methods
|
34
|
-
similar_words.select{|word| word.is_a?(MethodName) }
|
35
|
-
end
|
36
|
-
|
37
|
-
def instance_variable_names
|
38
|
-
_instance_variables.map {|word| InstanceVariableName.new(word.to_s) }
|
39
|
-
end
|
40
|
-
|
41
|
-
def similar_instance_variables
|
42
|
-
similar_words.select {|word| word.is_a?(InstanceVariableName) }
|
43
|
-
end
|
44
|
-
|
45
|
-
def format(word)
|
46
|
-
"#{word.prefix}#{word}"
|
47
|
-
end
|
48
|
-
|
49
|
-
class MethodName < String
|
50
|
-
def prefix; "#"; end
|
51
|
-
end
|
52
|
-
|
53
|
-
class LocalVariableName < String
|
54
|
-
def prefix; ""; end
|
55
|
-
end
|
56
|
-
|
57
|
-
class InstanceVariableName < String
|
58
|
-
def prefix; "@"; end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module DidYouMean
|
2
|
-
class SimilarAttributeFinder
|
3
|
-
include BaseFinder
|
4
|
-
attr_reader :columns, :attribute_name
|
5
|
-
|
6
|
-
def initialize(exception)
|
7
|
-
@columns = exception.frame_binding.eval("self.class").columns
|
8
|
-
@attribute_name = (/unknown attribute(: | ')(\w+)/ =~ exception.original_message) && $2
|
9
|
-
end
|
10
|
-
|
11
|
-
def words
|
12
|
-
columns.map(&:name)
|
13
|
-
end
|
14
|
-
|
15
|
-
alias target_word attribute_name
|
16
|
-
|
17
|
-
def format(column_name)
|
18
|
-
"%{column}: %{type}" % {
|
19
|
-
column: column_name,
|
20
|
-
type: columns.detect{|c| c.name == column_name }.type
|
21
|
-
}
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
finders["ActiveRecord::UnknownAttributeError"] = SimilarAttributeFinder
|
26
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module DidYouMean
|
2
|
-
class SimilarMethodFinder
|
3
|
-
include BaseFinder
|
4
|
-
attr_reader :method_name, :receiver
|
5
|
-
|
6
|
-
def initialize(exception)
|
7
|
-
@method_name, @receiver = exception.name, exception.receiver
|
8
|
-
end
|
9
|
-
|
10
|
-
def words
|
11
|
-
method_names = receiver.methods + receiver.singleton_methods
|
12
|
-
method_names.delete(@method_name)
|
13
|
-
method_names.uniq
|
14
|
-
end
|
15
|
-
|
16
|
-
alias target_word method_name
|
17
|
-
|
18
|
-
def format(word)
|
19
|
-
"#{separator}#{word}"
|
20
|
-
end
|
21
|
-
|
22
|
-
def class_method?
|
23
|
-
receiver.is_a? Class
|
24
|
-
end
|
25
|
-
|
26
|
-
def separator
|
27
|
-
class_method? ? "." : "#"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'jruby'
|
32
|
-
finders["NoMethodError"] = SimilarMethodFinder
|
33
|
-
end
|
34
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'did_you_mean/levenshtein'
|
2
|
-
|
3
|
-
module DidYouMean
|
4
|
-
class WordCollection
|
5
|
-
include Enumerable
|
6
|
-
attr_reader :words
|
7
|
-
|
8
|
-
def initialize(words)
|
9
|
-
@words = words
|
10
|
-
end
|
11
|
-
|
12
|
-
def each(&block) words.each(&block); end
|
13
|
-
|
14
|
-
def similar_to(target_word)
|
15
|
-
target_word = target_word.to_s
|
16
|
-
threshold = threshold(target_word)
|
17
|
-
|
18
|
-
map {|word| [Levenshtein.distance(word.to_s, target_word), word] }
|
19
|
-
.select {|distance, _| distance <= threshold }
|
20
|
-
.sort
|
21
|
-
.map(&:last)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def threshold(word)
|
27
|
-
(word.size * 0.3).ceil
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/test/all_test.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require_relative 'null_finder_test'
|
2
|
-
require_relative 'word_collection_test'
|
3
|
-
require_relative 'name_error_extension_test'
|
4
|
-
require_relative 'similar_name_finder_test'
|
5
|
-
|
6
|
-
if defined?(RUBY_ENGINE) && %w(ruby jruby).include?(RUBY_ENGINE)
|
7
|
-
require_relative 'similar_method_finder_test'
|
8
|
-
require_relative 'no_method_error_extension_test'
|
9
|
-
else
|
10
|
-
puts "didn't run the tests for NoMethodError extension."
|
11
|
-
puts "running tests for #{RUBY_ENGINE}." if defined?(RUBY_ENGINE)
|
12
|
-
end
|
13
|
-
|
14
|
-
if defined?(ActiveRecord)
|
15
|
-
require_relative 'similar_attribute_finder_test'
|
16
|
-
end
|
17
|
-
|
18
|
-
require_relative 'similar_class_finder_test'
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
|
3
|
-
class NameErrorExtensionTest < Minitest::Test
|
4
|
-
class TestFinder
|
5
|
-
def initialize(*); end
|
6
|
-
def did_you_mean?; "Y U SO SLOW?"; end
|
7
|
-
end
|
8
|
-
|
9
|
-
def setup
|
10
|
-
@old_finder = DidYouMean.finders["NameError"]
|
11
|
-
DidYouMean.finders["NameError"] = TestFinder
|
12
|
-
|
13
|
-
@error = assert_raises(NameError){ doesnt_exist }
|
14
|
-
end
|
15
|
-
|
16
|
-
def teardown
|
17
|
-
DidYouMean.finders["NameError"] = @old_finder
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_message
|
21
|
-
assert_match "Y U SO SLOW?", @error.to_s
|
22
|
-
assert_match "Y U SO SLOW?", @error.message
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_to_s_does_not_make_disruptive_changes_to_error_message
|
26
|
-
error = assert_raises(NameError) do
|
27
|
-
raise NameError, "uninitialized constant Object".freeze
|
28
|
-
end
|
29
|
-
|
30
|
-
assert_equal 1, error.to_s.scan("Y U SO SLOW?").count
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
class IgnoreCallersTest < Minitest::Test
|
35
|
-
class Boomer
|
36
|
-
def initialize(*)
|
37
|
-
raise Exception, "finder was created when it shouldn't!"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def setup
|
42
|
-
@org = DidYouMean.finders["NameError"]
|
43
|
-
DidYouMean.finders["NameError"] = Boomer
|
44
|
-
|
45
|
-
@error = assert_raises(NameError){ doesnt_exist }
|
46
|
-
end
|
47
|
-
|
48
|
-
def teardown
|
49
|
-
DidYouMean.finders["NameError"] = @org
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_ignore_missing_name
|
53
|
-
assert_nothing_raised { missing_name }
|
54
|
-
end
|
55
|
-
|
56
|
-
def test_ignore_safe_constantize
|
57
|
-
assert_nothing_raised { safe_constantize }
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def safe_constantize
|
63
|
-
@error.message
|
64
|
-
end
|
65
|
-
|
66
|
-
def missing_name
|
67
|
-
@error.message
|
68
|
-
end
|
69
|
-
|
70
|
-
def assert_nothing_raised
|
71
|
-
yield
|
72
|
-
end
|
73
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
|
3
|
-
class SimilarAttributeFinderTest < Minitest::Test
|
4
|
-
def setup
|
5
|
-
@error = assert_raises(ActiveRecord::UnknownAttributeError) do
|
6
|
-
User.new(flrst_name: "wrong flrst name")
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_similar_words
|
11
|
-
assert_suggestion @error.finder.similar_words, "first_name"
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_did_you_mean?
|
15
|
-
assert_match "Did you mean? first_name: string", @error.did_you_mean?
|
16
|
-
end
|
17
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
|
3
|
-
class Project
|
4
|
-
def self.bo0k
|
5
|
-
Bo0k
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class Book
|
10
|
-
class TableOfContents; end
|
11
|
-
|
12
|
-
def tableof_contents
|
13
|
-
TableofContents
|
14
|
-
end
|
15
|
-
|
16
|
-
class Page
|
17
|
-
def tableof_contents
|
18
|
-
TableofContents
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.tableof_contents
|
22
|
-
TableofContents
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class SimpleSimilarClassFinderTest < Minitest::Test
|
28
|
-
def setup
|
29
|
-
@error = assert_raises(NameError) { ::Bo0k }
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_similar_words
|
33
|
-
assert_suggestion @error.finder.similar_words, "Book"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class SimilarClassFinderInsideClassTest < Minitest::Test
|
38
|
-
def setup
|
39
|
-
@error = assert_raises(NameError) { Project.bo0k }
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_similar_words
|
43
|
-
assert_suggestion @error.finder.similar_words, "Book"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
class SimilarClassFinderInsideNestedClassTest < Minitest::Test
|
48
|
-
def setup
|
49
|
-
@error = assert_raises(NameError) { Book::Page.tableof_contents }
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_similar_words
|
53
|
-
assert_suggestion @error.finder.similar_words, "Book::TableOfContents"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class SimilarClassFinderForClassWithNamespaceTest < Minitest::Test
|
58
|
-
def setup
|
59
|
-
@error = assert_raises(NameError) { ::Book::TableofContents }
|
60
|
-
end
|
61
|
-
|
62
|
-
def test_similar_words
|
63
|
-
assert_suggestion @error.finder.similar_words, "Book::TableOfContents"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
class SimilarClassFinderFromInstanceTest < Minitest::Test
|
68
|
-
def setup
|
69
|
-
@error = assert_raises(NameError) { ::Book.new.tableof_contents }
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_similar_words
|
73
|
-
assert_suggestion @error.finder.similar_words, "Book::TableOfContents"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
class SimilarClassFinderFromNestedInstanceTest < Minitest::Test
|
78
|
-
def setup
|
79
|
-
@error = assert_raises(NameError) { ::Book::Page.new.tableof_contents }
|
80
|
-
end
|
81
|
-
|
82
|
-
def test_similar_words
|
83
|
-
assert_suggestion @error.finder.similar_words, "Book::TableOfContents"
|
84
|
-
end
|
85
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
|
3
|
-
class SimilarMethodFinderTest < Minitest::Test
|
4
|
-
class User
|
5
|
-
def friends; end
|
6
|
-
def first_name; end
|
7
|
-
def descendants; end
|
8
|
-
|
9
|
-
protected
|
10
|
-
def the_protected_method; end
|
11
|
-
|
12
|
-
private
|
13
|
-
def friend; end
|
14
|
-
def the_private_method; end
|
15
|
-
|
16
|
-
class << self
|
17
|
-
def load; end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
module UserModule
|
22
|
-
def from_module; end
|
23
|
-
end
|
24
|
-
|
25
|
-
def setup
|
26
|
-
user = User.new.extend(UserModule)
|
27
|
-
|
28
|
-
@error_from_instance_method = assert_raises(NoMethodError){ user.flrst_name }
|
29
|
-
@error_from_private_method = assert_raises(NoMethodError){ user.friend }
|
30
|
-
@error_from_module_method = assert_raises(NoMethodError){ user.fr0m_module }
|
31
|
-
@error_from_class_method = assert_raises(NoMethodError){ User.l0ad }
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_similar_words
|
35
|
-
assert_suggestion @error_from_instance_method.finder.similar_words, :first_name
|
36
|
-
assert_suggestion @error_from_private_method.finder.similar_words, :friends
|
37
|
-
assert_suggestion @error_from_module_method.finder.similar_words, :from_module
|
38
|
-
assert_suggestion @error_from_class_method.finder.similar_words, :load
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_did_you_mean?
|
42
|
-
assert_match "Did you mean? #first_name", @error_from_instance_method.did_you_mean?
|
43
|
-
assert_match "Did you mean? #friends", @error_from_private_method.did_you_mean?
|
44
|
-
assert_match "Did you mean? #from_module", @error_from_module_method.did_you_mean?
|
45
|
-
assert_match "Did you mean? .load", @error_from_class_method.did_you_mean?
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_similar_words_for_long_method_name
|
49
|
-
error = assert_raises(NoMethodError){ User.new.dependents }
|
50
|
-
assert_suggestion error.finder.similar_words, :descendants
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_private_methods_should_not_be_suggested
|
54
|
-
error = assert_raises(NoMethodError){ User.new.the_protected_method }
|
55
|
-
refute_includes error.finder.similar_words, 'the_protected_method'
|
56
|
-
|
57
|
-
error = assert_raises(NoMethodError){ User.new.the_private_method }
|
58
|
-
refute_includes error.finder.similar_words, 'the_private_method'
|
59
|
-
end
|
60
|
-
end
|