did_you_mean 0.9.10-java → 0.10.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|