typo_safe 0.0.1 → 0.0.2

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