axiom-types 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +37 -0
- data/.rspec +4 -0
- data/.rvmrc +1 -0
- data/.travis.yml +35 -0
- data/.yardopts +4 -0
- data/Gemfile +10 -0
- data/Gemfile.devtools +62 -0
- data/Guardfile +24 -0
- data/LICENSE +20 -0
- data/README.md +76 -0
- data/Rakefile +6 -0
- data/TODO +25 -0
- data/axiom-types.gemspec +26 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +100 -0
- data/config/roodi.yml +16 -0
- data/config/yardstick.yml +2 -0
- data/lib/axiom-types.rb +3 -0
- data/lib/axiom/types.rb +65 -0
- data/lib/axiom/types/array.rb +13 -0
- data/lib/axiom/types/boolean.rb +14 -0
- data/lib/axiom/types/class.rb +13 -0
- data/lib/axiom/types/date.rb +18 -0
- data/lib/axiom/types/date_time.rb +21 -0
- data/lib/axiom/types/decimal.rb +13 -0
- data/lib/axiom/types/encodable.rb +75 -0
- data/lib/axiom/types/float.rb +13 -0
- data/lib/axiom/types/hash.rb +45 -0
- data/lib/axiom/types/integer.rb +13 -0
- data/lib/axiom/types/length_comparable.rb +51 -0
- data/lib/axiom/types/numeric.rb +13 -0
- data/lib/axiom/types/object.rb +36 -0
- data/lib/axiom/types/set.rb +13 -0
- data/lib/axiom/types/string.rb +18 -0
- data/lib/axiom/types/support/options.rb +129 -0
- data/lib/axiom/types/symbol.rb +18 -0
- data/lib/axiom/types/time.rb +22 -0
- data/lib/axiom/types/type.rb +136 -0
- data/lib/axiom/types/value_comparable.rb +49 -0
- data/lib/axiom/types/version.rb +10 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/unit/axiom/types/class_methods/finalize_spec.rb +22 -0
- data/spec/unit/axiom/types/encodable/class_methods/extended_spec.rb +31 -0
- data/spec/unit/axiom/types/encodable/finalize_spec.rb +55 -0
- data/spec/unit/axiom/types/hash/class_methods/finalize_spec.rb +55 -0
- data/spec/unit/axiom/types/length_comparable/class_methods/extended_spec.rb +32 -0
- data/spec/unit/axiom/types/length_comparable/finalize_spec.rb +32 -0
- data/spec/unit/axiom/types/object/class_methods/coercion_method_spec.rb +27 -0
- data/spec/unit/axiom/types/object/class_methods/finalize_spec.rb +29 -0
- data/spec/unit/axiom/types/object/class_methods/primitive_spec.rb +27 -0
- data/spec/unit/axiom/types/options/accept_options_spec.rb +98 -0
- data/spec/unit/axiom/types/options/inherited_spec.rb +38 -0
- data/spec/unit/axiom/types/type/class_methods/constraint_spec.rb +32 -0
- data/spec/unit/axiom/types/type/class_methods/finalize_spec.rb +20 -0
- data/spec/unit/axiom/types/type/class_methods/include_predicate_spec.rb +25 -0
- data/spec/unit/axiom/types/type/class_methods/includes_spec.rb +45 -0
- data/spec/unit/axiom/types/type/class_methods/new_spec.rb +79 -0
- data/spec/unit/axiom/types/value_comparable/class_methods/extended_spec.rb +32 -0
- data/spec/unit/axiom/types/value_comparable/finalize_spec.rb +32 -0
- metadata +215 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Encodable, '.extended', :if => RUBY_VERSION >= '1.9' do
|
6
|
+
subject { object.extend(described_class) }
|
7
|
+
|
8
|
+
let(:object) { Class.new(Axiom::Types::Type) }
|
9
|
+
|
10
|
+
it 'delegates to the ancestor' do
|
11
|
+
# RSpec will reset stubbed methods after the example. A normal expectation
|
12
|
+
# causes a SystemStackError to be thrown, so we stub it first so that RSpec
|
13
|
+
# tracks the original method (if any), then we add our own stub that
|
14
|
+
# actually works, and finally when the example finishes RSpec will reset the
|
15
|
+
# Module#extended method back to it's original state.
|
16
|
+
Module.any_instance.stub(:extended).with(object)
|
17
|
+
|
18
|
+
delegated_ancestor = false
|
19
|
+
Module.send(:define_method, :extended) { |_| delegated_ancestor = true }
|
20
|
+
expect { subject }.to change { delegated_ancestor }.from(false).to(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds encoding method' do
|
24
|
+
expect { subject }.to change { object.respond_to?(:encoding) }.
|
25
|
+
from(false).to(true)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'sets the encoding default to UTF-8' do
|
29
|
+
expect(subject.encoding).to be(Encoding::UTF_8)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Encodable, '#finalize', :if => RUBY_VERSION >= '1.9' do
|
6
|
+
subject { object.finalize }
|
7
|
+
|
8
|
+
let(:object) { Class.new(Axiom::Types::Type).extend(described_class) }
|
9
|
+
|
10
|
+
context 'when an ascii compatible encoding (UTF-8) is used' do
|
11
|
+
it_should_behave_like 'a command method'
|
12
|
+
it_should_behave_like 'an idempotent method'
|
13
|
+
|
14
|
+
it { should be_frozen }
|
15
|
+
|
16
|
+
its(:constraint) { should be_frozen }
|
17
|
+
|
18
|
+
defined?(::Encoding) && Encoding.list.each do |encoding|
|
19
|
+
if encoding.ascii_compatible?
|
20
|
+
it "adds a constraint that returns true for #{encoding} encoding" do
|
21
|
+
should include(''.force_encoding(encoding))
|
22
|
+
end
|
23
|
+
else
|
24
|
+
it "adds a constraint that returns false for #{encoding} encoding" do
|
25
|
+
should_not include(''.force_encoding(encoding))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when an non-ascii compatible encoding (UTF-16BE) is used' do
|
32
|
+
before do
|
33
|
+
object.encoding Encoding::UTF_16BE
|
34
|
+
end
|
35
|
+
|
36
|
+
it_should_behave_like 'a command method'
|
37
|
+
it_should_behave_like 'an idempotent method'
|
38
|
+
|
39
|
+
it { should be_frozen }
|
40
|
+
|
41
|
+
its(:constraint) { should be_frozen }
|
42
|
+
|
43
|
+
defined?(::Encoding) && Encoding.list.each do |encoding|
|
44
|
+
if encoding.equal?(Encoding::UTF_16BE)
|
45
|
+
it "adds a constraint that returns true for #{encoding} encoding" do
|
46
|
+
should include(''.force_encoding(encoding))
|
47
|
+
end
|
48
|
+
else
|
49
|
+
it "adds a constraint that returns false for #{encoding} encoding" do
|
50
|
+
should_not include(''.force_encoding(encoding))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Hash, '.finalize' do
|
6
|
+
subject { object.finalize }
|
7
|
+
|
8
|
+
let(:object) { Class.new(Axiom::Types::Hash) }
|
9
|
+
|
10
|
+
context 'with the default key and value constraints' do
|
11
|
+
it_should_behave_like 'a command method'
|
12
|
+
it_should_behave_like 'an idempotent method'
|
13
|
+
|
14
|
+
it { should be_frozen }
|
15
|
+
|
16
|
+
its(:constraint) { should be_frozen }
|
17
|
+
|
18
|
+
it 'adds a constraint that returns true for a Hash' do
|
19
|
+
should include(Object.new => Object.new)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'adds a constraint that returns false for a non-Hash' do
|
23
|
+
should_not include(Object.new)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with custom key and value constraints' do
|
28
|
+
let(:key) { :name }
|
29
|
+
let(:value) { 'Dan Kubb' }
|
30
|
+
|
31
|
+
before do
|
32
|
+
object.key_type Axiom::Types::Symbol
|
33
|
+
object.value_type Axiom::Types::String
|
34
|
+
end
|
35
|
+
|
36
|
+
it_should_behave_like 'a command method'
|
37
|
+
it_should_behave_like 'an idempotent method'
|
38
|
+
|
39
|
+
it { should be_frozen }
|
40
|
+
|
41
|
+
its(:constraint) { should be_frozen }
|
42
|
+
|
43
|
+
it 'adds a constraint that returns true for valid keys and values' do
|
44
|
+
should include(key => value)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'adds a constraint that returns false for an invalid key' do
|
48
|
+
should_not include(key.to_s => value)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'adds a constraint that returns false for an invalid value' do
|
52
|
+
should_not include(key => value.to_sym)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::LengthComparable, '.extended' do
|
6
|
+
subject { object.extend(described_class) }
|
7
|
+
|
8
|
+
let(:object) { Class.new(Axiom::Types::Type) }
|
9
|
+
|
10
|
+
it 'delegates to the ancestor' do
|
11
|
+
# RSpec will reset stubbed methods after the example. A normal expectation
|
12
|
+
# causes a SystemStackError to be thrown, so we stub it first so that RSpec
|
13
|
+
# tracks the original method (if any), then we add our own stub that
|
14
|
+
# actually works, and finally when the example finishes RSpec will reset the
|
15
|
+
# Module#extended method back to it's original state.
|
16
|
+
Module.any_instance.stub(:extended).with(object)
|
17
|
+
|
18
|
+
delegated_ancestor = false
|
19
|
+
Module.send(:define_method, :extended) { |_| delegated_ancestor = true }
|
20
|
+
expect { subject }.to change { delegated_ancestor }.from(false).to(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds minimum_length method' do
|
24
|
+
expect { subject }.to change { object.respond_to?(:minimum_length) }.
|
25
|
+
from(false).to(true)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'adds maxumum_length method' do
|
29
|
+
expect { subject }.to change { object.respond_to?(:maximum_length) }.
|
30
|
+
from(false).to(true)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::LengthComparable, '#finalize' do
|
6
|
+
subject { object.finalize }
|
7
|
+
|
8
|
+
let(:object) do
|
9
|
+
Class.new(Axiom::Types::Type) do
|
10
|
+
extend Axiom::Types::LengthComparable
|
11
|
+
minimum_length 1
|
12
|
+
maximum_length 2
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it_should_behave_like 'a command method'
|
17
|
+
it_should_behave_like 'an idempotent method'
|
18
|
+
|
19
|
+
it { should be_frozen }
|
20
|
+
|
21
|
+
its(:constraint) { should be_frozen }
|
22
|
+
|
23
|
+
it 'adds a constraint that returns true for a length within range' do
|
24
|
+
should include('a')
|
25
|
+
should include('ab')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'adds a constraint that returns false for a length not within range' do
|
29
|
+
should_not include('')
|
30
|
+
should_not include('abc')
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Object, '.coercion_method' do
|
6
|
+
let(:object) { Class.new(described_class) }
|
7
|
+
let(:coercion_method) { :to_object }
|
8
|
+
|
9
|
+
context 'with no arguments' do
|
10
|
+
subject { object.coercion_method }
|
11
|
+
|
12
|
+
it { should be(coercion_method) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with a symbol' do
|
16
|
+
subject { object.coercion_method(symbol) }
|
17
|
+
|
18
|
+
let(:symbol) { :to_string }
|
19
|
+
|
20
|
+
it_should_behave_like 'a command method'
|
21
|
+
|
22
|
+
it 'sets the coercion_method' do
|
23
|
+
expect { subject }.to change { object.coercion_method }.
|
24
|
+
from(coercion_method).to(symbol)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Object, '.finalize' do
|
6
|
+
subject { object.finalize }
|
7
|
+
|
8
|
+
let(:object) do
|
9
|
+
# Class.new { ... } does not work with this on 1.8.7
|
10
|
+
object = Class.new(described_class)
|
11
|
+
object.primitive(::String)
|
12
|
+
object
|
13
|
+
end
|
14
|
+
|
15
|
+
it_should_behave_like 'a command method'
|
16
|
+
it_should_behave_like 'an idempotent method'
|
17
|
+
|
18
|
+
it { should be_frozen }
|
19
|
+
|
20
|
+
its(:constraint) { should be_frozen }
|
21
|
+
|
22
|
+
it 'adds a constraint that returns true for a valid primitive' do
|
23
|
+
should include('string')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'adds a constraint that returns false for an invalid primitive' do
|
27
|
+
should_not include(nil)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Object, '.primitive' do
|
6
|
+
let(:object) { Class.new(described_class) }
|
7
|
+
let(:primitive) { RUBY_VERSION < '1.9' ? ::Object : ::BasicObject }
|
8
|
+
|
9
|
+
context 'with no arguments' do
|
10
|
+
subject { object.primitive }
|
11
|
+
|
12
|
+
it { should be(primitive) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with a class' do
|
16
|
+
subject { object.primitive(klass) }
|
17
|
+
|
18
|
+
let(:klass) { ::String }
|
19
|
+
|
20
|
+
it_should_behave_like 'a command method'
|
21
|
+
|
22
|
+
it 'sets the primitive' do
|
23
|
+
expect { subject }.to change { object.primitive.object_id }.
|
24
|
+
from(primitive.object_id).to(klass.object_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Options, '#accept_options' do
|
6
|
+
subject { object.accept_options(*new_options) }
|
7
|
+
|
8
|
+
let(:object) { Class.new { extend Options, DescendantsTracker } }
|
9
|
+
let(:descendant) { Class.new(object) }
|
10
|
+
|
11
|
+
context 'with valid options' do
|
12
|
+
let(:new_options) { [ :primitive, :coerce_method ] }
|
13
|
+
|
14
|
+
it_should_behave_like 'a command method'
|
15
|
+
it_should_behave_like 'an idempotent method'
|
16
|
+
|
17
|
+
it 'adds methods to the object' do
|
18
|
+
expect(object).to_not respond_to(*new_options)
|
19
|
+
subject
|
20
|
+
expect(object).to respond_to(*new_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds methods to the object that can set a value' do
|
24
|
+
subject
|
25
|
+
object.primitive(::Symbol)
|
26
|
+
expect(object.primitive).to be(::Symbol)
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with the descendant class' do
|
30
|
+
let(:default) { ::String }
|
31
|
+
|
32
|
+
def force_inherit
|
33
|
+
descendant
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'option added before inherited' do
|
37
|
+
before do
|
38
|
+
subject
|
39
|
+
expect(object.primitive(default)).to be(object)
|
40
|
+
force_inherit
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'is idempotent' do
|
44
|
+
expect(descendant.accept_options(:primitive)).to be(descendant)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'adds the method to the descendants' do
|
48
|
+
expect(descendant).to respond_to(*new_options)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'sets the descendant defaults' do
|
52
|
+
expect(descendant.primitive).to be(default)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'adds methods to the descendant that can set a value' do
|
56
|
+
descendant.primitive(::Symbol)
|
57
|
+
expect(descendant.primitive).to be(::Symbol)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'option added after inherited' do
|
62
|
+
before do
|
63
|
+
force_inherit
|
64
|
+
subject
|
65
|
+
expect(object.primitive(default)).to be(object)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'is idempotent' do
|
69
|
+
expect(descendant.accept_options(:primitive)).to be(descendant)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'adds the method to the descendants' do
|
73
|
+
expect(descendant).to respond_to(*new_options)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not set the descendant defaults' do
|
77
|
+
expect(descendant.primitive).to be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'adds methods to the descendant that can set a value' do
|
81
|
+
descendant.primitive(::Symbol)
|
82
|
+
expect(descendant.primitive).to be(::Symbol)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with an option that conflicts with an existing method' do
|
89
|
+
let(:new_options) { [ :name ] }
|
90
|
+
|
91
|
+
specify do
|
92
|
+
expect { subject }.to raise_error(
|
93
|
+
Axiom::Types::Options::ReservedMethodError,
|
94
|
+
'method named `:name` is already defined'
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Options, '#inherited' do
|
6
|
+
subject { descendant }
|
7
|
+
|
8
|
+
let(:object) do
|
9
|
+
Class.new(ancestor) do
|
10
|
+
extend Options, DescendantsTracker
|
11
|
+
accept_options :primitive, :coerce_method
|
12
|
+
primitive ::String
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:ancestor) do
|
17
|
+
Class.new
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:descendant) do
|
21
|
+
Class.new(object)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'delegates to the ancestor' do
|
25
|
+
ancestor.should_receive(:inherited).twice
|
26
|
+
subject
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'adds the accepted option to the descendant' do
|
30
|
+
subject
|
31
|
+
expect(descendant).to respond_to(:primitive, :coerce_method)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets the default value for the descendant' do
|
35
|
+
subject
|
36
|
+
expect(descendant.primitive).to be(::String)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Axiom::Types::Type, '.constraint' do
|
6
|
+
let(:object) { Class.new(described_class) }
|
7
|
+
|
8
|
+
context 'with no arguments or block' do
|
9
|
+
subject { object.constraint }
|
10
|
+
|
11
|
+
it { should respond_to(:call) }
|
12
|
+
it { should be(Tautology) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with a callable object' do
|
16
|
+
subject { object.constraint(Contradiction) }
|
17
|
+
|
18
|
+
it_should_behave_like 'a command method'
|
19
|
+
|
20
|
+
its(:constraint) { should respond_to(:call) }
|
21
|
+
its(:constraint) { should_not be(Tautology) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with a block' do
|
25
|
+
subject { object.constraint { false } }
|
26
|
+
|
27
|
+
it_should_behave_like 'a command method'
|
28
|
+
|
29
|
+
its(:constraint) { should respond_to(:call) }
|
30
|
+
its(:constraint) { should_not be(Tautology) }
|
31
|
+
end
|
32
|
+
end
|