did_you_mean 1.0.0.rc1 → 1.0.0
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/.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
|