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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/README.md +5 -5
  4. data/Rakefile +5 -5
  5. data/benchmark/memory_usage.rb +1 -18
  6. data/evaluation/calculator.rb +3 -22
  7. data/lib/did_you_mean.rb +1 -1
  8. data/lib/did_you_mean/experimental.rb +17 -0
  9. data/lib/did_you_mean/{extra_features → experimental}/initializer_name_correction.rb +1 -1
  10. data/lib/did_you_mean/{extra_features → experimental}/ivar_name_correction.rb +7 -3
  11. data/lib/did_you_mean/experimental/key_error_name_correction.rb +32 -0
  12. data/lib/did_you_mean/extra_features.rb +2 -15
  13. data/lib/did_you_mean/spell_checker.rb +54 -0
  14. data/lib/did_you_mean/spell_checkers/method_name_checker.rb +2 -3
  15. data/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb +2 -7
  16. data/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb +4 -3
  17. data/lib/did_you_mean/version.rb +1 -1
  18. data/test/experimental/deprecated_features_test.rb +13 -0
  19. data/test/{extra_features → experimental}/initializer_name_correction_test.rb +0 -0
  20. data/test/experimental/key_error_test.rb +15 -0
  21. data/test/{extra_features → experimental}/method_name_checker_test.rb +1 -1
  22. data/test/spell_checker_test.rb +8 -16
  23. data/test/{correctable → spell_checking}/class_name_test.rb +0 -0
  24. data/test/{correctable → spell_checking}/method_name_test.rb +0 -0
  25. data/test/{correctable → spell_checking}/uncorrectable_name_test.rb +0 -0
  26. data/test/{correctable → spell_checking}/variable_name_test.rb +0 -0
  27. data/test/verbose_formatter_test.rb +1 -1
  28. metadata +23 -17
  29. 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: b69ea3a675f27d9da3419e086c21bd5930f16f90
4
- data.tar.gz: 56d723b6a3ab08a92e07412bc0efa2051920626f
3
+ metadata.gz: 28e1952e8b23b15341dd32e2b069ff5a1095c214
4
+ data.tar.gz: ab42a54b71d2ea36821f135af2182dbc42671abf
5
5
  SHA512:
6
- metadata.gz: 1aaf379cad209d36b9df5ed695a42a97bde164cb52619b6c47f1a2c1163ee3ebe1d040c906a46ba89cb3f6e78570f994cf357c08bb87044f7a46aadfbac70810
7
- data.tar.gz: 374348aefc081447d3e27f86feb1628ce6914140931e35738e3b230e66e077d245591671dc6c787798c8f4a06edad083051f92e4babfd882e386e051044006d3
6
+ metadata.gz: d91287f29b78c81341da7c6154272f25f8d9ca28b1d71599079746f24182830d1833493a270705fa3bdc20e003fb1566307772974b88fc6c7b904a7fe3b7a923
7
+ data.tar.gz: 36a5296b2b247ff3a8791623108e3518571f2d8ad4864bc3eddfb9e17cb648726ba7af9ab4f5dc26d1ad5b4fc47e66044a41e62e066ef4d68727fb64ad8bec59
@@ -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 [extra features](https://github.com/yuki24/did_you_mean#extra-features)
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
- ## Extra Features
55
+ ## Experimental Features
56
56
 
57
- Aside from the basic features above, the `did_you_mean` gem comes with 2 extra features. They can be enabled by calling `require 'did_you_mean/extra_features'`.
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 extra features should never be enabled in production as they impact Ruby's performance and uses an unstable Ruby API.**
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/extra_features'
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/extra_features'
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|extra_features)/ =~ path
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:extra_features") do |task|
23
+ Rake::TestTask.new("test:experimental") do |task|
24
24
  task.libs << "test"
25
- task.pattern = 'test/extra_features/**/*_test.rb'
25
+ task.pattern = 'test/experimental/**/*_test.rb'
26
26
  task.verbose = true
27
27
  task.warning = true
28
- task.ruby_opts << "-rdid_you_mean/extra_features"
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:extra_features)
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
@@ -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
@@ -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
 
@@ -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/spell_checkable"
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,7 +1,7 @@
1
1
  # -*- frozen-string-literal: true -*-
2
2
 
3
3
  module DidYouMean
4
- module ExtraFeatures
4
+ module Experimental
5
5
  module InitializerNameCorrection
6
6
  def method_added(name)
7
7
  super
@@ -1,5 +1,5 @@
1
1
  module DidYouMean
2
- module ExtraFeatures
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 candidates
16
- super.merge(receiver_name.to_s => @ivar_names)
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
- 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'
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 candidates
13
- { method_name => method_names }
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 candidates
14
- {class_name => class_names}
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 candidates
20
- { name => (lvar_names + method_names + ivar_names + cvar_names) }
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
@@ -1,3 +1,3 @@
1
1
  module DidYouMean
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -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
@@ -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
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class MethodNameWithExtraFeaturesTest < Minitest::Test
3
+ class ExperimentalMethodNameCorrectionTest < Minitest::Test
4
4
  def test_corrects_incorrect_ivar_name
5
5
  @number = 1
6
6
  @nubmer = nil
@@ -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('product_path', ['proc']).corrections
47
- assert_empty SpellChecker.new('fooo', ['fork']).corrections
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("name123456", %w(
55
+ actual = DidYouMean::SpellChecker.new(dictionary: %w(
64
56
  name12
65
57
  name123
66
58
  name1234
67
59
  name12345
68
60
  name123456
69
- )).corrections
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('input', ['input']).corrections
76
- assert_empty SpellChecker.new('input', [:input]).corrections
77
- assert_empty SpellChecker.new(:input, ['input']).corrections
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(input, dictionary).corrections
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
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class VerboseFormatterTest < Minitest::Test
4
4
  def setup
5
- @error = assert_raises(NameError){ self.inspectt }
5
+ @error = assert_raises(NoMethodError){ self.inspectt }
6
6
  end
7
7
 
8
8
  def test_message
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.1
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-05-15 00:00:00.000000000 Z
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/spell_checkable.rb
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/extra_features/initializer_name_correction_test.rb
104
- - test/extra_features/method_name_checker_test.rb
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/extra_features/initializer_name_correction_test.rb
140
- - test/extra_features/method_name_checker_test.rb
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