yaks 0.3.1 → 0.4.0.rc1
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.
- 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
|