did_you_mean 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5f579d6e5e2ef666475d3d06c97d5b7d63c30a3
4
- data.tar.gz: ace6e5be8e4d7c9b62c66e4f8a49ff312ca1332c
3
+ metadata.gz: d9eea98979aeabd698ae78189646cefa0314cec5
4
+ data.tar.gz: ace02ada986efdff6a6a533ae0b5d99b199fb3b5
5
5
  SHA512:
6
- metadata.gz: 47db0656a551476e4da2660ea2cf96ae3fbf2453e382f03d25bbb94a99ca7a873aca61f45886cca87b21cf4712138f8a38eaa08952ee4cf4267f70d20e6e3d4c
7
- data.tar.gz: f08a8fa9bc6f57b78bd6e9b3478f112c2e96fbb70bd2f9f04ca61b6fb1735af9e8d85fd02d38c61006ebcd0797965993148924d730358504d5aab74fae2cd8b6
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!?? [![Build Status](https://travis-ci.org/yuki24/did_you_mean.png?branch=master)](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
@@ -27,6 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rake-compiler"
28
28
 
29
29
  if RUBY_VERSION == "2.2.0"
30
- spec.add_development_dependency "minitest"
30
+ spec.add_development_dependency "minitest", '< 5.0.0'
31
31
  end
32
32
  end
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/name_error_extension"
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
- binding_of_caller_available = false
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
- if binding_of_caller_available
9
- class NameError
10
- original_set_backtrace = instance_method(:set_backtrace)
11
-
12
- define_method :set_backtrace do |*args|
13
- unless Thread.current[:__did_you_mean_exception_lock]
14
- Thread.current[:__did_you_mean_exception_lock] = true
15
- begin
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
- original_set_backtrace.bind(self).call(*args)
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
- def __did_you_mean_bindings_stack
25
- @__did_you_mean_bindings_stack || []
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
@@ -1,3 +1,3 @@
1
1
  module DidYouMean
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -36,9 +36,14 @@ class NameErrorExtensionTest < Test::Unit::TestCase
36
36
  end
37
37
 
38
38
  def test_did_you_mean?
39
- assert_equal "\n\nDid you mean?\n instance methods:\n\t#first_name\n\n", @errors[:from_instance_method].did_you_mean?
40
- assert_equal "\n\nDid you mean?\n instance methods:\n\t#from_module\n\n", @errors[:from_module_method].did_you_mean?
41
- assert_equal "\n\nDid you mean?\n local variables:\n\tuser\n\n", @instance_variable_error.did_you_mean?
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 @errors[:from_private_method].similar_methods.include?(:friends)
35
- assert @errors[:from_module_method].similar_methods.include?(:from_module)
36
- assert @errors[:from_class_method].similar_methods.include?(:load)
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
- assert_equal "\n\nDid you mean?\n\tNoMethodErrorExtensionTest::User#first_name\n\n", @errors[:from_instance_method].did_you_mean?
41
- assert_equal "\n\nDid you mean?\n\tNoMethodErrorExtensionTest::User#friends\n\n", @errors[:from_private_method].did_you_mean?
42
- assert_equal "\n\nDid you mean?\n\tNoMethodErrorExtensionTest::User#from_module\n\n", @errors[:from_module_method].did_you_mean?
43
- assert_equal "\n\nDid you mean?\n\tNoMethodErrorExtensionTest::User.load\n\n", @errors[:from_class_method].did_you_mean?
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 @errors[:from_private_method].did_you_mean?, @errors[:from_private_method].message
49
- assert_match @errors[:from_module_method].did_you_mean?, @errors[:from_module_method].message
50
- assert_match @errors[:from_class_method].did_you_mean?, @errors[:from_class_method].message
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.0
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-02-25 00:00:00.000000000 Z
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/name_error_extension.rb
251
- - lib/did_you_mean/no_method_error_extension.rb
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