taipo 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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