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.
@@ -12,7 +12,7 @@ Gem::Specification.new do |gem|
12
12
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
13
  gem.name = "lego"
14
14
  gem.require_paths = ["lib"]
15
- gem.version = '0.0.3'
15
+ gem.version = '0.0.4'
16
16
 
17
17
  gem.add_dependency 'activesupport'
18
18
 
@@ -8,15 +8,15 @@ module Lego
8
8
  require_relative 'lego/model'
9
9
 
10
10
  def self.value_parser(item, *args)
11
- if item.is_a?(Symbol)
12
- Lego::Value.const_get(item.to_s.camelize, false).new(*args)
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
- fail NameError
16
+ raise NameError
17
17
  end
18
18
  rescue NameError
19
- fail NameError, "Unknown Lego::Value parser: #{item.to_s.camelize}"
19
+ raise NameError, "Unknown Lego::Value parser: #{item.to_s}"
20
20
  end
21
21
 
22
22
  end
@@ -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
- value = attrs.delete(name)
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
@@ -8,6 +8,7 @@ end
8
8
 
9
9
  require_relative 'value/base'
10
10
  require_relative 'value/set'
11
+ require_relative 'value/array'
11
12
  require_relative 'value/string'
12
13
  require_relative 'value/date'
13
14
  require_relative 'value/integer'
@@ -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
@@ -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
@@ -3,12 +3,12 @@ require 'spec_helper'
3
3
  describe Lego::Model do
4
4
 
5
5
  class Person < Lego::Model
6
- attribute :name, :string
7
- attribute :age, :integer
6
+ attribute :name, String
7
+ attribute :age, Integer
8
8
  end
9
9
 
10
10
  class Family < Lego::Model
11
- attribute :last_name, :string
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
@@ -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(:string) }
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(:string, allow_empty: true) }
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(:string, allow_empty: false) }
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::String.new(default: handler) }
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('').should == [].to_set }
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.3
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-17 00:00:00.000000000 Z
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: 179225649257381862
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: 179225649257381862
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