rubype 0.2.4 → 0.2.5
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 +84 -6
- data/lib/rubype.rb +7 -2
- data/lib/rubype/ordinal.rb +13 -0
- data/lib/rubype/version.rb +1 -1
- data/test/test_rubype.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2acef96be19b3b93d4403a8ac88be41dfa2d45a9
|
4
|
+
data.tar.gz: 573fd9e52d7bbcec0e178964633ccb3e48e40342
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1e45168f79837035f25ba88da2fc53472026fc10d37ce2be1a61e65542692d3ddf365634343a6c7da23be7f47becccf6c034366617d99a970d7ad9eb2617625
|
7
|
+
data.tar.gz: 506debee9973cd93b74eb2f4248253cbc5328dc3c618d340543bd8ada6119bc305d3ca11c0e46715b55c4ce911c38c86febc899705e513ad03270298b06a5ddd
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/rubype) [](https://travis-ci.org/gogotanaka/Rubype) [](https://gemnasium.com/gogotanaka/Rubype) [](https://codeclimate.com/github/gogotanaka/Rubype)
|
4
4
|
|
5
|
+

|
5
6
|
```rb
|
6
7
|
# Assert class of both args is Numeric and class of return is String
|
7
8
|
def sum(x, y)
|
@@ -19,11 +20,22 @@ typesig :sum, [:to_i, Numeric] => Numeric
|
|
19
20
|
|
20
21
|
This gem brings you advantage of type without changing existing code's behavior.
|
21
22
|
|
23
|
+
## Good point:
|
24
|
+
* Meaningful error
|
25
|
+
* Executable documentation
|
26
|
+
* Don't need to check type of method's arguments and return.
|
27
|
+
* Type info itself is object, you can check it and even change it during run time.
|
28
|
+
|
29
|
+
## Bad point:
|
30
|
+
* Checking type run every time method call... it might be overhead, but it's not big deal.
|
31
|
+
* There is no static analysis.
|
32
|
+
|
22
33
|
# Feature
|
23
34
|
|
24
35
|
### Advantage of type
|
25
36
|
* Meaningful error
|
26
37
|
* Executable documentation
|
38
|
+
* Don't need to check type of method's arguments and return .
|
27
39
|
|
28
40
|
```rb
|
29
41
|
require 'rubype'
|
@@ -45,7 +57,7 @@ MyClass.new.sum(1, 2)
|
|
45
57
|
#=> 3
|
46
58
|
|
47
59
|
MyClass.new.sum(1, 'string')
|
48
|
-
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's
|
60
|
+
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's 2nd argument to be Numeric but got "string" instead
|
49
61
|
|
50
62
|
MyClass.new.wrong_sum(1, 2)
|
51
63
|
#=> Rubype::ReturnTypeError: Expected MyClass#wrong_sum to return Numeric but got "string" instead
|
@@ -63,7 +75,7 @@ MyClass.new.sum('1', 2)
|
|
63
75
|
#=> 3
|
64
76
|
|
65
77
|
MyClass.new.sum(:has_no_to_i, 2)
|
66
|
-
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's
|
78
|
+
#=> Rubype::ArgumentTypeError: Expected MyClass#sum's 1st argument to have method #to_i but got :has_no_to_i instead
|
67
79
|
|
68
80
|
|
69
81
|
# ex3: You can use Any class, if you want
|
@@ -78,7 +90,7 @@ People.new.marry(People.new)
|
|
78
90
|
#=> no error
|
79
91
|
|
80
92
|
People.new.marry('non people')
|
81
|
-
#=> Rubype::ArgumentTypeError: Expected People#marry's
|
93
|
+
#=> Rubype::ArgumentTypeError: Expected People#marry's 1st argument to be People but got "non people" instead
|
82
94
|
|
83
95
|
```
|
84
96
|
|
@@ -90,7 +102,7 @@ class MyClass
|
|
90
102
|
def method_with_type(x, y)
|
91
103
|
x + y
|
92
104
|
end
|
93
|
-
typesig :
|
105
|
+
typesig :method_with_type, [Numeric, Numeric] => Numeric
|
94
106
|
|
95
107
|
def method_without_type(x, y)
|
96
108
|
'string'
|
@@ -134,15 +146,81 @@ class MyClass
|
|
134
146
|
typesig :sum, [:to_i, Numeric] => Numeric
|
135
147
|
end
|
136
148
|
|
137
|
-
MyClass.new.method(:
|
149
|
+
MyClass.new.method(:sum).type_info
|
138
150
|
# => [:to_i, Numeric] => Numeric
|
139
151
|
|
152
|
+
MyClass.new.method(:sum).arg_types
|
153
|
+
# => [:to_i, Numeric]
|
154
|
+
|
155
|
+
MyClass.new.method(:sum).return_type
|
156
|
+
# => Numeric
|
157
|
+
|
140
158
|
```
|
141
159
|
|
160
|
+
## Benchmarks
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
require 'rubype'
|
164
|
+
require 'benchmark'
|
165
|
+
|
166
|
+
class RubypeCommonClass
|
167
|
+
def sum(x, y)
|
168
|
+
x + y
|
169
|
+
end
|
170
|
+
typesig :sum, [Numeric, Numeric] => Numeric
|
171
|
+
end
|
172
|
+
|
173
|
+
class CommonClass
|
174
|
+
def sum(x, y)
|
175
|
+
x + y
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class RubypeDucktypeClass
|
180
|
+
def sum(x, y)
|
181
|
+
x.to_i + y
|
182
|
+
end
|
183
|
+
typesig :sum, [:to_i, Numeric] => Numeric
|
184
|
+
end
|
185
|
+
|
186
|
+
class DucktypeClass
|
187
|
+
def sum(x, y)
|
188
|
+
x.to_i + y
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
N = 100_000
|
193
|
+
Benchmark.bm("RubypeDucktypeClass".length + 3) do |x|
|
194
|
+
x.report("RubypeCommonClass") { N.times { RubypeCommonClass.new.sum(1, 5) } }
|
195
|
+
x.report("CommonClass") { N.times { CommonClass.new.sum(1, 5) } }
|
196
|
+
end
|
197
|
+
|
198
|
+
Benchmark.bm("RubypeDucktypeClass".length + 3) do |x|
|
199
|
+
x.report("RubypeDucktypeClass") { N.times { RubypeDucktypeClass.new.sum(1, 5) } }
|
200
|
+
x.report("DucktypeClass") { N.times { DucktypeClass.new.sum(1, 5) } }
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
### Results
|
205
|
+
Ruby 2.2.0, Macbook Pro 2.9Ghz Intel Core i7, 8GB RAM
|
206
|
+
|
207
|
+
```
|
208
|
+
user system total real
|
209
|
+
RubypeCommonClass 0.530000 0.010000 0.540000 ( 0.566493)
|
210
|
+
CommonClass 0.030000 0.000000 0.030000 ( 0.035718)
|
211
|
+
user system total real
|
212
|
+
RubypeDucktypeClass 0.590000 0.010000 0.600000 ( 0.682504)
|
213
|
+
DucktypeClass 0.030000 0.000000 0.030000 ( 0.029856)
|
214
|
+
```
|
215
|
+
|
216
|
+
|
217
|
+
|
142
218
|
## Installation
|
143
219
|
|
144
220
|
gem install rubype or add gem 'rubype' to your Gemfile.
|
145
221
|
|
222
|
+
And `require 'rubype'`, enjoy typed Ruby.
|
223
|
+
|
146
224
|
This gem requires Ruby 2.0.0+.
|
147
225
|
|
148
226
|
### Contributing
|
@@ -161,7 +239,7 @@ Commit your changes (`git commit -am 'Add some feature'`)
|
|
161
239
|
|
162
240
|
Finished in 0.010961s, 547.3953 runs/s, 5017.7903 assertions/s.
|
163
241
|
|
164
|
-
|
242
|
+
7 runs, 61 assertions, 0 failures, 0 errors, 0 skips
|
165
243
|
|
166
244
|
Push to the branch (`git push origin my-new-feature`)
|
167
245
|
|
data/lib/rubype.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rubype/ordinal'
|
2
|
+
|
1
3
|
# Builtin Contracts
|
2
4
|
class Any; end
|
3
5
|
module Boolean; end
|
@@ -22,16 +24,19 @@ class Module
|
|
22
24
|
end
|
23
25
|
|
24
26
|
class Method
|
27
|
+
# @return [Hash]: { [ArgInfo_1, ArgInfo_2, ... ArgInfo_n] => RtnInfo }
|
25
28
|
def type_info
|
26
29
|
if methods_hash = Rubype.typed_method_info[owner]
|
27
30
|
methods_hash[name]
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
34
|
+
# @return [Array<Class, Symbol>]: [ArgInfo_1, ArgInfo_2, ... ArgInfo_n]
|
31
35
|
def arg_types
|
32
36
|
type_info.first.first if type_info
|
33
37
|
end
|
34
38
|
|
39
|
+
# @return [Class, Symbol]: RtnInfo
|
35
40
|
def return_type
|
36
41
|
type_info.first.last if type_info
|
37
42
|
end
|
@@ -80,10 +85,10 @@ module Rubype
|
|
80
85
|
case type_check(arg, type_info)
|
81
86
|
when :need_correct_class
|
82
87
|
raise ArgumentTypeError,
|
83
|
-
"Expected #{meth_caller.class}##{meth}'s #{i}
|
88
|
+
"Expected #{meth_caller.class}##{meth}'s #{i}#{ordinal(i)} argument to be #{type_info} but got #{arg.inspect} instead"
|
84
89
|
when :need_correct_method
|
85
90
|
raise ArgumentTypeError,
|
86
|
-
"Expected #{meth_caller.class}##{meth}'s #{i}
|
91
|
+
"Expected #{meth_caller.class}##{meth}'s #{i}#{ordinal(i)} argument to have method ##{type_info} but got #{arg.inspect} instead"
|
87
92
|
end
|
88
93
|
end
|
89
94
|
end
|
data/lib/rubype/version.rb
CHANGED
data/test/test_rubype.rb
CHANGED
@@ -103,10 +103,10 @@ class TestRubype < MiniTest::Unit::TestCase
|
|
103
103
|
assert_equal meth.return_type, String
|
104
104
|
|
105
105
|
err = assert_raises(Rubype::ReturnTypeError) { meth.(1,2) }
|
106
|
-
assert_equal err.message,
|
106
|
+
assert_equal err.message, %|Expected MyClass#test_mth to return String but got nil instead|
|
107
107
|
|
108
108
|
err = assert_raises(Rubype::ArgumentTypeError) { meth.(1,'2') }
|
109
|
-
assert_equal err.message,
|
109
|
+
assert_equal err.message, %|Expected MyClass#test_mth's 2nd argument to be Numeric but got "2" instead|
|
110
110
|
end
|
111
111
|
|
112
112
|
private
|
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gogotanaka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- ext/rubype/rubype.c
|
72
72
|
- ext/rubype/rubype.h
|
73
73
|
- lib/rubype.rb
|
74
|
+
- lib/rubype/ordinal.rb
|
74
75
|
- lib/rubype/version.rb
|
75
76
|
- rubype.gemspec
|
76
77
|
- test/minitest_helper.rb
|