lego 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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