taipo 1.4.0 → 1.5.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: cb7fcacea028acafa7344ccfc04fbc3c66e50c13
4
- data.tar.gz: 052b196e3ed53b4ade6d95abee2b57a266c12a9f
3
+ metadata.gz: f2d4388512ddb3fd352644adc19c2d597617c1ba
4
+ data.tar.gz: 424f50365c4c88bf5d3d511cf66bfdf59431504c
5
5
  SHA512:
6
- metadata.gz: 6cced24c850830e10102f010b0b098ff5ed7ebab426f10cee4b51dcdf1ff29622721e75228b9fc4be9f6fa35b8e4917e14961254a4dbfddb0b8110969aad3b0a
7
- data.tar.gz: 4e9bd2e042ef2f87808bbdab7c10657d96f75bb969aa725e0b02666e4160a433eb3279b601c232c87f4fce37163b23204d504a31a8307ee0649b7846ff510de4
6
+ metadata.gz: 13da1bffba5a77cc4a1c2040c0fe9d6ef0112c69968c30b89ca72bcff068f303593dbdc6ed276056571ae037a3170c41458e77911f3e0227527ddd9d8cef6efc
7
+ data.tar.gz: 167bffef5e9d7f24f0af153b3dfbb44d655067f0b1223a1505447e52e9cce84ef00895d60b4eddacdaedf85669296d7c504396a47e274f977288b146a343a9c0
@@ -3,7 +3,7 @@ env:
3
3
  - CC_TEST_REPORTER_ID=787a2f89b15c637323c7340d65ec17e898ac44480706b4b4122ea040c2a88f1d
4
4
  language: ruby
5
5
  rvm:
6
- - 2.4.1
6
+ - 2.4.2
7
7
  before_script:
8
8
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
9
9
  - chmod +x ./cc-test-reporter
data/README.md CHANGED
@@ -22,7 +22,10 @@ Run `gem install taipo` or add `gem 'taipo'` to your `Gemfile`.
22
22
 
23
23
  ## Usage
24
24
 
25
- Taipo provides two methods in the `Taipo::Check` module that we can mix into our classes: `#check` and `#review`.
25
+ Taipo can be used to check variables within a given context or check the return value of a given method:
26
+
27
+ * the `Taipo::Check` module provides two methods: `#check` and `#review`:
28
+ * the `Taipo::Result` module provides one method: `.result`.
26
29
 
27
30
  ### #check
28
31
 
@@ -78,6 +81,32 @@ The method `#review` will put the invalid arguments into an array and return tha
78
81
 
79
82
  [rdr]: http://www.rubydoc.info/gems/taipo/Taipo/Check#review-instance_method
80
83
 
84
+ ### .result
85
+
86
+ ```ruby
87
+ require 'taipo'
88
+
89
+ class Foo
90
+ include Taipo::Result
91
+
92
+ result :add, 'Integer'
93
+
94
+ def add(x, y)
95
+ x + y
96
+ end
97
+ end
98
+
99
+ foo = Foo.new
100
+ foo.add 4, 5 #=> 9
101
+ foo.add 'hello', 'world' #=> Taipo::TypeError
102
+ ```
103
+
104
+ The method `.result` will raise an exception if the return value of the specified method doesn't match the specified type definition.
105
+
106
+ [More information about `.result`][rde] is available in the documentation.
107
+
108
+ [rde]: http://www.rubydoc.info/gems/taipo/Taipo/Result/ClassMethods#result-instance_method
109
+
81
110
  ## Syntax
82
111
 
83
112
  Type definitions are written as Strings. Type definitions can consist of (1) names, (2) collections, (3) constraints and (4) sums.
@@ -1,33 +1,26 @@
1
1
  require 'taipo/version'
2
2
  require 'taipo/check'
3
- require 'taipo/parser'
3
+ require 'taipo/result'
4
4
 
5
5
  # A library for checking the types of objects
6
6
  #
