taipo 1.3.0 → 1.4.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.
@@ -0,0 +1,30 @@
1
+ module Taipo
2
+
3
+ # Refinements on core classes used in Taipo
4
+ #
5
+ # @since 1.4.0
6
+ # @api private
7
+ module Refinements
8
+
9
+ # Refinements to String
10
+ #
11
+ # @since 1.4.0
12
+ # @api private
13
+ refine String do
14
+
15
+ # Check if the string represents a bare constraint
16
+ #
17
+ # Taipo allows certain bare constraints to be written in type
18
+ # definitions. A bare constraint can be either an instance method
19
+ # or a symbol.
20
+ #
21
+ # @return [Boolean] the result
22
+ #
23
+ # @since 1.4.0
24
+ # @api private
25
+ def bare_constraint?
26
+ (self[0] == ':' && self[1] != ':') || self[0] == '#'
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,7 @@
1
1
  require 'taipo/exceptions'
2
- require 'taipo/type_element/child_type'
2
+ require 'taipo/type_elements'
3
+ require 'taipo/type_element/children'
4
+ require 'taipo/type_element/constraints'
3
5
  require 'taipo/type_element/constraint'
4
6
 
5
7
  module Taipo
@@ -16,13 +18,13 @@ module Taipo
16
18
  # @api private
17
19
  attr_accessor :name
18
20
 
19
- # The child type collection for this element
21
+ # The children for this element
20
22
  #
21
- # @since 1.0.0
23
+ # @since 1.4.0
22
24
  # @api private
23
- attr_accessor :child_type
25
+ attr_accessor :children
24
26
 
25
- # The constraint collection for this element
27
+ # The constraints for this element
26
28
  #
27
29
  # @since 1.0.0
28
30
  # @api private
@@ -31,53 +33,38 @@ module Taipo
31
33
  # Initialize a new type element
32
34
  #
33
35
  # @param name [String] the name of this type
34
- # @param child_type [Taipo::TypeElement::ChildType|NilClass] the child type
35
- # collection for this element
36
- # @param constraints [Array<Taipo::TypeElement::Constraints>|NilClass] an
37
- # array of constraints for this element
36
+ # @param children [Taipo::TypeElement::Children|NilClass] the children for
37
+ # this type
38
+ # @param constraints [Array<Taipo::TypeElement::Constraints>|NilClass] the
39
+ # constraints for this type
38
40
  #
39
- # @raise [::TypeError] if +name+, +child_type+ or +constraints+ was of the
41
+ # @raise [::TypeError] if +name+, +children+ or +constraints+ was of the
40
42
  # wrong type
41
- # @raise [::ArgumentError] if +name+, +child_type+ or +constraints+ was
42
- # blank or empty, or +child_type+ or +constraints+ was non-nil and this
43
- # is a duck type (ie. a method the type responds to) or a symbol
43
+ # @raise [::ArgumentError] if +name+, +children+ or +constraints+ was
44
+ # blank/empty
44
45
  #
45
46
  # @since 1.0.0
46
47
  # @api private
47
- def initialize(name:, child_type: nil, constraints: nil)
48
+ def initialize(name:, children: nil, constraints: nil)
48
49
  msg = 'Argument name was not a String.'
49
50
  raise ::TypeError, msg unless name.is_a? String
50
51
  msg = 'Argument name was an empty string.'
