axiom-types 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +37 -0
  3. data/.rspec +4 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +35 -0
  6. data/.yardopts +4 -0
  7. data/Gemfile +10 -0
  8. data/Gemfile.devtools +62 -0
  9. data/Guardfile +24 -0
  10. data/LICENSE +20 -0
  11. data/README.md +76 -0
  12. data/Rakefile +6 -0
  13. data/TODO +25 -0
  14. data/axiom-types.gemspec +26 -0
  15. data/config/flay.yml +3 -0
  16. data/config/flog.yml +2 -0
  17. data/config/mutant.yml +3 -0
  18. data/config/reek.yml +100 -0
  19. data/config/roodi.yml +16 -0
  20. data/config/yardstick.yml +2 -0
  21. data/lib/axiom-types.rb +3 -0
  22. data/lib/axiom/types.rb +65 -0
  23. data/lib/axiom/types/array.rb +13 -0
  24. data/lib/axiom/types/boolean.rb +14 -0
  25. data/lib/axiom/types/class.rb +13 -0
  26. data/lib/axiom/types/date.rb +18 -0
  27. data/lib/axiom/types/date_time.rb +21 -0
  28. data/lib/axiom/types/decimal.rb +13 -0
  29. data/lib/axiom/types/encodable.rb +75 -0
  30. data/lib/axiom/types/float.rb +13 -0
  31. data/lib/axiom/types/hash.rb +45 -0
  32. data/lib/axiom/types/integer.rb +13 -0
  33. data/lib/axiom/types/length_comparable.rb +51 -0
  34. data/lib/axiom/types/numeric.rb +13 -0
  35. data/lib/axiom/types/object.rb +36 -0
  36. data/lib/axiom/types/set.rb +13 -0
  37. data/lib/axiom/types/string.rb +18 -0
  38. data/lib/axiom/types/support/options.rb +129 -0
  39. data/lib/axiom/types/symbol.rb +18 -0
  40. data/lib/axiom/types/time.rb +22 -0
  41. data/lib/axiom/types/type.rb +136 -0
  42. data/lib/axiom/types/value_comparable.rb +49 -0
  43. data/lib/axiom/types/version.rb +10 -0
  44. data/spec/spec_helper.rb +37 -0
  45. data/spec/unit/axiom/types/class_methods/finalize_spec.rb +22 -0
  46. data/spec/unit/axiom/types/encodable/class_methods/extended_spec.rb +31 -0
  47. data/spec/unit/axiom/types/encodable/finalize_spec.rb +55 -0
  48. data/spec/unit/axiom/types/hash/class_methods/finalize_spec.rb +55 -0
  49. data/spec/unit/axiom/types/length_comparable/class_methods/extended_spec.rb +32 -0
  50. data/spec/unit/axiom/types/length_comparable/finalize_spec.rb +32 -0
  51. data/spec/unit/axiom/types/object/class_methods/coercion_method_spec.rb +27 -0
  52. data/spec/unit/axiom/types/object/class_methods/finalize_spec.rb +29 -0
  53. data/spec/unit/axiom/types/object/class_methods/primitive_spec.rb +27 -0
  54. data/spec/unit/axiom/types/options/accept_options_spec.rb +98 -0
  55. data/spec/unit/axiom/types/options/inherited_spec.rb +38 -0
  56. data/spec/unit/axiom/types/type/class_methods/constraint_spec.rb +32 -0
  57. data/spec/unit/axiom/types/type/class_methods/finalize_spec.rb +20 -0
  58. data/spec/unit/axiom/types/type/class_methods/include_predicate_spec.rb +25 -0
  59. data/spec/unit/axiom/types/type/class_methods/includes_spec.rb +45 -0
  60. data/spec/unit/axiom/types/type/class_methods/new_spec.rb +79 -0
  61. data/spec/unit/axiom/types/value_comparable/class_methods/extended_spec.rb +32 -0
  62. data/spec/unit/axiom/types/value_comparable/finalize_spec.rb +32 -0
  63. 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