maybeyoumeant 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rainbow', '~> 1.1.1'
4
+
5
+ # Add dependencies to develop your gem here.
6
+ # Include everything needed to run rake, tests, features, etc.
7
+ group :development do
8
+ gem "shoulda", ">= 0"
9
+ gem "bundler", "~> 1.0.0"
10
+ gem "jeweler", "~> 1.6.4"
11
+ gem "rcov", ">= 0"
12
+ gem 'rspec', "~> 1.3.2"
13
+ end
14
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Liehann Loots
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,56 @@
1
+ = Maybe You Meant
2
+
3
+ May You Meant is an IRB extension that guesses the method you meant to call
4
+ when you make a typo.
5
+
6
+ Say for example you call
7
+ 'hello world'.ucase
8
+ Maybe You Meant will output
9
+ Maybe you meant: 'hello world'.upcase
10
+ and automatically call the correct method and insert the new line into your
11
+ IRB history.
12
+
13
+ == Installation
14
+
15
+ gem install maybeyoumeant
16
+
17
+ The rainbow gem is installed for color console output.
18
+
19
+ Put the following into you .irbrc
20
+ require 'rubygems'
21
+ require 'maybeyoumeant'
22
+
23
+ == Configuration
24
+
25
+ # To turn off logging
26
+ MaybeYouMeant::Config.debug = false
27
+
28
+ # To not automatically call methods
29
+ MaybeYouMeant::Config.call_nearby = false
30
+
31
+ # To not add an updated line to IRB history
32
+ MaybeYouMeant::Config.add_to_history = false
33
+
34
+ # To remove the incorrect line from IRB history
35
+ MaybeYouMeant::Config.remove_from_history = true
36
+
37
+ == How It Works
38
+
39
+ Object is monkey-patched to intercept NoMethodErrors. Levenshtein distances are calculated
40
+ for other methods on the object, and the closest one is selected and called.
41
+
42
+ == Contributing to maybeyoumeant
43
+
44
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
45
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
46
+ * Fork the project
47
+ * Start a feature/bugfix branch
48
+ * Commit and push until you are happy with your contribution
49
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
50
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
51
+
52
+ == Copyright
53
+
54
+ Copyright (c) 2011 Liehann Loots. See LICENSE.txt for
55
+ further details.
56
+
data/Rakefile ADDED
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "maybeyoumeant"
18
+ gem.homepage = "http://github.com/liehann/maybeyoumeant"
19
+ gem.license = "MIT"
20
+ gem.summary = "IRB plugin that suggests corrections for misspelled method names."
21
+ gem.description = %Q{
22
+ If you misspell a method in IRB this plugin searches through methods
23
+ defined on the object and calls the closest method to the one you entered.
24
+ For example lets say you typed:
25
+ 'hello world'.capitalze
26
+ Instead of raising a NoMethodError 'hello world'.capitalize would automatically
27
+ be called.
28
+ }
29
+ gem.email = "liehannl@gmail.com"
30
+ gem.authors = ["Liehann Loots"]
31
+ # dependencies defined in Gemfile
32
+ end
33
+ Jeweler::RubygemsDotOrgTasks.new
34
+
35
+ require 'spec/rake/spectask'
36
+ Spec::Rake::SpecTask.new do |test|
37
+ test.libs << 'lib' << 'spec/lib'
38
+ # test.verbose = true
39
+ test.pattern = 'spec/**/*_spec.rb'
40
+ end
41
+
42
+ require 'rcov/rcovtask'
43
+ Rcov::RcovTask.new do |test|
44
+ test.libs << 'test'
45
+ test.pattern = 'test/**/test_*.rb'
46
+ test.verbose = true
47
+ test.rcov_opts << '--exclude "gems/*"'
48
+ end
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "maybeyoumeant #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
59
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,59 @@
1
+ module MaybeYouMeant::Config
2
+ # Set to true to log when a nearby message is automatically called.
3
+ # Default value is true.
4
+ def self.debug=(enabled)
5
+ raise 'Debug can only be set to true or false.' unless enabled == true || enabled == false
6
+ if enabled
7
+ MaybeYouMeant.logger = MaybeYouMeant::StdErrLogger.new
8
+ else
9
+ MaybeYouMeant.logger = MaybeYouMeant::NilLogger.new
10
+ end
11
+ end
12
+
13
+ # Indicates if debug logger is currently enabled.
14
+ def self.debug
15
+ MaybeYouMeant::StdErrLogger === MaybeYouMeant.logger
16
+ end
17
+
18
+ @@call_nearby = true
19
+
20
+ # When true if a nearby method is found it is automatically called.
21
+ # Defaults to true.
22
+ def self.call_nearby=(enabled)
23
+ @@call_nearby = enabled
24
+ end
25
+
26
+ def self.call_nearby
27
+ @@call_nearby
28
+ end
29
+
30
+ @@add_to_history = true
31
+
32
+ # When true and a nearby method is called the history is manipulated to
33
+ # have the nearby method name. Defaults to true.
34
+ # This is done with a simple search and replace and may not be perfect,
35
+ # especially for short methods.
36
+ # Say object foo has a method 'foob' you will get the following in your history:
37
+ # foo.foo -> foob.foob
38
+ def self.add_to_history=(enabled)
39
+ @@add_to_history = true
40
+ end
41
+
42
+ def self.add_to_history
43
+ @@add_to_history
44
+ end
45
+
46
+ @@remove_from_history = false
47
+
48
+ # When true and a nearby method is called the history is manipulated to
49
+ # have the line with the incorrect method name removed.
50
+ # Defaults to false.
51
+ def self.remove_from_history(enabled)
52
+ @@remove_from_history = enabled
53
+ end
54
+
55
+ def self.remove_from_history
56
+ @@remove_from_history
57
+ end
58
+
59
+ end
@@ -0,0 +1,41 @@
1
+ class MaybeYouMeant::Levenshtein
2
+ # Computes the Levenshtein distance between two strings.
3
+ #
4
+ # This is currently completely unoptimized, both in terms
5
+ # of space and time. Two planned optimizations are to only
6
+ # use two rows instead of a matrix, and as ultimately we only
7
+ # care about a threshold distance we only need to calculate
8
+ # a diagonal instead of the full distance. This could be
9
+ # further optimized to be evaluated lazily.
10
+ def self.distance(s, t)
11
+ m = s.length
12
+ n = t.length
13
+
14
+ d = ::Matrix.rows(Array.new(m + 1) { Array.new(n + 1, 0) }, false)
15
+
16
+ 0.upto m do |i|
17
+ d[i, 0] = i
18
+ end
19
+
20
+ 0.upto n do |j|
21
+ d[0, j] = j
22
+ end
23
+
24
+ 1.upto m do |i|
25
+ 1.upto n do |j|
26
+ # This is not unicode safe.
27
+ if s[i - 1] == t[j - 1]
28
+ d[i, j] = d[i - 1, j - 1]
29
+ else
30
+ d[i, j] = [
31
+ d[i - 1, j] + 1, # deletion
32
+ d[i, j - 1] + 1, # insertion
33
+ d[i - 1, j - 1] + 1 # substitution
34
+ ].min
35
+ end
36
+ end
37
+ end
38
+
39
+ return d[m, n]
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require 'matrix'
2
+
3
+ class MaybeYouMeant::Matrix
4
+
5
+ module InstanceMethods
6
+ def []=(i, j, v)
7
+ @rows[i][j] = v
8
+ end
9
+ end
10
+
11
+ # Add matrix methods if this is ruby 1.8 and they're not present.
12
+ unless ::Matrix.instance_methods.include?(:[]=)
13
+ ::Matrix.send(:include, InstanceMethods)
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ # Ignores all logging calls.
2
+ class MaybeYouMeant::NilLogger
3
+ def log(msg = nil, &block)
4
+ end
5
+ end
6
+
@@ -0,0 +1,40 @@
1
+ # Extends Object to call a method similar to the one called when the
2
+ # method has not been defined on the object.
3
+ module MaybeYouMeant::ObjectExtentions
4
+
5
+ module InstanceMethods
6
+ # Calls a nearby method if one is found. If no nearby method is
7
+ # found the original method_missing is called, which raises a
8
+ # NoMethodError.
9
+ def method_missing(method, *args, &block)
10
+ nearby = nearby_method(method)
11
+ # MaybeYouMeant.log { "Maybe you meant to call #{nearby}?" } if nearby
12
+ return super unless MaybeYouMeant::Config.call_nearby && nearby
13
+ MaybeYouMeant.tweak_history(method, nearby)
14
+ return send(nearby, *args, &block)
15
+ end
16
+
17
+ # Returns the closest matching method to methods already defined on the object.
18
+ def nearby_method(name)
19
+ max_distance = 2
20
+ max_distance = 1 if name.to_s.length <= 3
21
+
22
+ distance = {}
23
+ nearby_methods = self.methods.select do |method|
24
+ d = MaybeYouMeant::Levenshtein.distance(name.to_s, method.to_s)
25
+ distance[method] = d if d <= max_distance
26
+ d <= max_distance
27
+ end
28
+ return nil if nearby_methods.empty?
29
+ nearby_methods.sort! do |method, other|
30
+ distance[method] <=> distance[other]
31
+ end
32
+ nearby_methods[0]
33
+ end
34
+
35
+ end
36
+
37
+ # Add the instance methods to the base object.
38
+ Object.send(:include, InstanceMethods)
39
+ end
40
+
@@ -0,0 +1,8 @@
1
+ # Logs to STDERR.
2
+ class MaybeYouMeant::StdErrLogger
3
+ def log(msg = nil, &block)
4
+ msg ||= yield if block_given?
5
+ STDERR.puts(msg)
6
+ end
7
+ end
8
+
@@ -0,0 +1,44 @@
1
+ module MaybeYouMeant; end
2
+
3
+ require 'rubygems'
4
+ require 'rainbow'
5
+
6
+ require 'maybeyoumeant/matrix'
7
+ require 'maybeyoumeant/levenshtein'
8
+ require 'maybeyoumeant/object_extensions'
9
+ require 'maybeyoumeant/nil_logger'
10
+ require 'maybeyoumeant/std_err_logger'
11
+ require 'maybeyoumeant/config'
12
+
13
+ module MaybeYouMeant
14
+ @@logger = StdErrLogger.new
15
+ def self.logger=(logger)
16
+ @@logger = logger
17
+ end
18
+
19
+ def self.log(msg = nil, &block)
20
+ # msg ||= yield if block_given?
21
+ @@logger.log(msg, &block)
22
+ end
23
+
24
+
25
+ def self.tweak_history(method, nearby)
26
+ begin
27
+ @@irb ||= Kernel.const_get('IRB') && Kernel.const_get('Readline') && Readline.const_defined?('HISTORY')
28
+ rescue NameError
29
+ @@irb = false
30
+ end
31
+ return unless @@irb
32
+
33
+ line = Readline::HISTORY[-1]
34
+ Readline::HISTORY.pop if Config::remove_from_history
35
+
36
+ if Config.add_to_history
37
+ # Try to match .#{method}\W before replacing all occurences of method.
38
+ line.gsub!(/(\W|^)#{method}((?=\W)|$)/, "\\1#{nearby}")
39
+ log("Maybe you meant: #{line}".foreground(:black).bright)
40
+ Readline::HISTORY.push line
41
+ end
42
+ end
43
+ end
44
+
Binary file
data/spec/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'spec'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'maybeyoumeant'
15
+ include MaybeYouMeant
16
+
17
+ require 'foo'
18
+
data/spec/lib/foo.rb ADDED
@@ -0,0 +1,4 @@
1
+ class Foo
2
+ def foo
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ require 'helper'
2
+
3
+ describe Levenshtein do
4
+ it "calculates a zero distance between empty strings" do
5
+ Levenshtein.distance('', '').should == 0
6
+ end
7
+
8
+ it "calculates a one distance between two characters" do
9
+ Levenshtein.distance('a', 'b').should == 1
10
+ end
11
+
12
+ it "calculates a correct distance between two strings" do
13
+ Levenshtein.distance('kitten', 'sitting').should == 3
14
+ end
15
+ end
16
+
@@ -0,0 +1,14 @@
1
+ require 'helper'
2
+
3
+ describe Matrix do
4
+ it "adds an index setter to ::Matrix" do
5
+ ::Matrix.instance_methods.should include '[]='
6
+ end
7
+
8
+ it "should set values in a ::Matrix" do
9
+ m = ::Matrix[[0, 0, 0], [0, 0, 0]]
10
+ m[1, 2] = 3
11
+ m[1, 2].should == 3
12
+ end
13
+ end
14
+
@@ -0,0 +1,27 @@
1
+ require 'helper'
2
+
3
+ describe ObjectExtentions do
4
+ it 'finds a nearby method' do
5
+ Foo.new.nearby_method(:boo).should == 'foo'
6
+ end
7
+
8
+ it 'has a threshold of 2' do
9
+ Foo.new.nearby_method(:foobr).should == 'foo'
10
+ end
11
+
12
+ it 'returns nil if there are no nearby methods' do
13
+ Foo.new.nearby_method(:foobar).should be_nil
14
+ end
15
+
16
+ it 'calls a similar method if there is one' do
17
+ f = Foo.new
18
+ f.should_receive(:foo)
19
+ f.boo
20
+ end
21
+
22
+ it 'raises NoMethodError if there are no similar methods' do
23
+ f = Foo.new
24
+ lambda { f.foobar }.should raise_error NoMethodError
25
+ end
26
+ end
27
+
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maybeyoumeant
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Liehann Loots
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-18 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ prerelease: false
24
+ name: rainbow
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 17
31
+ segments:
32
+ - 1
33
+ - 1
34
+ - 1
35
+ version: 1.1.1
36
+ requirement: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ type: :development
39
+ prerelease: false
40
+ name: shoulda
41
+ version_requirements: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ requirement: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ type: :development
53
+ prerelease: false
54
+ name: bundler
55
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 23
61
+ segments:
62
+ - 1
63
+ - 0
64
+ - 0
65
+ version: 1.0.0
66
+ requirement: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ type: :development
69
+ prerelease: false
70
+ name: jeweler
71
+ version_requirements: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 7
77
+ segments:
78
+ - 1
79
+ - 6
80
+ - 4
81
+ version: 1.6.4
82
+ requirement: *id004
83
+ - !ruby/object:Gem::Dependency
84
+ type: :development
85
+ prerelease: false
86
+ name: rcov
87
+ version_requirements: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ requirement: *id005
97
+ - !ruby/object:Gem::Dependency
98
+ type: :development
99
+ prerelease: false
100
+ name: rspec
101
+ version_requirements: &id006 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ~>
105
+ - !ruby/object:Gem::Version
106
+ hash: 31
107
+ segments:
108
+ - 1
109
+ - 3
110
+ - 2
111
+ version: 1.3.2
112
+ requirement: *id006
113
+ description: "\n If you misspell a method in IRB this plugin searches through methods\n defined on the object and calls the closest method to the one you entered.\n For example lets say you typed:\n 'hello world'.capitalze\n Instead of raising a NoMethodError 'hello world'.capitalize would automatically\n be called.\n "
114
+ email: liehannl@gmail.com
115
+ executables: []
116
+
117
+ extensions: []
118
+
119
+ extra_rdoc_files:
120
+ - LICENSE.txt
121
+ - README.rdoc
122
+ files:
123
+ - .document
124
+ - Gemfile
125
+ - LICENSE.txt
126
+ - README.rdoc
127
+ - Rakefile
128
+ - VERSION
129
+ - lib/maybeyoumeant.rb
130
+ - lib/maybeyoumeant/config.rb
131
+ - lib/maybeyoumeant/levenshtein.rb
132
+ - lib/maybeyoumeant/matrix.rb
133
+ - lib/maybeyoumeant/nil_logger.rb
134
+ - lib/maybeyoumeant/object_extensions.rb
135
+ - lib/maybeyoumeant/std_err_logger.rb
136
+ - spec/.helper.rb.swo
137
+ - spec/helper.rb
138
+ - spec/lib/foo.rb
139
+ - spec/maybeyoumeant/.matrix_spec.rb.swo
140
+ - spec/maybeyoumeant/levenshtein_spec.rb
141
+ - spec/maybeyoumeant/matrix_spec.rb
142
+ - spec/maybeyoumeant/object_extensions_spec.rb
143
+ has_rdoc: true
144
+ homepage: http://github.com/liehann/maybeyoumeant
145
+ licenses:
146
+ - MIT
147
+ post_install_message:
148
+ rdoc_options: []
149
+
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 3
158
+ segments:
159
+ - 0
160
+ version: "0"
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ hash: 3
167
+ segments:
168
+ - 0
169
+ version: "0"
170
+ requirements: []
171
+
172
+ rubyforge_project:
173
+ rubygems_version: 1.3.7
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: IRB plugin that suggests corrections for misspelled method names.
177
+ test_files: []
178
+