7
- # Taipo is primarily intended as a replacement for cumbersome, error-prone
8
- # guard statements a user can put in their code to ensure that the variables
9
- # they are handling conform to expectations.
7
+ # Taipo is primarily intended as a replacement for verbose, error-prone guard
8
+ # statements. With Taipo, a user can ensure that the objects they are working
9
+ # with conform to expectations.
10
10
  #
11
- # By including the module {Taipo::Check}, a user can call the
12
- # {Taipo::Check#check} or {Taipo::Check#review} methods in their classes
13
- # whenever a guard statement is necessary. Expectations are written as type
14
- # definitions. A type definition contains the name of the type and the type
15
- # definitions of any elements it contains (if it is enumerable). Optional
16
- # constraints may be specified and sum types are also possible. See
17
- # {Taipo::Parser::Validater} for the full syntax.
11
+ # Taipo consists of two user-facing parts: {Taipo::Check} and {Taipo::Result}.
12
+ #
13
+ # * By including the module {Taipo::Check}, a user can call
14
+ # {Taipo::Check#check} or {Taipo::Check#review} in their methods to
15
+ # check the types of one or more given variables.
18
16
  #
19
- # Taipo works by:
20
- # 1. extracting the values of the arguments to be checked from a Binding;
21
- # 2. transforming the type definitions provided as Strings into an array of
22
- # {Taipo::TypeElement} instances; and
23
- # 3. checking whether the argument's value matches any of the instances of
24
- # {Taipo::TypeElement} in the array.
17
+ # * By including the module {Taipo::Result}, a user can call
18
+ # {Taipo::Result::ClassMethods#result} in their class definitions to check the
19
+ # return values of a given method.
25
20
  #
26
- # As syntactic sugar, the {Taipo::Check} module will by default alias
27
- # +Kernel#binding+ with the keyword +types+. This allows the user to call
28
- # {Taipo::Check#check} by writing +check types+ (with a similar syntax for
29
- # {Taipo::Check#review}). If the user does not want to alias, they can set
30
- # {Taipo.alias=} to +false+ before including or extending {Taipo::Check}.
21
+ # Taipo provides a rich syntax to express type definitions. This includes
22
+ # classes, collections, optionals and duck types. See
23
+ # {Taipo::Parser::Validater} for the full syntax.
31
24
  #
32
25
  # @since 1.0.0
33
26
  # @see https://github.com/pyrmont/taipo
@@ -1,6 +1,6 @@
1
1
  module Taipo
2
2
 
3
- # A cache of Taipo::TypeElement created from parsed type definitions
3
+ # A cache of {Taipo::TypeElement} objects created from parsed type definitions
4
4
  #
5
5
  # @since 1.0.0
6
6
  # @api private
@@ -12,7 +12,8 @@ module Taipo
12
12
  # @api private
13
13
  @@Cache = {}
14
14
 
15
- # Retrieve the Taipo::TypeElement object described by the type definition from the cache
15
+ # Retrieve the {Taipo::TypeElement} object described by the type definition
16
+ # from the cache
16
17
  #
17
18
  # @param k [String] the type definition
18
19
  #
@@ -25,7 +26,8 @@ module Taipo
25
26
  @@Cache[k]
26
27
  end
27
28
 
28
- # Save the Taipo::TypeElement object described by the type definition in the cache
29
+ # Save the {Taipo::TypeElement} object described by the type definition in
30
+ # the cache
29
31
  #
30
32
  # @param k [String] the type definition
31
33
  # @param v [Taipo::TypeElement] the object to be saved
@@ -1,13 +1,21 @@
1
- require 'taipo/cache'
2
- require 'taipo/exceptions'
3
- require 'taipo/parser'
4
- require 'taipo/type_elements'
5
- require 'taipo/type_element'
6
1
  require 'taipo/utilities'
7
2
 
8
3
  module Taipo
9
4
 
