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 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