did_you_mean 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a2019c6ac910b2da3d762dc857da433d6fe5eeed
4
+ data.tar.gz: fb22eb4265c9c6824036a4ffd4195d42168ee2fb
5
+ SHA512:
6
+ metadata.gz: f113cdda3082ce04d30c67a15b3608c60a330d7f9440bb603bfa8a7300a130b2411df8673475dd76dc46c284980516c31ebd612d4a4aa244d3b0726ca7b3bb81
7
+ data.tar.gz: e05fff9b45e6b98e9ca26f0e5ae993707fd780b8633ca325dd5e946017f0966dc9431b4b3cde73c1bc65fc0fe9b2350b9bc08ff872cf11686c58bb348bf477a6
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in did_you_mean.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Yuki Nishijima
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # Did You Mean!??
2
+
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
+
5
+ ## Examples
6
+
7
+ On irb:
8
+
9
+ ![Did you mean? on BetterErrors](https://raw2.github.com/yuki24/did_you_mean/master/docs/irb_example.png)
10
+
11
+ On rspec:
12
+
13
+ ![Did you mean? on BetterErrors](https://raw2.github.com/yuki24/did_you_mean/master/docs/rspec_example.png)
14
+
15
+ On BetterErrors:
16
+
17
+ ![Did you mean? on BetterErrors](https://raw2.github.com/yuki24/did_you_mean/master/docs/better_errors_example.png)
18
+
19
+ **Note that this gem makes Ruby nearly 5 times slower.** But patches are always welcome!
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```
26
+ gem 'did_you_mean', :group => :development
27
+ ```
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it (http://github.com/yuki24/did_you_mean/fork)
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
36
+
37
+ ## License
38
+
39
+ Copyright (c) 2014 Yuki Nishijima. See MIT-LICENSE for further details.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'did_you_mean/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "did_you_mean"
8
+ spec.version = DidYouMean::VERSION
9
+ spec.authors = ["Yuki Nishijima"]
10
+ spec.email = ["mail@yukinishijima.net"]
11
+ spec.summary = %q{Did you mean? in your ruby app}
12
+ spec.description = %q{When you made a typo or forgot the correct spelling and got NoMethodError and NameError, this gem tells you the right one you are looking for.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "text"
22
+ spec.add_dependency "binding_of_caller"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.5"
25
+ spec.add_development_dependency "rake"
26
+ end
Binary file
Binary file
@@ -0,0 +1,8 @@
1
+ require "text"
2
+ require "did_you_mean/version"
3
+
4
+ require "did_you_mean/core_ext/object"
5
+ require "did_you_mean/core_ext/name_error"
6
+
7
+ require "did_you_mean/name_error_extension"
8
+ require "did_you_mean/no_method_error_extension"
@@ -0,0 +1,28 @@
1
+ begin
2
+ require "binding_of_caller"
3
+ binding_of_caller_available = true
4
+ rescue LoadError => e
5
+ binding_of_caller_available = false
6
+ end
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
19
+ end
20
+ end
21
+ original_set_backtrace.bind(self).call(*args)
22
+ end
23
+
24
+ def __did_you_mean_bindings_stack
25
+ @__did_you_mean_bindings_stack || []
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ class Object
2
+ def method_missing(method_name, *args, &block)
3
+ super
4
+ rescue NoMethodError => error
5
+ error.instance_variable_set(:@receiver, self)
6
+ raise error
7
+ end
8
+ end
@@ -0,0 +1,85 @@
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
@@ -0,0 +1,49 @@
1
+ module DidYouMean
2
+ module NoMethodErrorExtension
3
+ attr_reader :receiver
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias all_backtrace backtrace
8
+ alias backtrace backtrace_without_unneeded_lines
9
+ end
10
+ end
11
+
12
+ def backtrace_without_unneeded_lines
13
+ all_backtrace.reject do |line|
14
+ line.include?("lib/did_you_mean/core_ext/object.rb")
15
+ end if all_backtrace.is_a?(Array)
16
+ end
17
+
18
+ def did_you_mean?
19
+ return if similar_methods.empty?
20
+
21
+ output = "\n\n"
22
+ output << "Did you mean?" << "\n"
23
+ output << similar_methods.map{|word| "\t#{receiver_name}#{separator}#{word}" }.join("\n") << "\n"
24
+ output << "\n"
25
+ end
26
+
27
+ def similar_methods
28
+ @similar_methods ||= (receiver.methods + receiver.singleton_methods).uniq.select do |method|
29
+ ::Text::Levenshtein.distance(method.to_s, name.to_s) <= 2
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def receiver_name
36
+ class_method? ? receiver.name : receiver.class.name
37
+ end
38
+
39
+ def separator
40
+ class_method? ? "." : "#"
41
+ end
42
+
43
+ def class_method?
44
+ receiver.is_a? Class
45
+ end
46
+ end
47
+ end
48
+
49
+ NoMethodError.send :include, DidYouMean::NoMethodErrorExtension
@@ -0,0 +1,3 @@
1
+ module DidYouMean
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'name_error_extension_test'
2
+ require_relative 'no_method_error_extension_test'
@@ -0,0 +1,49 @@
1
+ require_relative 'test_helper'
2
+
3
+ class NameErrorExtensionTest < Test::Unit::TestCase
4
+ class User
5
+ def call_flrst_name; f1rst_name; end
6
+ def call_fr0m_module; fr0m_module; end
7
+ def first_name; end
8
+ end
9
+
10
+ module UserModule
11
+ def from_module; end
12
+ end
13
+
14
+ def setup
15
+ user = User.new.extend(UserModule)
16
+
17
+ @errors = {
18
+ from_instance_method: assert_raise(NameError){ user.call_flrst_name },
19
+ from_module_method: assert_raise(NameError){ user.call_fr0m_module }
20
+ }
21
+
22
+ begin
23
+ userr
24
+ rescue NameError => e
25
+ @instance_variable_error = e
26
+ end
27
+ end
28
+
29
+ def test_similar_methods
30
+ assert @errors[:from_instance_method].similar_methods.include?(:first_name)
31
+ assert @errors[:from_module_method].similar_methods.include?(:from_module)
32
+ end
33
+
34
+ def test_similar_local_variables
35
+ assert @instance_variable_error.similar_local_variables.include?(:user)
36
+ end
37
+
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?
42
+ end
43
+
44
+ def test_message
45
+ assert_match @errors[:from_instance_method].did_you_mean?, @errors[:from_instance_method].message
46
+ assert_match @errors[:from_module_method].did_you_mean?, @errors[:from_module_method].message
47
+ assert_match @instance_variable_error.did_you_mean?, @instance_variable_error.message
48
+ end
49
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'test_helper'
2
+
3
+ class NoMethodErrorExtensionTest < Test::Unit::TestCase
4
+ class User
5
+ def friends; end
6
+ def first_name; end
7
+
8
+ private
9
+
10
+ def friend; end
11
+
12
+ class << self
13
+ def load; end
14
+ end
15
+ end
16
+
17
+ module UserModule
18
+ def from_module; end
19
+ end
20
+
21
+ def setup
22
+ user = User.new.extend(UserModule)
23
+
24
+ @errors = {
25
+ from_instance_method: assert_raise(NoMethodError){ user.flrst_name },
26
+ from_private_method: assert_raise(NoMethodError){ user.friend },
27
+ from_module_method: assert_raise(NoMethodError){ user.fr0m_module },
28
+ from_class_method: assert_raise(NoMethodError){ User.l0ad }
29
+ }
30
+ end
31
+
32
+ def test_similar_methods
33
+ 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)
37
+ end
38
+
39
+ 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?
44
+ end
45
+
46
+ def test_message
47
+ 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
51
+ end
52
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require 'did_you_mean'
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: did_you_mean
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yuki Nishijima
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: text
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: binding_of_caller
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: When you made a typo or forgot the correct spelling and got NoMethodError
70
+ and NameError, this gem tells you the right one you are looking for.
71
+ email:
72
+ - mail@yukinishijima.net
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - did_you_mean.gemspec
83
+ - docs/better_errors_example.png
84
+ - docs/irb_example.png
85
+ - docs/rspec_example.png
86
+ - lib/did_you_mean.rb
87
+ - lib/did_you_mean/core_ext/name_error.rb
88
+ - lib/did_you_mean/core_ext/object.rb
89
+ - lib/did_you_mean/name_error_extension.rb
90
+ - lib/did_you_mean/no_method_error_extension.rb
91
+ - lib/did_you_mean/version.rb
92
+ - test/all_test.rb
93
+ - test/name_error_extension_test.rb
94
+ - test/no_method_error_extension_test.rb
95
+ - test/test_helper.rb
96
+ homepage: ''
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.2.0
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Did you mean? in your ruby app
120
+ test_files:
121
+ - test/all_test.rb
122
+ - test/name_error_extension_test.rb
123
+ - test/no_method_error_extension_test.rb
124
+ - test/test_helper.rb