lego 0.0.3

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.
@@ -0,0 +1,20 @@
1
+ module Lego::Value
2
+ class String < Base
3
+
4
+ def parsers
5
+ [
6
+ ->(v) { v.respond_to?(:to_str) ? Lego.just(v.to_str) : Lego.fail("invalid string: '#{v}'") },
7
+ ->(v) { strip? ? Lego.just(v.strip) : Lego.just(v) },
8
+ ->(v) { (not allow_blank? and v.blank?) ? Lego.none : Lego.just(v) },
9
+ ]
10
+ end
11
+
12
+ def allow_blank?
13
+ @opts.fetch(:allow_blank, false)
14
+ end
15
+
16
+ def strip?
17
+ @opts.fetch(:strip, true)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Either::Fail do
4
+
5
+ subject { Lego::Either::Fail.new(2) }
6
+
7
+ describe '#initialize' do
8
+
9
+ its(:error?) { should == true }
10
+ its(:none?) { should == false }
11
+ its(:value?) { should == false }
12
+
13
+ its(:error) { should == 2 }
14
+
15
+ it 'fails for :value' do
16
+ ->{ subject.value }.should raise_error(NoMethodError)
17
+ end
18
+
19
+ it 'defines shortcut' do
20
+ Lego.fail(1).should be_error(1)
21
+ end
22
+ end
23
+
24
+ describe '#next' do
25
+ let(:callable) do
26
+ ->(err) { fail 'Do not call me' }
27
+ end
28
+
29
+ it 'does not call callable' do
30
+ ->{ subject.next(callable) }.should_not raise_error
31
+ end
32
+
33
+ it 'returns self' do
34
+ subject.next(callable).should eql(subject)
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Either::Just do
4
+
5
+ subject { Lego::Either::Just.new(2) }
6
+
7
+ describe '#initialize' do
8
+ its(:value?) { should == true }
9
+ its(:none?) { should == false }
10
+ its(:error?) { should == false }
11
+
12
+ its(:value) { should == 2 }
13
+
14
+ it 'fails for :error' do
15
+ ->{ subject.error }.should raise_error(NoMethodError)
16
+ end
17
+
18
+ it 'defines shortcut' do
19
+ Lego.just(1).should be_value
20
+ end
21
+ end
22
+
23
+ describe '#next' do
24
+ it 'binds and calls callable' do
25
+ callable = ->(val) { fail "Call me - #{val}" }
26
+ ->{ subject.next(callable) }.should raise_error('Call me - 2')
27
+ end
28
+
29
+ it 'returns new Just' do
30
+ callable = ->(num) { Lego.just(num * 20) }
31
+ subject.next(callable).should be_just(40)
32
+ end
33
+
34
+ it 'returns None' do
35
+ callable = ->(num) { Lego.none }
36
+ subject.next(callable).should be_nothing
37
+ end
38
+
39
+ it 'returns Fail' do
40
+ callable = ->(num) { Lego.fail('oops') }
41
+ subject.next(callable).should be_error('oops')
42
+ end
43
+
44
+ it 'fails on not Eitherish return value' do
45
+ callable = ->(num) { 'a string' }
46
+ ->{ subject.next(callable) }.should raise_error(TypeError, /Not Lego::Either/)
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Either::None do
4
+
5
+ subject { Lego::Either::None.new }
6
+
7
+ its(:none?) { should == true }
8
+ its(:value?) { should == false }
9
+ its(:error?) { should == false }
10
+
11
+ it 'fails for :value' do
12
+ ->{ subject.value }.should raise_error(NoMethodError)
13
+ end
14
+
15
+ it 'fails for :error' do
16
+ ->{ subject.error }.should raise_error(NoMethodError)
17
+ end
18
+
19
+ it 'defines shortcut' do
20
+ Lego.none.should be_none
21
+ end
22
+
23
+ describe '#next' do
24
+ let(:callable) do
25
+ ->(err) { fail 'Do not call me!' }
26
+ end
27
+
28
+ it 'does not call callable' do
29
+ ->{ subject.next(callable) }.should_not raise_error
30
+ end
31
+
32
+ it 'returns self' do
33
+ subject.next(callable).should eql(subject)
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Model do
4
+
5
+ class Person < Lego::Model
6
+ attribute :name, :string
7
+ attribute :age, :integer
8
+ end
9
+
10
+ class Family < Lego::Model
11
+ attribute :last_name, :string
12
+ attribute :father, Person
13
+ end
14
+
15
+ context 'Person' do
16
+ subject do
17
+ Person.new(name: 'Alice', age: '10')
18
+ end
19
+
20
+ it '::attributes' do
21
+ Person.attribute_names.should == [:name, :age]
22
+ end
23
+
24
+ its(:name){ should == 'Alice' }
25
+ its(:age){ should == 10 }
26
+
27
+ it '#attributes' do
28
+ subject.attributes.should == { name: 'Alice', age: 10 }
29
+ subject.attributes.should be_frozen
30
+ end
31
+
32
+ it 'raises error on unknown attribute' do
33
+ expect{ Person.new(name: 'Alice', age: '10', grade: 5) }.to raise_error(ArgumentError, "Unknown attributes: {:grade=>5}")
34
+ end
35
+
36
+ it 'fails on validation' do
37
+ expect{ Person.new(name: 'Alice') }.to raise_error(ArgumentError, ":age => missing value")
38
+ expect{ Person.new(name: 'Alice', age: Date.today) }.to raise_error(ArgumentError, /invalid integer/)
39
+ end
40
+ end
41
+
42
+ context 'equality' do
43
+ it '#==' do
44
+ Person.new(name: 'Alice', age: 10).should == Person.new(name: 'Alice', age: '10')
45
+ Person.new(name: 'Alice', age: 10).should_not == Person.new(name: 'Bob', age: '10')
46
+ Person.new(name: 'Alice', age: 10).should_not == Person.new(name: 'Alice', age: '12')
47
+ end
48
+
49
+ it '#eql?' do
50
+ Person.new(name: 'Alice', age: 10).should eql Person.new(name: 'Alice', age: '10')
51
+ Person.new(name: 'Alice', age: 10).should_not eql Person.new(name: 'Bob', age: '10')
52
+ Person.new(name: 'Alice', age: 10).should_not eql Person.new(name: 'Alice', age: '12')
53
+ end
54
+
55
+ it '#hash' do
56
+ Person.new(name: 'Alice', age: 10).hash.should == Person.new(name: 'Alice', age: '10').hash
57
+ Person.new(name: 'Alice', age: 10).hash.should_not == Person.new(name: 'Bob', age: '10').hash
58
+ Person.new(name: 'Alice', age: 10).hash.should_not == Person.new(name: 'Alice', age: '12').hash
59
+ end
60
+ end
61
+
62
+ context 'Family' do
63
+ it 'creates recursively from hash' do
64
+ family = Family.new(last_name: 'Kowalski', father: { name: 'Bob', age: '55' })
65
+ family.should be_instance_of(Family)
66
+ family.last_name.should == 'Kowalski'
67
+ family.father.should == Person.new(name: 'Bob', age: '55')
68
+ end
69
+
70
+ it 'creates from partial hash' do
71
+ family = Family.new(last_name: 'Kowalski', father: Person.new(name: 'Bob', age: '55'))
72
+ family.should be_instance_of(Family)
73
+ family.last_name.should == 'Kowalski'
74
+ family.father.should == Person.new(name: 'Bob', age: '55')
75
+ end
76
+ end
77
+
78
+
79
+ describe '#as_json' do
80
+ it 'serializes Family' do
81
+ family = Family.new(last_name: 'Kowalski', father: { name: 'Bob', age: '55' })
82
+ family.as_json.should == {
83
+ :last_name => "Kowalski",
84
+ :father => {
85
+ :name => "Bob",
86
+ :age => 55
87
+ }
88
+ }
89
+ end
90
+
91
+ end
92
+
93
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Value::Base do
4
+ let(:opts) { {} }
5
+
6
+ subject { Lego::Value::Base.new(opts) }
7
+
8
+ describe '#parse' do
9
+ context 'nil' do
10
+ specify { subject.parse(nil).should be_nothing }
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Value::Boolean do
4
+
5
+ describe '#parse' do
6
+ context 'nil' do
7
+ specify { subject.parse(nil).should be_nothing }
8
+ specify { subject.parse('').should be_nothing }
9
+ end
10
+
11
+ context 'boolean string' do
12
+ specify { subject.parse('true').should be_just(true) }
13
+ specify { subject.parse('false').should be_just(false) }
14
+ end
15
+
16
+ context 'boolean value' do
17
+ specify { subject.parse(true).should be_just(true) }
18
+ specify { subject.parse(false).should be_just(false) }
19
+ end
20
+
21
+ context 'invalid value' do
22
+ specify { subject.parse('wtf').should be_error("invalid boolean: 'wtf'") }
23
+ specify { subject.parse(' ').should be_error("invalid boolean: ' '") }
24
+ specify { subject.parse(0).should be_error("invalid boolean: '0'") }
25
+ end
26
+ end
27
+
28
+ describe '#coerce' do
29
+ context 'nothing' do
30
+ context 'default' do
31
+ it 'raises error' do
32
+ expect{ subject.coerce(nil) }.to raise_error(Lego::CoerceError, 'missing value')
33
+ expect{ subject.coerce('') }.to raise_error(Lego::CoerceError, 'missing value')
34
+ end
35
+ end
36
+
37
+ context 'default handler -> false' do
38
+ subject { Lego::Value::Boolean.new(default: ->{ false }) }
39
+ specify { subject.coerce(nil).should == false }
40
+ specify { subject.coerce('').should == false }
41
+ specify { subject.coerce(true).should == true }
42
+ end
43
+
44
+ context 'default handler -> true' do
45
+ subject { Lego::Value::Boolean.new(default: ->{ true }) }
46
+ specify { subject.coerce(nil).should == true }
47
+ specify { subject.coerce('').should == true }
48
+ specify { subject.coerce(false).should == false }
49
+ end
50
+ end
51
+
52
+ context 'failure' do
53
+ it 'raises error' do
54
+ expect{ subject.coerce(123) }.to raise_error(Lego::CoerceError, "invalid boolean: '123'")
55
+ end
56
+ end
57
+
58
+ context 'success' do
59
+ specify { subject.coerce(true).should == true }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Value::Date do
4
+
5
+ describe '#parse' do
6
+ subject { Lego::Value::Date.new }
7
+
8
+ context 'nil' do
9
+ specify { subject.parse(nil).should be_nothing }
10
+ specify { subject.parse('').should be_nothing }
11
+ end
12
+
13
+ context 'invalid date' do
14
+ specify { subject.parse(123).should be_error("invalid date: '123'") }
15
+ specify { subject.parse('2012-02').should be_error("invalid date: '2012-02'") }
16
+ end
17
+
18
+ it 'parses date' do
19
+ subject.parse(Date.new(2012, 02, 01)).should be_just(Date.new(2012, 02, 01))
20
+ end
21
+
22
+ it 'parses time' do
23
+ subject.parse(Time.new(2012, 02, 01, 8, 30)).should be_just(Date.new(2012, 02, 01))
24
+ end
25
+
26
+ it 'parses string date' do
27
+ subject.parse('2012-02-01').should be_just(Date.new(2012, 02, 01))
28
+ end
29
+
30
+ it 'parses string time' do
31
+ subject.parse('2012-02-01T08:30:00Z').should be_just(Date.new(2012, 02, 01))
32
+ end
33
+ end
34
+
35
+ describe '#coerce' do
36
+ context 'nothing' do
37
+ context 'default' do
38
+ it 'raises error' do
39
+ expect{ subject.coerce(nil) }.to raise_error(Lego::CoerceError, 'missing value')
40
+ expect{ subject.coerce('') }.to raise_error(Lego::CoerceError, 'missing value')
41
+ end
42
+ end
43
+
44
+ context 'with :default handler' do
45
+ subject { Lego::Value::String.new(default: handler) }
46
+
47
+ context 'nil handler' do
48
+ let(:handler) { ->{ nil } }
49
+ specify { subject.coerce(nil).should be_nil }
50
+ end
51
+
52
+ context 'lambda handler' do
53
+ let(:handler) { proc{ Date.new(2000, 1, 1) } }
54
+ specify { subject.coerce('').should == Date.new(2000, 1, 1) }
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'failure' do
60
+ it 'raises error' do
61
+ expect{ subject.coerce(123) }.to raise_error(Lego::CoerceError, "invalid date: '123'")
62
+ end
63
+ end
64
+
65
+ context 'success' do
66
+ it 'returns date' do
67
+ subject.coerce(Date.new(2012, 02, 01)).should == Date.new(2012, 02, 01)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lego::Value::Float do
4
+
5
+ describe '#parse' do
6
+ subject { Lego::Value::Float.new(opts) }
7
+
8
+ let(:opts){ {} }
9
+
10
+ context 'nil' do
11
+ specify { subject.parse(nil).should be_nothing }
12
+ specify { subject.parse('').should be_nothing }
13
+ end
14
+
15
+ context 'invalid float' do
16
+ specify { subject.parse(' ').should be_error("invalid float: ' '") }
17
+ specify { subject.parse('2012-02').should be_error("invalid float: '2012-02'") }
18
+ specify { subject.parse('invalid').should be_error("invalid float: 'invalid'") }
19
+ end
20
+
21
+ it 'parses integer as float' do
22
+ subject.parse(123).should be_just(123.0)
23
+ end
24
+
25
+ it 'parses float' do
26
+ subject.parse(123.12).should be_just(123.12)
27
+ end
28
+
29
+ it 'parses string integer as float' do
30
+ subject.parse('-134').should be_just(-134.0)
31
+ end
32
+
33
+ it 'parses string float' do
34
+ subject.parse('-134.11').should be_just(-134.11)
35
+ end
36
+
37
+ context 'minimum' do
38
+ let(:opts){ { min: 4 } }
39
+ specify { subject.parse(4).should be_just(4) }
40
+ specify { subject.parse(3.99999).should be_error("less than minimum of 4: '3.99999'") }
41
+ end
42
+
43
+ context 'maximum' do
44
+ let(:opts){ { max: 4 } }
45
+ specify { subject.parse(4).should be_just(4) }
46
+ specify { subject.parse(4.01).should be_error("more than maximum of 4: '4.01'") }
47
+ end
48
+
49
+ context 'minimium and maximum' do
50
+ let(:opts){ { min: 0, max: 4 } }
51
+ specify { subject.parse(1.1).should be_just(1.1) }
52
+ specify { subject.parse(-0.1).should be_error("less than minimum of 0: '-0.1'") }
53
+ specify { subject.parse(4.1).should be_error("more than maximum of 4: '4.1'") }
54
+ end
55
+ end
56
+
57
+ describe '#coerce' do
58
+ context 'nothing' do
59
+ context 'default' do
60
+ it 'raises error' do
61
+ expect{ subject.coerce(nil) }.to raise_error(Lego::CoerceError, 'missing value')
62
+ expect{ subject.coerce('') }.to raise_error(Lego::CoerceError, 'missing value')
63
+ end
64
+ end
65
+
66
+ context 'with :default handler' do
67
+ subject { Lego::Value::Float.new(default: handler) }
68
+
69
+ context 'nil handler' do
70
+ let(:handler) { ->{ nil } }
71
+ specify { subject.coerce(nil).should be_nil }
72
+ end
73
+
74
+ context 'lambda handler' do
75
+ let(:handler) { ->{ 42.0 } }
76
+ specify { subject.coerce('').should == 42.0 }
77
+ end
78
+ end
79
+ end
80
+
81
+ context 'failure' do
82
+ it 'raises error' do
83
+ expect{ subject.coerce('foo') }.to raise_error(Lego::CoerceError, "invalid float: 'foo'")
84
+ end
85
+ end
86
+
87
+ context 'success' do
88
+ it 'returns float' do
89
+ subject.coerce('42').should == 42.0
90
+ end
91
+ end
92
+ end
93
+ end