taipo 1.0.1 → 1.1.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: 5583e28b7b31de0e646bbdf2f03f274417946935
4
- data.tar.gz: 5b9d14b293cabde611addc66b7857c2a16a46929
3
+ metadata.gz: 47a30b8ff6c187495ba01a26d23e30af086686d9
4
+ data.tar.gz: b8b8473c03c6f3bcebe33ea60e9c41e59359e45d
5
5
  SHA512:
6
- metadata.gz: b9c8e5d7fc2d9a419fb91cb82d063e2c4e294ba5b43677546557a133b9e0dbae420c3984f499381ae4e4d54157eb254fa0106720e6e44c2b271bacb34a97e487
7
- data.tar.gz: 84aee96a5489147d14e7d2ed37ae66dbbbf8f7a6d9e12a365c4fb901160e91d6a58f6bbd6e5d59d03bbe3183b07030215fb5f0c5f74b644f28b88dbcddc3c885
6
+ metadata.gz: 6523770dc0da188909c896d2f9f005af8aed1aa7f11e75a48c8a3eee8d80838e7e88a333ea0cff45e2a18fad3a5f0e2b9d15463222a47ffff74e35cb2dbb6fde
7
+ data.tar.gz: 1abfe9da9fcf22d927afe9b8b7c14e7a3fd0b4ecca5c004a071d39c57d501640509496696a19d1d77c84328351d646ef17bff10660e768bed611fb2ca61a155a
data/.simplecov ADDED
@@ -0,0 +1,3 @@
1
+ SimpleCov.start 'rails' do
2
+ add_filter "/vendor/"
3
+ end
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ env:
2
+ global:
3
+ - CC_TEST_REPORTER_ID=787a2f89b15c637323c7340d65ec17e898ac44480706b4b4122ea040c2a88f1d
4
+ language: ruby
5
+ rvm:
6
+ - 2.4.1
7
+ before_script:
8
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
9
+ - chmod +x ./cc-test-reporter
10
+ - ./cc-test-reporter before-build
11
+ script:
12
+ - bundle exec rake test
13
+ after_script:
14
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  This is free and unencumbered software released into the public domain.
2
2
 
3
- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commerciaL, and by any means.
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
4
4
 
5
5
  In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