51
- raise ::ArgumentError, msg if name.empty?
52
- msg = 'Argument child_type was not Taipo::TypeElement::ChildType.'
53
- raise ::TypeError, msg unless (
54
- child_type.nil? ||
55
- child_type.is_a?(Taipo::TypeElement::ChildType)
56
- )
57
- msg = 'Argument child_type was empty.'
58
- raise ::ArgumentError, msg if child_type&.empty?
59
- msg = 'Argument constraints was not an Array.'
60
- raise ::TypeError, msg unless (constraints.nil? || constraints.is_a?(Array))
52
+ raise ::ArgumentError if name.empty?
53
+
54
+ msg = 'Argument children was not a Taipo::TypeElement::Children.'
55
+ raise ::TypeError unless children.nil? ||
56
+ children.is_a?(Taipo::TypeElement::Children)
57
+ msg = 'Argument children was empty.'
58
+ raise ::ArgumentError if !children.nil? && children.empty?
59
+
60
+ msg = 'Argument constraints was not a Taipo::TypeElement::Constraints.'
61
+ raise ::TypeError unless constraints.nil? ||
62
+ constraints.is_a?(Taipo::TypeElement::Constraints)
61
63
  msg = 'Argument constraints was empty.'
62
- raise ::ArgumentError, msg if constraints&.empty?
63
-
64
- if Taipo.instance_method?(name) || Taipo.symbol?(name)
65
- msg = 'Argument child_type should have been nil.'
66
- raise ::ArgumentError, msg unless child_type.nil?
67
- msg = 'Argument constraints should have been nil.'
68
- raise ::ArgumentError, msg unless constraints.nil?
69
-
70
- constraints = if Taipo.instance_method? name
71
- [Taipo::TypeElement::Constraint.new(name: nil,
72
- value: name[1..-1])]
73
- elsif Taipo.symbol? name
74
- [Taipo::TypeElement::Constraint.new(name: 'val',
75
- value: name)]
76
- end
77
- name = 'Object'
78
- end
64
+ raise ::ArgumentError if !constraints.nil? && constraints.empty?
65
+
79
66
  @name = name
80
- @child_type = child_type
67
+ @children = children
81
68
  @constraints = constraints
82
69
  end
83
70
 
@@ -95,21 +82,21 @@ module Taipo
95
82
  msg = 'Object to be compared must be of type Taipo::TypeElement.'
96
83
  raise ::TypeError, msg unless comp.is_a? Taipo::TypeElement
97
84
 
98
- @name == comp.name && @child_type == comp.child_type
85
+ @name == comp.name && @children == comp.children
99
86
  end
100
87
 
101
88
  # Set the element's constraints to +csts+
102
89
  #
103
- # @param csts [Array<Taipo::TypeElement::Constraint] the constraints
90
+ # @param csts [Taipo::TypeElement::Constraints] the constraints
104
91
  #
105
- # @raise [::TypeError] if +csts+ was not an Array
92
+ # @raise [::TypeError] if +csts+ was not a Taipo::TypeElement::Constraints
106
93
  # @raise [Taipo::SyntaxError] if there are constraints with the same name
107
94
  #
108
95
  # @since 1.0.0
109
96
  # @api private
110
97
  def constraints=(csts)
111
- msg = 'Argument csts was not an Array.'
112
- raise ::TypeError, msg unless csts.is_a? Array
98
+ msg = 'Argument csts was not a Taipo::TypeElement::Constraints.'
99
+ raise ::TypeError, msg unless csts.is_a? Taipo::TypeElement::Constraints
113
100
 
114
101
  names = Hash.new
115
102
  csts.each do |c|
@@ -137,7 +124,8 @@ module Taipo
137
124
  # @api private
138
125
  def match?(arg)
139
126
  return true if optional? && arg.nil?
140
- match_class?(arg) && match_constraints?(arg) && match_child_type?(arg)
127
+
128
+ match_class?(arg) && match_constraints?(arg) && match_children?(arg)
141
129
  end
142
130
 
143
131
  # Check if the class of the argument itself matches this element
@@ -161,16 +149,16 @@ module Taipo
161
149
  end
162
150
  end
163
151
 
164
- # Check if the class of the argument's child type matches
152
+ # Check if the class of the argument's children match
165
153
  #
166
154
  # @param arg [Object] the argument to compare
167
155
  #
168
156
  # @return [Boolean] the result
169
157
  #
170
- # @since 1.0.0
158
+ # @since 1.4.0
171
159
  # @api private