10
- # A dedicated namespace for methods meant to be included by the user
5
+ # A simple DSL for declaring type checks to run against specified variables
6
+ #
7
+ # {Taipo::Check} works by:
8
+ # 1. extracting the values of the arguments to be checked from a Binding;
9
+ # 2. transforming the type definitions provided as Strings into an array of
10
+ # {Taipo::TypeElement} instances; and
11
+ # 3. checking whether the argument's value matches any of the instances of
12
+ # {Taipo::TypeElement} in the array.
13
+ #
14
+ # As syntactic sugar, the {Taipo::Check} module will by default alias
15
+ # +Kernel#binding+ with the keyword +types+. This allows the user to call
16
+ # {Taipo::Check#check} by writing +check types+ (with a similar syntax for
17
+ # {Taipo::Check#review}). If the user does not want to alias, they can set
18
+ # {Taipo.alias=} to +false+ before including or extending {Taipo::Check}.
11
19
  #
12
20
  # @since 1.0.0
13
21
  module Check
@@ -70,35 +78,14 @@ module Taipo
70
78
  raise ::TypeError, msg unless context.is_a? Binding
71
79
 
72
80
  checks.reduce(Array.new) do |memo,(k,v)|
73
- arg = if k[0] == '@' && self.instance_variable_defined?(k)
74
- self.instance_variable_get k
75
- elsif k[0] != '@' && context.local_variable_defined?(k)
76
- context.local_variable_get k
77
- else
78
- msg = "Argument '#{k}' is not defined."
79
- raise Taipo::NameError, msg
80
- end
81
-
82
- types = if hit = Taipo::Cache[v]
83
- hit
84
- else
85
- Taipo::Cache[v] = Taipo::Parser.parse v
86
- end
81
+ arg = Taipo::Utilities.extract_variable(name: k,
82
+ object: self,
83
+ context: context)
87
84
 
88
- is_match = types.any? { |t| t.match? arg }
85
+ is_match = Taipo::Utilities.match? object: arg, definition: v
89
86
 
90
87
  unless collect_invalids || is_match
91
- if Taipo::Utilities.instance_method? v
92
- msg = "Object '#{k}' does not respond to #{v}."
93
- elsif Taipo::Utilities.symbol? v
94
- msg = "Object '#{k}' is not equal to #{v}."
95
- elsif arg.is_a? Enumerable
96
- type_def = Taipo::Utilities.object_to_type_def arg
97
- msg = "Object '#{k}' is #{type_def} but expected #{v}."
98
- else
99
- msg = "Object '#{k}' is #{arg.class.name} but expected #{v}."
100
- end
101
- raise Taipo::TypeError, msg
88
+ Taipo::Utilities.throw_error object: arg, name: k, definition: v
102
89
  end
103
90
 
104
91
  (is_match) ? memo : memo.push(k)
@@ -1,12 +1,7 @@
1
- require 'taipo/exceptions'
1
+ require 'taipo/cache'
2
2
  require 'taipo/parser/stack'
3
3
  require 'taipo/parser/validater'
4
4
  require 'taipo/refinements'
5
- require 'taipo/type_elements'
6
- require 'taipo/type_element'
7
- require 'taipo/type_element/children'
8
- require 'taipo/type_element/constraints'
9
- require 'taipo/type_element/constraint'
10
5
 
11
6
  module Taipo
12
7
 
@@ -19,57 +14,24 @@ module Taipo
19
14
 
20
15
  # Return a Taipo::TypeElements object based on +str+
21
16
  #
17
+ # This method acts as a wrapping method to {Taipo::Parser.parse_definition}.
18
+ # It first checks if the type definition has already been parsed and is in
19
+ # Taipo's cache.
20
+ #
22
21
  # @param str [String] a type definition
23
22
  #
24
- # @return [Taipo:TypeElements] the result
23
+ # @return [Taipo::TypeElements] the result
25
24
  #
26
25
  # @raise [::TypeError] if +str+ is not a String
27
26
  # @raise [Taipo::SyntaxError] if +str+ is not a valid type definition
28
27
  #
29
28
  # @since 1.0.0
30
29
  def self.parse(str)
31
- Taipo::Parser::Validater.validate str
32
-
33
- stack = Taipo::Parser::Stack.new
34
- i = 0
35
- subject = :implied
36
- chars = str.chars
37
- content = ''
38
-
39
- while (i < chars.size)
40
- reset = true
41
-
42
- case chars[i]
43
- when ' '
44
- i += 1
45
- next
46
- when '|'
47
- stack = process_sum stack, name: content
48
- subject = :implied
49
- when '<'
50
- stack = process_collection :open, stack, name: content
51
- subject = :implied
52
- when '>'
53
- stack = process_collection :close, stack, name: content
54
- subject = :made
55
- when ','
56
- stack = process_component stack, name: content
57
- subject = :implied
58
- when '('
59
- stack = process_subject stack, name: content, subject: subject
60
- stack, i = process_constraints stack, chars: chars, index: i+1
61
- else
62
- reset = false
63
- subject = :unmade
64
- end
65
-
66
- content = (reset) ? '' : content + chars[i]
67
- i += 1
30
+ if hit = Taipo::Cache[str]
31
+ hit
32
+ else
33
+ Taipo::Cache[str] = Taipo::Parser.parse_definition str
68
34
  end
69
-
70
- stack = process_end stack, name: content
71
-
72
- stack.result
73
35
  end
74
36
 
75
37
  # Check whether the character should be skipped
@@ -128,7 +90,7 @@ module Taipo
128
90
  content = ''
129
91
  str.each_char do |c|
130
92
  if c == '#' && in_name.nil?
131
- name = Taipo::TypeElement::Constraint::METHOD
93
+ name = '#'
132
94
  in_name = false
133
95
  elsif c == ':' && in_name.nil?
134
96
  name = 'val'
@@ -147,6 +109,60 @@ module Taipo
147
109
  return name, value
148
110
  end
149
111
 
112
+ # Return a Taipo::TypeElements object based on +str+
113
+ #
114
+ # @param str (see {Taipo::Parser.parse})
115
+ #
116
+ # @return (see {Taipo::Parser.parse})
117
+ #
118
+ # @raise (see {Taipo::Parser.parse})
119
+ #
120
+ # @since 1.5.0
121
+ # @api private
122
+ def self.parse_definition(str)
123
+ Taipo::Parser::Validater.validate str
124
+
125
+ stack = Taipo::Parser::Stack.new
126
+ i = 0
127
+ subject = :implied
128
+ chars = str.chars
129
+ content = ''
130
+
131
+ while (i < chars.size)
132
+ reset = true
133
+
134
+ case chars[i]
135
+ when ' '
136
+ i += 1
137
+ next
138
+ when '|'
139
+ stack = process_sum stack, name: content
140
+ subject = :implied
141
+ when '<'
142
+ stack = process_collection :open, stack, name: content
143
+ subject = :implied
144
+ when '>'
145
+ stack = process_collection :close, stack, name: content
146
+ subject = :made
147
+ when ','
148
+ stack = process_component stack, name: content
149
+ subject = :implied
150
+ when '('
151
+ stack = process_subject stack, name: content, subject: subject
152
+ stack, i = process_constraints stack, chars: chars, index: i+1
153
+ else
154
+ reset = false
155
+ subject = :unmade
156
+ end
157
+
158
+ content = (reset) ? '' : content + chars[i]
159
+ i += 1
160
+ end
161
+
162
+ stack = process_end stack, name: content
163
+ stack.result
164
+ end
165
+
150
166
  # Process a collection
151
167
  #
152
168
  # This method either adds or updates a collection on +stack+ depending on
@@ -210,7 +226,7 @@ module Taipo
210
226
  # @api private
211
227
  def self.process_constraint(stack, raw:)
212
228
  n, v = parse_constraint raw
213
- stack.add_constraint Taipo::TypeElement::Constraint.new(name: n, value: v)
229
+ stack.add_constraint name: n, value: v
214
230
  end
215
231
 
216
232
  # Process a series of constraints
@@ -112,8 +112,9 @@ module Taipo
112
112
  #
113
113
  # @since 1.4.0
