type 0.1.0

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