172
- def match_child_type?(arg)
173
- self_childless = @child_type.nil?
160
+ def match_children?(arg)
161
+ self_childless = @children.nil?
174
162
  arg_childless = !arg.is_a?(Enumerable) || arg.count == 0
175
163
  return true if self_childless
176
164
  return false if !self_childless && arg_childless
@@ -178,11 +166,11 @@ module Taipo
178
166
  arg.all? do |a|
179
167
  if !arg.is_a?(Array) && a.is_a?(Array)
180
168
  a.each.with_index.reduce(nil) do |memo,(component,index)|
181
- result = @child_type[index].any? { |c| c.match? component }
169
+ result = @children[index].any? { |c| c.match? component }
182
170
  (memo.nil?) ? result : memo && result
183
171
  end
184
172
  else # The elements of this collection have no components
185
- @child_type.first.any? { |c| c.match? a }
173
+ @children.first.any? { |c| c.match? a }
186
174
  end
187
175
  end
188
176
  end
@@ -198,9 +186,7 @@ module Taipo
198
186
  def match_constraints?(arg)
199
187
  return true if @constraints.nil?
200
188
 
201
- @constraints.all? do |c|
202
- c.constrain?(arg)
203
- end
189
+ @constraints.all? { |c| c.constrain?(arg) }
204
190
  end
205
191
 
206
192
  # Check whether this element is an optional
@@ -209,7 +195,8 @@ module Taipo
209
195
  # Taipo borrows the syntax used in some other languages of denoting
210
196
  # optional types by appending a question mark to the end of the class name.
211
197
  #
212
- # @note This merely checks whether +@name+ ends in a question mark.
198
+ # @note This merely checks whether {Taipo::TypeElement#name} ends in a
199
+ # question mark.
213
200
  #
214
201
  # @return [Boolean] the result
215
202
  #
@@ -221,20 +208,15 @@ module Taipo
221
208
 
222
209
  # Return the String representation of this TypeElement
223
210
  #
211
+ # @return [String] the representation as a String
212
+ #
224
213
  # @since 1.1.0
225
214
  # @api private
226
215
  def to_s
227
216
  name_str = @name
228
- child_type_str = (@child_type.nil?) ? '' : @child_type.to_s
229
- constraints_str = if @constraints.nil?
230
- ''
231
- else
232
- inner = @constraints.reduce('') do |memo,c|
233
- (memo == '') ? c.to_s : memo + ',' + c.to_s
234
- end
235
- '(' + inner + ')'
236
- end
237
- name_str + child_type_str + constraints_str
217
+ children_str = (@children.nil?) ? '' : @children.to_s
218
+ constraints_str = (@constraints.nil?) ? '' : @constraints.to_s
219
+ name_str + children_str + constraints_str
238
220
  end
239
221
  end
240
222
  end
@@ -0,0 +1,47 @@
1
+ require 'taipo/type_elements'
2
+
3
+ module Taipo
4
+ class TypeElement
5
+
6
+ # A set of {Taipo::TypeElements} representing the types of the children of
7
+ # a collection type
8
+ #
9
+ # @since 1.4.0
10
+ # @api private
11
+ class Children < Array
12
+
13
+ # Initialize a new set of children
14
+ #
15
+ # @note The +children+ argument is an array of {Taipo::TypeElements}
16
+ # because the element returned by an enumerator for a collection can
17
+ # consist of multiple components (eg. in a Hash, where it consists of
18
+ # two elements).
19
+ #
20
+ # @param children [Array<Taipo::TypeElements>] the components that make
21
+ # up the children of the collection
22
+ #
23
+ # @since 1.4.0
24
+ # @api private
25
+ def initialize(children = nil)
26
+ children&.each { |c| self.push c }
27
+ end
28
+
29
+ # Return the String representation of this object
30
+ #
31
+ # @return [String] the representation as a String
32
+ #
33
+ # @since 1.4.0
34
+ # @api private
35
+ def to_s
36
+ inner = self.reduce(nil) do |memo_e,component|
37
+ el = component.reduce(nil) do |memo_c,c|
38
+ (memo_c.nil?) ? c.to_s : memo_c + '|' + c.to_s
39
+ end
40
+ (memo_e.nil?) ? el : memo_e + ',' + el
41
+ end
42
+ '<' + inner + '>'
43
+ end
44
+ end
45
+ end
46
+ end
47
+
@@ -6,7 +6,7 @@ module Taipo
6
6
  # @since 1.0.0