114
114
  # @api private
115
- def add_constraint(constraint)
116
- self.last.push constraint
115
+ def add_constraint(name:, value:)
116
+ self.last.push Taipo::TypeElement::Constraint.new(name: name,
117
+ value: value)
117
118
  self
118
119
  end
119
120
 
@@ -18,8 +18,7 @@ module Taipo
18
18
  #
19
19
  # The validater does not check whether the name represents a valid name in
20
20
  # the current context nor does it check whether the name complies with
21
- # Ruby's requirements for names. Either situation will cause an exception
22
- # to be raised by {Taipo::Check#check} or {Taipo::Check#review}.
21
+ # Ruby's requirements for names.
23
22
  #
24
23
  # One special case is where the name is left blank. The validater will
25
24
  # accept this as valid. {Taipo::Parser} will implictly add the name
@@ -32,7 +31,7 @@ module Taipo
32
31
  #
33
32
  # As noted above, duck types can be specified by using a blank name. Duck
34
33
  # types are really constraints (discussed in further detail below) on the
35
- # class Object. While normally constraints need to be enclosed in
34
+ # class +Object+. While normally constraints need to be enclosed in
36
35
  # parentheses, if there is a blank name and only one method constraint, the
37
36
  # parentheses can be omitted. For defining duck types that respond to
38
37
  # multiple methods, the parentheses are required.
@@ -43,10 +42,10 @@ module Taipo
43
42
  #
44
43
  # It is possible to specify an 'optional' type by appending a question mark
45
44
  # to the name of the type. This shorthand functions similarly to defining a
46
- # sum type with NilClass (the implementation of how optional types are
45
+ # sum type with +NilClass+ (the implementation of how optional types are
47
46
  # checked is slightly different, however; see {Taipo::TypeElement#match?}).
48
47
  # It is not possible to define an optional duck type. For that, either the
49
- # implicit Object class should be specified (and then made optional), or a
48
+ # implicit +Object+ class should be specified (and then made optional), or a
50
49
  # sum type should be used.
51
50
  #
52
51
  # === Collections
@@ -62,8 +61,8 @@ module Taipo
62
61
  # definition for the child comes immediately after the opening angle
63
62
  # bracket.
64
63
  #
65
- # If +Enumerator#each+ returns multiple values (eg. such as with Hash), the
66
- # type definition for each value is delimited by a comma. It is optional
64
+ # If +Enumerator#each+ returns multiple values (eg. such as with +Hash+),
65
+ # the type definition for each value is delimited by a comma. It is optional
67
66
  # whether a space follows the comma.
68
67
  #
69
68
  # The type definition for a child element can contain all the components of
@@ -123,7 +122,7 @@ module Taipo
123
122
  # It's possible to approximate the enum idiom available in many languages
124
123
  # by creating a sum type consisting of Symbols. As a convenience, Taipo
125
124
  # parses these values as constraints on the Object class. In other words,
126
- # +':foo|:bar'+ is really +'Object(val: :foo)|Object(val: :bar)'+.
125
+ # the +:foo|:bar+ is really +Object(val: :foo)|Object(val: :bar)+.
127
126
  #
128
127
  # @since 1.0.0
129
128
  module Validater
