yaks 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +158 -56
  3. data/Rakefile +1 -3
  4. data/ataru_setup.rb +72 -0
  5. data/find_missing_tests.rb +34 -0
  6. data/lib/yaks.rb +8 -10
  7. data/lib/yaks/breaking_changes.rb +4 -6
  8. data/lib/yaks/builder.rb +1 -1
  9. data/lib/yaks/changelog.rb +1 -1
  10. data/lib/yaks/collection_mapper.rb +8 -5
  11. data/lib/yaks/collection_resource.rb +0 -1
  12. data/lib/yaks/config.rb +17 -7
  13. data/lib/yaks/configurable.rb +20 -14
  14. data/lib/yaks/default_policy.rb +82 -33
  15. data/lib/yaks/format.rb +7 -3
  16. data/lib/yaks/format/collection_json.rb +4 -4
  17. data/lib/yaks/format/hal.rb +1 -2
  18. data/lib/yaks/format/halo.rb +2 -4
  19. data/lib/yaks/format/json_api.rb +46 -27
  20. data/lib/yaks/html5_forms.rb +0 -2
  21. data/lib/yaks/mapper.rb +5 -5
  22. data/lib/yaks/mapper/association.rb +7 -7
  23. data/lib/yaks/mapper/association_mapper.rb +2 -0
  24. data/lib/yaks/mapper/attribute.rb +10 -4
  25. data/lib/yaks/mapper/config.rb +2 -2
  26. data/lib/yaks/mapper/form.rb +4 -10
  27. data/lib/yaks/mapper/form/config.rb +16 -17
  28. data/lib/yaks/mapper/form/dynamic_field.rb +1 -1
  29. data/lib/yaks/mapper/form/field.rb +16 -7
  30. data/lib/yaks/mapper/form/field/option.rb +5 -4
  31. data/lib/yaks/mapper/form/fieldset.rb +1 -1
  32. data/lib/yaks/mapper/form/legend.rb +18 -0
  33. data/lib/yaks/mapper/has_many.rb +1 -0
  34. data/lib/yaks/mapper/link.rb +7 -4
  35. data/lib/yaks/null_resource.rb +4 -5
  36. data/lib/yaks/pipeline.rb +2 -2
  37. data/lib/yaks/primitivize.rb +3 -2
  38. data/lib/yaks/reader/hal.rb +12 -13
  39. data/lib/yaks/reader/json_api.rb +50 -33
  40. data/lib/yaks/resource.rb +6 -7
  41. data/lib/yaks/resource/form.rb +2 -12
  42. data/lib/yaks/resource/form/field.rb +4 -3
  43. data/lib/yaks/resource/form/field/option.rb +1 -1
  44. data/lib/yaks/resource/form/fieldset.rb +1 -1
  45. data/lib/yaks/resource/form/legend.rb +18 -0
  46. data/lib/yaks/resource/has_fields.rb +13 -7
  47. data/lib/yaks/resource/link.rb +1 -1
  48. data/lib/yaks/runner.rb +5 -2
  49. data/lib/yaks/serializer.rb +2 -3
  50. data/lib/yaks/util.rb +7 -8
  51. data/lib/yaks/version.rb +1 -1
  52. data/spec/acceptance/acceptance_spec.rb +53 -38
  53. data/spec/acceptance/json_shared_examples.rb +45 -12
  54. data/spec/acceptance/models.rb +1 -1
  55. data/spec/integration/dynamic_form_fields_spec.rb +0 -1
  56. data/spec/integration/fieldset_spec.rb +18 -20
  57. data/spec/integration/map_to_resource_spec.rb +6 -6
  58. data/spec/json/{confucius.collection.json → confucius.collection_json.json} +0 -0
  59. data/spec/json/confucius.json_api.json +43 -27
  60. data/spec/json/list_of_quotes.collection_json.json +43 -0
  61. data/spec/json/list_of_quotes.hal.json +18 -0
  62. data/spec/json/list_of_quotes.json_api.json +25 -0
  63. data/spec/json/youtypeitwepostit.collection_json.json +45 -0
  64. data/spec/spec_helper.rb +4 -3
  65. data/spec/support/classes_for_policy_testing.rb +38 -14
  66. data/spec/support/deep_eql.rb +21 -18
  67. data/spec/support/pet_mapper.rb +2 -0
  68. data/spec/support/shared_contexts.rb +9 -9
  69. data/spec/unit/yaks/builder_spec.rb +41 -18
  70. data/spec/unit/yaks/collection_mapper_spec.rb +22 -19
  71. data/spec/unit/yaks/collection_resource_spec.rb +16 -8
  72. data/spec/unit/yaks/config_spec.rb +215 -19
  73. data/spec/unit/yaks/configurable_spec.rb +66 -7
  74. data/spec/unit/yaks/default_policy/derive_mapper_from_collection_spec.rb +47 -0
  75. data/spec/unit/yaks/default_policy/derive_mapper_from_item_spec.rb +114 -0
  76. data/spec/unit/yaks/default_policy/derive_mapper_from_object_spec.rb +29 -71
  77. data/spec/unit/yaks/default_policy_spec.rb +4 -5
  78. data/spec/unit/yaks/format/collection_json_spec.rb +35 -36
  79. data/spec/unit/yaks/format/hal_spec.rb +3 -3
  80. data/spec/unit/yaks/format/json_api_spec.rb +109 -68
  81. data/spec/unit/yaks/format_spec.rb +34 -0
  82. data/spec/unit/yaks/fp/callable_spec.rb +5 -3
  83. data/spec/unit/yaks/mapper/association_mapper_spec.rb +22 -4
  84. data/spec/unit/yaks/mapper/association_spec.rb +23 -11
  85. data/spec/unit/yaks/mapper/attribute_spec.rb +46 -7
  86. data/spec/unit/yaks/mapper/config_spec.rb +2 -3
  87. data/spec/unit/yaks/mapper/form/config_spec.rb +95 -0
  88. data/spec/unit/yaks/mapper/form/dynamic_field_spec.rb +30 -0
  89. data/spec/unit/yaks/mapper/form/field/option_spec.rb +48 -4
  90. data/spec/unit/yaks/mapper/form/field_spec.rb +43 -2
  91. data/spec/unit/yaks/mapper/form/fieldset_spec.rb +67 -8
  92. data/spec/unit/yaks/mapper/form/legend_spec.rb +52 -0
  93. data/spec/unit/yaks/mapper/form_spec.rb +84 -23
  94. data/spec/unit/yaks/mapper/has_many_spec.rb +39 -36
  95. data/spec/unit/yaks/mapper/has_one_spec.rb +28 -20
  96. data/spec/unit/yaks/mapper/link_spec.rb +68 -16
  97. data/spec/unit/yaks/mapper_spec.rb +118 -30
  98. data/spec/unit/yaks/null_resource_spec.rb +83 -52
  99. data/spec/unit/yaks/pipeline_spec.rb +101 -74
  100. data/spec/unit/yaks/primitivize_spec.rb +25 -6
  101. data/spec/unit/yaks/resource/form/field_spec.rb +5 -5
  102. data/spec/unit/yaks/resource/form/fieldset_spec.rb +7 -0
  103. data/spec/unit/yaks/resource/form/legend_spec.rb +8 -0
  104. data/spec/unit/yaks/resource/form_spec.rb +17 -37
  105. data/spec/unit/yaks/resource/has_fields_spec.rb +44 -3
  106. data/spec/unit/yaks/resource/link_spec.rb +11 -6
  107. data/spec/unit/yaks/resource_spec.rb +87 -98
  108. data/spec/unit/yaks/runner_spec.rb +112 -28
  109. data/spec/unit/yaks/serializer_spec.rb +1 -1
  110. data/spec/unit/yaks/util_spec.rb +30 -10
  111. data/spec/yaml/list_of_quotes.yaml +13 -0
  112. data/yaks.gemspec +21 -13
  113. metadata +129 -41
  114. data/lib/yaks/attributes.rb +0 -86
  115. data/lib/yaks/fp.rb +0 -26
  116. data/lib/yaks/identifier/link_relation.rb +0 -17
  117. data/resources/iana-link-relations.csv +0 -152
  118. data/spec/json/youtypeitwepostit.collection.json +0 -45
  119. data/spec/unit/yaks/attributes_spec.rb +0 -178
  120. data/spec/unit/yaks/fp_spec.rb +0 -29
