praxis-blueprints 3.2 → 3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +35 -0
- data/.travis.yml +5 -3
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -0
- data/Guardfile +13 -7
- data/Rakefile +4 -3
- data/lib/praxis-blueprints.rb +2 -1
- data/lib/praxis-blueprints/blueprint.rb +68 -89
- data/lib/praxis-blueprints/collection_view.rb +8 -11
- data/lib/praxis-blueprints/config_hash.rb +15 -12
- data/lib/praxis-blueprints/field_expander.rb +46 -52
- data/lib/praxis-blueprints/finalizable.rb +4 -8
- data/lib/praxis-blueprints/renderer.rb +29 -27
- data/lib/praxis-blueprints/version.rb +2 -1
- data/lib/praxis-blueprints/view.rb +23 -28
- data/praxis-blueprints.gemspec +35 -25
- data/spec/praxis-blueprints/blueprint_spec.rb +33 -57
- data/spec/praxis-blueprints/collection_view_spec.rb +6 -10
- data/spec/praxis-blueprints/config_hash_spec.rb +64 -0
- data/spec/praxis-blueprints/field_expander_spec.rb +30 -38
- data/spec/praxis-blueprints/renderer_spec.rb +57 -50
- data/spec/praxis-blueprints/view_spec.rb +8 -12
- data/spec/spec_helper.rb +11 -14
- data/spec/support/spec_blueprints.rb +6 -14
- metadata +44 -8
@@ -1,7 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe Praxis::FieldExpander do
|
4
|
-
|
5
5
|
let(:field_expander) { Praxis::FieldExpander.new }
|
6
6
|
|
7
7
|
let(:view) do
|
@@ -21,96 +21,95 @@ describe Praxis::FieldExpander do
|
|
21
21
|
let(:full_expected) do
|
22
22
|
{
|
23
23
|
name: true,
|
24
|
-
full_name: {first: true, last: true},
|
25
|
-
parents: {mother: true, father: true},
|
24
|
+
full_name: { first: true, last: true },
|
25
|
+
parents: { mother: true, father: true },
|
26
26
|
address: {
|
27
27
|
state: true,
|
28
28
|
street: true,
|
29
29
|
resident: {
|
30
30
|
name: true,
|
31
|
-
full_name: {first: true, last: true},
|
32
|
-
address: {street:true, state:true},
|
33
|
-
prior_addresses: [{street:true, state:true}]
|
31
|
+
full_name: { first: true, last: true },
|
32
|
+
address: { street: true, state: true },
|
33
|
+
prior_addresses: [{ street: true, state: true }]
|
34
34
|
}
|
35
35
|
},
|
36
|
-
prior_addresses: [{state: true}],
|
36
|
+
prior_addresses: [{ state: true }],
|
37
37
|
tags: [true]
|
38
38
|
}
|
39
39
|
end
|
40
40
|
|
41
41
|
context 'expanding a view' do
|
42
42
|
it 'expands all fields on the view, subviews, and related attributes' do
|
43
|
-
field_expander.expand(view,true).should eq(full_expected)
|
43
|
+
field_expander.expand(view, true).should eq(full_expected)
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'expands for a subset of the direct fields' do
|
47
|
-
field_expander.expand(view,name: true).should eq(
|
47
|
+
field_expander.expand(view, name: true).should eq(name: true)
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'expands for a subview' do
|
51
|
-
field_expander.expand(view,parents: true).should eq(
|
51
|
+
field_expander.expand(view, parents: true).should eq(parents: { mother: true, father: true })
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'expands for a related attribute' do
|
55
|
-
field_expander.expand(view,address: true).should eq(
|
55
|
+
field_expander.expand(view, address: true).should eq(address: full_expected[:address])
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'expands for a subset of a related attribute' do
|
59
|
-
field_expander.expand(view,address: {resident: true}).should eq(
|
59
|
+
field_expander.expand(view, address: { resident: true }).should eq(address: { resident: full_expected[:address][:resident] })
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'expands for a subset of a subview' do
|
63
|
-
field_expander.expand(view,parents: {mother: true}).should eq(
|
63
|
+
field_expander.expand(view, parents: { mother: true }).should eq(parents: { mother: true })
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'ignores fields not defined in the view' do
|
67
|
-
field_expander.expand(view,name: true, age: true).should eq(
|
67
|
+
field_expander.expand(view, name: true, age: true).should eq(name: true)
|
68
68
|
end
|
69
69
|
|
70
70
|
it 'expands a specific subattribute of a struct' do
|
71
|
-
field_expander.expand(view,full_name: {first: true}).should eq(
|
71
|
+
field_expander.expand(view, full_name: { first: true }).should eq(full_name: { first: true })
|
72
72
|
end
|
73
73
|
|
74
74
|
it 'wraps expanded collections in arrays' do
|
75
|
-
field_expander.expand(view,prior_addresses: {state: true}).should eq(
|
75
|
+
field_expander.expand(view, prior_addresses: { state: true }).should eq(prior_addresses: [{ state: true }])
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'wraps expanded collections in arrays' do
|
79
|
-
field_expander.expand(view, prior_addresses: true).should eq(
|
79
|
+
field_expander.expand(view, prior_addresses: true).should eq(prior_addresses: [{ state: true }])
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'expands for an Attributor::Model' do
|
84
|
-
field_expander.expand(FullName).should eq(
|
84
|
+
field_expander.expand(FullName).should eq(first: true, last: true)
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
87
|
it 'expands for a Blueprint' do
|
89
|
-
field_expander.expand(Person, parents: true).should eq(
|
88
|
+
field_expander.expand(Person, parents: true).should eq(parents: { father: true, mother: true })
|
90
89
|
end
|
91
90
|
|
92
91
|
it 'expands for an Attributor::Collection of an Attrbutor::Model' do
|
93
|
-
expected = [{first: true, last: true}]
|
92
|
+
expected = [{ first: true, last: true }]
|
94
93
|
field_expander.expand(Attributor::Collection.of(FullName)).should eq expected
|
95
94
|
end
|
96
95
|
|
97
96
|
it 'expands for an Attributor::Collection of a Blueprint' do
|
98
|
-
expected = [{name: true, resident: {full_name: {first: true, last: true}}}]
|
97
|
+
expected = [{ name: true, resident: { full_name: { first: true, last: true } } }]
|
99
98
|
|
100
|
-
field_expander.expand(Attributor::Collection.of(Address), name: true, resident:{full_name: true}).should eq expected
|
99
|
+
field_expander.expand(Attributor::Collection.of(Address), name: true, resident: { full_name: true }).should eq expected
|
101
100
|
end
|
102
101
|
|
103
102
|
it 'also expands array-wrapped field hashes for collections' do
|
104
|
-
expected = [{name: true, resident: {full_name: {first: true, last: true}}}]
|
105
|
-
field_expander.expand(Attributor::Collection.of(Address), [name: true, resident:{full_name: true}]).should eq expected
|
103
|
+
expected = [{ name: true, resident: { full_name: { first: true, last: true } } }]
|
104
|
+
field_expander.expand(Attributor::Collection.of(Address), [name: true, resident: { full_name: true }]).should eq expected
|
106
105
|
end
|
107
106
|
|
108
107
|
it 'expands for an Attributor::Collection of a primitive type' do
|
109
|
-
|
108
|
+
field_expander.expand(Attributor::Collection.of(String)).should eq([true])
|
110
109
|
end
|
111
110
|
|
112
111
|
it 'expands for for a primitive type' do
|
113
|
-
|
112
|
+
field_expander.expand(String).should eq(true)
|
114
113
|
end
|
115
114
|
|
116
115
|
context 'expanding a two-dimensional collection' do
|
@@ -121,12 +120,11 @@ describe Praxis::FieldExpander do
|
|
121
120
|
it 'expands the fields with proper nesting' do
|
122
121
|
field_expander.expand(matrix_type).should eq([[first: true, last: true]])
|
123
122
|
end
|
124
|
-
|
125
123
|
end
|
126
124
|
|
127
125
|
context 'circular expansions' do
|
128
126
|
it 'preserve field object identity for circular references' do
|
129
|
-
result = field_expander.expand(Address,true)
|
127
|
+
result = field_expander.expand(Address, true)
|
130
128
|
result.should be result[:resident][:address]
|
131
129
|
end
|
132
130
|
|
@@ -143,11 +141,10 @@ describe Praxis::FieldExpander do
|
|
143
141
|
result[:address][:resident].should be result
|
144
142
|
end
|
145
143
|
end
|
146
|
-
|
147
144
|
end
|
148
145
|
|
149
146
|
it 'optimizes duplicate field expansions' do
|
150
|
-
expect(field_expander.expand(FullName,true)).to be(field_expander.expand(FullName,true))
|
147
|
+
expect(field_expander.expand(FullName, true)).to be(field_expander.expand(FullName, true))
|
151
148
|
end
|
152
149
|
|
153
150
|
context 'expanding hash attributes' do
|
@@ -171,15 +168,10 @@ describe Praxis::FieldExpander do
|
|
171
168
|
expected = {
|
172
169
|
name: true,
|
173
170
|
simple_hash: true,
|
174
|
-
keyed_hash: {foo: true, bar: true},
|
175
|
-
some_struct: {something: true}
|
171
|
+
keyed_hash: { foo: true, bar: true },
|
172
|
+
some_struct: { something: true }
|
176
173
|
}
|
177
174
|
field_expander.expand(type, true).should eq(expected)
|
178
175
|
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
176
|
end
|
183
|
-
|
184
|
-
|
185
177
|
end
|
@@ -1,17 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require_relative '../spec_helper'
|
2
3
|
|
3
4
|
describe Praxis::Renderer do
|
4
|
-
|
5
5
|
let(:address) { Address.example }
|
6
|
-
let(:prior_addresses) { 2
|
6
|
+
let(:prior_addresses) { Array.new(2) { Address.example } }
|
7
7
|
let(:alias_one) { FullName.example }
|
8
8
|
let(:alias_two) { FullName.example }
|
9
9
|
let(:aliases) { [alias_one, alias_two] }
|
10
10
|
let(:metadata_hash) { { something: 'here' } }
|
11
|
-
let(:metadata) { Attributor::Hash.load(
|
11
|
+
let(:metadata) { Attributor::Hash.load(metadata_hash) }
|
12
12
|
|
13
13
|
let(:person) do
|
14
|
-
|
14
|
+
Person.example(
|
15
15
|
address: address,
|
16
16
|
email: nil,
|
17
17
|
prior_addresses: prior_addresses,
|
@@ -22,18 +22,17 @@ describe Praxis::Renderer do
|
|
22
22
|
)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
25
|
let(:fields) do
|
27
26
|
{
|
28
|
-
name:true,
|
29
|
-
email:true,
|
30
|
-
full_name: {first:true, last:true},
|
27
|
+
name: true,
|
28
|
+
email: true,
|
29
|
+
full_name: { first: true, last: true },
|
31
30
|
address: {
|
32
|
-
state:true,
|
33
|
-
street:true,
|
34
|
-
resident: {name:true}
|
31
|
+
state: true,
|
32
|
+
street: true,
|
33
|
+
resident: { name: true }
|
35
34
|
},
|
36
|
-
prior_addresses: [{name: true}],
|
35
|
+
prior_addresses: [{ name: true }],
|
37
36
|
work_address: true,
|
38
37
|
alive: true,
|
39
38
|
metadata: true,
|
@@ -49,45 +48,41 @@ describe Praxis::Renderer do
|
|
49
48
|
output.keys.should match_array([:name, :full_name, :alive, :address, :prior_addresses, :metadata, :aliases])
|
50
49
|
|
51
50
|
output[:name].should eq person.name
|
52
|
-
output[:full_name].should eq(
|
51
|
+
output[:full_name].should eq(first: person.full_name.first, last: person.full_name.last)
|
53
52
|
output[:alive].should be false
|
54
53
|
|
55
|
-
output[:address].should eq(
|
56
|
-
|
57
|
-
|
58
|
-
resident: {name: person.address.resident.name}
|
59
|
-
})
|
54
|
+
output[:address].should eq(state: person.address.state,
|
55
|
+
street: person.address.street,
|
56
|
+
resident: { name: person.address.resident.name })
|
60
57
|
|
61
|
-
expected_prior_addresses = prior_addresses.collect { |addr| {name: addr.name} }
|
58
|
+
expected_prior_addresses = prior_addresses.collect { |addr| { name: addr.name } }
|
62
59
|
output[:prior_addresses].should match_array(expected_prior_addresses)
|
63
60
|
|
64
|
-
expected_aliases = aliases.collect
|
65
|
-
output[:aliases].should match_array(
|
61
|
+
expected_aliases = aliases.collect(&:dump)
|
62
|
+
output[:aliases].should match_array(expected_aliases)
|
66
63
|
|
67
|
-
output[:metadata].should eq(
|
64
|
+
output[:metadata].should eq(metadata.dump)
|
68
65
|
end
|
69
66
|
|
70
67
|
context 'calls dump for non-Blueprint, but still Dumpable instances' do
|
71
68
|
it 'when rendering them in full as array members' do
|
72
69
|
alias_one.should_receive(:dump).and_call_original
|
73
|
-
output[:aliases].first.should eq(
|
70
|
+
output[:aliases].first.should eq(first: alias_one.first, last: alias_one.last)
|
74
71
|
end
|
75
72
|
it 'when rendering them in full as leaf object' do
|
76
73
|
metadata.should_receive(:dump).and_call_original
|
77
|
-
output[:metadata].should eq(
|
74
|
+
output[:metadata].should eq(metadata_hash)
|
78
75
|
end
|
79
|
-
|
80
76
|
end
|
81
77
|
|
82
78
|
it 'does not render attributes with nil values' do
|
83
79
|
output.should_not have_key(:email)
|
84
80
|
end
|
85
81
|
|
86
|
-
|
87
82
|
it 'sends the correct ActiveSupport::Notification' do
|
88
83
|
fields = {
|
89
|
-
name:true,
|
90
|
-
email:true
|
84
|
+
name: true,
|
85
|
+
email: true
|
91
86
|
}
|
92
87
|
|
93
88
|
notification_payload = {
|
@@ -96,16 +91,16 @@ describe Praxis::Renderer do
|
|
96
91
|
fields: fields
|
97
92
|
}
|
98
93
|
|
99
|
-
ActiveSupport::Notifications.should_receive(:instrument)
|
100
|
-
|
101
|
-
|
94
|
+
ActiveSupport::Notifications.should_receive(:instrument)
|
95
|
+
.with('praxis.blueprint.render', notification_payload)
|
96
|
+
.and_call_original
|
102
97
|
|
103
98
|
renderer.render(person, fields)
|
104
|
-
|
105
|
-
|
99
|
+
end
|
106
100
|
|
107
101
|
context 'with include_nil: true' do
|
108
102
|
let(:renderer) { Praxis::Renderer.new(include_nil: true) }
|
103
|
+
let(:address) { nil }
|
109
104
|
|
110
105
|
it 'renders attributes with nil values' do
|
111
106
|
output.should have_key :email
|
@@ -114,38 +109,42 @@ describe Praxis::Renderer do
|
|
114
109
|
output.should have_key :work_address
|
115
110
|
output[:work_address].should be nil
|
116
111
|
end
|
112
|
+
|
113
|
+
it 'renders nil directly for nil subobjects' do
|
114
|
+
output.should have_key :address
|
115
|
+
output[:address].should be nil
|
116
|
+
end
|
117
117
|
end
|
118
118
|
|
119
119
|
context '#render_collection' do
|
120
|
-
let(:people) { 10
|
120
|
+
let(:people) { Array.new(10) { Person.example(address: address, email: nil) } }
|
121
121
|
subject(:output) { renderer.render_collection(people, fields) }
|
122
122
|
|
123
123
|
it { should have(10).items }
|
124
124
|
|
125
125
|
it 'renders the collection' do
|
126
|
-
output.first.should eq(renderer.render(people.first,fields))
|
126
|
+
output.first.should eq(renderer.render(people.first, fields))
|
127
127
|
end
|
128
|
-
|
129
128
|
end
|
130
129
|
|
131
130
|
context 'rendering a two-dimmensional collection' do
|
132
|
-
let(:names) { 9
|
131
|
+
let(:names) { Array.new(9) { |i| Address.example(i.to_s, name: i.to_s) } }
|
133
132
|
let(:matrix_type) do
|
134
133
|
Attributor::Collection.of(Attributor::Collection.of(Address))
|
135
134
|
end
|
136
135
|
|
137
|
-
let(:matrix) { matrix_type.load(names.each_slice(3).collect { |slice| slice })
|
136
|
+
let(:matrix) { matrix_type.load(names.each_slice(3).collect { |slice| slice }) }
|
138
137
|
|
139
|
-
let(:fields) { [[{name: true}]] }
|
138
|
+
let(:fields) { [[{ name: true }]] }
|
140
139
|
|
141
140
|
it 'renders with render_collection and per-element field spec' do
|
142
|
-
rendered = renderer.render_collection(matrix,fields.first)
|
143
|
-
rendered.flatten.collect {|r| r[:name] }.should eq((0..8).collect(&:to_s))
|
141
|
+
rendered = renderer.render_collection(matrix, fields.first)
|
142
|
+
rendered.flatten.collect { |r| r[:name] }.should eq((0..8).collect(&:to_s))
|
144
143
|
end
|
145
144
|
|
146
145
|
it 'renders with render and proper field spec' do
|
147
|
-
rendered =
|
148
|
-
rendered.flatten.collect {|r| r[:name] }.should eq((0..8).collect(&:to_s))
|
146
|
+
rendered = renderer.render(matrix, fields)
|
147
|
+
rendered.flatten.collect { |r| r[:name] }.should eq((0..8).collect(&:to_s))
|
149
148
|
end
|
150
149
|
end
|
151
150
|
|
@@ -164,18 +163,16 @@ describe Praxis::Renderer do
|
|
164
163
|
render_2 = renderer.render(person, fields)
|
165
164
|
expect(render_1).to be(render_2)
|
166
165
|
end
|
167
|
-
|
168
166
|
end
|
169
167
|
|
170
168
|
context 'circular rendering' do
|
171
169
|
it do
|
172
170
|
field_expander = Praxis::FieldExpander.new
|
173
|
-
fields = field_expander.expand(Person,true)
|
171
|
+
fields = field_expander.expand(Person, true)
|
174
172
|
|
175
173
|
person.object.address.object.resident = person
|
176
174
|
expect { renderer.render(person, fields) }.to raise_error(Praxis::Renderer::CircularRenderingError)
|
177
175
|
end
|
178
|
-
|
179
176
|
end
|
180
177
|
|
181
178
|
context 'rendering hashes' do
|
@@ -186,8 +183,8 @@ describe Praxis::Renderer do
|
|
186
183
|
}
|
187
184
|
end
|
188
185
|
|
189
|
-
let(:data) { {id: 10, hash: {foo: 'bar'}} }
|
190
|
-
let(:object) { SimpleHash.load(data)}
|
186
|
+
let(:data) { { id: 10, hash: { foo: 'bar' } } }
|
187
|
+
let(:object) { SimpleHash.load(data) }
|
191
188
|
let(:renderer) { Praxis::Renderer.new }
|
192
189
|
|
193
190
|
subject(:output) { renderer.render(object, fields) }
|
@@ -205,8 +202,8 @@ describe Praxis::Renderer do
|
|
205
202
|
}
|
206
203
|
end
|
207
204
|
|
208
|
-
let(:data) { {id: 10, hash_collection: [{foo: 'bar'}]} }
|
209
|
-
let(:object) { SimpleHashCollection.load(data)}
|
205
|
+
let(:data) { { id: 10, hash_collection: [{ foo: 'bar' }] } }
|
206
|
+
let(:object) { SimpleHashCollection.load(data) }
|
210
207
|
let(:renderer) { Praxis::Renderer.new }
|
211
208
|
|
212
209
|
subject(:output) { renderer.render(object, fields) }
|
@@ -220,4 +217,14 @@ describe Praxis::Renderer do
|
|
220
217
|
end
|
221
218
|
end
|
222
219
|
|
220
|
+
context 'rendering a Blueprint with fields true' do
|
221
|
+
let(:fields) do
|
222
|
+
{
|
223
|
+
name: true,
|
224
|
+
address: true
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
its([:address]) { should eq person.address.dump }
|
229
|
+
end
|
223
230
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require_relative '../spec_helper'
|
2
3
|
|
3
4
|
describe Praxis::View do
|
4
|
-
|
5
5
|
let(:person) { Person.example(['person']) }
|
6
6
|
let(:address) { person.address }
|
7
7
|
|
@@ -34,7 +34,7 @@ describe Praxis::View do
|
|
34
34
|
subject(:contents) { view.contents }
|
35
35
|
its(:keys) { should match_array([:name, :email, :full_name, :parents, :address, :prior_addresses, :work_address]) }
|
36
36
|
it 'saves attributes defined on the Blueprint' do
|
37
|
-
[:name, :email, :full_name
|
37
|
+
[:name, :email, :full_name].each do |attr|
|
38
38
|
contents[attr].should be Person.attributes[attr]
|
39
39
|
end
|
40
40
|
end
|
@@ -49,7 +49,6 @@ describe Praxis::View do
|
|
49
49
|
contents[:prior_addresses].contents.should eq Address.views[:state].contents
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
52
|
context 'creating subviews' do
|
54
53
|
it 'creates subviews when a block is used' do
|
55
54
|
contents[:parents].should be_kind_of(Praxis::View)
|
@@ -85,33 +84,30 @@ describe Praxis::View do
|
|
85
84
|
it 'creates the sub-CollectionViews with a member view with the same contents of the parent' do
|
86
85
|
contents[:friends].should be_kind_of(Praxis::CollectionView)
|
87
86
|
contents[:friends].contents.should eq(view.contents)
|
88
|
-
contents[:friends].contents.keys.should match_array([:myself
|
87
|
+
contents[:friends].contents.keys.should match_array([:myself, :friends])
|
89
88
|
end
|
90
89
|
end
|
91
|
-
|
92
90
|
end
|
93
91
|
end
|
94
|
-
|
95
92
|
end
|
96
93
|
|
97
94
|
context '#describe' do
|
98
|
-
subject(:description) { view.describe}
|
99
|
-
its(:keys){ should =~ [:attributes, :type] }
|
95
|
+
subject(:description) { view.describe }
|
96
|
+
its(:keys) { should =~ [:attributes, :type] }
|
100
97
|
its([:type]) { should eq(:standard) }
|
101
98
|
context 'returns attributes' do
|
102
99
|
subject { description[:attributes] }
|
103
100
|
|
104
|
-
its(:keys){ should match_array view.contents.keys }
|
101
|
+
its(:keys) { should match_array view.contents.keys }
|
105
102
|
|
106
103
|
it 'should return empty hashes for attributes with no specially defined view' do
|
107
104
|
subject[:name].should eq({})
|
108
105
|
subject[:email].should eq({})
|
109
106
|
end
|
110
107
|
it 'should return the view name if specified' do
|
111
|
-
subject[:address].should eq(
|
112
|
-
subject[:prior_addresses].should eq(
|
108
|
+
subject[:address].should eq(view: :extended)
|
109
|
+
subject[:prior_addresses].should eq(view: :state)
|
113
110
|
end
|
114
111
|
end
|
115
112
|
end
|
116
|
-
|
117
113
|
end
|