rubype 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a1d6b7168106e0de86c7111cfe41bfe191a4217
4
- data.tar.gz: 2663b2cfef26e3eb90ee0c0932be7d34bb562ddb
3
+ metadata.gz: 1c0f2d837a9a63da1544d73541bc6b6ef5e60958
4
+ data.tar.gz: c875157ae021117e74ec28014d313e4a5fd72f1b
5
5
  SHA512:
6
- metadata.gz: 0dce04b7c97e12030468f044af562c51c04446ed033d17acd27da10355dae3751a0fd8bc74dc8c15e8cdde31d0c6d9710d420a2cf106c6a36495636ac4a9a20e
7
- data.tar.gz: 85d34a6e01587cab72001f08f13ca372c39a72efdb52b6b3506b10b12b7de0582f4087a60b8d23929ecb6454921a74e26edf1b0760607493083f40906b355e0f
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 => 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
- Matz has mentioned Ruby3.0 with static type at some confluences. But almost all rubyists(include me) are not sure how typed Ruby is.
13
-
14
- But it's worth thinking more. This gem is kind of trial without so much side-effect.
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
- ```ruby
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
- # It's totally OK!!
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
- #=> ArgumentError: Wrong type of argument, type of "str" should be Numeric
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
- #=> TypeError: Expected wrong_sum to return Numeric but got "str" instead
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
- # ex2
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
- type People >= Any, def marry(people)
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
- #=> ArgumentError: Wrong type of argument, type of "non people" should be People
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
-
@@ -1,3 +1,3 @@
1
1
  module Rubype
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
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 klasses [Array<Class>]
36
- def assert_arg_type(meth, args, klasses)
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
- if wrong_type?(arg, klasses[i])
39
- raise ArgumentError, "Wrong type of argument, type of #{arg.inspect} should be #{klasses[i]}"
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 meth [Symbol]
53
+ # @param caller [Module]
45
54
  # @param rtn [Object]
46
- # @param klass [Class]
47
- def assert_trn_type(meth, rtn, klass)
48
- if wrong_type?(rtn, klass)
49
- raise TypeError, "Expected #{meth} to return #{klass} but got #{rtn.inspect} instead"
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 klass [Class]
55
- # @return [Boolean]
56
- def wrong_type?(obj, klass)
57
- !(obj.is_a?(klass) || klass == Any)
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 test_correct_type
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(ArgumentError) { define_test_method(type_list, args, val).call(*args) }
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(TypeError) { define_test_method(type_list, args, val).call(*args) }
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.1
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-07 00:00:00.000000000 Z
11
+ date: 2014-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler