axiom-types 0.0.1
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 +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
|