@@ -0,0 +1,102 @@
1
+ require 'taipo/utilities'
2
+
3
+ module Taipo
4
+
5
+ # A simple DSL for declaring type checks to run against the return values of
6
+ # specified instance methods
7
+ #
8
+ # {Taipo::Result} works by:
9
+ # 1. adding the {Taipo::Result::ClassMethods#result} method to the including
10
+ # class;
11
+ # 2. prepending a module to the ancestor chain for the including class; and
12
+ # 3. defining a method on the ancestor with the same name as a particular
13
+ # instance method on the class and using that method to intercept method
14
+ # calls and check the return type.
15
+ #
16
+ # Because of how {Taipo::Result} makes the +result+ keyword available to
17
+ # classes, the documentation for this method is in
18
+ # {Taipo::Result::ClassMethods}.
19
+ #
20
+ # @since 1.5.0
21
+ module Result
22
+
23
+ # Add the {Taipo::Result::ClassMethods#result} method to the class including
24
+ # this module as well as prepend a module to the ancestor chain.
25
+ #
26
+ # @param base [Class] the class including this module
27
+ #
28
+ # @since 1.5.0
29
+ # @api private
30
+ def self.included(base)
31
+ base.extend ClassMethods
32
+ module_name = "#{base.class.name}Checker"
33
+ checker = const_defined?(module_name) ? const_get(module_name) :
34
+ const_set(module_name, Module.new)
35
+ base.prepend checker
36
+ end
37
+
38
+ # A helper module for holding the DSL methods
39
+ #
40
+ # @since 1.5.0
41
+ # @api private
42
+ module ClassMethods
43
+
44
+ # The DSL method used to declare a type check on the return value of a
45
+ # method. The intention is that a user will declare the type of result
46
+ # expected from a method by calling this method in the body of a class
47
+ # definition.
48
+ #
49
+ # For purposes of readability, the convention is that
50
+ # {Taipo::Result::ClassMethods#result} will be called near the beginning
51
+ # of the class definition but this is not a requirement and the user is
52
+ # free to call the method immediately before or after the relevant method
53
+ # definition.
54
+ #
55
+ # @param method_name [Symbol] the name of the instance method
56
+ # @param type [String] a type definition
57
+ #
58
+ # @since 1.5.0
59
+ # @api public
60
+ #
61
+ # @example
62
+ # require 'taipo'
63
+ #
64
+ # class A
65
+ # include Taipo::Result
66
+ #
67
+ # result :foo, 'String'
68
+ # result :bar, 'Integer'
69
+ #
70
+ # def foo(arg)
71
+ # arg.to_s
72
+ # end
73
+ #
74
+ # def bar(arg)
75
+ # arg.to_s
76
+ # end
77
+ # end
78
+ #
79
+ # a = A.new
80
+ # a.foo 'Hello world!' #=> "Hello world!"
81
+ # a.bar 42 #=> Taipo::TypeError
82
+ def result(method_name, type)
83
+ base = self
84
+ checker = const_get "#{base.class.name}Checker"
85
+ checker.class_eval do
86
+ define_method(method_name) do |*args, &block|
87
+ method_return_value = super(*args, &block)
88
+ if Taipo::Utilities.match?(object: method_return_value,
89
+ definition: type)
90
+ method_return_value
91
+ else
92
+ Taipo::Utilities.throw_error(object: method_return_value,
93
+ name: "#{base.name}##{method_name}",
94
+ definition: type,
95
+ result: true)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,3 @@
1
- require 'taipo/type_elements'
2
-
3
1
  module Taipo
4
2
  class TypeElement
5
3
 
@@ -1,5 +1,3 @@
1
- require 'taipo/type_element/constraint'
2
-
3
1
  module Taipo
4
2
  class TypeElement
5
3
 
@@ -11,7 +9,8 @@ module Taipo
11
9
 
12
10
  # Initialize a new set of {Taipo::TypeElement::Constraint}
13
11
  #
14
- # @param els [Array<Taipo::TypeElement::Constraint>] the constraints
12
+ # @param constraints [Array<Taipo::TypeElement::Constraint>] the
13
+ # constraints
15
14
  #
16
15
  # @since 1.4.0
17
16
  # @api private
@@ -1,3 +1,6 @@
1
+ require 'taipo/exceptions'
2
+ require 'taipo/parser'
3
+
1
4
  module Taipo
2
5
 
3
6
  # Utility methods for Taipo
@@ -6,6 +9,29 @@ module Taipo
6
9
  # @api private
7
10
  module Utilities
8
11
 
