taipo 1.2.0 → 1.3.0

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: 4be2ae174cd1d29fb2d7e4088ca69c9da0aecfb3
4
- data.tar.gz: ac87cf9977ef502fe802459da7ea0855587fecc2
3
+ metadata.gz: 5ae496b6a69d19aceb9f158a587cb7c2f7d0faa1
4
+ data.tar.gz: ec0096547e644b51f8759529a246b273ff5354e8
5
5
  SHA512:
6
- metadata.gz: bafc5ce5e262afe6a17ca0bb06476ba4f2120d337de2e44b9bef5d0814ce556466d70a0efbe99c9df7095e0588cba45db874328d1d0dab73a03e140887f5a85c
7
- data.tar.gz: b67fcb77105810336e8616b83a188115ddcde6ecc202221960a5c7e299be3a61a49e7b03e85a22cd998301a15c6cf456c7f0ba374b2986f2c2d2744db5db77d3
6
+ metadata.gz: cb51c506c37aaaca6232488649b8cf59246ea2a3f69e7efb5230b677e1c8d32e276949126202325ebea6c9e8f4d5f652287a47e0a6aa6afd08c23da0f4cd86eb
7
+ data.tar.gz: 6c1b5b0bb7512d4829167bb5d8400f204fb114cedf16350a0099f71a43a54139a5908c16f08d9a513f9ce23c6c4bbc80b0f4a29c1cd2289411f048bd295006c6
data/README.md CHANGED
@@ -102,6 +102,14 @@ It's possible to specify a duck type by writing the instance method (or methods)
102
102
  check types, a: '#to_s', b: '(#foo, #bar)'
103
103
  ```
104
104
 
105
+ #### Optional Types
106
+
107
+ If `nil` is permitted as a value, the optional shorthand `?` can be appended to the end of a class name to form an optional type. Collections and constraints can follow as usual.
108
+
109
+ ```ruby
110
+ check types, a: 'String?', b: 'Array?<Integer>', c: 'Integer?(min: 0)'
111
+ ```
112
+
105
113
  ### Collections
106
114
 
107
115
  Taipo can also check whether a variable has a collection of elements of the specified child type. A child type can consist of the same components as any other type (ie. a name, collection, constraint, sum).
@@ -88,6 +88,8 @@ module Taipo
88
88
  unless collect_invalids || is_match
89
89
  if Taipo::instance_method? v
90
90
  msg = "Object '#{k}' does not respond to #{v}."
91
+ elsif Taipo::symbol? v
92
+ msg = "Object '#{k}' is not equal to #{v}."
91
93
  elsif arg.is_a? Enumerable
92
94
  type_def = Taipo.object_to_type_def arg
93
95
  msg = "Object '#{k}' is #{type_def} but expected #{v}."
@@ -35,7 +35,7 @@ module Taipo
35
35
 
36
36
  case c
37
37
  when '|'
38
- unless attached? previous # content.empty? # Previous character must have been '>' or ')'.
38
+ unless attached? previous # Previous character must have been '>' or ')'.
39
39
  el = Taipo::TypeElement.new name: content
40
40
  content = ''
41
41
  elements = stack.pop
@@ -51,7 +51,7 @@ module Taipo
51
51
  first_component = Array.new
52
52
  stack.push first_component
53
53
  when '>'
54
- if attached? previous # content.empty? # Previous character must have been '>' or ')'.
54
+ if attached? previous # Previous character must have been '>' or ')'.
55
55
  last_component = stack.pop
56
56
  else
57
57
  el = Taipo::TypeElement.new name: content.strip
@@ -36,6 +36,18 @@ module Taipo
36
36
  # parentheses can be omitted. For defining duck types that respond to
37
37
  # multiple methods, the parentheses are required.
38
38
  #
39
+ # ==== Optional Types
40
+ #
41
+ # 'String?', 'Array<Integer?>', 'Symbol?|String?'
42
+ #
43
+ # It is possible to specify an 'optional' type by appending a question mark
44
+ # to the name of the type. This shorthand functions similarly to defining a
45
+ # sum type with NilClass (the implementation of how optional types are
46
+ # checked is slightly different, however; see {Taipo::TypeElement#match?}).
47
+ # It is not possible to define an optional duck type. For that, either the
48
+ # implicit Object class should be specified (and then made optional), or a
49
+ # sum type should be used.
50
+ #
39
51
  # === Collections
40
52
  #
41
53
  # 'Array<Integer>', 'Hash<Symbol, String>', 'Array<Array<Float>>'
@@ -136,6 +136,7 @@ module Taipo
136
136
  # @since 1.0.0
137
137
  # @api private
138
138
  def match?(arg)
139
+ return true if optional? && arg.nil?
139
140
  match_class?(arg) && match_constraints?(arg) && match_child_type?(arg)
140
141
  end
141
142
 
@@ -150,12 +151,13 @@ module Taipo
150
151
  # @since 1.0.0
151
152
  # @api private
152
153
  def match_class?(arg)
153
- if @name == 'Boolean'
154
+ actual_name = (optional?) ? @name[0..-2] : @name
155
+ if actual_name == 'Boolean'
154
156
  arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
155
157
  else
156
- msg = "Class to match #{@name} is not defined"
157
- raise Taipo::SyntaxError, msg unless Object.const_defined?(@name)
158
- arg.is_a? Object.const_get(@name)
158
+ msg = "Class to match #{actual_name} is not defined"
159
+ raise Taipo::SyntaxError, msg unless Object.const_defined?(actual_name)
160
+ arg.is_a? Object.const_get(actual_name)
159
161
  end
160
162
  end
161
163
 
@@ -174,7 +176,7 @@ module Taipo
174
176
  return false if !self_childless && arg_childless
175
177
 
176
178
  arg.all? do |a|
177
- if a.is_a?(Array) # The elements of this collection have components
179
+ if !arg.is_a?(Array) && a.is_a?(Array)
178
180
  a.each.with_index.reduce(nil) do |memo,(component,index)|
179
181
  result = @child_type[index].any? { |c| c.match? component }
180
182
  (memo.nil?) ? result : memo && result
@@ -201,6 +203,22 @@ module Taipo
201
203
  end
202
204
  end
203
205
 
206
+ # Check whether this element is an optional
207
+ #
208
+ # An optional type is a variation on a normal type that also matches +nil+.
209
+ # Taipo borrows the syntax used in some other languages of denoting
210
+ # optional types by appending a question mark to the end of the class name.
211
+ #
212
+ # @note This merely checks whether +@name+ ends in a question mark.
213
+ #
214
+ # @return [Boolean] the result
215
+ #
216
+ # @since 1.3.0
217
+ # @api private
218
+ def optional?
219
+ @name[-1] == '?'
220
+ end
221
+
204
222
  # Return the String representation of this TypeElement
205
223
  #
206
224
  # @since 1.1.0
@@ -8,7 +8,12 @@ module Taipo
8
8
 
9
9
  # Initialize a new collection
10
10
  #
11
- # @param components [Array<Taipo::TypeElement>] the components that will make up the ChildType
11
+ # @note The +components+ argument is two-dimensional array because the
12
+ # element returned by an enumerator for a collection can consist of
13
+ # multiple elements (eg. a Hash, where it consists of two elements).
14
+ #
15
+ # @param components [Array<Array<Taipo::TypeElement>>] the components that
16
+ # will make up the ChildType
12
17
  #
13
18
  # @since 1.0.0
14
19
  # @api private
@@ -1,3 +1,3 @@
1
1
  module Taipo
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taipo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Camilleri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-23 00:00:00.000000000 Z
11
+ date: 2018-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler