did_you_mean 1.0.0.rc1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Rakefile +22 -2
- data/lib/did_you_mean.rb +0 -8
- data/lib/did_you_mean/extra_features.rb +16 -0
- data/lib/did_you_mean/extra_features/initializer_name_correction.rb +18 -0
- data/lib/did_you_mean/extra_features/ivar_name_correction.rb +43 -0
- data/lib/did_you_mean/jaro_winkler.rb +1 -0
- data/lib/did_you_mean/spell_checkable.rb +6 -4
- data/lib/did_you_mean/spell_checkers/method_name_checker.rb +3 -39
- data/lib/did_you_mean/verbose_formatter.rb +16 -0
- data/lib/did_you_mean/version.rb +1 -1
- data/test/correctable/method_name_test.rb +0 -19
- data/test/edit_distance/jaro_winkler_test.rb +1 -0
- data/test/extra_features/initializer_name_correction_test.rb +15 -0
- data/test/extra_features/method_name_checker_test.rb +13 -0
- data/test/spell_checker_test.rb +8 -7
- data/test/verbose_formatter_test.rb +17 -0
- metadata +15 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4765e62fea49eb8f8863c01825a7ec89b1be951e
|
4
|
+
data.tar.gz: 4c993cb9c366008086539cf047ba7a81eddf7450
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e68859b53e91e551e698168bdb06a1ad0034e5a12457ca974a2d2147504f2627344af672de248c156d4a8df64afb0e298bf523b6242c1192f84f5cea94a9bcb2
|
7
|
+
data.tar.gz: 16f2eb958b3494439251ae0c33fb5f924370f7fab9632bbddaa062b8d6023214247bd98af7b0c806f1b0a17aa3e059a986a048bba1b93141dcfb658b1c96d84d
|
data/.travis.yml
CHANGED
data/Rakefile
CHANGED
@@ -3,12 +3,32 @@ require 'rake/testtask'
|
|
3
3
|
|
4
4
|
Rake::TestTask.new do |task|
|
5
5
|
task.libs << "test"
|
6
|
-
|
6
|
+
|
7
|
+
task.test_files = Dir['test/**/*_test.rb'].reject do |path|
|
8
|
+
/(verbose_formatter|extra_features)/ =~ path
|
9
|
+
end
|
10
|
+
|
11
|
+
task.verbose = true
|
12
|
+
task.warning = true
|
13
|
+
end
|
14
|
+
|
15
|
+
Rake::TestTask.new("test:verbose_formatter") do |task|
|
16
|
+
task.libs << "test"
|
17
|
+
task.pattern = 'test/verbose_formatter_test.rb'
|
18
|
+
task.verbose = true
|
19
|
+
task.warning = true
|
20
|
+
task.ruby_opts << "-rdid_you_mean/verbose_formatter"
|
21
|
+
end
|
22
|
+
|
23
|
+
Rake::TestTask.new("test:extra_features") do |task|
|
24
|
+
task.libs << "test"
|
25
|
+
task.pattern = 'test/extra_features/**/*_test.rb'
|
7
26
|
task.verbose = true
|
8
27
|
task.warning = true
|
28
|
+
task.ruby_opts << "-rdid_you_mean/extra_features"
|
9
29
|
end
|
10
30
|
|
11
|
-
task default: :test
|
31
|
+
task default: %i(test test:verbose_formatter test:extra_features)
|
12
32
|
|
13
33
|
namespace :test do
|
14
34
|
namespace :accuracy do
|
data/lib/did_you_mean.rb
CHANGED
@@ -9,14 +9,6 @@ require 'did_you_mean/spell_checkers/null_checker'
|
|
9
9
|
require "did_you_mean/formatter"
|
10
10
|
|
11
11
|
module DidYouMean
|
12
|
-
TRACE = TracePoint.new(:raise) do |tp|
|
13
|
-
e = tp.raised_exception
|
14
|
-
|
15
|
-
if SPELL_CHECKERS.include?(e.class.to_s) && !e.instance_variable_defined?(:@frame_binding)
|
16
|
-
e.instance_variable_set(:@frame_binding, tp.binding)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
12
|
IGNORED_CALLERS = []
|
21
13
|
|
22
14
|
SPELL_CHECKERS = Hash.new(NullChecker)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'did_you_mean'
|
2
|
+
|
3
|
+
module DidYouMean
|
4
|
+
TRACE = TracePoint.trace(:raise) do |tp|
|
5
|
+
e = tp.raised_exception
|
6
|
+
|
7
|
+
if SPELL_CHECKERS.include?(e.class.to_s) && !e.instance_variable_defined?(:@frame_binding)
|
8
|
+
e.instance_variable_set(:@frame_binding, tp.binding)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
NameError.send(:attr, :frame_binding)
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'did_you_mean/extra_features/initializer_name_correction'
|
16
|
+
require 'did_you_mean/extra_features/ivar_name_correction'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- frozen-string-literal: true -*-
|
2
|
+
|
3
|
+
module DidYouMean
|
4
|
+
module ExtraFeatures
|
5
|
+
module InitializerNameCorrection
|
6
|
+
def method_added(name)
|
7
|
+
super
|
8
|
+
|
9
|
+
distance = Levenshtein.distance(name.to_s, 'initialize')
|
10
|
+
if distance != 0 && distance <= 2
|
11
|
+
warn "warning: #{name} might be misspelled, perhaps you meant initialize?"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
::Class.prepend(InitializerNameCorrection)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module DidYouMean
|
2
|
+
module ExtraFeatures
|
3
|
+
module IvarNameCorrectable
|
4
|
+
REPLS = {
|
5
|
+
"(irb)" => -> { Readline::HISTORY.to_a.last }
|
6
|
+
}
|
7
|
+
|
8
|
+
def initialize(no_method_error)
|
9
|
+
super
|
10
|
+
|
11
|
+
@location = no_method_error.backtrace_locations.first
|
12
|
+
@ivar_names = no_method_error.frame_binding.receiver.instance_variables
|
13
|
+
end
|
14
|
+
|
15
|
+
def candidates
|
16
|
+
super.merge(receiver_name.to_s => @ivar_names)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def receiver_name
|
22
|
+
return unless receiver.nil?
|
23
|
+
|
24
|
+
abs_path = @location.absolute_path
|
25
|
+
lineno = @location.lineno
|
26
|
+
|
27
|
+
/@(\w+)*\.#{method_name}/ =~ line(abs_path, lineno).to_s && $1
|
28
|
+
end
|
29
|
+
|
30
|
+
def line(abs_path, lineno)
|
31
|
+
if REPLS[abs_path]
|
32
|
+
REPLS[abs_path].call
|
33
|
+
elsif File.exist?(abs_path)
|
34
|
+
File.open(abs_path) do |file|
|
35
|
+
file.detect { file.lineno == lineno }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
SPELL_CHECKERS['NoMethodError'].prepend(IvarNameCorrectable)
|
42
|
+
end
|
43
|
+
end
|
@@ -16,11 +16,13 @@ module DidYouMean
|
|
16
16
|
|
17
17
|
# Correct mistypes
|
18
18
|
threshold = (input.length * 0.25).ceil
|
19
|
-
|
19
|
+
has_mistype = seed.rindex {|c| Levenshtein.distance(normalize(c), input) <= threshold }
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
corrections = if has_mistype
|
22
|
+
seed.take(has_mistype + 1)
|
23
|
+
else
|
24
|
+
# Correct misspells
|
25
|
+
seed.select do |candidate|
|
24
26
|
candidate = normalize(candidate)
|
25
27
|
length = input.length < candidate.length ? input.length : candidate.length
|
26
28
|
|
@@ -6,6 +6,7 @@ module DidYouMean
|
|
6
6
|
def initialize(exception)
|
7
7
|
@method_name = exception.name
|
8
8
|
@receiver = exception.receiver
|
9
|
+
@has_args = !exception.args&.empty?
|
9
10
|
end
|
10
11
|
|
11
12
|
def candidates
|
@@ -13,48 +14,11 @@ module DidYouMean
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def method_names
|
16
|
-
method_names = receiver.methods + receiver.singleton_methods
|
17
|
+
method_names = receiver.methods + receiver.singleton_methods
|
18
|
+
method_names += receiver.private_methods if @has_args
|
17
19
|
method_names.delete(method_name)
|
18
20
|
method_names.uniq!
|
19
21
|
method_names
|
20
22
|
end
|
21
|
-
|
22
|
-
module IvarNameCorrectable
|
23
|
-
REPLS = {
|
24
|
-
"(irb)" => -> { Readline::HISTORY.to_a.last }
|
25
|
-
}
|
26
|
-
|
27
|
-
def initialize(exception)
|
28
|
-
super
|
29
|
-
|
30
|
-
@location = exception.backtrace_locations.first
|
31
|
-
@ivar_names = exception.instance_variable_get(:@frame_binding).receiver.instance_variables
|
32
|
-
end
|
33
|
-
|
34
|
-
def candidates
|
35
|
-
super.merge(receiver_name.to_s => @ivar_names)
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def receiver_name
|
41
|
-
return unless receiver.nil?
|
42
|
-
|
43
|
-
abs_path = @location.absolute_path
|
44
|
-
lineno = @location.lineno
|
45
|
-
|
46
|
-
/@(\w+)*\.#{method_name}/ =~ line(abs_path, lineno).to_s && $1
|
47
|
-
end
|
48
|
-
|
49
|
-
def line(abs_path, lineno)
|
50
|
-
if REPLS[abs_path]
|
51
|
-
REPLS[abs_path].call
|
52
|
-
elsif File.exist?(abs_path)
|
53
|
-
File.open(abs_path) do |file|
|
54
|
-
file.detect { file.lineno == lineno }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
23
|
end
|
60
24
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- frozen-string-literal: true -*-
|
2
|
+
require 'did_you_mean/formatter'
|
3
|
+
|
4
|
+
module DidYouMean
|
5
|
+
module VerboseFormatter
|
6
|
+
prepend_features DidYouMean::Formatter
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
return "" if @corrections.empty?
|
10
|
+
|
11
|
+
output = "\n\n Did you mean? ".dup
|
12
|
+
output << @corrections.join("\n ")
|
13
|
+
output << "\n "
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/did_you_mean/version.rb
CHANGED
@@ -71,23 +71,4 @@ class MethodNameTest < Minitest::Test
|
|
71
71
|
assert_correction :raise, error.corrections
|
72
72
|
assert_match "Did you mean? raise", error.to_s
|
73
73
|
end
|
74
|
-
|
75
|
-
D = DidYouMean
|
76
|
-
M = D::MethodNameChecker
|
77
|
-
|
78
|
-
def test_corrects_incorrect_ivar_name
|
79
|
-
D::TRACE.enable
|
80
|
-
D::SPELL_CHECKERS['NoMethodError'] = M.dup.prepend(M::IvarNameCorrectable)
|
81
|
-
|
82
|
-
@number = 1
|
83
|
-
@nubmer = nil
|
84
|
-
error = assert_raises(NoMethodError) { @nubmer.zero? }
|
85
|
-
remove_instance_variable :@nubmer
|
86
|
-
|
87
|
-
assert_correction :@number, error.corrections
|
88
|
-
assert_match "Did you mean? @number", error.to_s
|
89
|
-
ensure
|
90
|
-
D::TRACE.disable
|
91
|
-
D::SPELL_CHECKERS['NoMethodError'] = M
|
92
|
-
end
|
93
74
|
end
|
@@ -13,6 +13,7 @@ class JaroWinklerTest < Minitest::Test
|
|
13
13
|
assert_distance 0.8133, 'dixon', 'dicksonx'
|
14
14
|
assert_distance 0.0, 'fvie', 'ten'
|
15
15
|
assert_distance 0.9067, 'does_exist', 'doesnt_exist'
|
16
|
+
assert_distance 1.0, 'x', 'x'
|
16
17
|
end
|
17
18
|
|
18
19
|
def test_jarowinkler_distance_with_utf8_strings
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class InitializerNameCorrectionTest < Minitest::Test
|
4
|
+
def test_corrects_wrong_initializer_name
|
5
|
+
assert_output nil, "warning: intialize might be misspelled, perhaps you meant initialize?\n" do
|
6
|
+
Class.new { def intialize; end }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_does_not_correct_correct_initializer_name
|
11
|
+
assert_output nil, "" do
|
12
|
+
Class.new { def initialize; end }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MethodNameWithExtraFeaturesTest < Minitest::Test
|
4
|
+
def test_corrects_incorrect_ivar_name
|
5
|
+
@number = 1
|
6
|
+
@nubmer = nil
|
7
|
+
error = assert_raises(NoMethodError) { @nubmer.zero? }
|
8
|
+
remove_instance_variable :@nubmer
|
9
|
+
|
10
|
+
assert_correction :@number, error.corrections
|
11
|
+
assert_match "Did you mean? @number", error.to_s
|
12
|
+
end
|
13
|
+
end
|
data/test/spell_checker_test.rb
CHANGED
@@ -9,7 +9,7 @@ class SpellCheckerTest < Minitest::Test
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def test_spell_checker_corrects_mistypes
|
13
13
|
assert_spell 'foo', input: 'doo', dictionary: ['foo', 'fork']
|
14
14
|
assert_spell 'email', input: 'meail', dictionary: ['email', 'fail', 'eval']
|
15
15
|
assert_spell 'fail', input: 'fial', dictionary: ['email', 'fail', 'eval']
|
@@ -19,8 +19,9 @@ class SpellCheckerTest < Minitest::Test
|
|
19
19
|
assert_spell 'sub!', input: 'suv!', dictionary: ['sub', 'gsub', 'sub!']
|
20
20
|
assert_spell 'sub', input: 'suv', dictionary: ['sub', 'gsub', 'sub!']
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
assert_spell %w(gsub! gsub), input: 'gsuv!', dictionary: %w(sub gsub gsub!)
|
23
|
+
assert_spell %w(sub! sub gsub!), input: 'ssub!', dictionary: %w(sub sub! gsub gsub!)
|
24
|
+
assert_spell %i(read rand), input: 'raed', dictionary: File.methods + File.private_methods
|
24
25
|
|
25
26
|
group_methods = %w(groups group_url groups_url group_path)
|
26
27
|
assert_spell 'groups', input: 'group', dictionary: group_methods
|
@@ -40,19 +41,19 @@ class SpellCheckerTest < Minitest::Test
|
|
40
41
|
assert_spell 'GroupMembershipDecorator', dictionary: group_classes, input: 'GroupMemberhipDecorator'
|
41
42
|
|
42
43
|
names = %w(first_name_change first_name_changed? first_name_will_change!)
|
43
|
-
|
44
|
+
assert_spell names, input: 'first_name_change!', dictionary: names
|
44
45
|
|
45
46
|
assert_empty SpellChecker.new('product_path', ['proc']).corrections
|
46
47
|
assert_empty SpellChecker.new('fooo', ['fork']).corrections
|
47
48
|
end
|
48
49
|
|
49
|
-
def
|
50
|
+
def test_spell_checker_corrects_misspells
|
50
51
|
assert_spell 'descendants', input: 'dependents', dictionary: ['descendants']
|
51
52
|
assert_spell 'drag_to', input: 'drag', dictionary: ['drag_to']
|
52
53
|
assert_spell 'set_result_count', input: 'set_result', dictionary: ['set_result_count']
|
53
54
|
end
|
54
55
|
|
55
|
-
def
|
56
|
+
def test_spell_checker_sorts_results_by_simiarity
|
56
57
|
expected = %w(
|
57
58
|
name123456
|
58
59
|
name12345
|
@@ -75,6 +76,6 @@ class SpellCheckerTest < Minitest::Test
|
|
75
76
|
|
76
77
|
def assert_spell(expected, input: , dictionary: )
|
77
78
|
corrections = SpellChecker.new(input, dictionary).corrections
|
78
|
-
assert_equal
|
79
|
+
assert_equal Array(expected), corrections, "Expected to suggest #{expected}, but got #{corrections.inspect}"
|
79
80
|
end
|
80
81
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class VerboseFormatterTest < Minitest::Test
|
4
|
+
def setup
|
5
|
+
does_exist = does_exist = nil
|
6
|
+
@error = assert_raises(NameError){ doesnt_exist }
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_message
|
10
|
+
assert_equal <<~MESSAGE.chomp, @error.message
|
11
|
+
undefined local variable or method `doesnt_exist' for #{method(:to_s).super_method.call}
|
12
|
+
|
13
|
+
Did you mean? does_exist
|
14
|
+
|
15
|
+
MESSAGE
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: did_you_mean
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Nishijima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,6 +81,9 @@ files:
|
|
81
81
|
- evaluation/incorrect_words.yaml
|
82
82
|
- lib/did_you_mean.rb
|
83
83
|
- lib/did_you_mean/core_ext/name_error.rb
|
84
|
+
- lib/did_you_mean/extra_features.rb
|
85
|
+
- lib/did_you_mean/extra_features/initializer_name_correction.rb
|
86
|
+
- lib/did_you_mean/extra_features/ivar_name_correction.rb
|
84
87
|
- lib/did_you_mean/formatter.rb
|
85
88
|
- lib/did_you_mean/jaro_winkler.rb
|
86
89
|
- lib/did_you_mean/levenshtein.rb
|
@@ -90,6 +93,7 @@ files:
|
|
90
93
|
- lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
|
91
94
|
- lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
|
92
95
|
- lib/did_you_mean/spell_checkers/null_checker.rb
|
96
|
+
- lib/did_you_mean/verbose_formatter.rb
|
93
97
|
- lib/did_you_mean/version.rb
|
94
98
|
- test/core_ext/name_error_extension_test.rb
|
95
99
|
- test/correctable/class_name_test.rb
|
@@ -97,8 +101,11 @@ files:
|
|
97
101
|
- test/correctable/uncorrectable_name_test.rb
|
98
102
|
- test/correctable/variable_name_test.rb
|
99
103
|
- test/edit_distance/jaro_winkler_test.rb
|
104
|
+
- test/extra_features/initializer_name_correction_test.rb
|
105
|
+
- test/extra_features/method_name_checker_test.rb
|
100
106
|
- test/spell_checker_test.rb
|
101
107
|
- test/test_helper.rb
|
108
|
+
- test/verbose_formatter_test.rb
|
102
109
|
homepage: https://github.com/yuki24/did_you_mean
|
103
110
|
licenses:
|
104
111
|
- MIT
|
@@ -114,12 +121,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
121
|
version: 2.3.0dev
|
115
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
123
|
requirements:
|
117
|
-
- - "
|
124
|
+
- - ">="
|
118
125
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
126
|
+
version: '0'
|
120
127
|
requirements: []
|
121
128
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.5.
|
129
|
+
rubygems_version: 2.5.1
|
123
130
|
signing_key:
|
124
131
|
specification_version: 4
|
125
132
|
summary: '"Did you mean?" experience in Ruby'
|
@@ -130,5 +137,8 @@ test_files:
|
|
130
137
|
- test/correctable/uncorrectable_name_test.rb
|
131
138
|
- test/correctable/variable_name_test.rb
|
132
139
|
- test/edit_distance/jaro_winkler_test.rb
|
140
|
+
- test/extra_features/initializer_name_correction_test.rb
|
141
|
+
- test/extra_features/method_name_checker_test.rb
|
133
142
|
- test/spell_checker_test.rb
|
134
143
|
- test/test_helper.rb
|
144
|
+
- test/verbose_formatter_test.rb
|