taipo 1.4.0 → 1.5.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: 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