typo_safe 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TypoSafe
2
2
 
3
- Protect your Ruby code from typos!
3
+ Protect your Ruby code from typos! You've heard of Type Safe languages, well now Ruby can be Typo Safe! Typo Safe code will revolutionise programming.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,12 +18,47 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ I'm pretty sure you don't ever want to use this gem, not even in development.
22
+
23
+ # Typo Safe a single class written by a fat-fingered developer:
24
+
25
+ ```ruby
26
+ require 'typo_safe'
27
+
28
+ class SomeClass
29
+ include TypoSafe
30
+
31
+ def print_hell0
32
+ puts "hello from the print_hello method!"
33
+ end
34
+
35
+ def print_goodbye
36
+ puts "goodbye from the print_goodbye method!"
37
+ end
38
+ end
39
+
40
+ SomeClass.new.print_hello
41
+ SomeClass.new.pring_goodbye
42
+ ```
43
+
44
+ ## Why?!
45
+
46
+ A discussion in Campfire:
47
+
48
+ ![Alt text](http://i.imgur.com/oHTdT.png)
22
49
 
23
50
  ## Contributing
24
51
 
25
52
  1. Fork it
26
53
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
54
+ 3. Write, then test your changes (`rake test`)
55
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
56
+ 5. Push to the branch (`git push origin my-new-feature`)
57
+ 6. Create new Pull Request
58
+
59
+ ## TODO
60
+
61
+ * Make the gem respect public/private methods (at the moment you can call a private method by using a typo)
62
+ * Benchmark Levenshtein calculations and possibly cache
63
+ * At the moment when there is a conflict (two methods have the same Levenshtein distance) the first is picked, maybe favour locally defined methods over inherited methods
64
+ * Make including TypoSafe on Object work (for global protection!)
data/lib/typo_safe.rb ADDED
@@ -0,0 +1,32 @@
1
+ require "typo_safe/version"
2
+ require 'text'
3
+
4
+ module TypoSafe
5
+ @@maximum_levenshtein_distance_for_match = 4
6
+
7
+ def method_missing(requested_method, *args, &block)
8
+ distances = []
9
+ methods.select{ |m|
10
+ a = method(m).arity
11
+ (a >= 0 && a == args.size) || (a < 0 && (0 - a - 1) <= args.size)
12
+ }.each { |meth|
13
+ distance = Text::Levenshtein.distance(requested_method.to_s, meth.to_s)
14
+ distances << {meth: meth, dist: Text::Levenshtein.distance(requested_method.to_s, meth.to_s)} if distance <= @@maximum_levenshtein_distance_for_match
15
+ }
16
+
17
+ if distances.size == 0
18
+ super
19
+ else
20
+ closest_match = distances.sort_by{|dist| dist[:dist]}.first
21
+ if block_given?
22
+ send(closest_match[:meth], *args, &block)
23
+ else
24
+ send(closest_match[:meth], *args)
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.maximum_levenshtein_distance_for_match=(val)
30
+ @@maximum_levenshtein_distance_for_match = val
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
1
  module TypoSafe
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,54 @@
1
+ require 'minitest/autorun'
2
+ $:.unshift File.join(File.dirname(__FILE__), '..')
3
+ require 'typo_safe'
4
+
5
+ class TestTypoSafe < MiniTest::Unit::TestCase
6
+ def setup
7
+ @foo = Class.new do
8
+ include TypoSafe
9
+ def tset; true; end
10
+ def xxxxxxxxxx; true; end
11
+ def aaaaaaaaaa(an_obj, str); yield(an_obj, str); end
12
+ def bbbbbbbbbb(an_obj, str); an_obj.ping(str); end
13
+ def cccccccccc; end
14
+ def ccccccccbb(an_arg); end
15
+
16
+ # Methods for test_ignores_methods_that_are_not_arg_or_block_campatible
17
+ def arity_test_aaaaaaa(arg, other_arg); "aaaaaaa"; end
18
+ def arity_test_aaaaaab(arg); "aaaaaab"; end
19
+ end.new
20
+ end
21
+
22
+ def test_typo_method_dispatch
23
+ assert @foo.test
24
+ end
25
+
26
+ def test_passes_args_and_no_block
27
+ tbc = MiniTest::Mock.new
28
+ tbc.expect :ping, true, ["foo"]
29
+ @foo.bbbbbbbbba(tbc, "foo")
30
+ assert tbc.verify
31
+ end
32
+
33
+ def test_passes_args_and_block
34
+ tbc = MiniTest::Mock.new
35
+ tbc.expect :ping, true, ["foo"]
36
+ @foo.aaaaaaaaab(tbc, "foo") {|obj, arg| obj.ping(arg)}
37
+ assert tbc.verify
38
+ end
39
+
40
+ def test_requires_minimum_similarity
41
+ error = assert_raises(NoMethodError) {@foo.kdygfnkudfjh}
42
+ assert error.message.match /undefined method `kdygfnkudfjh' for #<#<Class/
43
+ end
44
+
45
+ def test_setting_minimum_similarity
46
+ TypoSafe::maximum_levenshtein_distance_for_match = 2
47
+ assert_raises(NoMethodError) {@foo.xxxxxxxyyy}
48
+ end
49
+
50
+ def test_ignores_methods_that_are_not_arg_or_block_campatible
51
+ assert_equal @foo.arity_test_aaaaaax(1, 2), "aaaaaaa"
52
+ assert_equal @foo.arity_test_aaaaaax(1), "aaaaaab"
53
+ end
54
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typo_safe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-10 00:00:00.000000000 Z
12
+ date: 2013-01-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: text
@@ -39,7 +39,9 @@ files:
39
39
  - LICENSE.txt
40
40
  - README.md
41
41
  - Rakefile
42
+ - lib/typo_safe.rb
42
43
  - lib/typo_safe/version.rb
44
+ - test/test_typo_safe.rb
43
45
  - typo_safe.gemspec
44
46
  homepage: ''
45
47
  licenses: []
@@ -65,4 +67,5 @@ rubygems_version: 1.8.24
65
67
  signing_key:
66
68
  specification_version: 3
67
69
  summary: You probably don't really want to use this in production. Or ever.
68
- test_files: []
70
+ test_files:
71
+ - test/test_typo_safe.rb