lego 0.0.3 → 0.0.4
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.
- data/lego.gemspec +1 -1
- data/lib/lego.rb +4 -4
- data/lib/lego/model.rb +12 -2
- data/lib/lego/value.rb +1 -0
- data/lib/lego/value/array.rb +50 -0
- data/lib/lego/value/string.rb +17 -0
- data/spec/lego/model_spec.rb +36 -3
- data/spec/lego/value/array_spec.rb +76 -0
- data/spec/lego/value/set_spec.rb +5 -5
- data/spec/lego/value/string_spec.rb +10 -0
- metadata +7 -4
data/lego.gemspec
CHANGED
data/lib/lego.rb
CHANGED
@@ -8,15 +8,15 @@ module Lego
|
|
8
8
|
require_relative 'lego/model'
|
9
9
|
|
10
10
|
def self.value_parser(item, *args)
|
11
|
-
if
|
12
|
-
Lego::Value.const_get(item.to_s
|
11
|
+
if (Lego::Value.const_defined?(item.to_s, false) rescue false)
|
12
|
+
Lego::Value.const_get(item.to_s, false).new(*args)
|
13
13
|
elsif item.respond_to?(:coerce)
|
14
14
|
item
|
15
15
|
else
|
16
|
-
|
16
|
+
raise NameError
|
17
17
|
end
|
18
18
|
rescue NameError
|
19
|
-
|
19
|
+
raise NameError, "Unknown Lego::Value parser: #{item.to_s}"
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
data/lib/lego/model.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/hash/deep_merge'
|
2
|
+
|
1
3
|
module Lego
|
2
4
|
class Model
|
3
5
|
class << self
|
@@ -16,9 +18,12 @@ module Lego
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def initialize(attrs={})
|
21
|
+
fail ArgumentError, "attrs must be hash: '#{attrs.inspect}'" unless attrs.respond_to?(:key?)
|
22
|
+
attrs = attrs.dup
|
19
23
|
@attributes = {}.tap do |h|
|
20
24
|
self.class.parsers.each do |name, parser|
|
21
|
-
|
25
|
+
name = name.to_sym
|
26
|
+
value = attrs.key?(name) ? attrs.delete(name) : attrs.delete(name.to_s)
|
22
27
|
begin
|
23
28
|
h[name] = parser.coerce(value)
|
24
29
|
rescue Lego::CoerceError => e
|
@@ -31,6 +36,10 @@ module Lego
|
|
31
36
|
|
32
37
|
attr_reader :attributes
|
33
38
|
|
39
|
+
def merge(other)
|
40
|
+
self.class.new(as_json.deep_merge(other))
|
41
|
+
end
|
42
|
+
|
34
43
|
def method_missing(name, *args, &block)
|
35
44
|
attributes.fetch(name.to_sym) { super }
|
36
45
|
end
|
@@ -58,7 +67,8 @@ module Lego
|
|
58
67
|
|
59
68
|
# Serialize
|
60
69
|
|
61
|
-
def as_json
|
70
|
+
def as_json(opts={})
|
71
|
+
raise NotImplementedError, 'as_json with arguments' unless opts.empty?
|
62
72
|
{}.tap do |h|
|
63
73
|
attributes.each do |attr, val|
|
64
74
|
h[attr] = val.as_json
|
data/lib/lego/value.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Lego::Value
|
2
|
+
class Array < Base
|
3
|
+
|
4
|
+
def initialize(type, opts={})
|
5
|
+
@_item_parser = Lego.value_parser(type)
|
6
|
+
super(opts)
|
7
|
+
end
|
8
|
+
|
9
|
+
def parsers
|
10
|
+
[
|
11
|
+
->(v) { v.respond_to?(:to_ary) ? Lego.just(v.to_ary) : Lego.fail("invalid array: '#{v}'") },
|
12
|
+
->(v) { check_length? ? enforce_length(v) : Lego.just(v) },
|
13
|
+
->(v) { parse_items(v) },
|
14
|
+
->(v) { (not allow_empty? and v.empty?) ? Lego.none : Lego.just(v) }
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_items(items)
|
21
|
+
items = items.map do |item|
|
22
|
+
new_item = @_item_parser.parse(item)
|
23
|
+
if new_item.value?
|
24
|
+
new_item.value
|
25
|
+
else
|
26
|
+
return new_item
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Lego.just(items)
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_length?
|
33
|
+
@opts.key?(:length)
|
34
|
+
end
|
35
|
+
|
36
|
+
def allow_empty?
|
37
|
+
@opts.fetch(:allow_empty, false)
|
38
|
+
end
|
39
|
+
|
40
|
+
def enforce_length(items)
|
41
|
+
length = @opts.fetch(:length).to_i
|
42
|
+
if items.length == length
|
43
|
+
Lego.just(items)
|
44
|
+
else
|
45
|
+
Lego.fail("length not #{length}: '#{items}'")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/lego/value/string.rb
CHANGED
@@ -6,6 +6,7 @@ module Lego::Value
|
|
6
6
|
->(v) { v.respond_to?(:to_str) ? Lego.just(v.to_str) : Lego.fail("invalid string: '#{v}'") },
|
7
7
|
->(v) { strip? ? Lego.just(v.strip) : Lego.just(v) },
|
8
8
|
->(v) { (not allow_blank? and v.blank?) ? Lego.none : Lego.just(v) },
|
9
|
+
->(v) { check_regexp? ? assert_regexp(v) : Lego.just(v) }
|
9
10
|
]
|
10
11
|
end
|
11
12
|
|
@@ -16,5 +17,21 @@ module Lego::Value
|
|
16
17
|
def strip?
|
17
18
|
@opts.fetch(:strip, true)
|
18
19
|
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def check_regexp?
|
24
|
+
@opts.key?(:matches)
|
25
|
+
end
|
26
|
+
|
27
|
+
def assert_regexp(v)
|
28
|
+
regex = @opts.fetch(:matches)
|
29
|
+
if regex =~ v
|
30
|
+
Lego.just(v)
|
31
|
+
else
|
32
|
+
Lego.fail("does not match (#{regex.inspect}): '#{v}'")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
19
36
|
end
|
20
37
|
end
|
data/spec/lego/model_spec.rb
CHANGED
@@ -3,12 +3,12 @@ require 'spec_helper'
|
|
3
3
|
describe Lego::Model do
|
4
4
|
|
5
5
|
class Person < Lego::Model
|
6
|
-
attribute :name,
|
7
|
-
attribute :age,
|
6
|
+
attribute :name, String
|
7
|
+
attribute :age, Integer
|
8
8
|
end
|
9
9
|
|
10
10
|
class Family < Lego::Model
|
11
|
-
attribute :last_name,
|
11
|
+
attribute :last_name, String
|
12
12
|
attribute :father, Person
|
13
13
|
end
|
14
14
|
|
@@ -37,6 +37,16 @@ describe Lego::Model do
|
|
37
37
|
expect{ Person.new(name: 'Alice') }.to raise_error(ArgumentError, ":age => missing value")
|
38
38
|
expect{ Person.new(name: 'Alice', age: Date.today) }.to raise_error(ArgumentError, /invalid integer/)
|
39
39
|
end
|
40
|
+
|
41
|
+
it 'fails on non-hash initialize' do
|
42
|
+
expect{ Person.new(nil) }.to raise_error(ArgumentError, "attrs must be hash: 'nil'")
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'dupes attributes' do
|
46
|
+
h = { name: 'Alice', age: 10 }
|
47
|
+
Person.new(h)
|
48
|
+
h.should == { name: 'Alice', age: 10 }
|
49
|
+
end
|
40
50
|
end
|
41
51
|
|
42
52
|
context 'equality' do
|
@@ -73,6 +83,13 @@ describe Lego::Model do
|
|
73
83
|
family.last_name.should == 'Kowalski'
|
74
84
|
family.father.should == Person.new(name: 'Bob', age: '55')
|
75
85
|
end
|
86
|
+
|
87
|
+
it 'initializes from string keys' do
|
88
|
+
family = Family.new('last_name' => 'Kowalski', 'father' => Person.new('name' => 'Bob', 'age' => '55'))
|
89
|
+
family.should be_instance_of(Family)
|
90
|
+
family.last_name.should == 'Kowalski'
|
91
|
+
family.father.should == Person.new(name: 'Bob', age: '55')
|
92
|
+
end
|
76
93
|
end
|
77
94
|
|
78
95
|
|
@@ -87,7 +104,23 @@ describe Lego::Model do
|
|
87
104
|
}
|
88
105
|
}
|
89
106
|
end
|
107
|
+
end
|
90
108
|
|
109
|
+
describe '#merge' do
|
110
|
+
let(:one) { Family.new(last_name: 'Kowalski', father: { name: 'Bob', age: '55' }) }
|
111
|
+
|
112
|
+
it 'returns new object' do
|
113
|
+
two = one.merge({})
|
114
|
+
two.should == one
|
115
|
+
two.should_not equal(one)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'merges changes' do
|
119
|
+
two = one.merge(last_name: 'Tesla', father: { name: 'Nikola' })
|
120
|
+
|
121
|
+
one.should == Family.new(last_name: 'Kowalski', father: { name: 'Bob', age: '55' })
|
122
|
+
two.should == Family.new(last_name: 'Tesla', father: { name: 'Nikola', age: '55' })
|
123
|
+
end
|
91
124
|
end
|
92
125
|
|
93
126
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lego::Value::Array do
|
4
|
+
|
5
|
+
subject { Lego::Value::Array.new(String) }
|
6
|
+
|
7
|
+
describe '#parse' do
|
8
|
+
context 'nil' do
|
9
|
+
specify { subject.parse(nil).should be_nothing }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'invalid array or item' do
|
13
|
+
specify { subject.parse('').should be_error("invalid array: ''") }
|
14
|
+
specify { subject.parse('2012-02').should be_error("invalid array: '2012-02'") }
|
15
|
+
specify { subject.parse(['one', 123, 'two']).should be_error("invalid string: '123'") }
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'parses array' do
|
19
|
+
subject.parse(['one']).should be_just(['one'])
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'allow empty' do
|
23
|
+
subject { Lego::Value::Array.new(String, allow_empty: true) }
|
24
|
+
specify { subject.parse([]).should be_just([]) }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'disallow empty' do
|
28
|
+
subject { Lego::Value::Array.new(String, allow_empty: false) }
|
29
|
+
specify { subject.parse([]).should be_nothing }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'check length' do
|
33
|
+
subject { Lego::Value::Array.new(Integer, length: 2) }
|
34
|
+
specify { subject.parse([]).should be_error("length not 2: '[]'") }
|
35
|
+
specify { subject.parse([1]).should be_error("length not 2: '[1]'") }
|
36
|
+
specify { subject.parse([1, 2, 3]).should be_error("length not 2: '[1, 2, 3]'") }
|
37
|
+
specify { subject.parse([42, 24]).should be_just([42, 24]) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#coerce' do
|
42
|
+
context 'missing' do
|
43
|
+
it 'raises error' do
|
44
|
+
expect{ subject.coerce(nil) }.to raise_error(Lego::CoerceError, 'missing value')
|
45
|
+
expect{ subject.coerce([]) }.to raise_error(Lego::CoerceError, 'missing value')
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with :default handler' do
|
49
|
+
subject { Lego::Value::Array.new(String, default: handler) }
|
50
|
+
|
51
|
+
context 'nil handler' do
|
52
|
+
let(:handler) { ->{ nil } }
|
53
|
+
specify { subject.coerce(nil).should be_nil }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'lambda handler' do
|
57
|
+
let(:handler) { proc{ ['default'] } }
|
58
|
+
specify { subject.coerce(nil).should == ['default'] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'failure' do
|
64
|
+
it 'raises error' do
|
65
|
+
expect{ subject.coerce([123]) }.to raise_error(Lego::CoerceError, "invalid string: '123'")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'success' do
|
70
|
+
it 'returns array' do
|
71
|
+
subject.coerce(['foo']).should == ['foo']
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/spec/lego/value/set_spec.rb
CHANGED
@@ -6,7 +6,7 @@ describe Lego::Value::Set do
|
|
6
6
|
Array(elem).to_set
|
7
7
|
end
|
8
8
|
|
9
|
-
subject { Lego::Value::Set.new(
|
9
|
+
subject { Lego::Value::Set.new(String) }
|
10
10
|
|
11
11
|
describe '#parse' do
|
12
12
|
context 'nil' do
|
@@ -28,12 +28,12 @@ describe Lego::Value::Set do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
context 'allow empty' do
|
31
|
-
subject { Lego::Value::Set.new(
|
31
|
+
subject { Lego::Value::Set.new(String, allow_empty: true) }
|
32
32
|
specify { subject.parse([]).should be_just(set([])) }
|
33
33
|
end
|
34
34
|
|
35
35
|
context 'disallow empty' do
|
36
|
-
subject { Lego::Value::Set.new(
|
36
|
+
subject { Lego::Value::Set.new(String, allow_empty: false) }
|
37
37
|
specify { subject.parse([]).should be_nothing }
|
38
38
|
end
|
39
39
|
end
|
@@ -47,7 +47,7 @@ describe Lego::Value::Set do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
context 'with :default handler' do
|
50
|
-
subject { Lego::Value::
|
50
|
+
subject { Lego::Value::Set.new(String, default: handler) }
|
51
51
|
|
52
52
|
context 'nil handler' do
|
53
53
|
let(:handler) { ->{ nil } }
|
@@ -56,7 +56,7 @@ describe Lego::Value::Set do
|
|
56
56
|
|
57
57
|
context 'lambda handler' do
|
58
58
|
let(:handler) { proc{ Set.new } }
|
59
|
-
specify { subject.coerce(
|
59
|
+
specify { subject.coerce(nil).should == [].to_set }
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -57,6 +57,16 @@ describe Lego::Value::String do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
context 'matches' do
|
61
|
+
subject { Lego::Value::String.new(opts.merge(matches: /^[A-Z]{3}$/)) }
|
62
|
+
|
63
|
+
specify { subject.parse(nil).should be_nothing }
|
64
|
+
specify { subject.parse(123).should be_error("invalid string: '123'") }
|
65
|
+
|
66
|
+
specify { subject.parse('abc').should be_error("does not match (/^[A-Z]{3}$/): 'abc'") }
|
67
|
+
specify { subject.parse('TLC').should be_just('TLC') }
|
68
|
+
end
|
69
|
+
|
60
70
|
context 'defaults' do
|
61
71
|
subject { Lego::Value::String.new }
|
62
72
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lego
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- lib/lego/either.rb
|
79
79
|
- lib/lego/model.rb
|
80
80
|
- lib/lego/value.rb
|
81
|
+
- lib/lego/value/array.rb
|
81
82
|
- lib/lego/value/base.rb
|
82
83
|
- lib/lego/value/boolean.rb
|
83
84
|
- lib/lego/value/date.rb
|
@@ -89,6 +90,7 @@ files:
|
|
89
90
|
- spec/lego/either/just_spec.rb
|
90
91
|
- spec/lego/either/none_spec.rb
|
91
92
|
- spec/lego/model_spec.rb
|
93
|
+
- spec/lego/value/array_spec.rb
|
92
94
|
- spec/lego/value/base_spec.rb
|
93
95
|
- spec/lego/value/boolean_spec.rb
|
94
96
|
- spec/lego/value/date_spec.rb
|
@@ -111,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
113
|
version: '0'
|
112
114
|
segments:
|
113
115
|
- 0
|
114
|
-
hash:
|
116
|
+
hash: 3608539237903599197
|
115
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
118
|
none: false
|
117
119
|
requirements:
|
@@ -120,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
122
|
version: '0'
|
121
123
|
segments:
|
122
124
|
- 0
|
123
|
-
hash:
|
125
|
+
hash: 3608539237903599197
|
124
126
|
requirements: []
|
125
127
|
rubyforge_project:
|
126
128
|
rubygems_version: 1.8.23
|
@@ -132,6 +134,7 @@ test_files:
|
|
132
134
|
- spec/lego/either/just_spec.rb
|
133
135
|
- spec/lego/either/none_spec.rb
|
134
136
|
- spec/lego/model_spec.rb
|
137
|
+
- spec/lego/value/array_spec.rb
|
135
138
|
- spec/lego/value/base_spec.rb
|
136
139
|
- spec/lego/value/boolean_spec.rb
|
137
140
|
- spec/lego/value/date_spec.rb
|