rubype 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -34
- data/lib/rubype.rb +14 -4
- data/lib/rubype/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a1d6b7168106e0de86c7111cfe41bfe191a4217
|
4
|
+
data.tar.gz: 2663b2cfef26e3eb90ee0c0932be7d34bb562ddb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0dce04b7c97e12030468f044af562c51c04446ed033d17acd27da10355dae3751a0fd8bc74dc8c15e8cdde31d0c6d9710d420a2cf106c6a36495636ac4a9a20e
|
7
|
+
data.tar.gz: 85d34a6e01587cab72001f08f13ca372c39a72efdb52b6b3506b10b12b7de0582f4087a60b8d23929ecb6454921a74e26edf1b0760607493083f40906b355e0f
|
data/README.md
CHANGED
@@ -1,9 +1,56 @@
|
|
1
|
-
# Ruby
|
1
|
+
# Ruby + Type = Rubype
|
2
|
+
|
3
|
+
```rb
|
4
|
+
def sum(x, y)
|
5
|
+
x + y
|
6
|
+
end
|
7
|
+
typesig sum: [Numeric, Numeric => Numeric]
|
8
|
+
```
|
9
|
+
|
10
|
+
This gem brings you advantage of type without changing existing code's behavior.
|
2
11
|
|
3
12
|
Matz has mentioned Ruby3.0 with static type at some confluences. But almost all rubyists(include me) are not sure how typed Ruby is.
|
4
13
|
|
5
14
|
But it's worth thinking more. This gem is kind of trial without so much side-effect.
|
6
15
|
|
16
|
+
# Feature
|
17
|
+
### Typed method can coexist with non-typed method
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# It's totally OK!!
|
21
|
+
class MyClass
|
22
|
+
def method_with_type(x, y)
|
23
|
+
x + y
|
24
|
+
end
|
25
|
+
typesig sum: [Numeric, Numeric => Numeric]
|
26
|
+
|
27
|
+
def method_without_type(x, y)
|
28
|
+
'string'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
### Duck typing
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
|
37
|
+
class MyClass
|
38
|
+
def foo(any_obj)
|
39
|
+
1
|
40
|
+
end
|
41
|
+
typesig sum: [Any => Numeric]
|
42
|
+
end
|
43
|
+
|
44
|
+
# It's totally OK!!
|
45
|
+
MyClass.new.foo(1)
|
46
|
+
# It's totally OK!!
|
47
|
+
MyClass.new.foo('str')
|
48
|
+
```
|
49
|
+
|
50
|
+
### Advantage of type
|
51
|
+
* Meaningful error
|
52
|
+
* Executable documentation
|
53
|
+
|
7
54
|
```rb
|
8
55
|
require 'rubype'
|
9
56
|
|
@@ -45,39 +92,7 @@ People.new.marry('non people')
|
|
45
92
|
#=> ArgumentError: Wrong type of argument, type of "non people" should be People
|
46
93
|
```
|
47
94
|
|
48
|
-
## Feature
|
49
|
-
### Typed method can coexist with non-typed method
|
50
|
-
|
51
|
-
```ruby
|
52
|
-
# It's totally OK!!
|
53
|
-
class MyClass
|
54
|
-
def sum(x, y)
|
55
|
-
x + y
|
56
|
-
end
|
57
|
-
typesig sum: [Numeric, Numeric => Numeric]
|
58
|
-
|
59
|
-
def sum_without_type(x, y)
|
60
|
-
'string'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
```
|
64
|
-
|
65
|
-
### Duck typing
|
66
95
|
|
67
|
-
```ruby
|
68
|
-
|
69
|
-
class MyClass
|
70
|
-
def foo(any_obj)
|
71
|
-
1
|
72
|
-
end
|
73
|
-
typesig sum: [Any => Numeric]
|
74
|
-
end
|
75
|
-
|
76
|
-
# It's totally OK!!
|
77
|
-
MyClass.new.foo(1)
|
78
|
-
# It's totally OK!!
|
79
|
-
MyClass.new.foo('str')
|
80
|
-
```
|
81
96
|
|
82
97
|
## Installation
|
83
98
|
|
@@ -104,4 +119,5 @@ Push to the branch (`git push origin my-new-feature`)
|
|
104
119
|
Create a new Pull Request
|
105
120
|
|
106
121
|
## Credits
|
107
|
-
[@chancancode](https://github.com/chancancode) first brought this to my attention. I've stolen some idea from
|
122
|
+
[@chancancode](https://github.com/chancancode) and [This article](http://blog.codeclimate.com/blog/2014/05/06/gradual-type-checking-for-ruby/) first brought this to my attention. I've stolen some idea from them.
|
123
|
+
|
data/lib/rubype.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "rubype/rubype"
|
2
|
-
|
3
1
|
# Builtin Contracts
|
4
2
|
class Any; end
|
5
3
|
module Boolean; end
|
@@ -13,14 +11,15 @@ class Module
|
|
13
11
|
@__rubype__
|
14
12
|
end
|
15
13
|
|
14
|
+
# @param hash [Hash] {method_name: [ArgClass1, ArgClass2, ... ArgClassn => RtnClass]}
|
16
15
|
def typesig(hash)
|
17
16
|
meth = hash.keys.first
|
18
17
|
*arg_types, type_pair = hash.values.first
|
19
18
|
|
20
19
|
__rubype__.send(:define_method, meth) do |*args, &block|
|
21
|
-
::Rubype.assert_arg_type
|
20
|
+
::Rubype.send(:assert_arg_type, meth, args, arg_types << type_pair.keys.first)
|
22
21
|
rtn = super(*args, &block)
|
23
|
-
::Rubype.assert_trn_type
|
22
|
+
::Rubype.send(:assert_trn_type, meth, rtn, type_pair.values.first)
|
24
23
|
rtn
|
25
24
|
end
|
26
25
|
self
|
@@ -29,6 +28,11 @@ end
|
|
29
28
|
|
30
29
|
module Rubype
|
31
30
|
class << self
|
31
|
+
private
|
32
|
+
|
33
|
+
# @param meth [Symbol]
|
34
|
+
# @param args [Array<Object>]
|
35
|
+
# @param klasses [Array<Class>]
|
32
36
|
def assert_arg_type(meth, args, klasses)
|
33
37
|
args.each_with_index do |arg, i|
|
34
38
|
if wrong_type?(arg, klasses[i])
|
@@ -37,12 +41,18 @@ module Rubype
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
44
|
+
# @param meth [Symbol]
|
45
|
+
# @param rtn [Object]
|
46
|
+
# @param klass [Class]
|
40
47
|
def assert_trn_type(meth, rtn, klass)
|
41
48
|
if wrong_type?(rtn, klass)
|
42
49
|
raise TypeError, "Expected #{meth} to return #{klass} but got #{rtn.inspect} instead"
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
53
|
+
# @param obj [Object]
|
54
|
+
# @param klass [Class]
|
55
|
+
# @return [Boolean]
|
46
56
|
def wrong_type?(obj, klass)
|
47
57
|
!(obj.is_a?(klass) || klass == Any)
|
48
58
|
end
|
data/lib/rubype/version.rb
CHANGED