did_you_mean 0.3.0 → 0.3.1
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/README.md +1 -1
- data/did_you_mean.gemspec +1 -1
- data/lib/did_you_mean.rb +3 -4
- data/lib/did_you_mean/core_ext/name_error.rb +75 -16
- data/lib/did_you_mean/core_ext/no_method_error.rb +29 -0
- data/lib/did_you_mean/method_finder.rb +24 -0
- data/lib/did_you_mean/version.rb +1 -1
- data/test/name_error_extension_test.rb +8 -3
- data/test/no_method_error_extension_test.rb +16 -10
- metadata +4 -18
- data/lib/did_you_mean/name_error_extension.rb +0 -85
- data/lib/did_you_mean/no_method_error_extension.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9eea98979aeabd698ae78189646cefa0314cec5
|
4
|
+
data.tar.gz: ace02ada986efdff6a6a533ae0b5d99b199fb3b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42e54d2533c9ac9d507c0490538cf3020e40f9a0fde864ebdfa7ca728549953d4527e0e7310c31fb62e7e56375d257db54c23137929b3d25e0fa80e0d87907bf
|
7
|
+
data.tar.gz: cde32b820adfee6dfc0efce16a6653dab84fcb9ffc7bef07e495e44d3b9c5629bbe5bf2acd10512b40e92befded13793980b2ce1e7d2cb1cd5056953a85e1a54
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Did You Mean!??
|
1
|
+
# Did You Mean!?? [](https://travis-ci.org/yuki24/did_you_mean)
|
2
2
|
|
3
3
|
Google tells you the right keyword if you made a typo. This gem gives similar experience when you get `NoMethodError` or `NameError` in Ruby.
|
4
4
|
|
data/did_you_mean.gemspec
CHANGED
data/lib/did_you_mean.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
require "text"
|
2
1
|
require "did_you_mean/version"
|
3
|
-
|
4
2
|
require "did_you_mean/method_missing"
|
3
|
+
require "did_you_mean/method_finder"
|
4
|
+
|
5
5
|
require "did_you_mean/core_ext/name_error"
|
6
|
-
require "did_you_mean/
|
7
|
-
require "did_you_mean/no_method_error_extension"
|
6
|
+
require "did_you_mean/core_ext/no_method_error"
|
@@ -1,28 +1,87 @@
|
|
1
1
|
begin
|
2
2
|
require "binding_of_caller"
|
3
|
-
binding_of_caller_available = true
|
4
3
|
rescue LoadError => e
|
5
|
-
|
4
|
+
puts "could not load binding_of_caller. please make sure it is included in the Gemfile."
|
5
|
+
raise e
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@__did_you_mean_bindings_stack = binding.callers.drop(1)
|
17
|
-
ensure
|
18
|
-
Thread.current[:__did_you_mean_exception_lock] = false
|
8
|
+
class NameError
|
9
|
+
begin
|
10
|
+
require "active_support/core_ext/name_error"
|
11
|
+
|
12
|
+
if method_defined?(:missing_name)
|
13
|
+
def missing_name_without_did_you_mean
|
14
|
+
if /undefined local variable or method/ !~ original_message
|
15
|
+
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ original_message
|
19
16
|
end
|
20
17
|
end
|
21
|
-
|
18
|
+
|
19
|
+
alias missing_name_with_did_you_mean missing_name
|
20
|
+
alias missing_name missing_name_without_did_you_mean
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
end
|
24
|
+
|
25
|
+
original_set_backtrace = instance_method(:set_backtrace)
|
26
|
+
|
27
|
+
define_method :set_backtrace do |*args|
|
28
|
+
unless Thread.current[:__did_you_mean_exception_lock]
|
29
|
+
Thread.current[:__did_you_mean_exception_lock] = true
|
30
|
+
begin
|
31
|
+
@__did_you_mean_bindings_stack = binding.callers.drop(1)
|
32
|
+
ensure
|
33
|
+
Thread.current[:__did_you_mean_exception_lock] = false
|
34
|
+
end
|
22
35
|
end
|
36
|
+
original_set_backtrace.bind(self).call(*args)
|
37
|
+
end
|
23
38
|
|
24
|
-
|
25
|
-
|
39
|
+
def __did_you_mean_bindings_stack
|
40
|
+
@__did_you_mean_bindings_stack || []
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s_with_did_you_mean
|
44
|
+
original_to_s + did_you_mean?.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
alias original_to_s to_s
|
48
|
+
alias to_s to_s_with_did_you_mean
|
49
|
+
alias original_message original_to_s
|
50
|
+
|
51
|
+
def did_you_mean?
|
52
|
+
return if !undefined_local_variable_or_method? || (similar_methods.empty? && similar_local_variables.empty?)
|
53
|
+
|
54
|
+
output = "\n\n"
|
55
|
+
output << " Did you mean?\n"
|
56
|
+
|
57
|
+
unless similar_methods.empty?
|
58
|
+
output << " instance methods: ##{similar_methods.first}\n"
|
59
|
+
output << similar_methods[1..-1].map{|word| "#{' ' * 23}##{word}\n" }.join
|
60
|
+
end
|
61
|
+
|
62
|
+
output << "\n" if !similar_methods.empty? && !similar_local_variables.empty?
|
63
|
+
|
64
|
+
unless similar_local_variables.empty?
|
65
|
+
output << " local variables: #{similar_local_variables.map.first}\n"
|
66
|
+
output << similar_local_variables[1..-1].map{|word| "#{' ' * 23}##{word}\n" }.join
|
26
67
|
end
|
68
|
+
|
69
|
+
output
|
70
|
+
end
|
71
|
+
|
72
|
+
def similar_methods
|
73
|
+
@similar_methods ||= DidYouMean::MethodFinder.new(frame_binding.eval("methods"), name).similar_methods
|
74
|
+
end
|
75
|
+
|
76
|
+
def similar_local_variables
|
77
|
+
@similar_local_variables ||= DidYouMean::MethodFinder.new(frame_binding.eval("local_variables"), name).similar_methods
|
78
|
+
end
|
79
|
+
|
80
|
+
def undefined_local_variable_or_method?
|
81
|
+
original_to_s.include?("undefined local variable or method")
|
82
|
+
end
|
83
|
+
|
84
|
+
def frame_binding
|
85
|
+
@frame_binding ||= __did_you_mean_bindings_stack.first
|
27
86
|
end
|
28
87
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class NoMethodError
|
2
|
+
def receiver
|
3
|
+
args.first
|
4
|
+
end
|
5
|
+
|
6
|
+
def did_you_mean?
|
7
|
+
return if similar_methods.empty?
|
8
|
+
|
9
|
+
output = "\n\n"
|
10
|
+
output << " Did you mean? #{separator}#{similar_methods.first}\n"
|
11
|
+
output << similar_methods[1..-1].map{|word| "#{' ' * 17}#{separator}#{word}\n" }.join
|
12
|
+
end
|
13
|
+
|
14
|
+
def similar_methods
|
15
|
+
@similar_methods ||= DidYouMean::MethodFinder.new(receiver.methods + receiver.singleton_methods, name).similar_methods
|
16
|
+
end
|
17
|
+
|
18
|
+
def receiver_name
|
19
|
+
class_method? ? receiver.name : receiver.class.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def separator
|
23
|
+
class_method? ? "." : "#"
|
24
|
+
end
|
25
|
+
|
26
|
+
def class_method?
|
27
|
+
receiver.is_a? Class
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "text"
|
2
|
+
|
3
|
+
module DidYouMean
|
4
|
+
class MethodFinder
|
5
|
+
def initialize(method_collection, target_method)
|
6
|
+
@method_collection = method_collection.uniq
|
7
|
+
@target_method = target_method
|
8
|
+
end
|
9
|
+
|
10
|
+
def similar_methods
|
11
|
+
@similar_methods ||= method_collection.select do |method|
|
12
|
+
::Text::Levenshtein.distance(method.to_s, target_method.to_s) <= sensitiveness
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :method_collection, :target_method
|
19
|
+
|
20
|
+
def sensitiveness
|
21
|
+
(target_method.size * 0.3).ceil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/did_you_mean/version.rb
CHANGED
@@ -36,9 +36,14 @@ class NameErrorExtensionTest < Test::Unit::TestCase
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_did_you_mean?
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
assert_match "Did you mean?", @errors[:from_instance_method].did_you_mean?
|
40
|
+
assert_match "instance methods: #first_name", @errors[:from_instance_method].did_you_mean?
|
41
|
+
|
42
|
+
assert_match "Did you mean?", @errors[:from_module_method].did_you_mean?
|
43
|
+
assert_match "instance methods: #from_module", @errors[:from_module_method].did_you_mean?
|
44
|
+
|
45
|
+
assert_match "Did you mean?", @instance_variable_error.did_you_mean?
|
46
|
+
assert_match "local variables: user", @instance_variable_error.did_you_mean?
|
42
47
|
end
|
43
48
|
|
44
49
|
def test_message
|
@@ -4,6 +4,7 @@ class NoMethodErrorExtensionTest < Test::Unit::TestCase
|
|
4
4
|
class User
|
5
5
|
def friends; end
|
6
6
|
def first_name; end
|
7
|
+
def descendants; end
|
7
8
|
|
8
9
|
private
|
9
10
|
|
@@ -31,22 +32,27 @@ class NoMethodErrorExtensionTest < Test::Unit::TestCase
|
|
31
32
|
|
32
33
|
def test_similar_methods
|
33
34
|
assert @errors[:from_instance_method].similar_methods.include?(:first_name)
|
34
|
-
assert
|
35
|
-
assert
|
36
|
-
assert
|
35
|
+
assert @errors[:from_private_method].similar_methods.include?(:friends)
|
36
|
+
assert @errors[:from_module_method].similar_methods.include?(:from_module)
|
37
|
+
assert @errors[:from_class_method].similar_methods.include?(:load)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_similar_methods_for_long_method_name
|
41
|
+
error = assert_raise(NoMethodError){ User.new.dependents }
|
42
|
+
assert error.similar_methods.include?(:descendants)
|
37
43
|
end
|
38
44
|
|
39
45
|
def test_did_you_mean?
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
46
|
+
assert_match "Did you mean? #first_name", @errors[:from_instance_method].did_you_mean?
|
47
|
+
assert_match "Did you mean? #friends", @errors[:from_private_method].did_you_mean?
|
48
|
+
assert_match "Did you mean? #from_module", @errors[:from_module_method].did_you_mean?
|
49
|
+
assert_match "Did you mean? .load", @errors[:from_class_method].did_you_mean?
|
44
50
|
end
|
45
51
|
|
46
52
|
def test_message
|
47
53
|
assert_match @errors[:from_instance_method].did_you_mean?, @errors[:from_instance_method].message
|
48
|
-
assert_match
|
49
|
-
assert_match
|
50
|
-
assert_match
|
54
|
+
assert_match @errors[:from_private_method].did_you_mean?, @errors[:from_private_method].message
|
55
|
+
assert_match @errors[:from_module_method].did_you_mean?, @errors[:from_module_method].message
|
56
|
+
assert_match @errors[:from_class_method].did_you_mean?, @errors[:from_class_method].message
|
51
57
|
end
|
52
58
|
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: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Nishijima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: text
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: minitest
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
83
|
description: It adds "did you mean?" experience on NoMethodError and NameError because
|
98
84
|
of a typo.
|
99
85
|
email:
|
@@ -247,8 +233,8 @@ files:
|
|
247
233
|
- ext/did_you_mean/vm_method.c
|
248
234
|
- lib/did_you_mean.rb
|
249
235
|
- lib/did_you_mean/core_ext/name_error.rb
|
250
|
-
- lib/did_you_mean/
|
251
|
-
- lib/did_you_mean/
|
236
|
+
- lib/did_you_mean/core_ext/no_method_error.rb
|
237
|
+
- lib/did_you_mean/method_finder.rb
|
252
238
|
- lib/did_you_mean/version.rb
|
253
239
|
- test/all_test.rb
|
254
240
|
- test/name_error_extension_test.rb
|
@@ -1,85 +0,0 @@
|
|
1
|
-
module DidYouMean
|
2
|
-
module NameErrorExtension
|
3
|
-
def self.included(base)
|
4
|
-
base.class_eval do
|
5
|
-
alias original_to_s to_s
|
6
|
-
alias to_s to_s_with_did_you_mean
|
7
|
-
|
8
|
-
alias original_message original_to_s
|
9
|
-
|
10
|
-
begin
|
11
|
-
require "active_support/core_ext/name_error"
|
12
|
-
if method_defined?(:missing_name)
|
13
|
-
alias missing_name_with_did_you_mean missing_name
|
14
|
-
alias missing_name missing_name_without_did_you_mean
|
15
|
-
end
|
16
|
-
rescue LoadError; end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def missing_name_without_did_you_mean
|
21
|
-
if /undefined local variable or method/ !~ original_message
|
22
|
-
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ original_message
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_s_with_did_you_mean
|
27
|
-
original_to_s + did_you_mean?.to_s
|
28
|
-
end
|
29
|
-
|
30
|
-
def did_you_mean?
|
31
|
-
return if !undefined_local_variable_or_method? || (similar_methods.empty? && similar_local_variables.empty?)
|
32
|
-
|
33
|
-
output = "\n\n"
|
34
|
-
output << "Did you mean?" << "\n"
|
35
|
-
|
36
|
-
unless similar_methods.empty?
|
37
|
-
output << " instance methods:" << "\n"
|
38
|
-
output << similar_methods.map{|word| "\t##{word}" }.join("\n") << "\n"
|
39
|
-
output << "\n"
|
40
|
-
end
|
41
|
-
|
42
|
-
unless similar_local_variables.empty?
|
43
|
-
output << " local variables:" << "\n"
|
44
|
-
output << similar_local_variables.map{|word| "\t#{word}" }.join("\n") << "\n"
|
45
|
-
output << "\n"
|
46
|
-
end
|
47
|
-
|
48
|
-
output
|
49
|
-
end
|
50
|
-
|
51
|
-
def similar_methods
|
52
|
-
@similar_methods ||= _methods.uniq.select do |method|
|
53
|
-
::Text::Levenshtein.distance(method.to_s, name.to_s) <= 2
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def similar_local_variables
|
58
|
-
@similar_local_variables ||= _local_variables.uniq.select do |method|
|
59
|
-
::Text::Levenshtein.distance(method.to_s, name.to_s) <= 2
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def undefined_local_variable_or_method?
|
66
|
-
original_to_s.include?("undefined local variable or method")
|
67
|
-
end
|
68
|
-
|
69
|
-
def _methods
|
70
|
-
@_methods ||= frame_binding.eval("methods")
|
71
|
-
end
|
72
|
-
|
73
|
-
def _local_variables
|
74
|
-
@_local_variables ||= frame_binding.eval("local_variables")
|
75
|
-
end
|
76
|
-
|
77
|
-
def frame_binding
|
78
|
-
@frame_binding ||= __did_you_mean_bindings_stack.detect do |binding|
|
79
|
-
!binding.eval("__FILE__").include?("lib/did_you_mean/core_ext/object.rb")
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
NameError.send :include, DidYouMean::NameErrorExtension
|
@@ -1,51 +0,0 @@
|
|
1
|
-
module DidYouMean
|
2
|
-
module NoMethodErrorExtension
|
3
|
-
def receiver
|
4
|
-
args.first
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.included(base)
|
8
|
-
base.class_eval do
|
9
|
-
alias all_backtrace backtrace
|
10
|
-
alias backtrace backtrace_without_unneeded_lines
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def backtrace_without_unneeded_lines
|
15
|
-
all_backtrace.reject do |line|
|
16
|
-
line.include?("lib/did_you_mean/core_ext/object.rb")
|
17
|
-
end if all_backtrace.is_a?(Array)
|
18
|
-
end
|
19
|
-
|
20
|
-
def did_you_mean?
|
21
|
-
return if similar_methods.empty?
|
22
|
-
|
23
|
-
output = "\n\n"
|
24
|
-
output << "Did you mean?" << "\n"
|
25
|
-
output << similar_methods.map{|word| "\t#{receiver_name}#{separator}#{word}" }.join("\n") << "\n"
|
26
|
-
output << "\n"
|
27
|
-
end
|
28
|
-
|
29
|
-
def similar_methods
|
30
|
-
@similar_methods ||= (receiver.methods + receiver.singleton_methods).uniq.select do |method|
|
31
|
-
::Text::Levenshtein.distance(method.to_s, name.to_s) <= 2
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def receiver_name
|
38
|
-
class_method? ? receiver.name : receiver.class.name
|
39
|
-
end
|
40
|
-
|
41
|
-
def separator
|
42
|
-
class_method? ? "." : "#"
|
43
|
-
end
|
44
|
-
|
45
|
-
def class_method?
|
46
|
-
receiver.is_a? Class
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
NoMethodError.send :include, DidYouMean::NoMethodErrorExtension
|