@@ -1,9 +1,10 @@
1
1
  RSpec.describe Yaks::Mapper::Form do
2
2
  include_context 'yaks context'
3
3
 
4
- let(:form) { described_class.create( full_args ) }
5
- let(:name) { :the_name }
6
- let(:full_args) { {name: name}.merge(args) }
4
+ let(:form) { described_class.create(*full_args, &block_arg) }
5
+ let(:name) { :the_name }
6
+ let(:full_args) { [{name: name}.merge(args)] }
7
+ let(:block_arg) { nil }
7
8
  let(:args) {
8
9
  {
9
10
  action: '/foo',
@@ -16,10 +17,31 @@ RSpec.describe Yaks::Mapper::Form do
16
17
  let(:fields) { [] }
17
18
  let(:mapper) { Yaks::Mapper.new(yaks_context) }
18
19
 
19
- describe '.create' do
20
- it 'should have a name of nil when ommitted' do
20
+ describe ".create" do
21
+ it "should have a name of nil when ommitted" do
21
22
  expect(described_class.create.name).to be_nil
22
23
  end
24
+
25
+ context "with a symbol arg" do
26
+ let(:full_args) { [:the_name] }
27
+ it "should use the symbol as the form's name" do
28
+ expect(form.name).to be :the_name
29
+ end
30
+ end
31
+
32
+ context "with a name given in the options hash" do
33
+ it "should use that name" do
34
+ expect(form.name).to be :the_name
35
+ end
36
+ end
37
+
38
+ context "with a block" do
39
+ let(:block_arg) { ->{ method 'POST' } }
40
+
41
+ it "should use it as configuration block" do
42
+ expect(form.method).to eql 'POST'
43
+ end
44
+ end
23
45
  end
24
46
 
25
47
  describe '#add_to_resource' do
@@ -38,6 +60,22 @@ RSpec.describe Yaks::Mapper::Form do
38
60
  }
39
61
  end
40
62
 
63
+ it "should add the form to the resource" do
64
+ expect(form.add_to_resource(Yaks::Resource.new, mapper, nil))
65
+ .to eql Yaks::Resource.new(
66
+ forms: [
67
+ Yaks::Resource::Form.new(
68
+ name: :the_name,
69
+ action: "/foo",
70
+ title: "a title",
71
+ method: "PATCH",
72
+ media_type: "application/hal+json",
73
+ fields: []
74
+ )
75
+ ]
76
+ )
77
+ end
78
+
41
79
  context 'with a truthy condition' do
42
80
  let(:form) { described_class.create { condition { true }}}
43
81
 
@@ -53,30 +91,53 @@ RSpec.describe Yaks::Mapper::Form do
53
91
  expect(form.add_to_resource(Yaks::Resource.new, mapper, nil).forms.length).to be 0
54
92
  end
55
93
  end
94
+ end
95
+
96
+ describe '#to_resource_form' do
97
+ let(:block_arg) do
98
+ -> do
99
+ method { 'POST' }
100
+ action { '/foo/bar' }
101
+
102
+ text :name, required: true
103
+ end
104
+ end
105
+
106
+ it "should create a matching Yaks::Resource::Form" do
107
+ expect(form.to_resource_form(mapper))
108
+ .to eql Yaks::Resource::Form.new(
109
+ name: :the_name,
110
+ action: "/foo/bar",
111
+ title: "a title",
112
+ method: "POST",
113
+ media_type: "application/hal+json",
114
+ fields: [
115
+ Yaks::Resource::Form::Field.new(name: :name, type: :text, required: true)
116
+ ]
117
+ )
118
+ end
56
119
 
57
- describe '#to_resource_form' do
58
- context 'with dynamic elements' do
59
- let(:form) do
60
- described_class.create(name) do
61
- dynamic do |object|
62
- text object.name
63
- end
120
+ context 'with dynamic elements' do
121
+ let(:args) {{}}
122
+ let(:block_arg) do
123
+ -> do
124
+ dynamic do |object|
125
+ text object.name
64
126
  end
65
127
  end
128
+ end
66
129
 
67
- it 'should render them based on the mapped object' do
68
- mapper.call(fake(name: :anthony)) # hack to set the mapper's object
69
- expect(form.to_resource_form(mapper)).to eql(
70
- Yaks::Resource::Form.new(
71
- name: :the_name,
72
- fields: [
73
- Yaks::Resource::Form::Field.new(name: :anthony, type: :text)
74
- ]
75
- )
130
+ it 'should render them based on the mapped object' do
131
+ mapper.call(fake(name: :anthony)) # HACK: set the mapper's object
132
+ expect(form.to_resource_form(mapper)).to eql(
133
+ Yaks::Resource::Form.new(
134
+ name: :the_name,
135
+ fields: [
136
+ Yaks::Resource::Form::Field.new(name: :anthony, type: :text)
137
+ ]
76
138
  )
77
- end
139
+ )
78
140
  end
79
141
  end
80
-
81
142
  end
82
143
  end
@@ -8,64 +8,67 @@ RSpec.describe Yaks::Mapper::HasMany do
8
8
  type 'closet'
9
9
  has_many :shoes,
10
10
  rel: 'http://foo/shoes',
11
- item_mapper: Class.new(Yaks::Mapper) { type 'shoe' ; attributes :size, :color }
11
+ item_mapper: Class.new(Yaks::Mapper) { type 'shoe'; attributes :size, :color }
12
12
  end
13
13
  end
14
14
 
15
15
  subject(:shoe_association) { closet_mapper.associations.first }
16
16
 
17
- its(:singular_name) { should eq 'shoe' }
17
+ describe "#singular_name" do
18
+ its(:singular_name) { should eq 'shoe' }
19
+ end
18
20
 
19
21
  let(:closet) {
20
22
  fake(
21
- :shoes => [
23
+ shoes: [
22
24
  fake(size: 9, color: :blue),
23
- fake(size: 11.5, color: :red),
25
+ fake(size: 11.5, color: :red)
24
26
  ]
25
27
  )
26
28
  }
27
29
 
28
- it 'should map the subresources' do
29
- expect(closet_mapper.call(closet).subresources).to eql([
30
- Yaks::CollectionResource.new(
31
- type: 'shoe',
32
- members: [
33
- Yaks::Resource.new(type: 'shoe', attributes: {:size => 9, :color => :blue}),
34
- Yaks::Resource.new(type: 'shoe', attributes: {:size => 11.5, :color => :red})
35
- ],
36
- rels: ['http://foo/shoes']
37
- )
38
- ])
39
- end
30
+ describe "#map_resource" do
31
+ it 'should map the subresources' do
32
+ expect(closet_mapper.call(closet).subresources).to eql([
33
+ Yaks::CollectionResource.new(
34
+ type: 'shoe',
35
+ members: [
36
+ Yaks::Resource.new(type: 'shoe', attributes: {size: 9, color: :blue}),
37
+ Yaks::Resource.new(type: 'shoe', attributes: {size: 11.5, color: :red})
38
+ ],
39
+ rels: ['http://foo/shoes']
40
+ )
41
+ ])
42
+ end
40
43
 
41
- it 'should map nil to a NullResource collection' do
42
- expect(closet_mapper.call(fake(shoes: nil)).subresources).to eql([
43
- Yaks::NullResource.new(collection: true, rels: ['http://foo/shoes'])
44
- ])
45
- end
44
+ it 'should map nil to a NullResource collection' do
45
+ expect(closet_mapper.call(fake(shoes: nil)).subresources).to eql([
46
+ Yaks::NullResource.new(collection: true, rels: ['http://foo/shoes'])
47
+ ])
48
+ end
46
49
 
47
- context 'without an explicit mapper' do
48
- let(:dress_mapper) {
49
- Class.new(Yaks::Mapper) { type 'dress' ; attributes :color }
50
- }
50
+ context 'without an explicit mapper' do
51
+ let(:dress_mapper) {
52
+ Class.new(Yaks::Mapper) { type 'dress'; attributes :color }
53
+ }
51
54
 
52
- before do
53
- closet_mapper_class.class_eval do
54
- has_many :dresses
55
- end
55
+ before do
56
+ closet_mapper_class.class_eval do
57
+ has_many :dresses
58
+ end
56
59
 
57
- stub(closet_mapper.policy).derive_mapper_from_association(any_args) do
58
- dress_mapper
60
+ stub(closet_mapper.policy).derive_mapper_from_association(any_args) do
61
+ dress_mapper
62
+ end
59
63
  end
60
- end
61
64
 
62
- it 'should derive it from policy' do
63
- expect(closet_mapper.policy).to equal policy
64
- closet_mapper.call(fake(shoes: [], dresses: [fake(color: 'blue')]))
65
+ it 'should derive it from policy' do
66
+ expect(closet_mapper.policy).to equal policy
67
+ closet_mapper.call(fake(shoes: [], dresses: [fake(color: 'blue')]))
68
+ end
65
69
  end
66
70
  end
67
71
 
68
-
69
72
  describe '#collection_mapper' do
70
73
  let(:collection_mapper) { Yaks::Undefined }
71
74
 
@@ -13,37 +13,45 @@ RSpec.describe Yaks::Mapper::HasOne do
13
13
 
14
14
  let(:association_mapper) { AuthorMapper }
15
15
  let(:name) { 'William S. Burroughs' }
16
- let(:author) { fake(:name => name) }
16
+ let(:author) { fake(name: name) }
17
17
 
18
18
  fake(:policy,
19
19
  derive_type_from_mapper_class: 'author',
20
20
  derive_mapper_from_association: AuthorMapper
21
21
  ){ Yaks::DefaultPolicy }
22
22
 
23
- its(:singular_name) { should eq 'author' }
24
-
25
- it 'should map to a single Resource' do
26
- expect(has_one.map_resource(author, yaks_context)).to eq Yaks::Resource.new(type: 'author', attributes: {name: name})
23
+ describe "#singular_name" do
24
+ its(:singular_name) { should eq 'author' }
27
25
  end
28
26
 
29
- context 'with no mapper specified' do
30
- subject(:subresource) { has_one.add_to_resource(Yaks::Resource.new, parent_mapper, yaks_context) }
31
- let(:association_mapper) { Yaks::Undefined }
32
- fake(:parent_mapper) { Yaks::Mapper }
33
-
34
- before do
35
- stub(parent_mapper).load_association(:author) { author }
27
+ describe "#map_resource" do
28
+ it 'should map to a single Resource' do
29
+ expect(has_one.map_resource(author, yaks_context))
30
+ .to eq Yaks::Resource.new(type: 'author', attributes: {name: name})
36
31
  end
37
32
 
38
- it 'should derive one based on policy' do
39
- expect(subresource).to eql(
40
- Yaks::Resource.new(
41
- subresources: [
42
- Yaks::Resource.new(type: 'author', attributes: {name: name}, rels: ['http://rel'])
43
- ]
33
+ context 'with no mapper specified' do
34
+ subject(:subresource) {
35
+ has_one.add_to_resource(Yaks::Resource.new, parent_mapper, yaks_context)
36
+ }
37
+ let(:association_mapper) { Yaks::Undefined }
38
+ fake(:parent_mapper) { Yaks::Mapper }
39
+
40
+ before do
41
+ stub(parent_mapper).load_association(:author) { author }
42
+ end
43
+
44
+ it 'should derive one based on policy' do
45
+ expect(subresource).to eql(
46
+ Yaks::Resource.new(subresources: [
47
+ Yaks::Resource.new(
48
+ type: 'author',
49
+ attributes: {name: name},
50
+ rels: ['http://rel']
51
+ )
52
+ ])
44
53
  )
45
- )
54
+ end
46
55
  end
47
-
48
56
  end
49
57
  end
@@ -32,7 +32,7 @@ RSpec.describe Yaks::Mapper::Link do
32
32
  let(:template) { ->{ link_computer } }
33
33
  before do
34
34
  mapper_class.class_eval do
35
- def link_computer ; end
35
+ def link_computer; end
36
36
  end
37
37
  end
38
38
 
@@ -43,34 +43,60 @@ RSpec.describe Yaks::Mapper::Link do
43
43
  end
44
44
  end
45
45
 
46
- context 'with :replace => true' do
47
- let(:options) { { remove: true } }
46
+ context 'with remove: true' do
47
+ let(:options) { {remove: true} }
48
48
  let(:resource) {
49
- Yaks::Resource.new.add_link(Yaks::Resource::Link.new(rel: :next, uri: '/api/next'))
49
+ Yaks::Resource.new(links: [
50
+ Yaks::Resource::Link.new(rel: :next, uri: '/api/next'),
51
+ Yaks::Resource::Link.new(rel: :prev, uri: '/api/prev')
52
+ ])
50
53
  }
51
54
 
52
55
  it 'should remove earlier links of the same rel' do
53
56
  expect(link.add_to_resource(resource, mapper, yaks_context)).to eql(
54
- Yaks::Resource.new
57
+ Yaks::Resource.new(links: [
58
+ Yaks::Resource::Link.new(rel: :prev, uri: '/api/prev')
59
+ ])
60
+ )
61
+ end
62
+ end
63
+
64
+ context 'with a link with the same rel already present' do
65
+ let(:resource) {
66
+ Yaks::Resource.new(links: [Yaks::Resource::Link.new(rel: :next, uri: '/api/next')])
67
+ }
68
+
69
+ it 'should keep both links' do
70
+ expect(link.add_to_resource(resource, mapper, yaks_context)).to eql(
71
+ Yaks::Resource.new(links: [
72
+ Yaks::Resource::Link.new(rel: :next, uri: "/api/next"),
73
+ Yaks::Resource::Link.new(rel: :next, uri: "/foo/bar/3/4")
74
+ ])
55
75
  )
56
76
  end
57
77
  end
58
78
 
59
- context 'with :replace => true' do
60
- let(:options) { { replace: true } }
79
+ context 'with replace: true' do
80
+ let(:options) { {replace: true} }
61
81
  let(:resource) {
62
- Yaks::Resource.new.add_link(Yaks::Resource::Link.new(rel: :next, uri: '/api/next'))
82
+ Yaks::Resource.new(links: [
83
+ Yaks::Resource::Link.new(rel: :next, uri: '/api/next'),
84
+ Yaks::Resource::Link.new(rel: :prev, uri: '/api/prev')
85
+ ])
63
86
  }
64
87
 
65
88
  it 'should replace earlier links of the same rel' do
66
89
  expect(link.add_to_resource(resource, mapper, yaks_context)).to eql(
67
- Yaks::Resource.new(links: [Yaks::Resource::Link.new(rel: :next, uri: "/foo/bar/3/4")])
90
+ Yaks::Resource.new(links: [
91
+ Yaks::Resource::Link.new(rel: :prev, uri: "/api/prev"),
92
+ Yaks::Resource::Link.new(rel: :next, uri: "/foo/bar/3/4")
93
+ ])
68
94
  )
69
95
  end
70
96
  end
71
97
 
72
98
  context 'with :if defined and resolving to true' do
73
- let(:options) { { if: ->{ true } } }
99
+ let(:options) { {if: ->{ true }} }
74
100
 
75
101
  it 'should render the link' do
76
102
  expect(link.add_to_resource(Yaks::Resource.new, mapper, yaks_context)).to eql(
@@ -80,7 +106,7 @@ RSpec.describe Yaks::Mapper::Link do
80
106
  end
81
107
 
82
108
  context 'with :if defined and resolving to false' do
83
- let(:options) { { if: ->{ false } } }
109
+ let(:options) { {if: ->{ false }} }
84
110
 
85
111
  it 'should not render the link' do
86
112
  expect(link.add_to_resource(Yaks::Resource.new, mapper, yaks_context)).to eql(
@@ -153,6 +179,10 @@ RSpec.describe Yaks::Mapper::Link do
153
179
  it 'should not propagate :expand' do
154
180
  expect(resource_link.options.key?(:expand)).to be false
155
181
  end
182
+
183
+ it 'should keep the link template intact' do
184
+ expect(resource_link.uri).to eql "/foo/bar/{x}/{y}"
185
+ end
156
186
  end
157
187
 
158
188
  context 'with partial expansion' do
@@ -164,7 +194,7 @@ RSpec.describe Yaks::Mapper::Link do
164
194
  end
165
195
 
166
196
  context 'with a title set' do
167
- let(:options) { { title: 'link title' } }
197
+ let(:options) { {title: 'link title'} }
168
198
 
169
199
  it 'should set the title on the resource link' do
170
200
  expect(resource_link.title).to eq 'link title'
@@ -172,7 +202,7 @@ RSpec.describe Yaks::Mapper::Link do
172
202
  end
173
203
 
174
204
  context 'with a title lambda' do
175
- let(:options) { { title: -> { "say #{mapper_method}" } } }
205
+ let(:options) { {title: -> { "say #{mapper_method}" }} }
176
206
 
177
207
  before do
178
208
  mapper_class.class_eval do
@@ -196,7 +226,7 @@ RSpec.describe Yaks::Mapper::Link do
196
226
  end
197
227
 
198
228
  context 'with a if lambda resolving to true' do
199
- let(:options) { { if: -> { add_link? } } }
229
+ let(:options) { {if: -> { add_link? }} }
200
230
 
201
231
  before do
202
232
  mapper_class.class_eval do
@@ -212,7 +242,7 @@ RSpec.describe Yaks::Mapper::Link do
212
242
  end
213
243
 
214
244
  context 'with a if lambda resolving to false' do
215
- let(:options) { { if: -> { add_link? } } }
245
+ let(:options) { {if: -> { add_link? }} }
216
246
 
217
247
  before do
218
248
  mapper_class.class_eval do
@@ -230,7 +260,7 @@ RSpec.describe Yaks::Mapper::Link do
230
260
 
231
261
  describe '.create' do
232
262
  it 'should take positional arguments' do
233
- expect(Yaks::Mapper::Link.create(:foo, :bar, {baz: 3}))
263
+ expect(Yaks::Mapper::Link.create(:foo, :bar, baz: 3))
234
264
  .to eql Yaks::Mapper::Link.new(rel: :foo, template: :bar, options: {baz: 3})
235
265
  end
236
266
 
@@ -238,4 +268,26 @@ RSpec.describe Yaks::Mapper::Link do
238
268
  expect(Yaks::Mapper::Link.create(:foo, :bar).options).to eql({})
239
269
  end
240
270
  end
271
+
272
+ describe '#templated?' do
273
+ context 'when no expansion behavior is set (defaults to expand fully)' do
274
+ let(:options) { {} }
275
+ its(:templated?) { should be false }
276
+ end
277
+
278
+ context 'when the link does not expand' do
279
+ let(:options) { {expand: false} }
280
+ its(:templated?) { should be true }
281
+ end
282
+
283
+ context 'when the link expands fully' do
284
+ let(:options) { {expand: true} }
285
+ its(:templated?) { should be false }
286
+ end
287
+
288
+ context 'when the link expands partially' do
289
+ let(:options) { {expand: [:x]} }
290
+ its(:templated?) { should be true }
291
+ end
292
+ end
241
293
  end