composite_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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/Guardfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/Rakefile +1 -0
- data/composite_type.gemspec +28 -0
- data/lib/composite_type/version.rb +3 -0
- data/lib/composite_type.rb +182 -0
- data/spec/lib/composite_type_spec.rb +228 -0
- data/spec/spec_helper.rb +14 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9faec747d46328dfe04c62ee29c4a1e98684db41
|
4
|
+
data.tar.gz: bd68565039a8dbca31c5cc486ca2a705c964e871
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4d9548cd6e18641f59295f6f7017f92d6ef9196b057b586552130da90f44374abedb7e4a66b8dee63482dc829f7a1fde4c5cb539299bea1e5821a397f98fa5a0
|
7
|
+
data.tar.gz: d0f8c5ec6cce22afca349bb85d464919b3b3eb4b7015e912f45b047274a5265633d99f578218d98449275cd39c960ff0af128c86a349fac2131bc467ca9155bb
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Kurt Stephens
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# TypedAttr
|
2
|
+
|
3
|
+
Typed Attributes and Composite Types for Functional Programming in Ruby
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
TypedAttr simplifies typed functional programming in Ruby.
|
8
|
+
|
9
|
+
The creation of data types is central to functional programming.
|
10
|
+
Ruby does not enforce any typing of object attributes or method parameters.
|
11
|
+
|
12
|
+
TypedAttr introduces a class macro "typed_attr". It constructs an #initialize method
|
13
|
+
given a list of attributes and their expected types.
|
14
|
+
|
15
|
+
Example:
|
16
|
+
|
17
|
+
require 'typed_attr'
|
18
|
+
class Account
|
19
|
+
typed_attr name: String, amount: Money
|
20
|
+
end
|
21
|
+
Account.new("Foo", Money.new(1234))
|
22
|
+
Account.new("Foo", 1234) # => raise TypeError
|
23
|
+
|
24
|
+
Use "typecheck" to perform checks on values:
|
25
|
+
|
26
|
+
def m x, y
|
27
|
+
typecheck x, String
|
28
|
+
typecheck y, Positive, Integer
|
29
|
+
x * y
|
30
|
+
end
|
31
|
+
m("string", -1) # => raise TypeError
|
32
|
+
m("string", 2) # => "stringstring"
|
33
|
+
|
34
|
+
The type assertions use the #=== matching operator.
|
35
|
+
|
36
|
+
Composite Types can be constructed to match deeper data structures:
|
37
|
+
|
38
|
+
h = { "a" => 1, "b" => :symbol }
|
39
|
+
typecheck h, Hash.of(String.with(Integer|Symbol))
|
40
|
+
|
41
|
+
Defining types through Modules:
|
42
|
+
|
43
|
+
module Even
|
44
|
+
def self.=== x
|
45
|
+
Integer === x and x.even?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
Array.of(Even) === [ 2, 4, 10 ]
|
49
|
+
|
50
|
+
Composite types create dynamic Modules that redefine the #=== pattern matching operator.
|
51
|
+
Thus composite types can be used in "case when" clauses:
|
52
|
+
|
53
|
+
case h
|
54
|
+
when Hash.of(String.with(Users)) ...
|
55
|
+
when Hash.of(Symbol.with(Object)) ...
|
56
|
+
end
|
57
|
+
|
58
|
+
Logical operators: #|, #&, #~ are supported:
|
59
|
+
|
60
|
+
a = [ 1, 2, 3 ]
|
61
|
+
typecheck a, Array.of(Positive & Numeric)
|
62
|
+
typecheck a, Array.of(~ NilClass)
|
63
|
+
|
64
|
+
b = [ 1, -2, 3 ]
|
65
|
+
typecheck b, Array.of(Positive & Numeric) # => raise TypeError
|
66
|
+
|
67
|
+
c = [ 1, nil, 3 ]
|
68
|
+
typecheck c, Array.of(~ NilClass) # => raise TypeError
|
69
|
+
|
70
|
+
Composite types are cached indefinitely, therefore anonymous Modules cannot be composed.
|
71
|
+
|
72
|
+
## Installation
|
73
|
+
|
74
|
+
Add this line to your application's Gemfile:
|
75
|
+
|
76
|
+
gem 'typed_attr'
|
77
|
+
|
78
|
+
And then execute:
|
79
|
+
|
80
|
+
$ bundle
|
81
|
+
|
82
|
+
Or install it yourself as:
|
83
|
+
|
84
|
+
$ gem install typed_attr
|
85
|
+
|
86
|
+
## Contributing
|
87
|
+
|
88
|
+
1. Fork it
|
89
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
90
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
91
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
92
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'composite_type/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "composite_type"
|
8
|
+
spec.version = CompositeType::VERSION
|
9
|
+
spec.authors = ["Kurt Stephens"]
|
10
|
+
spec.email = ["ks.github@kurtstephens.com"]
|
11
|
+
spec.description = %q{Composite Types for Ruby}
|
12
|
+
spec.summary = %q{Array.of(String)}
|
13
|
+
spec.homepage = "https://github.com/kstephens/composite_type"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
24
|
+
spec.add_development_dependency "simplecov", "~> 0.8"
|
25
|
+
spec.add_development_dependency "pry", "~> 0.9"
|
26
|
+
spec.add_development_dependency "guard", "~> 2.6"
|
27
|
+
spec.add_development_dependency "guard-rspec", "~> 4.3"
|
28
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class Module
|
4
|
+
class CompositeType < self
|
5
|
+
class Error < ::StandardError; end
|
6
|
+
|
7
|
+
def self.new_cached *types
|
8
|
+
key = [ self, types ]
|
9
|
+
(Thread.current[:'Module::CompositeType.cache'] ||= { })[key] ||=
|
10
|
+
CACHE_MUTEX.synchronize do
|
11
|
+
CACHE[key] ||= new(types)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
CACHE = { }
|
15
|
+
CACHE_MUTEX = Mutex.new
|
16
|
+
|
17
|
+
def initialize types
|
18
|
+
raise Error, "cannot create CompositeType from unamed object" unless types.all?{|x| x.name}
|
19
|
+
@_t = types
|
20
|
+
end
|
21
|
+
attr_reader :_t
|
22
|
+
def name; to_s; end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Matches nothing.
|
26
|
+
module Void
|
27
|
+
def === x
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ContainerType < CompositeType
|
33
|
+
def === x
|
34
|
+
@_t[0] === x and x.all?{|e| @_t[1] === e }
|
35
|
+
end
|
36
|
+
def to_s
|
37
|
+
@to_s ||= "#{@_t[0]}.of(#{@_t[1]})".freeze
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Constructs a type of that matches an Enumerable with an element type.
|
42
|
+
#
|
43
|
+
# Array.of(String)
|
44
|
+
def of t
|
45
|
+
ContainerType.new_cached(self, t)
|
46
|
+
end
|
47
|
+
|
48
|
+
class EnumeratedType < CompositeType
|
49
|
+
def === x
|
50
|
+
Enumerable === x and
|
51
|
+
@_t.size == x.size and
|
52
|
+
begin
|
53
|
+
i = -1
|
54
|
+
@_t.all?{|t| t === x[i += 1]}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
def to_s
|
58
|
+
@to_s ||= "#{@_t[0]}.with(#{@_t[1..-1] * ','})".freeze
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Constructs a type of Enumerable elements.
|
63
|
+
#
|
64
|
+
# String.with(Integer, Float) === [ "foo", 1, 1.2 ]
|
65
|
+
# Hash.of(String.with(Integer))
|
66
|
+
def with *types
|
67
|
+
EnumeratedType.new_cached(self, *types)
|
68
|
+
end
|
69
|
+
|
70
|
+
class DisjunctiveType < CompositeType
|
71
|
+
def === x
|
72
|
+
@_t[0] === x or @_t[1] === x
|
73
|
+
end
|
74
|
+
def to_s
|
75
|
+
@to_s ||= "(#{@_t[0]}|#{@_t[1]})".freeze
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Constructs a type which can be A OR B.
|
80
|
+
#
|
81
|
+
# Array.of(String|Integer)
|
82
|
+
def | t
|
83
|
+
case
|
84
|
+
when t <= self
|
85
|
+
self
|
86
|
+
when self <= t
|
87
|
+
t
|
88
|
+
else
|
89
|
+
DisjunctiveType.new_cached(self, t)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class ConjunctiveType < CompositeType
|
94
|
+
def === x
|
95
|
+
@_t[0] === x and @_t[1] === x
|
96
|
+
end
|
97
|
+
def to_s
|
98
|
+
@to_s ||= "(#{@_t[0]}&#{@_t[1]})".freeze
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Constructs a type which must be A AND B.
|
103
|
+
#
|
104
|
+
# Array.of(Positive & Integer)
|
105
|
+
def & t
|
106
|
+
ConjunctiveType.new_cached(self, t)
|
107
|
+
end
|
108
|
+
|
109
|
+
class NegativeType < CompositeType
|
110
|
+
def === x
|
111
|
+
! (@_t[0] === x)
|
112
|
+
end
|
113
|
+
def to_s
|
114
|
+
@to_s ||= "(~#{@_t[0]})".freeze
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Constructs a type which must not be A.
|
119
|
+
#
|
120
|
+
# Array.of(~ NilClass)
|
121
|
+
def ~@
|
122
|
+
case self
|
123
|
+
when NegativeType
|
124
|
+
self._t.first
|
125
|
+
else
|
126
|
+
NegativeType.new_cached(self)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Numeric origin/continuum types.
|
132
|
+
|
133
|
+
# Objects that are Numeric or respond to :to_numeric.
|
134
|
+
module Numericlike
|
135
|
+
def self.=== x
|
136
|
+
case
|
137
|
+
when Numeric === x
|
138
|
+
x
|
139
|
+
when x.respond_to?(:to_numeric)
|
140
|
+
x.to_numeric
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Objects that are > 0.
|
146
|
+
module Positive
|
147
|
+
def self.=== x
|
148
|
+
x > 0 rescue nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Objects that are < 0.
|
153
|
+
module Negative
|
154
|
+
def self.=== x
|
155
|
+
x < 0 rescue nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Objects that are <= 0.
|
160
|
+
module NonPositive
|
161
|
+
def self.=== x
|
162
|
+
x <= 0 rescue nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Objects that are Numericlike and >= 0.
|
167
|
+
module NonNegative
|
168
|
+
def self.=== x
|
169
|
+
x >= 0 rescue nil
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Objects that can do IO.
|
174
|
+
#
|
175
|
+
# Note: IO and StringIO do not share a common ancestor Module
|
176
|
+
# that distingushes them as being capable of "IO".
|
177
|
+
# So we create one here -- devdriven.com 2013/11/14
|
178
|
+
require 'stringio'
|
179
|
+
module IOable
|
180
|
+
::IO.send(:include, self)
|
181
|
+
::StringIO.send(:include, self)
|
182
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'composite_type'
|
3
|
+
|
4
|
+
describe Class::CompositeType do
|
5
|
+
it "should cache instances" do
|
6
|
+
t1 = Array.of(String.with(~ Float | Symbol & Hash))
|
7
|
+
t2 = Array.of(String.with(~ Float | Symbol & Hash))
|
8
|
+
expect(t1.object_id) .to eql(t2.object_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should fail for anonymous Modules" do
|
12
|
+
expect {
|
13
|
+
Module.new | String
|
14
|
+
}.to raise_error(Module::CompositeType::Error, "cannot create CompositeType from unamed object")
|
15
|
+
end
|
16
|
+
|
17
|
+
context Numericlike do
|
18
|
+
subject { Numericlike }
|
19
|
+
let(:numeric_like) do
|
20
|
+
Class.new do
|
21
|
+
def to_numeric; -1234; end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be true for Numeric" do
|
26
|
+
v = 1234
|
27
|
+
x = subject === v
|
28
|
+
expect(x) .to eql(v)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should be true for anything that responds to :to_numeric" do
|
32
|
+
v = numeric_like.new
|
33
|
+
x = subject === v
|
34
|
+
expect(x) .to eql(-1234)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be false for non-Numeric" do
|
38
|
+
v = "a String"
|
39
|
+
expect(v.respond_to?(:to_numeric)) .to be_falsey
|
40
|
+
expect(subject === v) .to be_falsey
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "EnumeratedType" do
|
45
|
+
subject { Integer.with(Symbol, String) }
|
46
|
+
it "should not fail" do
|
47
|
+
expect(subject === [ 1, :symbol, "string" ]) .to be_truthy
|
48
|
+
end
|
49
|
+
it "should fail" do
|
50
|
+
expect(subject === [ ]) .to be_falsey
|
51
|
+
end
|
52
|
+
it "should fail" do
|
53
|
+
expect(subject === [ 1, :symbol, :wrong ]) .to be_falsey
|
54
|
+
end
|
55
|
+
it "should fail" do
|
56
|
+
expect(subject === [ 1, :symbol, "string", :too_many]) .to be_falsey
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "ContainerType" do
|
61
|
+
subject { Array.of(String) }
|
62
|
+
it "should not fail when empty" do
|
63
|
+
expect(subject === [ ]) .to be_truthy
|
64
|
+
end
|
65
|
+
it "should not fail with a matching element" do
|
66
|
+
expect(subject === [ "String" ]) .to be_truthy
|
67
|
+
end
|
68
|
+
it "should fail when contains unmatching element" do
|
69
|
+
expect(subject === [ "String", 1234 ]) .to be_falsey
|
70
|
+
end
|
71
|
+
it "should fail when is not an Enumerable" do
|
72
|
+
expect(subject === 1234) .to be_falsey
|
73
|
+
end
|
74
|
+
it "should fail when is not equvalent" do
|
75
|
+
expect(subject === { }) .to be_falsey
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "EnumeratedType" do
|
80
|
+
subject { Hash.of(String.with(Integer)) }
|
81
|
+
it "should not fail when empty" do
|
82
|
+
expect(subject === { }) .to be_truthy
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not fail" do
|
86
|
+
expect(subject === { "foo" => 1 }) .to be_truthy
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should fail" do
|
90
|
+
expect(subject === { "foo" => :symbol }) .to be_falsey
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should fail" do
|
94
|
+
expect(subject === { :symbol => 2 }) .to be_falsey
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "DisjunctiveType" do
|
99
|
+
it "should reduce to the greater type if A or B are subclasses of each other" do
|
100
|
+
expect(Float | Numeric) .to equal(Numeric)
|
101
|
+
expect(Numeric | Float ) .to equal(Numeric)
|
102
|
+
expect(Numeric | Numeric) .to equal(Numeric)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should not reduce if A and B are disjunctive" do
|
106
|
+
expect((Numericlike | Numeric).to_s) .to eq("(Numericlike|Numeric)")
|
107
|
+
expect((String | Array).to_s) .to eq("(String|Array)")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should not fail when empty" do
|
111
|
+
v = [ ]
|
112
|
+
expect(Array.of(String | Integer) === v) .to be_truthy
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should not fail" do
|
116
|
+
v = [ "String", 1234 ]
|
117
|
+
expect(Array.of(String | Integer) === v) .to be_truthy
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should fail" do
|
121
|
+
v = [ "String", 1234, :symbol ]
|
122
|
+
expect(Array.of(String | Integer) === v) .to be_falsey
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "ConjunctiveType" do
|
127
|
+
subject { Array.of(Positive & Integer) }
|
128
|
+
it "should not fail when empty" do
|
129
|
+
v = [ ]
|
130
|
+
expect(subject === v) .to be_truthy
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not fail" do
|
134
|
+
v = [ 1, 2 ]
|
135
|
+
expect(subject === v) .to be_truthy
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should fail" do
|
139
|
+
v = [ 0, 1 ]
|
140
|
+
expect(subject === v) .to be_falsey
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should fail" do
|
144
|
+
v = [ 1, :symbol ]
|
145
|
+
expect(subject === v) .to be_falsey
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "NegativeType" do
|
150
|
+
it "~~A == A" do
|
151
|
+
t = String
|
152
|
+
expect(~ ~ t) .to equal(t)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should be true for match" do
|
156
|
+
v = [ 1, :symbol ]
|
157
|
+
expect(Array.of(~ NilClass) === v) .to be_truthy
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should be false for match" do
|
161
|
+
v = [ 1, nil, :symbol ]
|
162
|
+
expect((~ NilClass) === 1) .to be_truthy
|
163
|
+
expect((~ NilClass) === nil) .to be_falsey
|
164
|
+
expect(Array.of(~ NilClass) === v) .to be_falsey
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context Negative do
|
169
|
+
subject { Negative }
|
170
|
+
it "matches non-positive Numerics" do
|
171
|
+
expect(subject === -1) .to be_truthy
|
172
|
+
expect(subject === -0.5) .to be_truthy
|
173
|
+
expect(subject === 0) .to be_falsey
|
174
|
+
expect(subject === 0.5) .to be_falsey
|
175
|
+
expect(subject === 1) .to be_falsey
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context Negative do
|
180
|
+
subject { Negative }
|
181
|
+
it "matches non-positive Numerics" do
|
182
|
+
expect(subject === -1) .to be_truthy
|
183
|
+
expect(subject === -0.5) .to be_truthy
|
184
|
+
expect(subject === 0) .to be_falsey
|
185
|
+
expect(subject === 0.5) .to be_falsey
|
186
|
+
expect(subject === 1) .to be_falsey
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context NonPositive do
|
191
|
+
subject { NonPositive }
|
192
|
+
it "matches non-positive Numerics" do
|
193
|
+
expect(subject === -1) .to be_truthy
|
194
|
+
expect(subject === -0.5) .to be_truthy
|
195
|
+
expect(subject === 0) .to be_truthy
|
196
|
+
expect(subject === 0.5) .to be_falsey
|
197
|
+
expect(subject === 1) .to be_falsey
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context NonNegative do
|
202
|
+
subject { NonNegative }
|
203
|
+
it "matches non-negative Numerics" do
|
204
|
+
expect(subject === -1) .to be_falsey
|
205
|
+
expect(subject === -0.5) .to be_falsey
|
206
|
+
expect(subject === 0) .to be_truthy
|
207
|
+
expect(subject === 0.5) .to be_truthy
|
208
|
+
expect(subject === 1) .to be_truthy
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "misc" do
|
213
|
+
it "example 1" do
|
214
|
+
h = { "a" => 1, "b" => :symbol }
|
215
|
+
expect(Hash.of(String.with(Integer | Symbol)) === h) .to be_truthy
|
216
|
+
end
|
217
|
+
|
218
|
+
it "example 2" do
|
219
|
+
h = { "a" => 1, "b" => "string" }
|
220
|
+
expect(Hash.of(String.with(Integer | Symbol)) === h) .to be_falsey
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should handle to_s" do
|
224
|
+
expect(Hash.of(String.with(Integer | Symbol)).to_s) .to eq("Hash.of(String.with((Integer|Symbol)))")
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: composite_type
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kurt Stephens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.9'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.9'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.6'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.6'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard-rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.3'
|
111
|
+
description: Composite Types for Ruby
|
112
|
+
email:
|
113
|
+
- ks.github@kurtstephens.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- Gemfile
|
121
|
+
- Guardfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- composite_type.gemspec
|
126
|
+
- lib/composite_type.rb
|
127
|
+
- lib/composite_type/version.rb
|
128
|
+
- spec/lib/composite_type_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
homepage: https://github.com/kstephens/composite_type
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.2.2
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Array.of(String)
|
154
|
+
test_files:
|
155
|
+
- spec/lib/composite_type_spec.rb
|
156
|
+
- spec/spec_helper.rb
|