type 0.1.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,17 @@
1
+ #!/bin/bash
2
+ echo -e "\033[0;36mRuby Appraiser: running\033[0m"
3
+ bundle exec ruby-appraiser --mode=staged
4
+ result_code=$?
5
+ if [ $result_code -gt "0" ]; then
6
+ echo -en "\033[0;31m" # RED
7
+ echo "[✘] Ruby Appraiser found newly-created defects and "
8
+ echo " has blocked your commit."
9
+ echo " Fix the defects and commit again."
10
+ echo " To bypass, commit again with --no-verify."
11
+ echo -en "\033[0m" # RESET
12
+ exit $result_code
13
+ else
14
+ echo -en "\033[0;32m" # GREEN
15
+ echo "[✔] Ruby Appraiser ok"
16
+ echo -en "\033[0m" #RESET
17
+ fi
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Next release (TBD)
4
+
5
+ ## 0.1.0 (2014-01-29)
6
+
7
+ - Initial Implementation
@@ -0,0 +1,41 @@
1
+ # Contributing
2
+
3
+ `Type` is [Apache-liennsed](LICENSE.txt).
4
+
5
+ ## Git-Flow
6
+
7
+ `Type` follows the [git-flow][] branching model, which means that every
8
+ commit on `master` is a release. The default working branch is `develop`, so
9
+ in general please keep feature pull-requests based against the current
10
+ `develop`.
11
+
12
+ - fork the repo on github
13
+ - use the git-flow model to start your feature (based on develop) or
14
+ hotfix (based on master)
15
+ - make some commits (please include specs & changelog)
16
+ - submit a pull-request
17
+
18
+ ## Bug Reporting
19
+
20
+ Please include clear steps-to-reproduce. Spec files are especially welcome;
21
+ a failing spec can be contributed as a pull-request against `master`, but make
22
+ sure it's not already fixed in develop.
23
+
24
+ ## Documentation
25
+
26
+ `Type` uses YARDOC, and so must your pull-requests if you add functionality or
27
+ change the api.
28
+
29
+ ## Ruby Appraiser
30
+
31
+ `Type` uses the [ruby-appraiser][] gem via [pre-commit][] hook, which can be
32
+ activated by installing [icefox/git-hooks][] and running `git-hooks --install`.
33
+ Reek and Rubocop are strong guidelines; use them to reduce defects as much as
34
+ you can, but if you believe clarity will be sacrificed they can be bypassed
35
+ with the `--no-verify` flag.
36
+
37
+ [git-flow]: http://nvie.com/posts/a-successful-git-branching-model/
38
+ [pre-commit]: .githooks/pre-commit/ruby-appraiser
39
+ [ruby-appraiser]: https://github.com/simplymeasured/ruby-appraiser
40
+ [icefox/git-hooks]: https://github.com/icefox/git-hooks
41
+ [pull-request-hack]: http://felixge.de/2013/03/11/the-pull-request-hack.html
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Gem dependencies are specified in the gemspec
6
+ gemspec
@@ -0,0 +1,13 @@
1
+ Copyright 2013 Simply Measured
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,218 @@
1
+ # Type
2
+
3
+ `Type` is a ruby library for type validation and type casting. It allows you to
4
+ have guarantees on data structures, and is exceptionally useful for working with
5
+ external APIs that blow up opaquely on type errors.
6
+
7
+ See [the Changelog](CHANGELOG.md) for version history.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'type'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install type
22
+
23
+ ## Basic API
24
+
25
+ `Type::Definition`s respond to two public methods: `#cast!` and `valid?`, each
26
+ of which take a single argument and either cast or validate the given object.
27
+ For convenience, named type definitions have global aliases defined on `Type`:
28
+
29
+ ~~~ ruby
30
+ # For example, `Type::Int32`, which is a built-in `Type::Definition`
31
+ Type::Int32?(input) # alias for Type::Int32.valid?(input)
32
+ Type::Int32!(input) # alias for Type::Int32.cast!(input)
33
+ ~~~
34
+
35
+ ## Usage
36
+
37
+ `Type` comes with a variety of built-in type defintions, which can be used for
38
+ validation or casting.
39
+
40
+ ### Scalar Type Definitions:
41
+
42
+ The most basic type definitions are scalar
43
+
44
+ ~~~ ruby
45
+ # Validating
46
+ Type::Int32?(1)
47
+ # => true
48
+ Type::Int32?(8_589_934_592) # out of Int32 range
49
+ # => false
50
+ Type::Int32?(3.14)
51
+ # => false
52
+ Type::Int32?('three')
53
+ # => false
54
+
55
+ # Casting
56
+ Type::Int32!(1)
57
+ # => 1
58
+ Type::Int32!(1<<33)
59
+ Type::CastError: Could not cast 8589934592(Fixnum) with Type::Int32
60
+ Type::Int32!(3.14)
61
+ # => 3
62
+ Type::Int32!('three')
63
+ #! Type::CastError: Could not cast "three"(String) with Type::Int32
64
+ ~~~
65
+
66
+ ### Nilable Type Definitions:
67
+
68
+ Any `Type::Definition` can be declared nilable -- that is, it will report `nil`
69
+ as a valid value, and will ignore `nil` when casting.
70
+
71
+ ~~~ ruby
72
+ # Validating
73
+ Type::Int32.valid?(nil)
74
+ # => false
75
+ Type::Int32.nilable.valid?(nil)
76
+ # => true
77
+
78
+ # Casting
79
+ Type::Int32.cast!(nil)
80
+ #! Type::CastError: Could not cast nil(NilClass) with Type::Int32
81
+ Type::Int32.nilable.cast!(nil)
82
+ # => nil
83
+ ~~~
84
+
85
+ ### Collection Type Definitions:
86
+
87
+ `Type` also comes with built-in, named definitions for `Array`, `Set`, and
88
+ `Hash` collections, which are available in the same manner:
89
+
90
+ ~~~ ruby
91
+ # Validating
92
+ Type::Array?([1,2,3])
93
+ # => true
94
+ Type::Hash?({'foo'=>'bar'})
95
+ # => true
96
+ Type::Set?([1,2,3])
97
+ # => false
98
+ Type::Set?(Set.new([1,2,3]))
99
+ # => true
100
+
101
+ # Casting
102
+ Type::Array!([1,2,3])
103
+ # => [1,2,3]
104
+ Type::Hash!([['foo','bar']])
105
+ # => {'foo'=>'bar'}
106
+ Type::Set!([1,2,3])
107
+ # => <Set: {1, 2, 3}>
108
+ Type::Set!('foo')
109
+ #! Type::CastError: Could not cast "foo"(String) with Type::Set
110
+ ~~~
111
+
112
+ ### Constrained Collection Type Definitions:
113
+
114
+ The real power of type-casting collections is when their contents can also be
115
+ constrained:
116
+
117
+ ~~~ ruby
118
+ # Validating:
119
+ # specify any Type::Definition, or the name of a globally-registered one:
120
+ Type::Array.of(Type::Int32).valid?([12, 13])
121
+ # => true
122
+ Type::Array.of(:Int32).valid?(['12', '13'])
123
+ # => false
124
+ Type::Array.of(:Int32).valid?(['three','two'])
125
+ # => false
126
+ Type::Hash.of(:String => :Int64).valid?({'id'=>'1234567890'})
127
+ # => false
128
+ Type::Hash.of(:String => :Int64).valid?({'id'=>1234567890})
129
+ # => true
130
+
131
+ # Casting:
132
+ Type::Array.of(:Int32).cast!([12, 13])
133
+ # => [12, 13]
134
+ Type::Array.of(:Int32).cast!(['12', '13'])
135
+ # => [12, 13]
136
+ Type::Array.of(:Int32).cast!(['three','two'])
137
+ #! Type::CastError: Could not cast ["three", "two"](Array) with Type::Array(Int32),
138
+ #! caused by <Type::CastError: Could not cast "three"(String) with Type::Int32>
139
+ Type::Hash.of(:String => :Int64).cast!({'id'=>'1234567890'})
140
+ # => {'id'=>1234567890}
141
+ Type::Hash.of(:String => :Int64).cast!({'id'=>1234567890})
142
+ # => {'id'=>1234567890}
143
+ ~~~
144
+
145
+ ### Nilable Constrained Collection Type Definitions
146
+
147
+ The contents of your constrained collections can also be nilable:
148
+
149
+ ~~~ ruby
150
+ # Validating:
151
+ Type::Array.of(Type::Int32.nilable).valid?([nil, 3])
152
+ # => true
153
+ Type::Array.of(:Int32?).valid?([nil,4])
154
+ # => true
155
+
156
+ # Casting
157
+ Type::Array.of(Type::Int32.nilable).cast!([nil, '3'])
158
+ # => [nil, 3]
159
+ Type::Array.of(:Int32?).cast!([nil,4])
160
+ # => [nil, 4]
161
+ ~~~
162
+
163
+ ## Advanced Usage
164
+
165
+ ### Custom Type Defintions
166
+
167
+ ~~~ ruby
168
+ my_int32 = Type.scalar do
169
+ int32_range = (-(1 << 31) ... (1 << 31))
170
+ validate do |input|
171
+ input.kind_of?(Integer) && int32_range.include?(input)
172
+ end
173
+ cast do |input|
174
+ Kernel::Integer(input)
175
+ end
176
+ end
177
+
178
+ my_int32.valid?('100') # => false
179
+ my_int32.valid?(100) # => true
180
+ my_int32.cast!(1<<10) # => 1024
181
+ my_int32.cast!("100") # => 100
182
+
183
+
184
+ simple_int32 = Type.scalar.from(Integer) do
185
+ int32_range = (-(1 << 31) ... (1 << 31))
186
+ validate do |input|
187
+ int32_range.include?(input)
188
+ end
189
+ end
190
+
191
+ simple_int32.valid?('100') # => false
192
+ simple_int32.valid?(100) # => true
193
+ simple_int32.cast!(1<<10) # => 1024
194
+ simple_int32.cast!("100") # => 100
195
+
196
+ Type.scalar(:OddInt).from(:Integer) do
197
+ validate(&:odd?)
198
+ cast do |input|
199
+ input.even? ? input + 1 : input
200
+ end
201
+ end
202
+
203
+ Type::OddInt?(4)
204
+ # => false
205
+ Type::OddInt!(4)
206
+ # => 5
207
+ ~~~
208
+
209
+ If you find that you're using one or more custom type definitions on a regular
210
+ basis, please consider contributing them.
211
+
212
+ ## Contributing
213
+
214
+ 1. Fork it
215
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
216
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
217
+ 4. Push to the branch (`git push origin my-new-feature`)
218
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.pattern = FileList['spec/**/*_spec.rb']
9
+ spec.verbose = true
10
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require 'type/version'
4
+ require 'type/error'
5
+ require 'type/definition'
6
+
7
+ # Type is a library for type-casting and type-validation
8
+ module Type
9
+ class << self
10
+ # @overload find(query)
11
+ # @param query [Type::Definition]
12
+ # @overload find(query)
13
+ # @param query [String, Symbol]
14
+ # Find a named Type::Defintion. If the query ends with a ?,
15
+ # a nilable representation of the reolved type definition is returned.
16
+ # @return [Type::Definition]
17
+ def find(query)
18
+ return query if query.kind_of?(Definition)
19
+
20
+ query = String(query)
21
+ nilable = query.end_with?('?') && query.slice!(-1)
22
+
23
+ definition = const_get(query)
24
+ (nilable ? definition.nilable : definition)
25
+ end
26
+ alias_method :[], :find
27
+
28
+ # @api private
29
+ # @param [Type::Definition]
30
+ # @return [void]
31
+ def register(definition)
32
+ if (name = definition.name)
33
+ const_set(name, definition)
34
+ (class << self; self; end).instance_exec do
35
+ define_method("#{name}!") { |x| definition.cast!(x) }
36
+ define_method("#{name}?") { |x| definition.valid?(x) }
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # Required after, since they rely on the above methods.
43
+ require 'type/builtin'
44
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: utf-8
2
+
3
+ # The built-in types are defined here.
4
+ module Type
5
+ scalar(:Integer) do
6
+ validate do |input|
7
+ input.kind_of?(::Integer)
8
+ end
9
+ cast do |input|
10
+ Kernel::Integer(input)
11
+ end
12
+ end
13
+
14
+ scalar(:Int32).from(:Integer) do
15
+ int32_range = (-(1 << 31) ... (1 << 31))
16
+ validate do |input|
17
+ int32_range.include?(input)
18
+ end
19
+ end
20
+
21
+ scalar(:Int64).from(:Integer) do
22
+ int64_range = (-(1 << 63) ... (1 << 63))
23
+ validate do |input|
24
+ int64_range.include?(input)
25
+ end
26
+ end
27
+
28
+ scalar(:UInt32).from(:Integer) do
29
+ int32_range = (0 ... (1 << 32))
30
+ validate do |input|
31
+ int32_range.include?(input)
32
+ end
33
+ end
34
+
35
+ scalar(:UInt64).from(:Integer) do
36
+ int64_range = (0 ... (1 << 64))
37
+ validate do |input|
38
+ int64_range.include?(input)
39
+ end
40
+ end
41
+
42
+ scalar(:Float) do
43
+ validate do |input|
44
+ input.kind_of?(::Float)
45
+ end
46
+ cast do |input|
47
+ Kernel::Float(input)
48
+ end
49
+ end
50
+
51
+ scalar(:Float32).from(:Float) do
52
+ validate do |input|
53
+ input.finite?
54
+ end
55
+ end
56
+
57
+ scalar(:Float64).from(:Float) do
58
+ validate do |input|
59
+ input.finite?
60
+ end
61
+ end
62
+
63
+ scalar(:Boolean) do
64
+ require 'set'
65
+ booleans = Set.new([true, false])
66
+ validate do |input|
67
+ booleans.include?(input)
68
+ end
69
+ end
70
+
71
+ scalar(:String) do
72
+ validate do |input|
73
+ input.kind_of?(::String)
74
+ end
75
+ cast do |input|
76
+ Kernel::String(input)
77
+ end
78
+ end
79
+
80
+ collection(:Array) do
81
+ validate do |input|
82
+ input.kind_of?(::Array)
83
+ end
84
+ cast do |input|
85
+ Kernel::Array(input)
86
+ end
87
+ end
88
+
89
+ collection(:Hash) do
90
+ validate do |input|
91
+ input.kind_of?(::Hash)
92
+ end
93
+ cast do |input|
94
+ if Kernel.respond_to?(:Hash)
95
+ Kernel::Hash(input)
96
+ else
97
+ ::Hash[input]
98
+ end
99
+ end
100
+ end
101
+
102
+ collection(:Set) do
103
+ require 'set'
104
+ validate do |input|
105
+ input.kind_of?(::Set)
106
+ end
107
+ cast do |input|
108
+ ::Set.new(input)
109
+ end
110
+ end
111
+ end