6
6
 
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/taipo.svg)](https://badge.fury.io/rb/taipo) [![Inline docs](http://inch-ci.org/github/pyrmont/taipo.svg?branch=master)](http://inch-ci.org/github/pyrmont/taipo)
2
+ [![Build Status](https://travis-ci.org/pyrmont/taipo.svg?branch=master)](https://travis-ci.org/pyrmont/taipo)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/7b5dcb371ee422b27f0c/test_coverage)](https://codeclimate.com/github/pyrmont/taipo/test_coverage)
2
4
 
3
5
  # Taipo
4
6
 
@@ -138,6 +140,12 @@ Found a bug? I’d love to know about it. The best way is to report them in the
138
140
 
139
141
  If you’re interested in contributing to Taipo, feel free to fork and submit a pull request.
140
142
 
143
+ ## Versioning
144
+
145
+ Taipo uses [Semantic Versioning 2.0.0][sv2].
146
+
147
+ [sv2]: http://semver.org/
148
+
141
149
  ## Colophon
142
150
 
143
151
  Taipo began as, and remains primarily, an exercise to improve my programming skills. If Taipo has piqued your interest in adding type checks to Ruby, consider some of the other options, such as [Contracts][cnt], [Rtype][rty], [Rubype][rub] or [Sig][sig].
data/lib/taipo/check.rb CHANGED
@@ -13,8 +13,13 @@ module Taipo
13
13
  # Syntactic sugar to allow a user to write +check types,...+ and +review
14
14
  # types,...+
15
15
  #
16
- # @since 1.0.0
17
- alias types binding
16
+ # The alias is written with underscores in order to support making aliasing
17
+ # with the keyword +types+ optional (since it's possible the user wishes to
18
+ # use this keyword for other purposes).
19
+ #
20
+ # @since 1.1.0
21
+ # @api private
22
+ alias __types__ binding
18
23
 
19
24
  # Check whether the given arguments match the given type definition in the
20
25
  # given context
@@ -39,7 +44,7 @@ module Taipo
39
44
  # @since 1.0.0
40
45
  #
41
46
  # @example
42
- # require 'taipo/taipo'
47
+ # require 'taipo'
43
48
  #
44
49
  # class A
45
50
  # include Taipo::Check
@@ -57,10 +62,10 @@ module Taipo
57
62
  #
58
63
  # a = A.new()
59
64
  # a.foo('Hello world!') #=> "Hello world!"
60
- # a.bar('Goodbye world!') #=> raise Taipo::TypeError
65
+ # a.bar('Goodbye world!') #=> Taipo::TypeError
61
66
  def check(context, collect_invalids = false, **checks)
62
67
  msg = "The first argument to this method must be of type Binding."
63
- raise TypeError, msg unless context.is_a? Binding
68
+ raise ::TypeError, msg unless context.is_a? Binding
64
69
 
65
70
  checks.reduce(Array.new) do |memo,(k,v)|
66
71
  arg = if k[0] == '@' && self.instance_variable_defined?(k)
@@ -84,8 +89,8 @@ module Taipo
84
89
  if Taipo::instance_method? v
85
90
  msg = "Object '#{k}' does not respond to #{v}."
86
91
  elsif arg.is_a? Enumerable
87
- type_string = arg.class.name + Taipo.child_types_string(arg)
88
- msg = "Object '#{k}' is #{type_string} but expected #{v}."
92
+ type_def = Taipo.object_to_type_def arg
93
+ msg = "Object '#{k}' is #{type_def} but expected #{v}."
89
94
  else
90
95
  msg = "Object '#{k}' is #{arg.class.name} but expected #{v}."
91
96
  end
@@ -113,5 +118,34 @@ module Taipo
113
118
  def review(context, **checks)
114
119
  self.check(context, true, checks)
115
120
  end
121
+
122
+ # Perform operations if this module is extended
123
+ #
124
+ # This is the callback called by Ruby when a module is included. In this
125
+ # case, the callback will alias the method +__types__+ as +types+ if
126
+ # {Taipo.alias?} returns true.
127
+ #
128
+ # @param extender [Class|Module] the class or module extending this module
129
+ #
130
+ # @since 1.1.0
131
+ # @api private
132
+ def self.extended(extender)
133
+ extender.singleton_class.send(:alias_method, :types, :__types__) if
134
+ Taipo.alias?
135
+ end
136
+
137
+ # Perform operations if this module is included
138
+ #
139
+ # This is the callback called by Ruby when a module is included. In this
140
+ # case, the callback will alias the method +__types__+ as +types+ if
141
+ # {Taipo.alias?} returns true.
142
+ #
143
+ # @param includer [Class|Module] the class or module including this module
144
+ #
145
+ # @since 1.1.0
146
+ # @api private
147
+ def self.included(includer)
148
+ includer.send(:alias_method, :types, :__types__) if Taipo.alias?
149
+ end
116
150
  end
117
- end
151
+ end
@@ -23,7 +23,18 @@ module Taipo
23
23
  # One special case is where the name is left blank. The validater will
24
24
  # accept this as valid. {Taipo::Parser} will implictly add the name
25
25
  # 'Object' when parsing the type definition. This allows a clean syntax for
26
- # duck types (which are really constraints on the class Object).
26
+ # duck types (discussed in further detail below).
27
+ #
28
+ # ==== Duck Types
29
+ #
30
+ # '#to_s', '(#foo, #bar)'
31
+ #
32
+ # As noted above, duck types can be specified by using a blank name. Duck
33
+ # types are really constraints (discussed in further detail below) on the
34
+ # class Object. While normally constraints need to be enclosed in
35
+ # parentheses, if there is a blank name and only one method constraint, the
36
+ # parentheses can be omitted. For defining duck types that respond to
37
+ # multiple methods, the parentheses are required.
27
38
  #
28
39
  # === Collections
29
40
  #
@@ -11,10 +11,24 @@ module Taipo
11
11
  # @param components [Array<Taipo::TypeElement>] the components that will make up the ChildType
12
12
  #
13
13
  # @since 1.0.0
14
- # @api private
14
+ # @api private
15
15
  def initialize(components = nil)
16
16
  components.each { |c| self.push c } unless components.nil?
17
17
  end
18
+
19
+ # Return the String representation of this ChildType
20
+ #
21
+ # @since 1.1.0
22
+ # @api private
23
+ def to_s
24
+ inner = self.reduce(nil) do |memo_e,component|
25
+ el = component.reduce(nil) do |memo_c,c|
26
+ (memo_c.nil?) ? c.to_s : memo_c + '|' + c.to_s
27
+ end
28
+ (memo_e.nil?) ? el : memo_e + ',' + el
29
+ end
30
+ '<' + inner + '>'
31
+ end
18
32
  end
19
33
  end
20
34
  end
@@ -196,5 +196,24 @@ module Taipo
196
196
  c.constrain?(arg)
197
197
  end
198
198
  end
199
+
200
+ # Return the String representation of this TypeElement
201
+ #
202
+ # @since 1.1.0
203
+ # @api private
204
+ def to_s
205
+ name_str = @name
206
+ child_type_str = (@child_type.nil?) ? '' : @child_type.to_s
207
+ constraints_str = if @constraints.nil?
208
+ ''
209
+ else
210
+ inner = @constraints.reduce('') do |memo,c|
211
+ (memo == '') ? c.to_s : memo + ',' + c.to_s
212
+ end
213
+ '(' + inner + ')'
214
+ end
215
+ name_str + child_type_str + constraints_str
216
+ end
217
+
199
218
  end
200
219
  end
data/lib/taipo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Taipo
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/taipo.rb CHANGED
@@ -11,19 +11,58 @@ require 'taipo/parser'
11
11
  # By including the module {Taipo::Check}, a user can call the
12
12
  # {Taipo::Check#check} or {Taipo::Check#review} methods in their classes
13
13
  # whenever a guard statement is necessary. Expectations are written as type
14
- # definitions that can specify the type of the variable (including sum types)
15
- # and the types of any elements it contains, all subject to a given constraints
16
- # (see {Taipo::Parser::Validater} for the full syntax).
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.
17
18
  #
18
- # As syntactic sugar, the {Taipo::Check} module also aliases +Kernel#binding+
19
- # with the keyword +types+. This allows the user to call {Taipo::Check#check}
20
- # and {Taipo::Check#review} by writing +check types, ...+ and
21
- # +review types, ...+ respectively.
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.
25
+ #
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}.
22
31
  #
23
32
  # @since 1.0.0
24
33
  # @see https://github.com/pyrmont/taipo
25
34
  module Taipo
26
35
 
36
+ # The setting for whether +Kernel#binding+ should be aliased with the keyword
37
+ # +types+.
38
+ #
39
+ # @since 1.1.0
40
+ # @api private
41
+ @@alias = true
42
+
43
+ # Set whether +Kernel#binding+ should be aliased with the keyword +types+.
44
+ #
45
+ # @param v [Boolean] Whether to alias
46
+ #
47
+ # @since 1.1.0
48
+ # @api private
49
+ def self.alias=(v)
50
+ msg = "The argument to this method must be a Boolean."
51
+ raise ::TypeError, msg unless v.is_a?(TrueClass) || v.is_a?(FalseClass)
52
+
53
+ @@alias = v
54
+ end
55
+
56
+ # Check whether +Kernel#binding+ should be aliased with the keyword +types+.
57
+ #
58
+ # @return [Boolean] the result
59
+ #
60
+ # @since 1.1.0
61
+ # @api private
62
+ def self.alias?
63
+ @@alias
64
+ end
65
+
27
66
  # Check if a string is the name of an instance method
28
67
  #
29
68
  # @note All this does is check whether the given string begins with a hash
@@ -39,17 +78,60 @@ module Taipo
39
78
  str[0] == '#'
40
79
  end
41
80
 
42
- # Convert an array into a type definition for the types of the elements in the array
81
+ # Return the type definition for an object
43
82
  #
44
- # @param arg [Array] the array of elements
83
+ # @note This assume that each element returned by Enumerator#each has the same
84
+ # number of components.
45
85
  #
46
- # @return [String] a type definition of the types in the array
86
+ # @param arg [Object] the object
47
87
  #
48
- # @since 1.0.0
88
+ # @return [String] a type definition of the object
89
+ #
90
+ # @since 1.1.0
91
+ # @api private
92
+ def self.object_to_type_def(obj)
93
+ return obj.class.name unless obj.is_a? Enumerable
94
+
95
+ if obj.is_a? Array
96
+ element_types = Hash.new
97
+ obj.each { |o| element_types[self.object_to_type_def(o)] = true }
98
+ if element_types.empty?
99
+ obj.class.name
100
+ else
101
+ obj.class.name + '<' + element_types.keys.join('|') + '>'
102
+ end
103
+ else
104
+ element_types = Array.new
105
+ obj.each.with_index do |element,index_e|
106
+ element.each.with_index do |component,index_c|
107
+ element_types[index_c] = Hash.new if index_e == 0
108
+ c_type = self.object_to_type_def(component)
109
+ element_types[index_c][c_type] = true
110
+ end
111
+ end
112
+ inner = element_types.reduce('') do |memo,e|
113
+ e_type = e.keys.join('|')
114
+ (memo == '') ? e_type : memo + ',' + e_type
115
+ end
116
+ if element_types.empty?
117
+ obj.class.name
118
+ else
119
+ obj.class.name + '<' + inner + '>'
120
+ end
121
+ end
122
+ end
123
+
124
+ # Return a String representation of an array of {Taipo::TypeElement}
125
+ #
126
+ # @param arg [Array<Taipo::TypeElement>] the array of {Taipo::TypeElement}
127
+ #
128
+ # @return [String] the String representation
129
+ #
130
+ # @since 1.1.0
49
131
  # @api private
50
- def self.child_types_string(arg)
51
- child_types = Hash.new
52
- arg.each { |a| child_types[a.class.name] = true }
53
- '<' + child_types.keys.join('|') + '>'
132
+ def self.types_to_s(types)
133
+ types.reduce('') do |memo,t|
134
+ (memo == '') ? t.to_s : memo + '|' + t.to_s
135
+ end
54
136
  end
55
- end
137
+ end
data/taipo.gemspec CHANGED
@@ -25,5 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "minitest", "~> 5.10.3"
26
26
  spec.add_development_dependency "minitest-reporters", "~> 1.1.19"
27
27
  spec.add_development_dependency "shoulda-context", "~> 1.2.0"
28
+ spec.add_development_dependency "simplecov", "~> 0.15.1"
28
29
  spec.add_development_dependency "yard", "~> 0.9.12"
29
30
  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.0.1
4
+ version: 1.1.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-19 00:00:00.000000000 Z
11
+ date: 2018-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 1.2.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.15.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.15.1
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: yard
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +118,8 @@ extensions: []
104
118
  extra_rdoc_files: []
105
119
  files:
106
120
  - ".gitignore"
121
+ - ".simplecov"
122
+ - ".travis.yml"
107
123
  - Gemfile
108
124
  - LICENSE.md
109
125
  - README.md