yaks 0.3.1 → 0.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +0 -2
- data/LICENSE +7 -0
- data/README.md +160 -35
- data/Rakefile +2 -1
- data/lib/yaks/collection_mapper.rb +25 -18
- data/lib/yaks/collection_resource.rb +11 -17
- data/lib/yaks/config.rb +96 -0
- data/lib/yaks/default_policy.rb +34 -4
- data/lib/yaks/fp.rb +18 -0
- data/lib/yaks/mapper/association.rb +19 -27
- data/lib/yaks/mapper/class_methods.rb +4 -2
- data/lib/yaks/mapper/config.rb +24 -39
- data/lib/yaks/mapper/has_many.rb +7 -6
- data/lib/yaks/mapper/has_one.rb +4 -3
- data/lib/yaks/mapper/link.rb +52 -55
- data/lib/yaks/mapper.rb +38 -26
- data/lib/yaks/null_resource.rb +3 -3
- data/lib/yaks/primitivize.rb +29 -27
- data/lib/yaks/resource/link.rb +4 -0
- data/lib/yaks/resource.rb +18 -7
- data/lib/yaks/serializer/collection_json.rb +38 -0
- data/lib/yaks/serializer/hal.rb +55 -0
- data/lib/yaks/serializer/json_api.rb +61 -0
- data/lib/yaks/serializer.rb +25 -4
- data/lib/yaks/util.rb +2 -42
- data/lib/yaks/version.rb +1 -1
- data/lib/yaks.rb +10 -32
- data/notes.org +72 -0
- data/shaved_yak.gif +0 -0
- data/spec/acceptance/acceptance_spec.rb +46 -0
- data/spec/acceptance/models.rb +28 -0
- data/spec/integration/map_to_resource_spec.rb +11 -15
- data/spec/json/confucius.hal.json +23 -0
- data/spec/json/confucius.json_api.json +22 -0
- data/spec/json/john.hal.json +29 -0
- data/spec/json/youtypeitwepostit.collection.json +45 -0
- data/spec/spec_helper.rb +12 -1
- data/spec/support/shared_contexts.rb +7 -10
- data/spec/support/youtypeit_models_mappers.rb +20 -0
- data/spec/unit/yaks/collection_mapper_spec.rb +84 -0
- data/spec/unit/yaks/collection_resource_spec.rb +72 -0
- data/spec/unit/yaks/config_spec.rb +129 -0
- data/spec/unit/yaks/fp_spec.rb +31 -0
- data/spec/unit/yaks/mapper/association_spec.rb +80 -0
- data/spec/{yaks → unit/yaks}/mapper/class_methods_spec.rb +4 -4
- data/spec/unit/yaks/mapper/config_spec.rb +191 -0
- data/spec/unit/yaks/mapper/has_many_spec.rb +46 -0
- data/spec/unit/yaks/mapper/has_one_spec.rb +34 -0
- data/spec/unit/yaks/mapper/link_spec.rb +152 -0
- data/spec/unit/yaks/mapper_spec.rb +177 -0
- data/spec/unit/yaks/resource_spec.rb +40 -0
- data/spec/{yaks/hal_serializer_spec.rb → unit/yaks/serializer/hal_spec.rb} +2 -2
- data/spec/unit/yaks/serializer_spec.rb +12 -0
- data/spec/unit/yaks/util_spec.rb +43 -0
- data/spec/yaml/confucius.yaml +10 -0
- data/spec/yaml/youtypeitwepostit.yaml +9 -0
- data/yaks.gemspec +7 -8
- metadata +92 -53
- data/Gemfile.lock +0 -111
- data/lib/yaks/hal_serializer.rb +0 -59
- data/lib/yaks/json_api_serializer.rb +0 -59
- data/lib/yaks/link_lookup.rb +0 -23
- data/lib/yaks/mapper/lookup.rb +0 -19
- data/lib/yaks/mapper/map_links.rb +0 -17
- data/lib/yaks/profile_registry.rb +0 -60
- data/lib/yaks/rel_registry.rb +0 -20
- data/lib/yaks/shared_options.rb +0 -15
- data/spec/support/shorthands.rb +0 -22
- data/spec/yaks/collection_resource_spec.rb +0 -9
- data/spec/yaks/mapper/association_spec.rb +0 -21
- data/spec/yaks/mapper/config_spec.rb +0 -77
- data/spec/yaks/mapper/has_one_spec.rb +0 -16
- data/spec/yaks/mapper/link_spec.rb +0 -38
- data/spec/yaks/mapper/map_links_spec.rb +0 -46
- data/spec/yaks/mapper_spec.rb +0 -65
- data/spec/yaks/resource_spec.rb +0 -23
@@ -0,0 +1,20 @@
|
|
1
|
+
module Youtypeitwepostit
|
2
|
+
class Message
|
3
|
+
include Virtus.model
|
4
|
+
|
5
|
+
attribute :id, Integer
|
6
|
+
attribute :text, String
|
7
|
+
attribute :date_posted, String
|
8
|
+
end
|
9
|
+
|
10
|
+
class MessageMapper < Yaks::Mapper
|
11
|
+
link :self, 'http://www.youtypeitwepostit.com/api/{id}'
|
12
|
+
link :profile, 'http://www.youtypeitwepostit.com/profiles/message'
|
13
|
+
|
14
|
+
attributes :text, :date_posted
|
15
|
+
end
|
16
|
+
|
17
|
+
class CollectionMapper < Yaks::CollectionMapper
|
18
|
+
link :self, 'http://www.youtypeitwepostit.com/api/'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::CollectionMapper do
|
4
|
+
include_context 'fixtures'
|
5
|
+
|
6
|
+
subject(:mapper) { described_class.new(collection, context) }
|
7
|
+
let(:context) {
|
8
|
+
{ resource_mapper: resource_mapper,
|
9
|
+
policy: policy,
|
10
|
+
env: {}
|
11
|
+
}
|
12
|
+
}
|
13
|
+
let(:collection) { [] }
|
14
|
+
let(:resource_mapper) { Class.new(Yaks::Mapper) { type 'the_type' } }
|
15
|
+
let(:policy) { Yaks::DefaultPolicy.new }
|
16
|
+
|
17
|
+
its(:to_resource) {
|
18
|
+
should eql Yaks::CollectionResource.new(
|
19
|
+
type: 'the_type',
|
20
|
+
links: [],
|
21
|
+
attributes: {},
|
22
|
+
members: []
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
context 'with members' do
|
27
|
+
let(:collection) { [boingboing, wassup]}
|
28
|
+
let(:resource_mapper) { PetMapper }
|
29
|
+
|
30
|
+
its(:to_resource) {
|
31
|
+
should eql Yaks::CollectionResource.new(
|
32
|
+
type: 'pet',
|
33
|
+
links: [],
|
34
|
+
attributes: {},
|
35
|
+
members: [
|
36
|
+
Yaks::Resource.new(type: 'pet', attributes: {:id => 2, :species => "dog", :name => "boingboing"}),
|
37
|
+
Yaks::Resource.new(type: 'pet', attributes: {:id => 3, :species => "cat", :name => "wassup"})
|
38
|
+
]
|
39
|
+
)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with collection attributes' do
|
44
|
+
subject(:mapper) {
|
45
|
+
Class.new(Yaks::CollectionMapper) do
|
46
|
+
attributes :foo, :bar
|
47
|
+
end.new(collection, context)
|
48
|
+
}
|
49
|
+
|
50
|
+
let(:collection) {
|
51
|
+
Class.new(SimpleDelegator) do
|
52
|
+
def foo ; 123 ; end
|
53
|
+
def bar ; 'pi la~~~' ; end
|
54
|
+
end.new([])
|
55
|
+
}
|
56
|
+
|
57
|
+
its(:to_resource) {
|
58
|
+
should eql Yaks::CollectionResource.new(
|
59
|
+
type: 'the_type',
|
60
|
+
links: [],
|
61
|
+
attributes: { foo: 123, bar: 'pi la~~~' },
|
62
|
+
members: []
|
63
|
+
)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with collection links' do
|
68
|
+
subject(:mapper) {
|
69
|
+
Class.new(Yaks::CollectionMapper) do
|
70
|
+
link :self, 'http://api.example.com/orders'
|
71
|
+
end.new(collection, context)
|
72
|
+
}
|
73
|
+
|
74
|
+
its(:to_resource) {
|
75
|
+
should eql Yaks::CollectionResource.new(
|
76
|
+
type: 'the_type',
|
77
|
+
links: [ Yaks::Resource::Link.new(:self, 'http://api.example.com/orders', {}) ],
|
78
|
+
attributes: { },
|
79
|
+
members: []
|
80
|
+
)
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::CollectionResource do
|
4
|
+
subject(:collection) { described_class.new(init_opts) }
|
5
|
+
let(:init_opts) { {} }
|
6
|
+
|
7
|
+
its(:collection?) { should equal true }
|
8
|
+
|
9
|
+
context 'with nothing passed in the contstructor' do
|
10
|
+
its(:type) { should be_nil }
|
11
|
+
its(:links) { should eql [] }
|
12
|
+
its(:attributes) { should eql({}) }
|
13
|
+
its(:members) { should eql [] }
|
14
|
+
its(:subresources) { should eql({}) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with a full constructor' do
|
18
|
+
let(:init_opts) {
|
19
|
+
{
|
20
|
+
type: 'order',
|
21
|
+
links: [
|
22
|
+
Yaks::Resource::Link.new('http://rels/summary', 'http://order/10/summary', {}),
|
23
|
+
Yaks::Resource::Link.new(:profile, 'http://rels/collection', {})
|
24
|
+
],
|
25
|
+
attributes: { total: 10.00 },
|
26
|
+
members: [
|
27
|
+
Yaks::Resource.new(
|
28
|
+
type: 'order',
|
29
|
+
links: [Yaks::Resource::Link.new(:self, 'http://order/10', {})],
|
30
|
+
attributes: { customer: 'John Doe', price: 10.00 }
|
31
|
+
)
|
32
|
+
]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
its(:type) { should eql 'order' }
|
37
|
+
its(:links) { should eql [
|
38
|
+
Yaks::Resource::Link.new('http://rels/summary', 'http://order/10/summary', {}),
|
39
|
+
Yaks::Resource::Link.new(:profile, 'http://rels/collection', {})
|
40
|
+
]
|
41
|
+
}
|
42
|
+
its(:attributes) { should eql( total: 10.00 ) }
|
43
|
+
its(:members) { should eql [
|
44
|
+
Yaks::Resource.new(
|
45
|
+
type: 'order',
|
46
|
+
links: [Yaks::Resource::Link.new(:self, 'http://order/10', {})],
|
47
|
+
attributes: { customer: 'John Doe', price: 10.00 }
|
48
|
+
)
|
49
|
+
]
|
50
|
+
}
|
51
|
+
|
52
|
+
its(:subresources) { should eql(
|
53
|
+
'http://rels/collection' => Yaks::CollectionResource.new(
|
54
|
+
type: 'order',
|
55
|
+
attributes: { total: 10.00 },
|
56
|
+
links: [
|
57
|
+
Yaks::Resource::Link.new('http://rels/summary', 'http://order/10/summary', {}),
|
58
|
+
Yaks::Resource::Link.new(:profile, 'http://rels/collection', {})
|
59
|
+
],
|
60
|
+
members: [
|
61
|
+
Yaks::Resource.new(
|
62
|
+
type: 'order',
|
63
|
+
links: [Yaks::Resource::Link.new(:self, 'http://order/10', {})],
|
64
|
+
attributes: { customer: 'John Doe', price: 10.00 }
|
65
|
+
)
|
66
|
+
]
|
67
|
+
)
|
68
|
+
)
|
69
|
+
}
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::Config do
|
4
|
+
include_context 'fixtures'
|
5
|
+
|
6
|
+
def self.configure(&blk)
|
7
|
+
subject(:config) { described_class.new(&blk) }
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'defaults' do
|
11
|
+
configure {}
|
12
|
+
|
13
|
+
its(:default_format) { should equal :hal }
|
14
|
+
its(:policy_class) { should < Yaks::DefaultPolicy }
|
15
|
+
|
16
|
+
it 'should have empty format options' do
|
17
|
+
expect(config.options_for_format(:hal)).to eql({})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a default format' do
|
22
|
+
configure do
|
23
|
+
default_format :json_api
|
24
|
+
end
|
25
|
+
|
26
|
+
its(:default_format) { should equal :json_api }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with a custom policy class' do
|
30
|
+
MyPolicy = Struct.new(:options)
|
31
|
+
configure do
|
32
|
+
policy MyPolicy
|
33
|
+
end
|
34
|
+
|
35
|
+
its(:policy_class) { should equal MyPolicy }
|
36
|
+
its(:policy) { should be_a MyPolicy }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with a rel template' do
|
40
|
+
configure do
|
41
|
+
rel_template 'http://rel/foo'
|
42
|
+
end
|
43
|
+
|
44
|
+
its(:policy_options) { should eql(rel_template: 'http://rel/foo') }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with format options' do
|
48
|
+
configure do
|
49
|
+
format_options :hal, plural_links: [:self, :profile]
|
50
|
+
end
|
51
|
+
|
52
|
+
specify do
|
53
|
+
expect(config.options_for_format(:hal)).to eql(plural_links: [:self, :profile])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#serialize' do
|
58
|
+
configure do
|
59
|
+
rel_template 'http://api.mysuperfriends.com/{association_name}'
|
60
|
+
format_options :hal, plural_links: [:copyright]
|
61
|
+
end
|
62
|
+
|
63
|
+
specify do
|
64
|
+
expect(config.serialize(john)).to eql(load_json_fixture 'john.hal')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#mapper_namespace' do
|
69
|
+
module MyMappers
|
70
|
+
class PetMapper < Yaks::Mapper
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
configure do
|
75
|
+
mapper_namespace MyMappers
|
76
|
+
end
|
77
|
+
|
78
|
+
specify do
|
79
|
+
expect(config.policy.derive_mapper_from_object(boingboing)).to eql(MyMappers::PetMapper)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#map_to_primitive' do
|
84
|
+
class TheMapper < Yaks::Mapper
|
85
|
+
attributes :a_date
|
86
|
+
end
|
87
|
+
|
88
|
+
TheModel = Struct.new(:a_date)
|
89
|
+
|
90
|
+
configure do
|
91
|
+
map_to_primitive Date do |object|
|
92
|
+
object.iso8601
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
let(:model) {
|
97
|
+
TheModel.new(Date.new(2014, 5, 6))
|
98
|
+
}
|
99
|
+
|
100
|
+
specify do
|
101
|
+
expect(config.serialize(model, mapper: TheMapper)).to eq({"a_date"=>"2014-05-06"})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'passing in a rack env' do
|
106
|
+
configure do
|
107
|
+
default_format :collection_json
|
108
|
+
end
|
109
|
+
|
110
|
+
let(:rack_env) {
|
111
|
+
{ 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json' }
|
112
|
+
}
|
113
|
+
|
114
|
+
it 'should detect serializer based on accept header' do
|
115
|
+
rack_env = { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json' }
|
116
|
+
expect(config.serializer_class({}, rack_env)).to equal Yaks::Serializer::JsonApi
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should know to pick the best match' do
|
120
|
+
rack_env = { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json;q=0.7' }
|
121
|
+
expect(config.serializer_class({}, rack_env)).to equal Yaks::Serializer::Hal
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should fall back to the default when no mime type is recognized' do
|
125
|
+
rack_env = { 'HTTP_ACCEPT' => 'text/html, application/json' }
|
126
|
+
expect(config.serializer_class({}, rack_env)).to equal Yaks::Serializer::CollectionJson
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::FP do
|
4
|
+
include described_class
|
5
|
+
|
6
|
+
describe '#curry_method' do
|
7
|
+
def method_3args(a,b,c)
|
8
|
+
"#{a}-#{b}-#{c}"
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should curry the method' do
|
12
|
+
expect(curry_method(:method_3args).(1).(2,3)).to eql "1-2-3"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#send_with_args' do
|
17
|
+
it 'should bind the arguments' do
|
18
|
+
expect(send_with_args(:+, 'foo').('bar')).to eql 'barfoo'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should bind the block' do
|
22
|
+
expect(send_with_args(:map) {|x| x.upcase }.(['bar'])).to eql ['BAR']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#identity_function' do
|
27
|
+
it 'should return whatever you pass it' do
|
28
|
+
expect(identity_function.(:foo)).to equal :foo
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::Mapper::Association do
|
4
|
+
include Yaks::FP
|
5
|
+
|
6
|
+
let(:name) { :shoes }
|
7
|
+
let(:mapper) { Yaks::Mapper }
|
8
|
+
let(:rel) { Yaks::Undefined }
|
9
|
+
let(:collection_mapper) { Yaks::Undefined }
|
10
|
+
let(:parent_mapper) { Yaks::Undefined }
|
11
|
+
let(:map_resource) { ->(obj, policy) {} }
|
12
|
+
let(:lookup) { ->(*) {} }
|
13
|
+
let(:policy) { Yaks::DefaultPolicy.new }
|
14
|
+
let(:context) { { policy: policy, env: {} } }
|
15
|
+
|
16
|
+
its(:name) { should equal :shoes }
|
17
|
+
|
18
|
+
subject(:association) do
|
19
|
+
described_class.new(name, mapper, rel, collection_mapper)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#create_subresource' do
|
23
|
+
subject(:resource_pair) do
|
24
|
+
association.create_subresource(parent_mapper, lookup, context)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with a rel specified' do
|
28
|
+
let(:rel) { 'http://api.com/rels/shoes' }
|
29
|
+
|
30
|
+
it 'should use the specified rel' do
|
31
|
+
expect(resource_pair[0]).to eql 'http://api.com/rels/shoes'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'without a rel specified' do
|
36
|
+
it 'should infer a rel based on policy' do
|
37
|
+
expect(policy)
|
38
|
+
.to receive(:derive_rel_from_association)
|
39
|
+
.with(parent_mapper, association)
|
40
|
+
.and_return('http://api.com/rel/derived')
|
41
|
+
|
42
|
+
expect(resource_pair[0]).to eql 'http://api.com/rel/derived'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:lookup) { { shoes: 'unmapped resource' } }
|
47
|
+
|
48
|
+
it 'should delegate to the map_resource method, to be overridden in child classes' do
|
49
|
+
expect(association)
|
50
|
+
.to receive(:map_resource)
|
51
|
+
.with('unmapped resource', context)
|
52
|
+
.and_return('mapped resource')
|
53
|
+
|
54
|
+
expect(resource_pair[1]).to eql 'mapped resource'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#association_mapper' do
|
59
|
+
context 'with a specified mapper' do
|
60
|
+
let(:mapper) { :a_specific_mapper_class }
|
61
|
+
|
62
|
+
it 'should return the mapper' do
|
63
|
+
expect(association.association_mapper(nil)).to equal :a_specific_mapper_class
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with the mapper undefined' do
|
68
|
+
let(:mapper) { Yaks::Undefined }
|
69
|
+
|
70
|
+
it 'should derive a mapper based on policy' do
|
71
|
+
expect(policy)
|
72
|
+
.to receive(:derive_mapper_from_association)
|
73
|
+
.with(association)
|
74
|
+
.and_return(:a_derived_mapper_class)
|
75
|
+
|
76
|
+
expect(association.association_mapper(policy)).to equal :a_derived_mapper_class
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Yaks::Mapper::ClassMethods do
|
3
|
+
RSpec.describe Yaks::Mapper::ClassMethods do
|
4
4
|
subject { Class.new { extend Yaks::Mapper::ClassMethods } }
|
5
5
|
|
6
6
|
describe 'attributes' do
|
@@ -9,7 +9,7 @@ describe Yaks::Mapper::ClassMethods do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'should allow setting them' do
|
12
|
-
expect( subject.attributes ).to eq
|
12
|
+
expect( subject.attributes ).to eq [:foo, :bar]
|
13
13
|
end
|
14
14
|
|
15
15
|
describe 'with inheritance' do
|
@@ -17,11 +17,11 @@ describe Yaks::Mapper::ClassMethods do
|
|
17
17
|
before { child.attributes(:baz) }
|
18
18
|
|
19
19
|
it 'should inherit attributes from the parent' do
|
20
|
-
expect(child.attributes).to eq
|
20
|
+
expect(child.attributes).to eq [:foo, :bar, :baz]
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'should not alter the parent' do
|
24
|
-
expect(subject.attributes).to eq
|
24
|
+
expect(subject.attributes).to eq [:foo, :bar]
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::Mapper::Config do
|
4
|
+
Undefined = Yaks::Undefined
|
5
|
+
|
6
|
+
subject(:config) { described_class.new(nil, [], [], []) }
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
subject(:config) { described_class.new('foo', [:a], [:b], [:c]) }
|
10
|
+
|
11
|
+
its(:type) { should eql 'foo' }
|
12
|
+
its(:attributes) { should eql [:a] }
|
13
|
+
its(:links) { should eql [:b] }
|
14
|
+
its(:associations) { should eql [:c] }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#updated' do
|
18
|
+
context 'with no updates' do
|
19
|
+
let(:config) {
|
20
|
+
super()
|
21
|
+
.type('foo')
|
22
|
+
.attributes(:a, :b, :c)
|
23
|
+
.link(:foo, 'http://bar')
|
24
|
+
.has_many(:bars)
|
25
|
+
}
|
26
|
+
|
27
|
+
it 'should update attributes' do
|
28
|
+
expect(config.updated(attributes: [:foo])).to eql described_class.new(
|
29
|
+
'foo',
|
30
|
+
[:foo],
|
31
|
+
[Yaks::Mapper::Link.new(:foo, 'http://bar', {})],
|
32
|
+
[Yaks::Mapper::HasMany.new(:bars, Undefined, Undefined, Undefined)]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should update links' do
|
37
|
+
expect(config.updated(links: [:foo])).to eql described_class.new(
|
38
|
+
'foo',
|
39
|
+
[:a, :b, :c],
|
40
|
+
[:foo],
|
41
|
+
[Yaks::Mapper::HasMany.new(:bars, Undefined, Undefined, Undefined)]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should update associations' do
|
46
|
+
expect(config.updated(associations: [:foo])).to eql described_class.new(
|
47
|
+
'foo',
|
48
|
+
[:a, :b, :c],
|
49
|
+
[Yaks::Mapper::Link.new(:foo, 'http://bar', {})],
|
50
|
+
[:foo]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#attributes' do
|
57
|
+
context 'an empty config' do
|
58
|
+
it 'should return an empty attributes list' do
|
59
|
+
expect(config.attributes).to eq []
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should add attributes' do
|
64
|
+
expect(config.attributes(:foo, :bar, :baz).attributes)
|
65
|
+
.to eq [:foo, :bar, :baz]
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should be chainable' do
|
69
|
+
expect(
|
70
|
+
config
|
71
|
+
.attributes(:foo, :bar)
|
72
|
+
.attributes(:baz)
|
73
|
+
.attributes
|
74
|
+
).to eq [:foo, :bar, :baz]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#links' do
|
79
|
+
context 'an empty config' do
|
80
|
+
it 'should have an empty link list' do
|
81
|
+
expect(config.links).to eq []
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'adding links' do
|
86
|
+
let(:config) {
|
87
|
+
subject
|
88
|
+
.link(:self, '/foo/bar/{id}')
|
89
|
+
.link(:profile, '/profile/foo')
|
90
|
+
}
|
91
|
+
|
92
|
+
it 'should have the links in the link list' do
|
93
|
+
expect(config.links).to include Yaks::Mapper::Link.new(:profile, '/profile/foo', {})
|
94
|
+
expect(config.links).to include Yaks::Mapper::Link.new(:self, '/foo/bar/{id}', {})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'links with the same rel' do
|
99
|
+
let(:config) {
|
100
|
+
subject
|
101
|
+
.link(:self, '/foo/self')
|
102
|
+
.link(:self, '/foo/me')
|
103
|
+
}
|
104
|
+
|
105
|
+
it 'should have the links in the defined order' do
|
106
|
+
expect(config.links).to eql [
|
107
|
+
Yaks::Mapper::Link.new(:self, '/foo/self', {}),
|
108
|
+
Yaks::Mapper::Link.new(:self, '/foo/me', {})
|
109
|
+
]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#has_one' do
|
115
|
+
context 'with a mapper specified' do
|
116
|
+
let(:config) { subject.has_one :mother, mapper: Yaks::Mapper }
|
117
|
+
|
118
|
+
it 'should have the association configured' do
|
119
|
+
expect(config.associations).to eq [
|
120
|
+
Yaks::Mapper::HasOne.new(:mother, Yaks::Mapper, Undefined, Undefined)
|
121
|
+
]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with no options' do
|
126
|
+
let(:config) { subject.has_one :mother }
|
127
|
+
|
128
|
+
it 'should have undefined mapper, rel, collection_mapper' do
|
129
|
+
expect(config.associations).to eq [
|
130
|
+
Yaks::Mapper::HasOne.new(:mother, Undefined, Undefined, Undefined)
|
131
|
+
]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'with a rel specified' do
|
136
|
+
let(:config) { subject.has_one :mother, rel: '/api/rels/mother' }
|
137
|
+
|
138
|
+
it 'should have the rel' do
|
139
|
+
expect(config.associations).to eq [
|
140
|
+
Yaks::Mapper::HasOne.new(:mother, Undefined, '/api/rels/mother', Undefined)
|
141
|
+
]
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#has_many' do
|
148
|
+
context 'with a mapper specified' do
|
149
|
+
let(:config) { subject.has_many :shoes, mapper: Yaks::Mapper }
|
150
|
+
|
151
|
+
it 'should have the association configured' do
|
152
|
+
expect(config.associations).to eq [
|
153
|
+
Yaks::Mapper::HasMany.new(:shoes, Yaks::Mapper, Undefined, Undefined)
|
154
|
+
]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'with no options' do
|
159
|
+
let(:config) { subject.has_many :shoes }
|
160
|
+
|
161
|
+
it 'should have undefined mapper, rel, collection_mapper' do
|
162
|
+
expect(config.associations).to eq [
|
163
|
+
Yaks::Mapper::HasMany.new(:shoes, Undefined, Undefined, Undefined)
|
164
|
+
]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'with a collection mapper set' do
|
169
|
+
let(:config) { subject.has_many :shoes, collection_mapper: :a_collection_mapper }
|
170
|
+
|
171
|
+
it 'should have the association configured' do
|
172
|
+
expect(config.associations).to eq [
|
173
|
+
Yaks::Mapper::HasMany.new(:shoes, Undefined, Undefined, :a_collection_mapper)
|
174
|
+
]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'multiple associations' do
|
180
|
+
let(:config) {
|
181
|
+
subject
|
182
|
+
.has_many(:shoes)
|
183
|
+
.has_one(:mother)
|
184
|
+
}
|
185
|
+
|
186
|
+
it 'should have them all present' do
|
187
|
+
expect(config.associations).to include Yaks::Mapper::HasOne.new(:mother, Undefined, Undefined, Undefined)
|
188
|
+
expect(config.associations).to include Yaks::Mapper::HasMany.new(:shoes, Undefined, Undefined, Undefined)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::Mapper::HasMany do
|
4
|
+
let(:closet_mapper) do
|
5
|
+
Class.new(Yaks::Mapper) do
|
6
|
+
has_many :shoes,
|
7
|
+
rel: 'http://foo/shoes',
|
8
|
+
mapper: Class.new(Yaks::Mapper) { type 'shoes' ; attributes :size, :color }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:closet) {
|
13
|
+
double(
|
14
|
+
:shoes => [
|
15
|
+
double(size: 9, color: :blue),
|
16
|
+
double(size: 11.5, color: :red),
|
17
|
+
]
|
18
|
+
)
|
19
|
+
}
|
20
|
+
|
21
|
+
it 'should map the subresources' do
|
22
|
+
expect(closet_mapper.new(closet, policy: Yaks::DefaultPolicy.new, env: {}).map_subresources).to eql(
|
23
|
+
"http://foo/shoes" => Yaks::CollectionResource.new(
|
24
|
+
type: 'shoes',
|
25
|
+
members: [
|
26
|
+
Yaks::Resource.new(type: 'shoes', attributes: {:size => 9, :color => :blue}),
|
27
|
+
Yaks::Resource.new(type: 'shoes', attributes: {:size => 11.5, :color => :red})
|
28
|
+
]
|
29
|
+
)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#collection_mapper' do
|
34
|
+
let(:collection_mapper) { Yaks::Undefined }
|
35
|
+
subject(:has_many) { described_class.new(:name, :mapper, :rel, collection_mapper) }
|
36
|
+
|
37
|
+
context 'when the collection mapper is undefined' do
|
38
|
+
its(:collection_mapper) { should equal Yaks::CollectionMapper }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when the collection mapper is specified' do
|
42
|
+
let(:collection_mapper) { :foo }
|
43
|
+
its(:collection_mapper) { should equal :foo }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|