halogen 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +244 -0
- data/Rakefile +6 -0
- data/examples/extensions.md +25 -0
- data/examples/simple.rb +142 -0
- data/halogen.gemspec +32 -0
- data/lib/halogen.rb +157 -0
- data/lib/halogen/collection.rb +44 -0
- data/lib/halogen/configuration.rb +15 -0
- data/lib/halogen/definition.rb +73 -0
- data/lib/halogen/definitions.rb +23 -0
- data/lib/halogen/embeds.rb +105 -0
- data/lib/halogen/embeds/definition.rb +19 -0
- data/lib/halogen/errors.rb +7 -0
- data/lib/halogen/hash_util.rb +37 -0
- data/lib/halogen/links.rb +41 -0
- data/lib/halogen/links/definition.rb +59 -0
- data/lib/halogen/properties.rb +40 -0
- data/lib/halogen/properties/definition.rb +8 -0
- data/lib/halogen/railtie.rb +11 -0
- data/lib/halogen/resource.rb +58 -0
- data/lib/halogen/version.rb +5 -0
- data/spec/halogen/collection_spec.rb +78 -0
- data/spec/halogen/configuration_spec.rb +11 -0
- data/spec/halogen/definition_spec.rb +108 -0
- data/spec/halogen/definitions_spec.rb +24 -0
- data/spec/halogen/embeds/definition_spec.rb +23 -0
- data/spec/halogen/embeds_spec.rb +201 -0
- data/spec/halogen/links/definition_spec.rb +68 -0
- data/spec/halogen/links_spec.rb +59 -0
- data/spec/halogen/properties_spec.rb +41 -0
- data/spec/halogen/resource_spec.rb +77 -0
- data/spec/halogen_spec.rb +127 -0
- data/spec/spec_helper.rb +11 -0
- metadata +179 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Definitions do
|
6
|
+
let :definitions do
|
7
|
+
Halogen::Definitions.new
|
8
|
+
end
|
9
|
+
|
10
|
+
let :definition do
|
11
|
+
Halogen::Definition.new(:name, {}, nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#add' do
|
15
|
+
it 'validates and adds definition' do
|
16
|
+
expect(definitions.keys).to eq([])
|
17
|
+
|
18
|
+
definitions.add(definition)
|
19
|
+
|
20
|
+
expect(definitions.keys).to eq(['Halogen::Definition'])
|
21
|
+
expect(definitions['Halogen::Definition']).to eq([definition])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Embeds::Definition do
|
6
|
+
describe '#validate' do
|
7
|
+
it 'returns true with procedure' do
|
8
|
+
result = Halogen::Embeds::Definition.new(:name, {}, proc {}).validate
|
9
|
+
|
10
|
+
expect(result).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'raises exception without procedure' do
|
14
|
+
expect {
|
15
|
+
Halogen::Embeds::Definition.new(:name, {}, nil).validate
|
16
|
+
}.to raise_error do |exception|
|
17
|
+
expect(exception).to be_an_instance_of(Halogen::InvalidDefinition)
|
18
|
+
expect(exception.message).to(
|
19
|
+
eq('Embed name must be defined with a proc'))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Embeds do
|
6
|
+
let :klass do
|
7
|
+
Class.new do
|
8
|
+
include Halogen
|
9
|
+
include Halogen::Embeds
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Halogen::Embeds::ClassMethods do
|
14
|
+
it 'adds simple embed definition' do
|
15
|
+
expect(klass.definitions).to receive(:add)
|
16
|
+
|
17
|
+
klass.embed(:foo, {}) { 'bar' }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe Halogen::Embeds::InstanceMethods do
|
22
|
+
describe '#embedded' do
|
23
|
+
describe 'when no embeds are defined' do
|
24
|
+
it 'returns empty hash when no embeds are requested' do
|
25
|
+
representer = klass.new
|
26
|
+
|
27
|
+
expect(representer.embedded).to eq({})
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns empty hash when embeds are requested' do
|
31
|
+
representer = klass.new(embed: { foo: true })
|
32
|
+
|
33
|
+
expect(representer.embedded).to eq({})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'when embeds are defined' do
|
38
|
+
before :each do
|
39
|
+
klass.embed(:just_nil, {}) { nil }
|
40
|
+
klass.embed(:non_repr, {}) { 'some object' }
|
41
|
+
klass.embed(:child_repr, {}) { child }
|
42
|
+
|
43
|
+
klass.send(:define_method, :child) do
|
44
|
+
# just build another representer instance to be rendered
|
45
|
+
Class.new { include Halogen }.new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns empty hash when no embeds are requested' do
|
50
|
+
representer = klass.new(embed: {})
|
51
|
+
|
52
|
+
expect(representer.embedded).to eq({})
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'builds embedded resources as expected' do
|
56
|
+
embed_opts = { just_nil: true, non_repr: true, child_repr: true }
|
57
|
+
|
58
|
+
representer = klass.new(embed: embed_opts)
|
59
|
+
|
60
|
+
expect(representer.embedded).to eq(child_repr: {})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#embedded_child' do
|
66
|
+
let :representer do
|
67
|
+
klass.new
|
68
|
+
end
|
69
|
+
|
70
|
+
let :child_class do
|
71
|
+
Class.new do
|
72
|
+
include Halogen
|
73
|
+
|
74
|
+
property(:foo) { 'bar' }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
let :child do
|
79
|
+
child_class.new
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns nil if value is falsey' do
|
83
|
+
[nil, false, 0].each do |value|
|
84
|
+
expect(representer.embedded_child(:foo, value)).to be_nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'when value is an array' do
|
89
|
+
it 'renders children' do
|
90
|
+
array = [child, nil, 0, child, 1]
|
91
|
+
|
92
|
+
result = representer.embedded_child(:embed_key, array)
|
93
|
+
|
94
|
+
expect(result).to eq([{ foo: 'bar' }, { foo: 'bar' }])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'when value is a representer' do
|
99
|
+
it 'renders child' do
|
100
|
+
result = representer.embedded_child(:embed_key, child)
|
101
|
+
|
102
|
+
expect(result).to eq(foo: 'bar')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#child_embed_opts' do
|
108
|
+
it 'returns empty options for unknown key' do
|
109
|
+
representer = klass.new
|
110
|
+
|
111
|
+
opts = representer.send(:child_embed_opts, :unknown_key)
|
112
|
+
|
113
|
+
expect(opts).to eq({})
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'returns empty options for known key with no child options' do
|
117
|
+
representer = klass.new(embed: { requested_key: 1 })
|
118
|
+
|
119
|
+
opts = representer.send(:child_embed_opts, 'requested_key')
|
120
|
+
|
121
|
+
expect(opts).to eq({})
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns child options for known key with child options' do
|
125
|
+
representer = klass.new(embed: { requested_key: { child_key: 0 } })
|
126
|
+
|
127
|
+
opts = representer.send(:child_embed_opts, 'requested_key')
|
128
|
+
|
129
|
+
expect(opts).to eq(child_key: 0)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns deeply nested child options' do
|
133
|
+
representer = klass.new(
|
134
|
+
embed: {
|
135
|
+
requested_key: {
|
136
|
+
child_key: { grandchild_key: { great_grandchild_key: 1 } }
|
137
|
+
}
|
138
|
+
}
|
139
|
+
)
|
140
|
+
|
141
|
+
opts = representer.send(:child_embed_opts, 'requested_key')
|
142
|
+
|
143
|
+
expect(opts).to eq(
|
144
|
+
child_key: { grandchild_key: { great_grandchild_key: 1 } })
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#render_child' do
|
149
|
+
let :representer do
|
150
|
+
klass.new
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'returns nil if child is not a representer' do
|
154
|
+
[nil, 1, ''].each do |child|
|
155
|
+
expect(representer.render_child(child, {})).to be_nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'renders child representer with correct parent and options' do
|
160
|
+
child = Class.new do
|
161
|
+
include Halogen
|
162
|
+
|
163
|
+
property(:verify_parent) { parent.object_id }
|
164
|
+
property(:verify_opts) { options[:embed] }
|
165
|
+
end.new
|
166
|
+
|
167
|
+
result = representer.render_child(child, foo: 'bar')
|
168
|
+
|
169
|
+
expect(result).to eq(
|
170
|
+
verify_parent: representer.object_id,
|
171
|
+
verify_opts: { foo: 'bar' })
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe '#embed_options' do
|
176
|
+
it 'stringifies top level keys' do
|
177
|
+
representer = klass.new(embed: { some: { options: 1 } })
|
178
|
+
|
179
|
+
expect(representer.embed_options).to eq('some' => { options: 1 })
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#embed?' do
|
184
|
+
it 'is true for expected values' do
|
185
|
+
[1, 2, true, '1', '2', 'true', 'yes'].each do |value|
|
186
|
+
representer = klass.new(embed: { foo: value })
|
187
|
+
|
188
|
+
expect(representer.embed?('foo')).to eq(true)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'is false for expected values' do
|
193
|
+
[0, false, '0', 'false'].each do |value|
|
194
|
+
representer = klass.new(embed: { foo: value })
|
195
|
+
|
196
|
+
expect(representer.embed?('foo')).to eq(false)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Links::Definition do
|
6
|
+
describe '#validate' do
|
7
|
+
it 'returns true with procedure' do
|
8
|
+
result = Halogen::Links::Definition.new(:name, proc {}).validate
|
9
|
+
|
10
|
+
expect(result).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'raises exception without procedure or explicit value' do
|
14
|
+
expect {
|
15
|
+
Halogen::Links::Definition.new(:name, nil).validate
|
16
|
+
}.to raise_error do |exception|
|
17
|
+
expect(exception).to be_an_instance_of(Halogen::InvalidDefinition)
|
18
|
+
expect(exception.message).to(
|
19
|
+
eq('Link requires either procedure or explicit value'))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.build_options' do
|
25
|
+
it 'has expected value without options hash' do
|
26
|
+
options = Halogen::Links::Definition.build_options([])
|
27
|
+
|
28
|
+
expect(options).to eq(attrs: {})
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has expected value with options hash' do
|
32
|
+
options = Halogen::Links::Definition.build_options([foo: 'bar'])
|
33
|
+
|
34
|
+
expect(options).to eq(attrs: {}, foo: 'bar')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'merges attrs from options' do
|
38
|
+
options = Halogen::Links::Definition.build_options([
|
39
|
+
:templated,
|
40
|
+
attrs: { properties: {} },
|
41
|
+
foo: 'bar'])
|
42
|
+
|
43
|
+
expect(options).to(
|
44
|
+
eq(attrs: { properties: {}, templated: true }, foo: 'bar'))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.build_attrs' do
|
49
|
+
it 'returns empty hash if no keywords are provided' do
|
50
|
+
expect(Halogen::Links::Definition.build_attrs([])).to eq({})
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'builds expected hash with recognized keywords' do
|
54
|
+
attrs = Halogen::Links::Definition.build_attrs([:templated])
|
55
|
+
|
56
|
+
expect(attrs).to eq(templated: true)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises exception if unrecognized keyword is included' do
|
60
|
+
expect {
|
61
|
+
Halogen::Links::Definition.build_attrs([:templated, :wat])
|
62
|
+
}.to raise_error do |exception|
|
63
|
+
expect(exception.class).to eq(Halogen::InvalidDefinition)
|
64
|
+
expect(exception.message).to eq('Unrecognized link keyword: wat')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Links do
|
6
|
+
let :klass do
|
7
|
+
Class.new { include Halogen }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Halogen::Links::ClassMethods do
|
11
|
+
describe '#link' do
|
12
|
+
describe 'with procedure' do
|
13
|
+
it 'builds simple definition' do
|
14
|
+
link = klass.link(:self) { 'path' }
|
15
|
+
|
16
|
+
expect(link.name).to eq(:self)
|
17
|
+
expect(link.options).to eq(attrs: {})
|
18
|
+
expect(link.procedure.call).to eq('path')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'builds complex definition' do
|
22
|
+
link = klass.link(
|
23
|
+
:self, :templated, foo: 'foo', attrs: { bar: 'bar' }) { 'path' }
|
24
|
+
|
25
|
+
expect(link.name).to eq(:self)
|
26
|
+
expect(link.options).to eq(
|
27
|
+
foo: 'foo', attrs: { templated: true, bar: 'bar' })
|
28
|
+
expect(link.procedure.call).to eq('path')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'without procedure' do
|
33
|
+
describe 'with explicit value' do
|
34
|
+
it 'builds simple definition' do
|
35
|
+
link = klass.link(:self, value: 'path')
|
36
|
+
|
37
|
+
expect(link.name).to eq(:self)
|
38
|
+
expect(link.options).to eq(attrs: {}, value: 'path')
|
39
|
+
expect(link.procedure).to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'builds complex definition' do
|
43
|
+
link = klass.link(
|
44
|
+
:self,
|
45
|
+
:templated,
|
46
|
+
foo: 'foo', attrs: { bar: 'bar' }, value: 'path')
|
47
|
+
|
48
|
+
expect(link.name).to eq(:self)
|
49
|
+
expect(link.options).to eq(
|
50
|
+
foo: 'foo',
|
51
|
+
attrs: { templated: true, bar: 'bar' },
|
52
|
+
value: 'path')
|
53
|
+
expect(link.procedure).to be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Properties do
|
6
|
+
let :klass do
|
7
|
+
Class.new { include Halogen }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Halogen::Properties::ClassMethods do
|
11
|
+
describe '#property' do
|
12
|
+
it 'defines property 'do
|
13
|
+
expect {
|
14
|
+
klass.property(:foo)
|
15
|
+
}.to change(klass.definitions, :size).by(1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Halogen::Properties::InstanceMethods do
|
21
|
+
let :repr do
|
22
|
+
klass.new
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#render' do
|
26
|
+
it 'merges super with rendered properties' do
|
27
|
+
allow(repr).to receive(:properties).and_return(foo: 'bar')
|
28
|
+
|
29
|
+
expect(repr.render).to eq(foo: 'bar')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#properties' do
|
34
|
+
it 'builds properties from definitions' do
|
35
|
+
klass.property(:foo, value: 'bar')
|
36
|
+
|
37
|
+
expect(repr.properties).to eq(foo: 'bar')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Halogen::Resource do
|
6
|
+
let :klass do
|
7
|
+
Class.new do
|
8
|
+
include Halogen
|
9
|
+
include Halogen::Resource
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.included' do
|
14
|
+
it 'raises error if base is already a collection' do
|
15
|
+
resource_class = Class.new do
|
16
|
+
include Halogen
|
17
|
+
include Halogen::Collection
|
18
|
+
end
|
19
|
+
|
20
|
+
expect {
|
21
|
+
resource_class.send :include, Halogen::Resource
|
22
|
+
}.to raise_error do |exception|
|
23
|
+
expect(exception).to be_an_instance_of(Halogen::InvalidResource)
|
24
|
+
expect(exception.message).to match(/has already defined a collection/i)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Halogen::Resource::ClassMethods do
|
30
|
+
describe '#define_resource' do
|
31
|
+
it 'defines resource' do
|
32
|
+
klass.define_resource :foo
|
33
|
+
|
34
|
+
expect(klass.resource_name).to eq('foo')
|
35
|
+
expect(klass.new(nil).respond_to?(:foo)).to eq(true)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#property' do
|
40
|
+
it 'returns result of super if procedure is present' do
|
41
|
+
original = proc { 'bar' }
|
42
|
+
|
43
|
+
definition = klass.property(:foo, &original)
|
44
|
+
|
45
|
+
expect(definition.procedure).to eq(original)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns result of super if value is present' do
|
49
|
+
definition = klass.property(:foo, value: 'bar')
|
50
|
+
|
51
|
+
expect(definition.procedure).to be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'assigns procedure without original procedure or value' do
|
55
|
+
definition = klass.property(:foo)
|
56
|
+
|
57
|
+
expect(definition.procedure).to be_an_instance_of(Proc)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe Halogen::Resource::InstanceMethods do
|
63
|
+
describe '#initialize' do
|
64
|
+
it 'raises error if resource is not provided' do
|
65
|
+
expect { klass.new }.to raise_error(ArgumentError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'assigns resource' do
|
69
|
+
resource = double(:resource)
|
70
|
+
|
71
|
+
repr = klass.new(resource)
|
72
|
+
|
73
|
+
expect(repr.instance_variable_get(:@resource)).to eq(resource)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|