did_you_mean 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/README.md +5 -5
- data/Rakefile +5 -5
- data/benchmark/memory_usage.rb +1 -18
- data/evaluation/calculator.rb +3 -22
- data/lib/did_you_mean.rb +1 -1
- data/lib/did_you_mean/experimental.rb +17 -0
- data/lib/did_you_mean/{extra_features → experimental}/initializer_name_correction.rb +1 -1
- data/lib/did_you_mean/{extra_features → experimental}/ivar_name_correction.rb +7 -3
- data/lib/did_you_mean/experimental/key_error_name_correction.rb +32 -0
- data/lib/did_you_mean/extra_features.rb +2 -15
- data/lib/did_you_mean/spell_checker.rb +54 -0
- data/lib/did_you_mean/spell_checkers/method_name_checker.rb +2 -3
- data/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb +2 -7
- data/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb +4 -3
- data/lib/did_you_mean/version.rb +1 -1
- data/test/experimental/deprecated_features_test.rb +13 -0
- data/test/{extra_features → experimental}/initializer_name_correction_test.rb +0 -0
- data/test/experimental/key_error_test.rb +15 -0
- data/test/{extra_features → experimental}/method_name_checker_test.rb +1 -1
- data/test/spell_checker_test.rb +8 -16
- data/test/{correctable → spell_checking}/class_name_test.rb +0 -0
- data/test/{correctable → spell_checking}/method_name_test.rb +0 -0
- data/test/{correctable → spell_checking}/uncorrectable_name_test.rb +0 -0
- data/test/{correctable → spell_checking}/variable_name_test.rb +0 -0
- data/test/verbose_formatter_test.rb +1 -1
- metadata +23 -17
- data/lib/did_you_mean/spell_checkable.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28e1952e8b23b15341dd32e2b069ff5a1095c214
|
4
|
+
data.tar.gz: ab42a54b71d2ea36821f135af2182dbc42671abf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d91287f29b78c81341da7c6154272f25f8d9ca28b1d71599079746f24182830d1833493a270705fa3bdc20e003fb1566307772974b88fc6c7b904a7fe3b7a923
|
7
|
+
data.tar.gz: 36a5296b2b247ff3a8791623108e3518571f2d8ad4864bc3eddfb9e17cb648726ba7af9ab4f5dc26d1ad5b4fc47e66044a41e62e066ef4d68727fb64ad8bec59
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,7 @@ _<sup>released on 2015-12-25 05:13:04 UTC</sup>_
|
|
5
5
|
#### Features
|
6
6
|
|
7
7
|
- Introduced a [verbose formatter](https://github.com/yuki24/did_you_mean#verbose-formatter)
|
8
|
-
- Introduced an easy way to enabling [
|
8
|
+
- Introduced an easy way to enabling [experimental features](https://github.com/yuki24/did_you_mean#experimental-features)
|
9
9
|
|
10
10
|
#### Bug Fixes
|
11
11
|
|
data/README.md
CHANGED
@@ -52,16 +52,16 @@ full_name.starts_with?("Y")
|
|
52
52
|
# Did you mean? start_with?
|
53
53
|
```
|
54
54
|
|
55
|
-
##
|
55
|
+
## Experimental Features
|
56
56
|
|
57
|
-
Aside from the basic features above, the `did_you_mean` gem comes with
|
57
|
+
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'`.
|
58
58
|
|
59
|
-
**Keep in mind that these
|
59
|
+
**Keep in mind that these experimental features should never be enabled in production as they would impact Ruby's performance and uses an unstable Ruby API.**
|
60
60
|
|
61
61
|
### Correcting an Instance Variable When It's Incorrectly Typed
|
62
62
|
|
63
63
|
```ruby
|
64
|
-
require 'did_you_mean/
|
64
|
+
require 'did_you_mean/experimental'
|
65
65
|
|
66
66
|
@full_name = "Yuki Nishijima"
|
67
67
|
@full_anme.split(" ")
|
@@ -71,7 +71,7 @@ require 'did_you_mean/extra_features'
|
|
71
71
|
|
72
72
|
### Displaying a Warning When `initialize` is Incorrectly Typed
|
73
73
|
```ruby
|
74
|
-
require 'did_you_mean/
|
74
|
+
require 'did_you_mean/experimental'
|
75
75
|
|
76
76
|
class Person
|
77
77
|
def intialize
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ Rake::TestTask.new do |task|
|
|
5
5
|
task.libs << "test"
|
6
6
|
|
7
7
|
task.test_files = Dir['test/**/*_test.rb'].reject do |path|
|
8
|
-
/(verbose_formatter|
|
8
|
+
/(verbose_formatter|experimental)/ =~ path
|
9
9
|
end
|
10
10
|
|
11
11
|
task.verbose = true
|
@@ -20,16 +20,16 @@ Rake::TestTask.new("test:verbose_formatter") do |task|
|
|
20
20
|
task.ruby_opts << "-rdid_you_mean/verbose_formatter"
|
21
21
|
end
|
22
22
|
|
23
|
-
Rake::TestTask.new("test:
|
23
|
+
Rake::TestTask.new("test:experimental") do |task|
|
24
24
|
task.libs << "test"
|
25
|
-
task.pattern = 'test/
|
25
|
+
task.pattern = 'test/experimental/**/*_test.rb'
|
26
26
|
task.verbose = true
|
27
27
|
task.warning = true
|
28
|
-
task.ruby_opts << "-rdid_you_mean/
|
28
|
+
task.ruby_opts << "-rdid_you_mean/experimental"
|
29
29
|
end
|
30
30
|
|
31
31
|
if RUBY_ENGINE != 'jruby'
|
32
|
-
task default: %i(test test:verbose_formatter test:
|
32
|
+
task default: %i(test test:verbose_formatter test:experimental)
|
33
33
|
else
|
34
34
|
task default: %i(test test:verbose_formatter)
|
35
35
|
end
|
data/benchmark/memory_usage.rb
CHANGED
@@ -7,26 +7,9 @@ require 'did_you_mean'
|
|
7
7
|
# error = (self.fooo rescue $!)
|
8
8
|
# executable = -> { error.to_s }
|
9
9
|
|
10
|
-
class SpellChecker
|
11
|
-
include DidYouMean::SpellCheckable
|
12
|
-
|
13
|
-
def initialize(words)
|
14
|
-
@words = words
|
15
|
-
end
|
16
|
-
|
17
|
-
def correct(input)
|
18
|
-
@corrections, @input = nil, input
|
19
|
-
corrections
|
20
|
-
end
|
21
|
-
|
22
|
-
def candidates
|
23
|
-
{ @input => @words }
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
10
|
METHODS = ''.methods
|
28
11
|
INPUT = 'start_with?'
|
29
|
-
collection = SpellChecker.new(METHODS)
|
12
|
+
collection = DidYouMean::SpellChecker.new(dictionary: METHODS)
|
30
13
|
executable = proc { collection.correct(INPUT) }
|
31
14
|
|
32
15
|
GC.disable
|
data/evaluation/calculator.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- frozen-string-literal: true -*-
|
2
2
|
|
3
3
|
require 'benchmark'
|
4
|
+
require 'did_you_mean'
|
4
5
|
|
5
6
|
def report(message, &block)
|
6
7
|
time = 1000 * Benchmark.realtime(&block)
|
@@ -21,7 +22,7 @@ def report(message, &block)
|
|
21
22
|
time
|
22
23
|
end
|
23
24
|
|
24
|
-
puts "\n"
|
25
|
+
puts "did_you_mean version: #{DidYouMean::VERSION}\n\n"
|
25
26
|
|
26
27
|
report "loading program" do
|
27
28
|
require 'yaml'
|
@@ -37,25 +38,6 @@ report "loading program" do
|
|
37
38
|
end
|
38
39
|
rescue LoadError, NameError
|
39
40
|
end
|
40
|
-
|
41
|
-
class SpellChecker
|
42
|
-
include DidYouMean::SpellCheckable
|
43
|
-
|
44
|
-
def initialize(words)
|
45
|
-
@words = words
|
46
|
-
end
|
47
|
-
|
48
|
-
def correct(input)
|
49
|
-
@corrections, @input = nil, input
|
50
|
-
corrections
|
51
|
-
end
|
52
|
-
|
53
|
-
def candidates
|
54
|
-
{ @input => @words }
|
55
|
-
end
|
56
|
-
|
57
|
-
private def normalize(str); str; end
|
58
|
-
end
|
59
41
|
end
|
60
42
|
|
61
43
|
report "loading dictionary" do
|
@@ -66,7 +48,7 @@ report "loading dictionary" do
|
|
66
48
|
end
|
67
49
|
|
68
50
|
report "loading corrent/incorrect words" do
|
69
|
-
SPELL_CHECKER = SpellChecker.new(DICTIONARY)
|
51
|
+
SPELL_CHECKER = DidYouMean::SpellChecker.new(dictionary: DICTIONARY)
|
70
52
|
INCORRECT_WORDS = YAML.load(open("evaluation/incorrect_words.yaml").read)
|
71
53
|
end
|
72
54
|
|
@@ -78,7 +60,6 @@ filename = "log/words_not_corrected_#{Time.now.to_i}.yml"
|
|
78
60
|
puts <<-MSG
|
79
61
|
|
80
62
|
Total number of test data: #{INCORRECT_WORDS.size}
|
81
|
-
did_you_mean version: #{DidYouMean::VERSION}
|
82
63
|
|
83
64
|
MSG
|
84
65
|
|
data/lib/did_you_mean.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "did_you_mean/version"
|
2
2
|
require "did_you_mean/core_ext/name_error"
|
3
3
|
|
4
|
-
require "did_you_mean/
|
4
|
+
require "did_you_mean/spell_checker"
|
5
5
|
require 'did_you_mean/spell_checkers/name_error_checkers'
|
6
6
|
require 'did_you_mean/spell_checkers/method_name_checker'
|
7
7
|
require 'did_you_mean/spell_checkers/null_checker'
|
@@ -0,0 +1,17 @@
|
|
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/experimental/initializer_name_correction'
|
16
|
+
require 'did_you_mean/experimental/ivar_name_correction'
|
17
|
+
require 'did_you_mean/experimental/key_error_name_correction'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module DidYouMean
|
2
|
-
module
|
2
|
+
module Experimental
|
3
3
|
module IvarNameCorrection
|
4
4
|
REPLS = {
|
5
5
|
"(irb)" => -> { Readline::HISTORY.to_a.last }
|
@@ -12,8 +12,12 @@ module DidYouMean
|
|
12
12
|
@ivar_names = no_method_error.frame_binding.receiver.instance_variables
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
super
|
15
|
+
def corrections
|
16
|
+
super + ivar_name_corrections
|
17
|
+
end
|
18
|
+
|
19
|
+
def ivar_name_corrections
|
20
|
+
@ivar_name_corrections ||= SpellChecker.new(dictionary: @ivar_names).correct(receiver_name.to_s)
|
17
21
|
end
|
18
22
|
|
19
23
|
private
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DidYouMean
|
2
|
+
module Experimental
|
3
|
+
module KeyErrorWithNameAndKeys
|
4
|
+
FILE_REGEXP = %r"#{Regexp.quote(__FILE__)}"
|
5
|
+
|
6
|
+
def fetch(name, *)
|
7
|
+
super
|
8
|
+
rescue KeyError => e
|
9
|
+
e.instance_variable_set(:@name, name)
|
10
|
+
e.instance_variable_set(:@keys, keys)
|
11
|
+
$@.delete_if { |s| FILE_REGEXP =~ s } if $@
|
12
|
+
|
13
|
+
raise e
|
14
|
+
end
|
15
|
+
end
|
16
|
+
Hash.prepend KeyErrorWithNameAndKeys
|
17
|
+
|
18
|
+
class KeyNameChecker
|
19
|
+
def initialize(key_error)
|
20
|
+
@name = key_error.instance_variable_get(:@name)
|
21
|
+
@keys = key_error.instance_variable_get(:@keys)
|
22
|
+
end
|
23
|
+
|
24
|
+
def corrections
|
25
|
+
@corrections ||= SpellChecker.new(dictionary: @keys).correct(@name).map(&:inspect)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
SPELL_CHECKERS["KeyError"] = KeyNameChecker
|
30
|
+
KeyError.prepend DidYouMean::Correctable
|
31
|
+
end
|
32
|
+
end
|
@@ -1,16 +1,3 @@
|
|
1
|
-
require 'did_you_mean'
|
1
|
+
warn "The file 'did_you_mean/extra_features.rb' has been moved to 'did_you_mean/experimental.rb'. Please `require 'did_you_mean/experimental'` instead."
|
2
2
|
|
3
|
-
|
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'
|
3
|
+
require 'did_you_mean/experimental'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- frozen-string-literal: true -*-
|
2
|
+
|
3
|
+
require "did_you_mean/levenshtein"
|
4
|
+
require "did_you_mean/jaro_winkler"
|
5
|
+
|
6
|
+
module DidYouMean
|
7
|
+
class SpellChecker
|
8
|
+
def initialize(dictionary: )
|
9
|
+
@dictionary = dictionary
|
10
|
+
end
|
11
|
+
|
12
|
+
def correct(input)
|
13
|
+
input = normalize(input)
|
14
|
+
threshold = input.length > 3 ? 0.834 : 0.77
|
15
|
+
|
16
|
+
seed = @dictionary.select {|candidate| JaroWinkler.distance(normalize(candidate), input) >= threshold }
|
17
|
+
seed.reject! {|candidate| input == candidate.to_s }
|
18
|
+
seed.sort_by! {|candidate| JaroWinkler.distance(candidate.to_s, input) }
|
19
|
+
seed.reverse!
|
20
|
+
|
21
|
+
# Correct mistypes
|
22
|
+
threshold = (input.length * 0.25).ceil
|
23
|
+
has_mistype = seed.rindex {|c| Levenshtein.distance(normalize(c), input) <= threshold }
|
24
|
+
|
25
|
+
corrections = if has_mistype
|
26
|
+
seed.take(has_mistype + 1)
|
27
|
+
else
|
28
|
+
# Correct misspells
|
29
|
+
seed.select do |candidate|
|
30
|
+
candidate = normalize(candidate)
|
31
|
+
length = input.length < candidate.length ? input.length : candidate.length
|
32
|
+
|
33
|
+
Levenshtein.distance(candidate, input) < length
|
34
|
+
end.first(1)
|
35
|
+
end
|
36
|
+
|
37
|
+
corrections
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def normalize(str_or_symbol) #:nodoc:
|
43
|
+
str = if str_or_symbol.is_a?(String)
|
44
|
+
str_or_symbol.dup
|
45
|
+
else
|
46
|
+
str_or_symbol.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
str.downcase!
|
50
|
+
str.tr!("@", "")
|
51
|
+
str
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module DidYouMean
|
2
2
|
class MethodNameChecker
|
3
|
-
include SpellCheckable
|
4
3
|
attr_reader :method_name, :receiver
|
5
4
|
|
6
5
|
def initialize(exception)
|
@@ -9,8 +8,8 @@ module DidYouMean
|
|
9
8
|
@has_args = !exception.args&.empty?
|
10
9
|
end
|
11
10
|
|
12
|
-
def
|
13
|
-
|
11
|
+
def corrections
|
12
|
+
@corrections ||= SpellChecker.new(dictionary: method_names).correct(method_name)
|
14
13
|
end
|
15
14
|
|
16
15
|
def method_names
|
@@ -3,15 +3,14 @@ require 'delegate'
|
|
3
3
|
|
4
4
|
module DidYouMean
|
5
5
|
class ClassNameChecker
|
6
|
-
include SpellCheckable
|
7
6
|
attr_reader :class_name
|
8
7
|
|
9
8
|
def initialize(exception)
|
10
9
|
@class_name, @receiver = exception.name, exception.receiver
|
11
10
|
end
|
12
11
|
|
13
|
-
def
|
14
|
-
|
12
|
+
def corrections
|
13
|
+
@corrections ||= SpellChecker.new(dictionary: class_names).correct(class_name).map(&:full_name)
|
15
14
|
end
|
16
15
|
|
17
16
|
def class_names
|
@@ -22,10 +21,6 @@ module DidYouMean
|
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
|
-
def corrections
|
26
|
-
super.map(&:full_name)
|
27
|
-
end
|
28
|
-
|
29
24
|
def scopes
|
30
25
|
@scopes ||= @receiver.to_s.split("::").inject([Object]) do |_scopes, scope|
|
31
26
|
_scopes << _scopes.last.const_get(scope)
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module DidYouMean
|
4
4
|
class VariableNameChecker
|
5
|
-
include SpellCheckable
|
6
5
|
attr_reader :name, :method_names, :lvar_names, :ivar_names, :cvar_names
|
7
6
|
|
8
7
|
def initialize(exception)
|
@@ -16,8 +15,10 @@ module DidYouMean
|
|
16
15
|
@cvar_names += receiver.class_variables if receiver.kind_of?(Module)
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
|
18
|
+
def corrections
|
19
|
+
@corrections ||= SpellChecker
|
20
|
+
.new(dictionary: (lvar_names + method_names + ivar_names + cvar_names))
|
21
|
+
.correct(name)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
data/lib/did_you_mean/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DeprecatedExtraFeaturesTest < Minitest::Test
|
4
|
+
def test_message
|
5
|
+
message = "The file 'did_you_mean/extra_features.rb' has been moved to " \
|
6
|
+
"'did_you_mean/experimental.rb'. Please `require 'did_you_mean/experimental'` " \
|
7
|
+
"instead.\n"
|
8
|
+
|
9
|
+
assert_output nil, message do
|
10
|
+
require 'did_you_mean/extra_features'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ExperimentalKeyErrorNameCorrectionTest < Minitest::Test
|
4
|
+
def test_corrects_hash_key_name
|
5
|
+
hash = { "foo" => 1, bar: 2 }
|
6
|
+
|
7
|
+
error = assert_raises(KeyError) { hash.fetch(:bax) }
|
8
|
+
assert_correction ":bar", error.corrections
|
9
|
+
assert_match "Did you mean? :bar", error.to_s
|
10
|
+
|
11
|
+
error = assert_raises(KeyError) { hash.fetch("fooo") }
|
12
|
+
assert_correction %("foo"), error.corrections
|
13
|
+
assert_match %(Did you mean? "foo"), error.to_s
|
14
|
+
end
|
15
|
+
end
|
data/test/spell_checker_test.rb
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class SpellCheckerTest < Minitest::Test
|
4
|
-
SpellChecker = Struct.new(:input, :words) do
|
5
|
-
include DidYouMean::SpellCheckable
|
6
|
-
|
7
|
-
def candidates
|
8
|
-
{ input => words }
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
4
|
def test_spell_checker_corrects_mistypes
|
13
5
|
assert_spell 'foo', input: 'doo', dictionary: ['foo', 'fork']
|
14
6
|
assert_spell 'email', input: 'meail', dictionary: ['email', 'fail', 'eval']
|
@@ -43,8 +35,8 @@ class SpellCheckerTest < Minitest::Test
|
|
43
35
|
names = %w(first_name_change first_name_changed? first_name_will_change!)
|
44
36
|
assert_spell names, input: 'first_name_change!', dictionary: names
|
45
37
|
|
46
|
-
assert_empty SpellChecker.new(
|
47
|
-
assert_empty SpellChecker.new(
|
38
|
+
assert_empty DidYouMean::SpellChecker.new(dictionary: ['proc']).correct('product_path')
|
39
|
+
assert_empty DidYouMean::SpellChecker.new(dictionary: ['fork']).correct('fooo')
|
48
40
|
end
|
49
41
|
|
50
42
|
def test_spell_checker_corrects_misspells
|
@@ -60,27 +52,27 @@ class SpellCheckerTest < Minitest::Test
|
|
60
52
|
name123
|
61
53
|
)
|
62
54
|
|
63
|
-
actual = SpellChecker.new(
|
55
|
+
actual = DidYouMean::SpellChecker.new(dictionary: %w(
|
64
56
|
name12
|
65
57
|
name123
|
66
58
|
name1234
|
67
59
|
name12345
|
68
60
|
name123456
|
69
|
-
)).
|
61
|
+
)).correct('name123456')
|
70
62
|
|
71
63
|
assert_equal expected, actual
|
72
64
|
end
|
73
65
|
|
74
66
|
def test_spell_checker_excludes_input_from_dictionary
|
75
|
-
assert_empty SpellChecker.new(
|
76
|
-
assert_empty SpellChecker.new(
|
77
|
-
assert_empty SpellChecker.new(:
|
67
|
+
assert_empty DidYouMean::SpellChecker.new(dictionary: ['input']).correct('input')
|
68
|
+
assert_empty DidYouMean::SpellChecker.new(dictionary: [:input]).correct('input')
|
69
|
+
assert_empty DidYouMean::SpellChecker.new(dictionary: ['input']).correct(:input)
|
78
70
|
end
|
79
71
|
|
80
72
|
private
|
81
73
|
|
82
74
|
def assert_spell(expected, input: , dictionary: )
|
83
|
-
corrections = SpellChecker.new(
|
75
|
+
corrections = DidYouMean::SpellChecker.new(dictionary: dictionary).correct(input)
|
84
76
|
assert_equal Array(expected), corrections, "Expected to suggest #{expected}, but got #{corrections.inspect}"
|
85
77
|
end
|
86
78
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
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.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Nishijima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,13 +80,15 @@ files:
|
|
80
80
|
- evaluation/incorrect_words.yaml
|
81
81
|
- lib/did_you_mean.rb
|
82
82
|
- lib/did_you_mean/core_ext/name_error.rb
|
83
|
+
- lib/did_you_mean/experimental.rb
|
84
|
+
- lib/did_you_mean/experimental/initializer_name_correction.rb
|
85
|
+
- lib/did_you_mean/experimental/ivar_name_correction.rb
|
86
|
+
- lib/did_you_mean/experimental/key_error_name_correction.rb
|
83
87
|
- lib/did_you_mean/extra_features.rb
|
84
|
-
- lib/did_you_mean/extra_features/initializer_name_correction.rb
|
85
|
-
- lib/did_you_mean/extra_features/ivar_name_correction.rb
|
86
88
|
- lib/did_you_mean/formatter.rb
|
87
89
|
- lib/did_you_mean/jaro_winkler.rb
|
88
90
|
- lib/did_you_mean/levenshtein.rb
|
89
|
-
- lib/did_you_mean/
|
91
|
+
- lib/did_you_mean/spell_checker.rb
|
90
92
|
- lib/did_you_mean/spell_checkers/method_name_checker.rb
|
91
93
|
- lib/did_you_mean/spell_checkers/name_error_checkers.rb
|
92
94
|
- lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
|
@@ -95,14 +97,16 @@ files:
|
|
95
97
|
- lib/did_you_mean/verbose_formatter.rb
|
96
98
|
- lib/did_you_mean/version.rb
|
97
99
|
- test/core_ext/name_error_extension_test.rb
|
98
|
-
- test/correctable/class_name_test.rb
|
99
|
-
- test/correctable/method_name_test.rb
|
100
|
-
- test/correctable/uncorrectable_name_test.rb
|
101
|
-
- test/correctable/variable_name_test.rb
|
102
100
|
- test/edit_distance/jaro_winkler_test.rb
|
103
|
-
- test/
|
104
|
-
- test/
|
101
|
+
- test/experimental/deprecated_features_test.rb
|
102
|
+
- test/experimental/initializer_name_correction_test.rb
|
103
|
+
- test/experimental/key_error_test.rb
|
104
|
+
- test/experimental/method_name_checker_test.rb
|
105
105
|
- test/spell_checker_test.rb
|
106
|
+
- test/spell_checking/class_name_test.rb
|
107
|
+
- test/spell_checking/method_name_test.rb
|
108
|
+
- test/spell_checking/uncorrectable_name_test.rb
|
109
|
+
- test/spell_checking/variable_name_test.rb
|
106
110
|
- test/test_helper.rb
|
107
111
|
- test/verbose_formatter_test.rb
|
108
112
|
homepage: https://github.com/yuki24/did_you_mean
|
@@ -131,13 +135,15 @@ specification_version: 4
|
|
131
135
|
summary: '"Did you mean?" experience in Ruby'
|
132
136
|
test_files:
|
133
137
|
- test/core_ext/name_error_extension_test.rb
|
134
|
-
- test/correctable/class_name_test.rb
|
135
|
-
- test/correctable/method_name_test.rb
|
136
|
-
- test/correctable/uncorrectable_name_test.rb
|
137
|
-
- test/correctable/variable_name_test.rb
|
138
138
|
- test/edit_distance/jaro_winkler_test.rb
|
139
|
-
- test/
|
140
|
-
- test/
|
139
|
+
- test/experimental/deprecated_features_test.rb
|
140
|
+
- test/experimental/initializer_name_correction_test.rb
|
141
|
+
- test/experimental/key_error_test.rb
|
142
|
+
- test/experimental/method_name_checker_test.rb
|
141
143
|
- test/spell_checker_test.rb
|
144
|
+
- test/spell_checking/class_name_test.rb
|
145
|
+
- test/spell_checking/method_name_test.rb
|
146
|
+
- test/spell_checking/uncorrectable_name_test.rb
|
147
|
+
- test/spell_checking/variable_name_test.rb
|
142
148
|
- test/test_helper.rb
|
143
149
|
- test/verbose_formatter_test.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# -*- frozen-string-literal: true -*-
|
2
|
-
|
3
|
-
require "did_you_mean/levenshtein"
|
4
|
-
require "did_you_mean/jaro_winkler"
|
5
|
-
|
6
|
-
module DidYouMean
|
7
|
-
module SpellCheckable
|
8
|
-
def corrections
|
9
|
-
@corrections ||= candidates.flat_map do |input, candidates|
|
10
|
-
input = normalize(input)
|
11
|
-
threshold = input.length > 3 ? 0.834 : 0.77
|
12
|
-
|
13
|
-
seed = candidates.select {|candidate| JaroWinkler.distance(normalize(candidate), input) >= threshold }
|
14
|
-
seed.reject! {|candidate| input == candidate.to_s }
|
15
|
-
seed.sort_by! {|candidate| JaroWinkler.distance(candidate.to_s, input) }
|
16
|
-
seed.reverse!
|
17
|
-
|
18
|
-
# Correct mistypes
|
19
|
-
threshold = (input.length * 0.25).ceil
|
20
|
-
has_mistype = seed.rindex {|c| Levenshtein.distance(normalize(c), input) <= threshold }
|
21
|
-
|
22
|
-
corrections = if has_mistype
|
23
|
-
seed.take(has_mistype + 1)
|
24
|
-
else
|
25
|
-
# Correct misspells
|
26
|
-
seed.select do |candidate|
|
27
|
-
candidate = normalize(candidate)
|
28
|
-
length = input.length < candidate.length ? input.length : candidate.length
|
29
|
-
|
30
|
-
Levenshtein.distance(candidate, input) < length
|
31
|
-
end.first(1)
|
32
|
-
end
|
33
|
-
|
34
|
-
corrections
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def candidates
|
39
|
-
raise NotImplementedError
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def normalize(str_or_symbol) #:nodoc:
|
45
|
-
str = if str_or_symbol.is_a?(String)
|
46
|
-
str_or_symbol.dup
|
47
|
-
else
|
48
|
-
str_or_symbol.to_s
|
49
|
-
end
|
50
|
-
|
51
|
-
str.downcase!
|
52
|
-
str.tr!("@", "")
|
53
|
-
str
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|