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 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