7
7
  # @api private
8
8
  class Constraint
9
-
9
+
10
10
  # The identifier for an instance method
11
11
  #
12
12
  # @since 1.0.0
@@ -27,30 +27,24 @@ module Taipo
27
27
 
28
28
  # Initialize a new constraint
29
29
  #
30
- # @param name [String|NilClass] the name of the constraint (if nil, this
30
+ # @param name [String|NilClass] the name of the constraint (if nil, this
31
31
  # is an instance method)
32
32
  # @param value [String|NilClass] the value of the constraint (sometimes a
33
33
  # Constraint is initialized before the value is known)
34
34
  #
35
- # @raise [::TypeError] if +name+ or +value+ were of the wrong type, or
36
- # +value+ was not of the correct type for the type of constraint
35
+ # @raise [::TypeError] if +name+ was the wrong type, or
36
+ # +value+ was not of the correct type for the type of constraint
37
37
  # @raise [::ArgumentError] if +name+ was blank
38
38
  #
39
39
  # @since 1.0.0
40
- # @api private
40
+ # @api private
41
41
  def initialize(name: nil, value: nil)
42
42
  msg = 'Argument name was not nil or a String.'
43
43
  raise ::TypeError, msg unless name.nil? || name.is_a?(String)
44
44
  msg = 'Argument name was an empty string.'
45
- raise ::ArgumentError, msg if name&.empty?
45
+ raise ::ArgumentError, msg if !name.nil? && name.empty?
46
46
 
47
- @name = if name.nil?
48
- Constraint::METHOD
49
- elsif name == ':'
50
- 'val'
51
- else
52
- name
53
- end
47
+ @name = name
54
48
  @value = self.parse_value value
55
49
  end
56
50
 
@@ -97,6 +91,8 @@ module Taipo
97
91
  #
98
92
  # @param v [Object] the value
99
93
  #
94
+ # @return [Object] the parsed value
95
+ #
100
96
  # @raise [::TypeError] if the value is not appropriate for this type of
101
97
  # constraint
102
98
  #
@@ -125,7 +121,7 @@ module Taipo
125
121
 
126
122
  # Return the String representation of this constraint
127
123
  #
128
- # @return [String] the String representation
124
+ # @return [String] the representation as a String
129
125
  #
130
126
  # @since 1.0.0
131
127
  # @api private
@@ -144,7 +140,7 @@ module Taipo
144
140
 
145
141
  # Set +v+ to be the value for this constraint
146
142
  #
147
- # @param v [Object] the value to set (this will be parsed using
143
+ # @param v [Object] the value to set (this will be parsed using
148
144
  # {#parse_value})
149
145
  #
150
146
  # @raise [::TypeError] if the value is not appropriate for this type of
