typecheck 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7fc2715192b042a94bf221a43234bb59c613c3f
4
+ data.tar.gz: 1115a334c7936dd145fb090c6ba1675e5e92a470
5
+ SHA512:
6
+ metadata.gz: ccd6d3916e84953acc37f4b78d929e633f75790ccf0ecb803c1f08f1188e3e8a4e36e371a1eb885b96c20700a5a67864c52923dfbafa34d347af161f6c7864ea
7
+ data.tar.gz: ae6bcf095e29626885089680b6ed9464cf5bf2378a5f2b14e03c0eed8f5da3d0d76f3723958274023793116be1677b945bbf8f224ccd0f4a984d33b8b4ddee27
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ # gem "rails"
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ typecheck (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ abstract_type (0.0.7)
10
+ adamantium (0.2.0)
11
+ ice_nine (~> 0.11.0)
12
+ memoizable (~> 0.4.0)
13
+ anima (0.2.0)
14
+ abstract_type (~> 0.0.7)
15
+ adamantium (~> 0.1)
16
+ equalizer (~> 0.0.8)
17
+ ast (1.1.0)
18
+ concord (0.1.5)
19
+ adamantium (~> 0.2.0)
20
+ equalizer (~> 0.0.9)
21
+ diff-lcs (1.2.5)
22
+ equalizer (0.0.9)
23
+ ice_nine (0.11.0)
24
+ inflecto (0.0.2)
25
+ memoizable (0.4.2)
26
+ thread_safe (~> 0.3, >= 0.3.1)
27
+ morpher (0.2.2)
28
+ abstract_type (~> 0.0.7)
29
+ adamantium (~> 0.2.0)
30
+ anima (~> 0.2.0)
31
+ ast (~> 1.1.0)
32
+ concord (~> 0.1.4)
33
+ equalizer (~> 0.0.9)
34
+ ice_nine (~> 0.11.0)
35
+ procto (~> 0.0.2)
36
+ mutant (0.5.10)
37
+ abstract_type (~> 0.0.7)
38
+ adamantium (~> 0.2.0)
39
+ anima (~> 0.2.0)
40
+ concord (~> 0.1.4)
41
+ diff-lcs (~> 1.2)
42
+ equalizer (~> 0.0.9)
43
+ ice_nine (~> 0.11.0)
44
+ inflecto (~> 0.0.2)
45
+ memoizable (~> 0.4.2)
46
+ morpher (~> 0.2.1)
47
+ parser (~> 2.1)
48
+ procto (~> 0.0.2)
49
+ unparser (~> 0.1.10)
50
+ mutant-rspec (0.5.10)
51
+ mutant (~> 0.5.10)
52
+ rspec-core (>= 2.14.1, <= 3.0.0.beta2)
53
+ parser (2.1.8)
54
+ ast (~> 1.1)
55
+ slop (~> 3.4, >= 3.4.5)
56
+ procto (0.0.2)
57
+ rake (10.3.1)
58
+ rspec (2.14.1)
59
+ rspec-core (~> 2.14.0)
60
+ rspec-expectations (~> 2.14.0)
61
+ rspec-mocks (~> 2.14.0)
62
+ rspec-core (2.14.8)
63
+ rspec-expectations (2.14.5)
64
+ diff-lcs (>= 1.1.3, < 2.0)
65
+ rspec-mocks (2.14.6)
66
+ slop (3.5.0)
67
+ thread_safe (0.3.3)
68
+ unparser (0.1.12)
69
+ abstract_type (~> 0.0.7)
70
+ adamantium (~> 0.2.0)
71
+ concord (~> 0.1.4)
72
+ equalizer (~> 0.0.9)
73
+ parser (~> 2.1)
74
+ procto (~> 0.0.2)
75
+
76
+ PLATFORMS
77
+ ruby
78
+
79
+ DEPENDENCIES
80
+ mutant-rspec (~> 0.5.10)
81
+ rake (~> 10.2)
82
+ rspec (~> 2.14)
83
+ typecheck!
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2014 Arne Brasseur
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ [![Gem Version](https://badge.fury.io/rb/typecheck.png)][gem]
2
+ [![Build Status](https://secure.travis-ci.org/plexus/typecheck.png?branch=master)][travis]
3
+ [![Code Climate](https://codeclimate.com/github/plexus/typecheck.png)][codeclimate]
4
+
5
+ [gem]: https://rubygems.org/gems/typecheck
6
+ [travis]: https://travis-ci.org/plexus/typecheck
7
+ [codeclimate]: https://codeclimate.com/github/plexus/typecheck
8
+
9
+ # Typecheck
10
+
11
+ Type checking for Ruby methods.
12
+
13
+ Validate the arguments and return value of a function, based on a type signature. Supports duck-type checking.
14
+
15
+ Example below is for Ruby 2.1, for 2.0 and earlier pass the function name explicitly :
16
+
17
+ ```ruby
18
+ def foo(..)
19
+ end
20
+ typecheck '...', :foo
21
+ ```
22
+
23
+ Features by example:
24
+
25
+ ```ruby
26
+ require 'typecheck'
27
+
28
+ class Checked
29
+ extend Typecheck
30
+
31
+ typecheck 'Numeric -> Numeric',
32
+ def double_me(num)
33
+ num + num
34
+ end
35
+
36
+ typecheck 'String, Symbol -> Fixnum',
37
+ def strsym_num(str, sym)
38
+ str.length
39
+ end
40
+
41
+ # Duck typing FTW!
42
+ typecheck '#to_str -> Symbol',
43
+ def duck(str)
44
+ str.to_str.upcase.intern
45
+ end
46
+
47
+ typecheck '#begin;#end -> Symbol',
48
+ def multi(range)
49
+ ('x' * range.end).chars.drop(range.begin).join.intern
50
+ end
51
+
52
+ typecheck '#to_str|Fixnum -> Symbol',
53
+ def choice(x)
54
+ :foo
55
+ end
56
+
57
+ typecheck '[Fixnum],[String] -> Numeric',
58
+ def arrays(nums, strings)
59
+ (nums + strings.map(&:length)).inject(:+)
60
+ end
61
+
62
+ typecheck 'Fixnum,String,Symbol -> Numeric',
63
+ def optional(num, str = nil, sym = nil)
64
+ num
65
+ end
66
+ end
67
+ ```
68
+
69
+ ## LICENSE
70
+
71
+ Copyright (c) 2014 Arne Brasseur, MIT License. See LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ ########################################
2
+ # Testing
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'mutant'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default => :mutant
10
+
11
+ task :mutant do
12
+ pattern = ENV.fetch('PATTERN', 'Typecheck*')
13
+ result = Mutant::CLI.run(%w[-Ilib -rtypecheck --use rspec --score 100] + [pattern])
14
+ fail unless result == Mutant::CLI::EXIT_SUCCESS
15
+ end
16
+
17
+ ########################################
18
+ # Packaging
19
+
20
+ require 'rubygems/package_task'
21
+ spec = Gem::Specification.load(File.expand_path('../typecheck.gemspec', __FILE__))
22
+ gem = Gem::PackageTask.new(spec)
23
+ gem.define
24
+
25
+ desc "Push gem to rubygems.org"
26
+ task :push => :gem do
27
+ # sh "git tag v#{Typecheck::VERSION}"
28
+ sh "git push --tags"
29
+ sh "gem push pkg/typecheck-#{Typecheck::VERSION}.gem"
30
+ end
data/lib/typecheck.rb ADDED
@@ -0,0 +1,93 @@
1
+ module Typecheck
2
+ VERSION = '0.1.0'
3
+
4
+ def typecheck(signature, method)
5
+ alias_method "#{method}_unchecked", method
6
+ define_method method, &SignatureCompiler.new.call(signature, method)
7
+ end
8
+
9
+ class SignatureCompiler
10
+ def call(signature, method)
11
+ unchecked_method = "#{method}_unchecked"
12
+ check_args, check_out = parse_sig(signature)
13
+ ->(*args) do
14
+ check_args.(*args)
15
+ send(unchecked_method, *args).tap do |result|
16
+ check_out.(result)
17
+ end
18
+ end
19
+ end
20
+
21
+ def parse_sig(sig)
22
+ in_t, out_t = sig.split('->').map(&:strip)
23
+ in_ts = in_t.split(',').map(&:strip)
24
+
25
+ in_checks = in_ts.map(&method(:parse_type_list))
26
+ out_check = parse_type_list(out_t)
27
+
28
+ args_checker = ->(*args) {
29
+ args.each.with_index do |arg, idx|
30
+ in_checks[idx].(arg)
31
+ end
32
+ }
33
+ [args_checker, out_check]
34
+ end
35
+
36
+ def pred_or(preds)
37
+ ->(value) { preds.any?{|pred| pred.(value) } }
38
+ end
39
+
40
+ def pred_and(preds)
41
+ ->(value) { preds.all?{|pred| pred.(value) } }
42
+ end
43
+
44
+ def parse_type_list(choices)
45
+ types = choices.split('|').map(&:strip)
46
+ pred_or [
47
+ pred_or(types.map {|choice|
48
+ parse_type(choice, :check)
49
+ }),
50
+ pred_or(types.map {|choice|
51
+ parse_type(choice, :raise)
52
+ })
53
+ ]
54
+ end
55
+
56
+ def parse_type(subtype, check_or_raise)
57
+ pred_and subtype.split(';').map(&:strip).map { |type|
58
+ case type
59
+ when /#(.*)/
60
+ method("#{check_or_raise}_respond_to").to_proc.curry.($1)
61
+ when /\[(.*)\]/
62
+ method("#{check_or_raise}_array").to_proc.curry.(eval($1))
63
+ else
64
+ method("#{check_or_raise}_class").to_proc.curry.(eval(type))
65
+ end
66
+ }
67
+ end
68
+
69
+ def check_respond_to(method, value)
70
+ value.respond_to? method
71
+ end
72
+
73
+ def raise_respond_to(method, value)
74
+ raise "Expected #{value.inspect}, to respond_to #{method}" unless check_respond_to(method, value)
75
+ end
76
+
77
+ def check_array(type, array)
78
+ array.all? {|element| check_class(type, element) }
79
+ end
80
+
81
+ def raise_array(type, array)
82
+ raise "Bad type: #{array.inspect} to only contain #{klz}" unless check_array(type, array)
83
+ end
84
+
85
+ def check_class(klz, value)
86
+ value.is_a? klz
87
+ end
88
+
89
+ def raise_class(klz, value)
90
+ raise "Bad type: #{value.inspect}, expected #{klz}" unless check_class(klz, value)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Typecheck do
4
+ shared_examples 'valid' do |method, args, result|
5
+ it 'should call the original method' do
6
+ expect(subject.send(method, *args)).to eq result
7
+ end
8
+ end
9
+
10
+ shared_examples 'invalid' do |method, args|
11
+ it 'should typecheck' do
12
+ expect{subject.send(method, *args)}.to raise_exception
13
+ end
14
+ end
15
+
16
+ let(:checked_class) do
17
+ Class.new do
18
+ extend Typecheck
19
+
20
+ def double_me(num)
21
+ num + num
22
+ end
23
+ typecheck 'Numeric -> Numeric', :double_me
24
+
25
+ def bad_out(num)
26
+ :sym
27
+ end
28
+ typecheck 'Numeric -> Numeric', :bad_out
29
+
30
+ def strsym_num(str, sym)
31
+ str.length
32
+ end
33
+ typecheck 'String, Symbol -> Fixnum', :strsym_num
34
+
35
+ def duck(str)
36
+ str.to_str.upcase.intern
37
+ end
38
+ typecheck '#to_str -> Symbol', :duck
39
+
40
+ def multi(range)
41
+ ('x' * range.end).chars.drop(range.begin).join.intern
42
+ end
43
+ typecheck '#begin;#end -> Symbol', :multi
44
+
45
+ def choice(x)
46
+ :foo
47
+ end
48
+ typecheck '#to_str|Fixnum -> Symbol', :choice
49
+
50
+ def arrays(nums, strings)
51
+ (nums + strings.map(&:length)).inject(:+)
52
+ end
53
+ typecheck '[Fixnum],[String] -> Numeric', :arrays
54
+
55
+ def optional(num, str = nil, sym = nil)
56
+ num
57
+ end
58
+ typecheck 'Fixnum,String,Symbol -> Numeric', :optional
59
+ end
60
+ end
61
+
62
+ subject { checked_class.new }
63
+
64
+ include_examples 'valid', :double_me, [7], 14
65
+ include_examples 'invalid', :double_me, ['foo']
66
+ include_examples 'invalid', :double_me, []
67
+
68
+ include_examples 'invalid', :bad_out, [42]
69
+
70
+ include_examples 'valid', :strsym_num, ['foo', :bar], 3
71
+ include_examples 'invalid', :strsym_num, [:foo, 'bar']
72
+
73
+ include_examples 'valid', :duck, ['foo'], :FOO
74
+ include_examples 'invalid', :duck, [7]
75
+
76
+ include_examples 'valid', :multi, [3..5], :xx
77
+ include_examples 'invalid', :multi, [Class.new { def begin ; end }.new]
78
+ include_examples 'invalid', :multi, [7]
79
+
80
+ include_examples 'valid', :choice, ['foo'], :foo
81
+ include_examples 'valid', :choice, [9], :foo
82
+ include_examples 'invalid', :choice, [5..9]
83
+ include_examples 'invalid', :choice, [5.9]
84
+
85
+ include_examples 'valid', :arrays, [[1,2], ['x', 'y']], 5
86
+ include_examples 'invalid', :arrays, [[:foo], ['x']]
87
+
88
+ include_examples 'valid', :optional, [1, 'x', :foo], 1
89
+ include_examples 'valid', :optional, [1, 'x'], 1
90
+ include_examples 'valid', :optional, [1], 1
91
+ include_examples 'invalid', :optional, [1, 'x', 'y'], 1
92
+ include_examples 'invalid', :optional, []
93
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require 'typecheck'
data/typecheck.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../lib/typecheck', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'typecheck'
7
+ gem.version = Typecheck::VERSION
8
+ gem.authors = [ 'Arne Brasseur' ]
9
+ gem.email = [ 'arne@arnebrasseur.net' ]
10
+ gem.description = 'Type checking for Ruby methods.'
11
+ gem.summary = gem.description
12
+ gem.homepage = 'https://github.com/plexus/typecheck'
13
+ gem.license = 'MIT'
14
+
15
+ gem.require_paths = %w[lib]
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.test_files = `git ls-files -- spec`.split($/)
18
+ gem.extra_rdoc_files = %w[README.md LICENSE]
19
+
20
+ gem.add_development_dependency 'rake' , '~> 10.2'
21
+ gem.add_development_dependency 'rspec' , '~> 2.14'
22
+ gem.add_development_dependency 'mutant-rspec' , '~> 0.5.10'
23
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typecheck
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Arne Brasseur
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '10.2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '10.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '2.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mutant-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.5.10
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.5.10
55
+ description: Type checking for Ruby methods.
56
+ email:
57
+ - arne@arnebrasseur.net
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files:
61
+ - README.md
62
+ - LICENSE
63
+ files:
64
+ - Gemfile
65
+ - Gemfile.lock
66
+ - LICENSE
67
+ - README.md
68
+ - Rakefile
69
+ - lib/typecheck.rb
70
+ - spec/integration/typecheck_spec.rb
71
+ - spec/spec_helper.rb
72
+ - typecheck.gemspec
73
+ homepage: https://github.com/plexus/typecheck
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.0.14
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Type checking for Ruby methods.
97
+ test_files:
98
+ - spec/integration/typecheck_spec.rb
99
+ - spec/spec_helper.rb