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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7c7fb90c7b7bb6d84aed99e298cfd92344c334b
4
- data.tar.gz: 66a55c98516f1d8b2f4b1a1454516532406c3e24
3
+ metadata.gz: 2acef96be19b3b93d4403a8ac88be41dfa2d45a9
4
+ data.tar.gz: 573fd9e52d7bbcec0e178964633ccb3e48e40342
5
5
  SHA512:
6
- metadata.gz: 56da79236105ebf887d96f031d596efdff6272ee1e1a548f3d87c23b63fde00bc49eb77c18de1146177e6157f0acf92bb46669f1b89026a147ab65f1b5b41c81
7
- data.tar.gz: b2d3b9172ec56df5b2b5f1488c2b8022048d699202e0eadb26399aad46078d4b7da70d699ea6e3694e709819cd7ce5de9ed61a0ede214858c92fe60039e3ade4
6
+ metadata.gz: a1e45168f79837035f25ba88da2fc53472026fc10d37ce2be1a61e65542692d3ddf365634343a6c7da23be7f47becccf6c034366617d99a970d7ad9eb2617625
7
+ data.tar.gz: 506debee9973cd93b74eb2f4248253cbc5328dc3c618d340543bd8ada6119bc305d3ca11c0e46715b55c4ce911c38c86febc899705e513ad03270298b06a5ddd
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubype.svg)](http://badge.fury.io/rb/rubype) [![Build Status](https://travis-ci.org/gogotanaka/Rubype.svg?branch=develop)](https://travis-ci.org/gogotanaka/Rubype) [![Dependency Status](https://gemnasium.com/gogotanaka/Rubype.svg)](https://gemnasium.com/gogotanaka/Rubype) [![Code Climate](https://codeclimate.com/github/gogotanaka/Rubype/badges/gpa.svg)](https://codeclimate.com/github/gogotanaka/Rubype)
4
4
 
5
+ ![210414.png](https://qiita-image-store.s3.amazonaws.com/0/30440/0aafba03-1a4c-4676-5377-75f906aaeab9.png)
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 2th argument to be Numeric but got "string" instead
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 1th argument to have method #to_i but got :has_no_to_i instead
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 1th argument to be People but got "non people" instead
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 :sum, [Numeric, Numeric] => Numeric
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(:foo).type_info
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
- 6 runs, 55 assertions, 0 failures, 0 errors, 0 skips
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
 
@@ -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}th argument to be #{type_info} but got #{arg.inspect} instead"
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}th argument to have method ##{type_info} but got #{arg.inspect} instead"
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
@@ -0,0 +1,13 @@
1
+ # Borrowed from ActiveSupport::Inflector
2
+ def ordinal(number)
3
+ if (11..13).include?(number % 100)
4
+ "th"
5
+ else
6
+ case number % 10
7
+ when 1; "st"
8
+ when 2; "nd"
9
+ when 3; "rd"
10
+ else "th"
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Rubype
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
@@ -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, "Expected MyClass#test_mth to return String but got nil instead"
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, "Expected MyClass#test_mth's 2th argument to be Numeric but got \"2\" instead"
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
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-08 00:00:00.000000000 Z
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