taipo 1.2.0 → 1.3.0

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