rubype 0.2.1 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +75 -46
- data/lib/rubype/version.rb +1 -1
- data/lib/rubype.rb +33 -15
- data/test/test_rubype.rb +23 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c0f2d837a9a63da1544d73541bc6b6ef5e60958
|
4
|
+
data.tar.gz: c875157ae021117e74ec28014d313e4a5fd72f1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae15d7157d172321c3b31982bbbb83611f4e163a8278b27c7b96971b7be6d0ee1244d6d4fc8591ccd28549f1e5842929ea8cb274a69f01d0a425c586eeebd292
|
7
|
+
data.tar.gz: b7fe02373c1ce6a849ca232ad5f0139da7cf2f5655ce3d81e065e5abc0f04c7e6a0c0e9b789102076c6d6df1e028d2773ce79fd707e0e9c504e8644cbdcf1832
|
data/README.md
CHANGED
@@ -1,51 +1,23 @@
|
|
1
1
|
# Ruby + Type = Rubype
|
2
2
|
|
3
3
|
```rb
|
4
|
+
# Assert class of both args is Numeric and class of return is String
|
4
5
|
def sum(x, y)
|
5
|
-
x + y
|
6
|
+
(x + y).to_s
|
6
7
|
end
|
7
|
-
typesig sum: [Numeric, Numeric =>
|
8
|
-
```
|
9
|
-
|
10
|
-
This gem brings you advantage of type without changing existing code's behavior.
|
8
|
+
typesig sum: [Numeric, Numeric => String]
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
10
|
+
# Assert first arg has method #to_i
|
11
|
+
def sum(x, y)
|
12
|
+
x.to_i + y
|
30
13
|
end
|
14
|
+
typesig sum: [:to_i, Numeric => Numeric]
|
31
15
|
```
|
32
16
|
|
33
|
-
### Duck typing
|
34
17
|
|
35
|
-
|
36
|
-
|
37
|
-
class MyClass
|
38
|
-
def foo(any_obj)
|
39
|
-
1
|
40
|
-
end
|
41
|
-
typesig sum: [Any => Numeric]
|
42
|
-
end
|
18
|
+
This gem brings you advantage of type without changing existing code's behavior.
|
43
19
|
|
44
|
-
#
|
45
|
-
MyClass.new.foo(1)
|
46
|
-
# It's totally OK!!
|
47
|
-
MyClass.new.foo('str')
|
48
|
-
```
|
20
|
+
# Feature
|
49
21
|
|
50
22
|
### Advantage of type
|
51
23
|
* Meaningful error
|
@@ -54,7 +26,7 @@ MyClass.new.foo('str')
|
|
54
26
|
```rb
|
55
27
|
require 'rubype'
|
56
28
|
|
57
|
-
# ex1
|
29
|
+
# ex1: Assert class of args and return
|
58
30
|
class MyClass
|
59
31
|
def sum(x, y)
|
60
32
|
x + y
|
@@ -71,27 +43,85 @@ MyClass.new.sum(1, 2)
|
|
71
43
|
#=> 3
|
72
44
|
|
73
45
|
MyClass.new.sum(1, 'string')
|
74
|
-
#=>
|
46
|
+
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's 2th argument to be Numeric but got "string" instead
|
75
47
|
|
76
48
|
MyClass.new.wrong_sum(1, 2)
|
77
|
-
#=>
|
49
|
+
#=> Rubype::ReturnTypeError: Expected MyClass#wrong_sum to return Numeric but got "string" instead
|
50
|
+
|
51
|
+
|
52
|
+
# ex2: Assert object has specified method
|
53
|
+
class MyClass
|
54
|
+
def sum(x, y)
|
55
|
+
x.to_i + y
|
56
|
+
end
|
57
|
+
typesig sum: [:to_i, Numeric => Numeric]
|
58
|
+
end
|
78
59
|
|
60
|
+
MyClass.new.sum('1', 2)
|
61
|
+
#=> 3
|
79
62
|
|
80
|
-
|
63
|
+
MyClass.new.sum(:has_no_to_i, 2)
|
64
|
+
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's 1th argument to have method #to_i but got :has_no_to_i instead
|
65
|
+
|
66
|
+
|
67
|
+
# ex3: You can use Any class, if you want
|
81
68
|
class People
|
82
|
-
|
69
|
+
def marry(people)
|
83
70
|
# Your Ruby code as usual
|
84
71
|
end
|
72
|
+
typesig marry: [People => Any]
|
85
73
|
end
|
86
|
-
typesig marry: [People => Any]
|
87
74
|
|
88
75
|
People.new.marry(People.new)
|
89
76
|
#=> no error
|
90
77
|
|
91
78
|
People.new.marry('non people')
|
92
|
-
#=>
|
79
|
+
#=> Rubype::ArgumentTypeError: Expected People#marry's 1th argument to be People but got "non people" instead
|
80
|
+
|
81
|
+
```
|
82
|
+
|
83
|
+
### Typed method can coexist with non-typed method
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# It's totally OK!!
|
87
|
+
class MyClass
|
88
|
+
def method_with_type(x, y)
|
89
|
+
x + y
|
90
|
+
end
|
91
|
+
typesig sum: [Numeric, Numeric => Numeric]
|
92
|
+
|
93
|
+
def method_without_type(x, y)
|
94
|
+
'string'
|
95
|
+
end
|
96
|
+
end
|
93
97
|
```
|
94
98
|
|
99
|
+
### Duck typing
|
100
|
+
You can use `Any` class.
|
101
|
+
```ruby
|
102
|
+
class MyClass
|
103
|
+
def foo(any_obj)
|
104
|
+
1
|
105
|
+
end
|
106
|
+
typesig foo: [Any => Numeric]
|
107
|
+
|
108
|
+
def sum(x, y)
|
109
|
+
x.to_i + y
|
110
|
+
end
|
111
|
+
typesig sum: [:to_i, Numeric => Numeric]
|
112
|
+
end
|
113
|
+
|
114
|
+
# It's totally OK!!
|
115
|
+
MyClass.new.foo(1)
|
116
|
+
# It's totally OK!!
|
117
|
+
MyClass.new.foo(:sym)
|
118
|
+
|
119
|
+
|
120
|
+
# It's totally OK!!
|
121
|
+
MyClass.new.sum(1, 2)
|
122
|
+
# It's totally OK!!
|
123
|
+
MyClass.new.sum('1', 2)
|
124
|
+
```
|
95
125
|
|
96
126
|
|
97
127
|
## Installation
|
@@ -116,8 +146,7 @@ Commit your changes (`git commit -am 'Add some feature'`)
|
|
116
146
|
|
117
147
|
Push to the branch (`git push origin my-new-feature`)
|
118
148
|
|
119
|
-
Create a new Pull Request
|
149
|
+
Create a new Pull Request to `develop` branch
|
120
150
|
|
121
151
|
## Credits
|
122
152
|
[@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/version.rb
CHANGED
data/lib/rubype.rb
CHANGED
@@ -17,9 +17,9 @@ class Module
|
|
17
17
|
*arg_types, type_pair = hash.values.first
|
18
18
|
|
19
19
|
__rubype__.send(:define_method, meth) do |*args, &block|
|
20
|
-
::Rubype.send(:assert_arg_type, meth, args, arg_types << type_pair.keys.first)
|
20
|
+
::Rubype.send(:assert_arg_type, self, meth, args, arg_types << type_pair.keys.first)
|
21
21
|
rtn = super(*args, &block)
|
22
|
-
::Rubype.send(:assert_trn_type, meth, rtn, type_pair.values.first)
|
22
|
+
::Rubype.send(:assert_trn_type, self, meth, rtn, type_pair.values.first)
|
23
23
|
rtn
|
24
24
|
end
|
25
25
|
self
|
@@ -27,34 +27,52 @@ class Module
|
|
27
27
|
end
|
28
28
|
|
29
29
|
module Rubype
|
30
|
+
class ArgumentTypeError < ::TypeError; end
|
31
|
+
class ReturnTypeError < ::TypeError; end
|
32
|
+
|
30
33
|
class << self
|
31
34
|
private
|
32
35
|
|
36
|
+
# @param caller [Module]
|
33
37
|
# @param meth [Symbol]
|
34
38
|
# @param args [Array<Object>]
|
35
|
-
# @param
|
36
|
-
def assert_arg_type(meth, args,
|
39
|
+
# @param type_infos [Array<Class, Symbol>]
|
40
|
+
def assert_arg_type(caller, meth, args, type_infos)
|
37
41
|
args.each_with_index do |arg, i|
|
38
|
-
|
39
|
-
|
42
|
+
case type_check(arg, type_infos[i])
|
43
|
+
when :need_correct_class
|
44
|
+
raise ArgumentTypeError,
|
45
|
+
"Expected #{caller.class}##{meth}'s #{i+1}th argument to be #{type_infos[i]} but got #{arg.inspect} instead"
|
46
|
+
when :need_correct_method
|
47
|
+
raise ArgumentTypeError,
|
48
|
+
"Expected #{caller.class}##{meth}'s #{i+1}th argument to have method ##{type_infos[i]} but got #{arg.inspect} instead"
|
40
49
|
end
|
41
50
|
end
|
42
51
|
end
|
43
52
|
|
44
|
-
# @param
|
53
|
+
# @param caller [Module]
|
45
54
|
# @param rtn [Object]
|
46
|
-
# @param
|
47
|
-
def assert_trn_type(meth, rtn,
|
48
|
-
|
49
|
-
|
55
|
+
# @param type_info [Class, Symbol]
|
56
|
+
def assert_trn_type(caller, meth, rtn, type_info)
|
57
|
+
case type_check(rtn, type_info)
|
58
|
+
when :need_correct_class
|
59
|
+
raise ReturnTypeError,
|
60
|
+
"Expected #{caller.class}##{meth} to return #{type_info} but got #{rtn.inspect} instead"
|
61
|
+
when :need_correct_method
|
62
|
+
raise ReturnTypeError,
|
63
|
+
"Expected #{caller.class}##{meth} to have method ##{type_info} but got #{rtn.inspect} instead"
|
50
64
|
end
|
51
65
|
end
|
52
66
|
|
53
67
|
# @param obj [Object]
|
54
|
-
# @param
|
55
|
-
# @return [
|
56
|
-
def
|
57
|
-
!(obj.is_a?(
|
68
|
+
# @param type_info [Class, Symbol]
|
69
|
+
# @return [Symbol]
|
70
|
+
def type_check(obj, type_info)
|
71
|
+
if type_info.is_a?(Module) && !(obj.is_a?(type_info) || type_info == Any)
|
72
|
+
:need_correct_class
|
73
|
+
elsif type_info.is_a?(Symbol) && !(obj.respond_to?(type_info))
|
74
|
+
:need_correct_method
|
75
|
+
end
|
58
76
|
end
|
59
77
|
end
|
60
78
|
end
|
data/test/test_rubype.rb
CHANGED
@@ -13,7 +13,7 @@ class TestRubype < MiniTest::Unit::TestCase
|
|
13
13
|
@hash = { test: :hash }
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def test_correct_type_by_class
|
17
17
|
assert_correct_type [Numeric => Numeric], [@numeric], @numeric
|
18
18
|
assert_correct_type [Numeric => Array ], [@numeric], @array
|
19
19
|
assert_correct_type [Numeric => String ], [@numeric], @string
|
@@ -29,6 +29,17 @@ class TestRubype < MiniTest::Unit::TestCase
|
|
29
29
|
assert_correct_type [Boolean, Symbol => Symbol ], [true, @symbol ], @symbol
|
30
30
|
end
|
31
31
|
|
32
|
+
def test_correct_type_by_sym
|
33
|
+
assert_correct_type [Numeric => :to_i], [@numeric], @numeric
|
34
|
+
assert_correct_type [Numeric => :to_i], [@numeric], @string
|
35
|
+
|
36
|
+
assert_correct_type [Numeric => :to_s], [@numeric], @numeric
|
37
|
+
assert_correct_type [Numeric => :to_s], [@numeric], @string
|
38
|
+
assert_correct_type [Numeric => :to_s], [@numeric], @symbol
|
39
|
+
assert_correct_type [Numeric => :to_s], [@numeric], @array
|
40
|
+
assert_correct_type [Numeric => :to_s], [@numeric], @hash
|
41
|
+
end
|
42
|
+
|
32
43
|
def test_wrong_return_type
|
33
44
|
assert_wrong_rtn [Numeric => Numeric], [@numeric], @array
|
34
45
|
assert_wrong_rtn [Numeric => Numeric], [@numeric], @string
|
@@ -41,6 +52,10 @@ class TestRubype < MiniTest::Unit::TestCase
|
|
41
52
|
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], @hash
|
42
53
|
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], @symbol
|
43
54
|
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], true
|
55
|
+
|
56
|
+
assert_wrong_rtn [Numeric => :to_i], [@numeric], @symbol
|
57
|
+
assert_wrong_rtn [Numeric => :to_i], [@numeric], @array
|
58
|
+
assert_wrong_rtn [Numeric => :to_i], [@numeric], @hash
|
44
59
|
end
|
45
60
|
|
46
61
|
def test_wrong_args_type
|
@@ -55,6 +70,11 @@ class TestRubype < MiniTest::Unit::TestCase
|
|
55
70
|
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, @hash ], @numeric
|
56
71
|
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, @symbol], @numeric
|
57
72
|
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, true ], @numeric
|
73
|
+
|
74
|
+
assert_wrong_arg [Numeric => :to_i], [@array ], @numeric
|
75
|
+
assert_wrong_arg [Numeric => :to_i], [@hash ], @numeric
|
76
|
+
assert_wrong_arg [Numeric => :to_i], [@symbol], @numeric
|
77
|
+
assert_wrong_arg [Numeric => :to_i], [true ], @numeric
|
58
78
|
end
|
59
79
|
|
60
80
|
def test_any
|
@@ -79,11 +99,11 @@ class TestRubype < MiniTest::Unit::TestCase
|
|
79
99
|
end
|
80
100
|
|
81
101
|
def assert_wrong_arg(type_list, args, val)
|
82
|
-
assert_raises(
|
102
|
+
assert_raises(Rubype::ArgumentTypeError) { define_test_method(type_list, args, val).call(*args) }
|
83
103
|
end
|
84
104
|
|
85
105
|
def assert_wrong_rtn(type_list, args, val)
|
86
|
-
assert_raises(
|
106
|
+
assert_raises(Rubype::ReturnTypeError) { define_test_method(type_list, args, val).call(*args) }
|
87
107
|
end
|
88
108
|
|
89
109
|
def define_test_method(type_list, args, val)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubype
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gogotanaka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|