12
+ # Return a named variable from either an object or a binding
13
+ #
14
+ # @param name [String] the name of the variable
15
+ # @param object [Object] the object in which the variable may exist
16
+ # @param context [Binding] the binding in which the variable may exist
17
+ #
18
+ # @return [Object] the variable
19
+ #
20
+ # @raise [Taipo::NameError] if no variable with +name+ exists
21
+ #
22
+ # @since 1.5.0
23
+ # @api private
24
+ def self.extract_variable(name:, object:, context:)
25
+ if name[0] == '@' && object.instance_variable_defined?(name)
26
+ object.instance_variable_get name
27
+ elsif name[0] != '@' && context.local_variable_defined?(name)
28
+ context.local_variable_get name
29
+ else
30
+ msg = "Argument '#{name}' is not defined."
31
+ raise Taipo::NameError, msg
32
+ end
33
+ end
34
+
9
35
  # Check if a string is the name of an instance method
10
36
  #
11
37
  # @note All this does is check whether the given string begins with a hash
@@ -21,6 +47,27 @@ module Taipo
21
47
  str[0] == '#'
22
48
  end
23
49
 
50
+ # Check if an object matches a given type definition
51
+ #
52
+ # @param object [Object] the object to check
53
+ # @param definition [String] the type definiton to check against
54
+ #
55
+ # @return [Boolean] the result
56
+ #
57
+ # @raise [::TypeError] if +definition+ is not a String
58
+ # @raise [Taipo::SyntaxError] if the type definitions in +checks+ are
59
+ # invalid
60
+ #
61
+ # @since 1.5.0
62
+ # @api private
63
+ def self.match?(object:, definition:)
64
+ msg = "The 'definition' argument must be of type String."
65
+ raise ::TypeError, msg unless definition.is_a? String
66
+
67
+ types = Taipo::Parser.parse definition
68
+ types.any? { |t| t.match? object }
69
+ end
70
+
24
71
  # Return the type definition for an object
25
72
  #
26
73
  # @note This assume that each element returned by Enumerator#each has the same
@@ -77,5 +124,37 @@ module Taipo
77
124
  def self.symbol?(str)
78
125
  str[0] == ':'
79
126
  end
127
+
128
+ # Throw an error with an appropriate message for a given object not matching
129
+ # a given type definition
130
+ #
131
+ # @param object [Object] the object that does not match +definition+
132
+ # @param name [String] the name of the object (with the code that originally
133
+ # called the #check method)
134
+ # @param definition [String] the type definition that does not match +object+
135
+ # @param result [Boolean] whether this is being called in respect of a
136
+ # return value (such as by {Taipo::Result::ClassMethods#result})
137
+ #
138
+ # @raise [Taipo::TypeError] the error
139
+ #
140
+ # @since 1.5.0
141
+ # @api private
142
+ def self.throw_error(object:, name:, definition:, result: false)
143
+ subject = (result) ? "The return value of #{name}" : "Object '#{name}'"
144
+
145
+ if Taipo::Utilities.instance_method? definition
146
+ msg = "#{subject} does not respond to #{definition}."
147
+ elsif Taipo::Utilities.symbol? definition
148
+ msg = "#{subject} is not equal to #{definition}."
149
+ elsif object.is_a? Enumerable
150
+ type_def = Taipo::Utilities.object_to_type_def object
151
+ msg = "#{subject} is #{type_def} but expected #{definition}."
152
+ else
153
+ class_name = object.class.name
154
+ msg = "#{subject} is #{class_name} but expected #{definition}."
155
+ end
156
+
157
+ raise Taipo::TypeError, msg
158
+ end
80
159
  end
81
- end
160
+ end
@@ -1,3 +1,3 @@
1
1
  module Taipo
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.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.4.0
4
+ version: 1.5.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-02-01 00:00:00.000000000 Z
11
+ date: 2018-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -136,6 +136,7 @@ files:
136
136
  - lib/taipo/parser/syntax_state.rb
137
137
  - lib/taipo/parser/validater.rb
138
138
  - lib/taipo/refinements.rb
139
+ - lib/taipo/result.rb
139
140
  - lib/taipo/type_element.rb
140
141
  - lib/taipo/type_element/children.rb
141
142
  - lib/taipo/type_element/constraint.rb