@@ -0,0 +1,36 @@
1
+ require 'taipo/type_element/constraint'
2
+
3
+ module Taipo
4
+ class TypeElement
5
+
6
+ # A set of {Taipo::TypeElement::Constraint} objects
7
+ #
8
+ # @since 1.4.0
9
+ # @api private
10
+ class Constraints < Array
11
+
12
+ # Initialize a new set of {Taipo::TypeElement::Constraint}
13
+ #
14
+ # @param els [Array<Taipo::TypeElement::Constraint>] the constraints
15
+ #
16
+ # @since 1.4.0
17
+ # @api private
18
+ def initialize(constraints = nil)
19
+ constraints&.each { |c| self.push c }
20
+ end
21
+
22
+ # Return the String representation of this object
23
+ #
24
+ # @return [String] the representation as a String
25
+ #
26
+ # @since 1.4.0
27
+ # @api private
28
+ def to_s
29
+ inner = self.reduce('') do |memo,c|
30
+ (memo == '') ? c.to_s : memo + ',' + c.to_s
31
+ end
32
+ '(' + inner + ')'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ require 'taipo/type_element'
2
+
3
+ module Taipo
4
+
5
+ # A set of {Taipo::TypeElement} objects
6
+ #
7
+ # @since 1.4.0
8
+ # @api private
9
+ class TypeElements < Array
10
+
11
+ # Initialize a new set of {Taipo::TypeElement}
12
+ #
13
+ # @param els [Array<Taipo::TypeElement>] the elements
14
+ #
15
+ # @since 1.4.0
16
+ # @api private
17
+ def initialize(els = nil)
18
+ els&.each { |el| self.push el }
19
+ end
20
+
21
+ # Return the String representation of this object
22
+ #
23
+ # @return [String] the representation as a String
24
+ #
25
+ # @since 1.4.0
26
+ # @api private
27
+ def to_s
28
+ self.reduce('') do |memo,el|
29
+ (memo == '') ? el.to_s : memo + '|' + el.to_s
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,81 @@
1
+ module Taipo
2
+
3
+ # Utility methods for Taipo
4
+ #
5
+ # @since 1.4.0
6
+ # @api private
7
+ module Utilities
8
+
9
+ # Check if a string is the name of an instance method
10
+ #
11
+ # @note All this does is check whether the given string begins with a hash
12
+ # symbol.
13
+ #
14
+ # @param str [String] the string to check
15
+ #
16
+ # @return [Boolean] the result
17
+ #
18
+ # @since 1.4.0
19
+ # @api private
20
+ def self.instance_method?(str)
21
+ str[0] == '#'
22
+ end
23
+
24
+ # Return the type definition for an object
25
+ #
26
+ # @note This assume that each element returned by Enumerator#each has the same
27
+ # number of components.
28
+ #
29
+ # @param obj [Object] the object
30
+ #
31
+ # @return [String] a type definition of the object
32
+ #
33
+ # @since 1.4.0
34
+ # @api private
35
+ def self.object_to_type_def(obj)
36
+ return obj.class.name unless obj.is_a? Enumerable
37
+
38
+ if obj.is_a? Array
39
+ element_types = Hash.new
40
+ obj.each { |o| element_types[self.object_to_type_def(o)] = true }
41
+ if element_types.empty?
42
+ obj.class.name
43
+ else
44
+ obj.class.name + '<' + element_types.keys.join('|') + '>'
45
+ end
46
+ else
47
+ element_types = Array.new
48
+ obj.each.with_index do |element,index_e|
49
+ element.each.with_index do |component,index_c|
50
+ element_types[index_c] = Hash.new if index_e == 0
51
+ c_type = self.object_to_type_def(component)
52
+ element_types[index_c][c_type] = true
53
+ end
54
+ end
55
+ inner = element_types.reduce('') do |memo,e|
56
+ e_type = e.keys.join('|')
57
+ (memo == '') ? e_type : memo + ',' + e_type
58
+ end
59
+ if element_types.empty?
60
+ obj.class.name
61
+ else
62
+ obj.class.name + '<' + inner + '>'
63
+ end
64
+ end
65
+ end
66
+
67
+ # Check if a string is the name of a symbol
68
+ #
69
+ # @note All this does is check whether the given string begins with a colon.
70
+ #
71
+ # @param str [String] the string to check
72
+ #
73
+ # @return [Boolean] the result
74
+ #
75
+ # @since 1.4.0
76
+ # @api private
77
+ def self.symbol?(str)
78
+ str[0] == ':'
79
+ end
80
+ end
81
+ end