yaks 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +88 -16
- data/Rakefile +1 -1
- data/ataru_setup.rb +6 -0
- data/lib/yaks/behaviour/optional_includes.rb +29 -0
- data/lib/yaks/format/json_api.rb +13 -13
- data/lib/yaks/mapper/link.rb +10 -5
- data/lib/yaks/reader/json_api.rb +25 -21
- data/lib/yaks/resource/link.rb +1 -1
- data/lib/yaks/version.rb +1 -1
- data/spec/json/confucius.json_api.json +20 -8
- data/spec/spec_helper.rb +11 -21
- data/spec/support/fixtures.rb +1 -1
- data/spec/unit/yaks/behaviour/optional_includes_spec.rb +63 -0
- data/spec/unit/yaks/default_policy/derive_mapper_from_collection_spec.rb +3 -3
- data/spec/unit/yaks/format/json_api_spec.rb +38 -12
- data/yaks.gemspec +4 -2
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17023a2415d74fcf0ea0c840dfa7f1d2fcb8afb4
|
4
|
+
data.tar.gz: 3fc6a562a7ba74d7064d2898f04ed32d1ba1b3b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 159c85fc53b2b7aa8e6fbc9a23fa3c1fa969f667e36e96c58e97ac4bc76b52bf7aaa1e95e309d319c121eeaafd30dc71a7de7fef3cca0173aae844593f3a5238
|
7
|
+
data.tar.gz: c0ec3d24906a2b12accd6548e574679e239def3d9e71228e1c35812c302c8c92afc118d3af47927b82e5c329bfeee0190df7b8b8f54300c1f3bc7eb1ca6a21db
|
data/README.md
CHANGED
@@ -43,6 +43,7 @@ requested. These formats are presently supported:
|
|
43
43
|
- [Filtering](#user-content-filtering)
|
44
44
|
- [Links](#user-content-links)
|
45
45
|
- [Associations](#user-content-associations)
|
46
|
+
- [Behaviours](#user-content-behaviours)
|
46
47
|
- [Calling Yaks](#user-content-calling-yaks)
|
47
48
|
- [Rack env](#user-content-rack-env)
|
48
49
|
- [Namespace](#user-content-namespace)
|
@@ -68,6 +69,12 @@ requested. These formats are presently supported:
|
|
68
69
|
- [How to contribute](#user-content-how-to-contribute)
|
69
70
|
- [License](#user-content-license)
|
70
71
|
|
72
|
+
## Packages
|
73
|
+
|
74
|
+
- [yaks-sinatra](yaks-sinatra/README.md)
|
75
|
+
- [yaks-html](yaks-html/README.md)
|
76
|
+
- [yaks-transit](yaks-transit/README.md)
|
77
|
+
|
71
78
|
## State of Development
|
72
79
|
|
73
80
|
Recent focus has been on stabilizing the core classes, improving
|
@@ -309,6 +316,59 @@ class ShowMapper < Yaks::Mapper
|
|
309
316
|
end
|
310
317
|
```
|
311
318
|
|
319
|
+
### Behaviours
|
320
|
+
|
321
|
+
Yaks provides mixins to change how your mappers work. These need to be
|
322
|
+
required separately, they are not loaded by default.
|
323
|
+
|
324
|
+
#### OptionalIncludes
|
325
|
+
|
326
|
+
You may choose to not render associations by default, but to only do
|
327
|
+
so when the client explicitly asks for them. This can be done by
|
328
|
+
including `Yaks::Behaviour::OptionalIncludes`.
|
329
|
+
|
330
|
+
Which associations to load is specified with the the `include` query
|
331
|
+
parameter. You can use dots to load nested associated.
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
require "yaks/behaviour/optional_includes"
|
335
|
+
|
336
|
+
class PostMapper < Yaks::Mapper
|
337
|
+
include Yaks::Behaviour::OptionalIncludes
|
338
|
+
|
339
|
+
has_one :author
|
340
|
+
has_many :comments
|
341
|
+
end
|
342
|
+
|
343
|
+
class AuthorMapper < Yaks::Mapper
|
344
|
+
include Yaks::Behaviour::OptionalIncludes
|
345
|
+
|
346
|
+
has_one :profile
|
347
|
+
end
|
348
|
+
```
|
349
|
+
|
350
|
+
```
|
351
|
+
GET /post/42?include=comments,author.profile
|
352
|
+
```
|
353
|
+
|
354
|
+
Note that this will only work when Yaks has access to the Rack
|
355
|
+
environment. When using an existing integration like `yaks-sinatra`
|
356
|
+
this will be handled for you.
|
357
|
+
|
358
|
+
To force an association to always be included, override its `if`
|
359
|
+
condition to always return true.
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
require "yaks/behaviour/optional_includes"
|
363
|
+
|
364
|
+
class PostMapper < Yaks::Mapper
|
365
|
+
include Yaks::Behaviour::OptionalIncludes
|
366
|
+
|
367
|
+
has_one :author
|
368
|
+
has_many :comments, if: ->{ true }
|
369
|
+
end
|
370
|
+
```
|
371
|
+
|
312
372
|
## Calling Yaks
|
313
373
|
|
314
374
|
Once you have a Yaks instance, you can call it with `call`
|
@@ -513,19 +573,6 @@ at your API entry point should do the trick.
|
|
513
573
|
|
514
574
|
### JSON-API
|
515
575
|
|
516
|
-
The JSON-API spec has evolved since the Yaks formatter was
|
517
|
-
implemented. It is also not the most suitable format for Yaks
|
518
|
-
feature-set due to its strong convention-driven nature and weak
|
519
|
-
support for hypermedia.
|
520
|
-
|
521
|
-
At this time, The JSON-API specification has not reached a 1.0 release.
|
522
|
-
Some changes to the Yaks JSON-API formatter may still be required
|
523
|
-
before it is completely compatible with the latest version of the
|
524
|
-
specification.
|
525
|
-
|
526
|
-
If you would like to see better JSON-API support, get in touch. We
|
527
|
-
might be able to work something out.
|
528
|
-
|
529
576
|
```ruby
|
530
577
|
Yaks.new do
|
531
578
|
default_format :json_api
|
@@ -552,6 +599,8 @@ yaks = Yaks.new do
|
|
552
599
|
end
|
553
600
|
```
|
554
601
|
|
602
|
+
For optional includes, see [`Yaks::Behaviour::OptionalIncludes`](#user-content-behaviours).
|
603
|
+
|
555
604
|
### Collection+JSON
|
556
605
|
|
557
606
|
Collection+JSON has support for write templates. To use them, the `:template`
|
@@ -802,15 +851,38 @@ end
|
|
802
851
|
|
803
852
|
For JSON based formats, the "syntax tree" is merely a structure of Ruby primitives that have a JSON equivalent. If your mappers return non-primitive attribute values, you can define how they should be converted. For example, JSON has no notion of dates. If your mappers return these types as attributes, then Yaks needs to know how to turn these into primitives. To add extra types, use `map_to_primitive`
|
804
853
|
|
854
|
+
Here's an example with a custom `Currency` class, which can be represented as an integer.
|
855
|
+
|
805
856
|
```ruby
|
806
857
|
Yaks.new do
|
807
|
-
map_to_primitive
|
808
|
-
|
858
|
+
map_to_primitive Currency do |currency|
|
859
|
+
currency.to_i
|
809
860
|
end
|
810
861
|
end
|
811
862
|
```
|
812
863
|
|
813
|
-
|
864
|
+
One notable use case is representing dates and times. The JSON
|
865
|
+
specification does not define any syntax for these, so the only
|
866
|
+
solution is to represent them either as numbers or strings. If you're
|
867
|
+
not sure what to do with these then the ISO8601 standard is a safe
|
868
|
+
bet. It defines a way to represent times and dates as strings, and is
|
869
|
+
also adopted by the W3C in [RFC3339](http://tools.ietf.org/html/rfc3339).
|
870
|
+
|
871
|
+
An alternative representation that is sometimes used is "unix time",
|
872
|
+
defined as the numbers of seconds passed since 1 January 1970.
|
873
|
+
|
874
|
+
Here's an example for a Rails app, so including ActiveSupport's `TimeWithZone`.
|
875
|
+
|
876
|
+
```ruby
|
877
|
+
Yaks.new do
|
878
|
+
map_to_primitive Date, Time, DateTime, ActiveSupport::TimeWithZone, &:iso8601
|
879
|
+
end
|
880
|
+
```
|
881
|
+
|
882
|
+
`map_to_primitive` can also be used to transform alternative data
|
883
|
+
structures, like those from [Hamster](https://github.com/hamstergem/hamster),
|
884
|
+
into Ruby arrays and hashes. Use `call()` to recursively turn things into
|
885
|
+
primitives.
|
814
886
|
|
815
887
|
```ruby
|
816
888
|
Yaks.new do
|
data/Rakefile
CHANGED
data/ataru_setup.rb
CHANGED
@@ -33,6 +33,12 @@ class HomeMapper < Yaks::Mapper; end
|
|
33
33
|
|
34
34
|
class SpecialMapper < Yaks::Mapper; end
|
35
35
|
|
36
|
+
module ActiveSupport
|
37
|
+
class TimeWithZone < Time ; end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Currency ; end
|
41
|
+
|
36
42
|
module Setup
|
37
43
|
def setup
|
38
44
|
# Do some nice setup that is run before every snippet
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "rack/utils"
|
2
|
+
|
3
|
+
module Yaks
|
4
|
+
module Behaviour
|
5
|
+
module OptionalIncludes
|
6
|
+
RACK_KEY = "yaks.optional_includes".freeze
|
7
|
+
|
8
|
+
def associations
|
9
|
+
super.select do |association|
|
10
|
+
association.if != Undefined || include_association?(association)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def include_association?(association)
|
17
|
+
includes = env.fetch(RACK_KEY) do
|
18
|
+
query_string = env.fetch("QUERY_STRING", nil)
|
19
|
+
query = Rack::Utils.parse_query(query_string)
|
20
|
+
env[RACK_KEY] = query.fetch("include", "").split(",").map { |r| r.split(".") }
|
21
|
+
end
|
22
|
+
|
23
|
+
includes.any? do |relationship|
|
24
|
+
relationship[mapper_stack.size].eql?(association.name.to_s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/yaks/format/json_api.rb
CHANGED
@@ -42,31 +42,31 @@ module Yaks
|
|
42
42
|
attributes = resource.attributes.reject { |k| k.equal?(:id) }
|
43
43
|
result[:attributes] = attributes if attributes.any?
|
44
44
|
|
45
|
-
|
46
|
-
result[:
|
47
|
-
|
48
|
-
result
|
45
|
+
relationships = serialize_relationships(resource.subresources)
|
46
|
+
result[:relationships] = relationships unless relationships.empty?
|
47
|
+
links = serialize_links(resource.links)
|
48
|
+
result[:links] = links unless links.empty?
|
49
49
|
|
50
50
|
result
|
51
51
|
end
|
52
52
|
|
53
|
-
# @param [
|
53
|
+
# @param [Array] subresources
|
54
54
|
# @return [Hash]
|
55
|
-
def
|
55
|
+
def serialize_relationships(subresources)
|
56
56
|
subresources.each_with_object({}) do |resource, hsh|
|
57
|
-
|
58
|
-
hsh[resource.rels.first.sub(/^rel:/, '')] = serialize_subresource_link(resource)
|
57
|
+
hsh[resource.rels.first.sub(/^rel:/, '').to_sym] = serialize_relationship(resource)
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
62
61
|
# @param [Yaks::Resource] resource
|
63
|
-
# @return [Array,
|
64
|
-
def
|
62
|
+
# @return [Array, Hash]
|
63
|
+
def serialize_relationship(resource)
|
65
64
|
if resource.collection?
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
data = resource.map { |r| {type: pluralize(r.type), id: r[:id].to_s} }
|
66
|
+
elsif !resource.null_resource?
|
67
|
+
data = {type: pluralize(resource.type), id: resource[:id].to_s}
|
69
68
|
end
|
69
|
+
{data: data}
|
70
70
|
end
|
71
71
|
|
72
72
|
# @param [Hash] subresources
|
data/lib/yaks/mapper/link.rb
CHANGED
@@ -25,7 +25,7 @@ module Yaks
|
|
25
25
|
# it will receive the mapper instance as argument. Otherwise it is evaluated in the mapper context
|
26
26
|
class Link
|
27
27
|
extend Forwardable, Util
|
28
|
-
include Attribs.new(:rel, :template, options: {}), Util
|
28
|
+
include Attribs.new(:rel, :template, options: {}.freeze), Util
|
29
29
|
|
30
30
|
def self.create(*args)
|
31
31
|
args, options = extract_options(args)
|
@@ -62,11 +62,16 @@ module Yaks
|
|
62
62
|
uri = mapper.expand_uri(template, options.fetch(:expand, true))
|
63
63
|
return if uri.nil?
|
64
64
|
|
65
|
-
|
65
|
+
attrs = {
|
66
66
|
rel: rel,
|
67
|
-
uri: uri
|
68
|
-
|
69
|
-
|
67
|
+
uri: uri
|
68
|
+
}
|
69
|
+
|
70
|
+
resource_link_options(mapper).tap do |opts|
|
71
|
+
attrs[:options] = opts unless opts.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
Resource::Link.new(attrs)
|
70
75
|
end
|
71
76
|
|
72
77
|
private
|
data/lib/yaks/reader/json_api.rb
CHANGED
@@ -12,12 +12,12 @@ module Yaks
|
|
12
12
|
else
|
13
13
|
attributes = parsed_json['data'].dup
|
14
14
|
links = attributes.delete('links') || {}
|
15
|
+
relationships = attributes.delete('relationships') || {}
|
15
16
|
type = attributes.delete('type')
|
16
17
|
attributes.merge!(attributes.delete('attributes') || {})
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
links = convert_links(Hash[resource_links])
|
19
|
+
embedded = convert_embedded(Hash[relationships], included)
|
20
|
+
links = convert_links(Hash[links])
|
21
21
|
|
22
22
|
Resource.new(
|
23
23
|
type: Util.singularize(type),
|
@@ -28,29 +28,33 @@ module Yaks
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def convert_embedded(
|
32
|
-
|
33
|
-
# A Link doesn't have to contain a `
|
31
|
+
def convert_embedded(relationships, included)
|
32
|
+
relationships.flat_map do |rel, relationship|
|
33
|
+
# A Link doesn't have to contain a `data` member.
|
34
34
|
# It can contain URLs instead, or as well, but we are only worried about *embedded* links here.
|
35
|
-
|
36
|
-
# Resource
|
35
|
+
data = relationship['data']
|
36
|
+
# Resource data MUST be represented as one of the following:
|
37
37
|
#
|
38
38
|
# * `null` for empty to-one relationships.
|
39
|
-
# * a "
|
39
|
+
# * a "resource identifier object" for non-empty to-one relationships.
|
40
40
|
# * an empty array ([]) for empty to-many relationships.
|
41
|
-
# * an array of
|
42
|
-
if
|
43
|
-
|
44
|
-
elsif
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
# * an array of resource identifier objects for non-empty to-many relationships.
|
42
|
+
if data.nil?
|
43
|
+
NullResource.new(rels: [rel])
|
44
|
+
elsif data.is_a? Array
|
45
|
+
if data.empty?
|
46
|
+
NullResource.new(collection: true, rels: [rel])
|
47
|
+
else
|
48
|
+
CollectionResource.new(
|
49
|
+
members: data.map { |link|
|
50
|
+
data = included.find{ |item| (item['id'] == link['id']) && (item['type'] == link['type']) }
|
51
|
+
call('data' => data, 'included' => included)
|
52
|
+
},
|
53
|
+
rels: [rel]
|
54
|
+
)
|
55
|
+
end
|
52
56
|
else
|
53
|
-
data = included.find{ |item| (item['id'] ==
|
57
|
+
data = included.find{ |item| (item['id'] == data['id']) && (item['type'] == data['type']) }
|
54
58
|
call('data' => data, 'included' => included).with(rels: [rel])
|
55
59
|
end
|
56
60
|
end.compact
|
data/lib/yaks/resource/link.rb
CHANGED
data/lib/yaks/version.rb
CHANGED
@@ -7,12 +7,16 @@
|
|
7
7
|
"pinyin": "Kongzi",
|
8
8
|
"latinized": "Confucius"
|
9
9
|
},
|
10
|
-
"
|
11
|
-
"
|
10
|
+
"relationships": {
|
11
|
+
"works": {
|
12
|
+
"data": [{"type": "works", "id": "11"}, {"type": "works", "id": "12"}]
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"links": {
|
16
|
+
"profile": "http://literature.example.com/profiles/scholar",
|
12
17
|
"self": "http://literature.example.com/authors/kongzi",
|
13
|
-
|
14
|
-
|
15
|
-
}
|
18
|
+
"http://literature.example.com/rels/quotes": "http://literature.example.com/quotes/?author=kongzi&q={query}"
|
19
|
+
}
|
16
20
|
},
|
17
21
|
"included": [
|
18
22
|
{
|
@@ -22,11 +26,15 @@
|
|
22
26
|
"chinese_name": "論語",
|
23
27
|
"english_name": "Analects"
|
24
28
|
},
|
29
|
+
"relationships": {
|
30
|
+
"quotes": {
|
31
|
+
"data": [{"type": "quotes", "id": "17"}, {"type": "quotes", "id": "18"}]
|
32
|
+
},
|
33
|
+
"era": {"data": {"type": "erae", "id": "99"}}
|
34
|
+
},
|
25
35
|
"links": {
|
26
36
|
"profile": "http://literature.example.com/profiles/work",
|
27
|
-
"self": "http://literature.example.com/work/11"
|
28
|
-
"quotes": {"linkage": [{"type": "quotes", "id": "17"}, {"type": "quotes", "id": "18"}]},
|
29
|
-
"era": {"linkage": {"type": "erae", "id": "99"}}
|
37
|
+
"self": "http://literature.example.com/work/11"
|
30
38
|
}
|
31
39
|
},
|
32
40
|
{
|
@@ -57,6 +65,10 @@
|
|
57
65
|
"chinese_name": "易經",
|
58
66
|
"english_name": "Commentaries to the Yi-jing"
|
59
67
|
},
|
68
|
+
"relationships": {
|
69
|
+
"quotes": { "data": [] },
|
70
|
+
"era": { "data": null }
|
71
|
+
},
|
60
72
|
"links": {
|
61
73
|
"profile": "http://literature.example.com/profiles/work",
|
62
74
|
"self": "http://literature.example.com/work/12"
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
-
require 'rspec/its'
|
2
|
-
require 'bogus/rspec'
|
3
|
-
require 'timeout'
|
4
|
-
|
5
1
|
require 'yaks'
|
6
2
|
require 'yaks-html'
|
7
3
|
require 'virtus'
|
8
4
|
|
5
|
+
require_relative '../../shared/rspec_config'
|
6
|
+
|
9
7
|
require 'fixture_helpers'
|
10
8
|
|
9
|
+
RSpec.configure do |rspec|
|
10
|
+
rspec.include FixtureHelpers
|
11
|
+
end
|
12
|
+
|
13
|
+
Bogus.configure do |bogus|
|
14
|
+
bogus.search_modules << Yaks
|
15
|
+
bogus.search_modules << Yaks::Mapper
|
16
|
+
end
|
17
|
+
|
11
18
|
require_relative 'support/models'
|
12
19
|
require_relative 'support/pet_mapper'
|
13
20
|
require_relative 'support/pet_peeve_mapper'
|
@@ -17,20 +24,3 @@ require_relative 'support/shared_contexts'
|
|
17
24
|
require_relative 'support/youtypeit_models_mappers'
|
18
25
|
require_relative 'support/deep_eql'
|
19
26
|
require_relative 'support/classes_for_policy_testing'
|
20
|
-
|
21
|
-
RSpec.configure do |rspec|
|
22
|
-
rspec.include FixtureHelpers
|
23
|
-
rspec.backtrace_exclusion_patterns = [] if ENV['FULLSTACK']
|
24
|
-
rspec.disable_monkey_patching!
|
25
|
-
rspec.raise_errors_for_deprecations!
|
26
|
-
if defined?(Mutant)
|
27
|
-
rspec.around(:each) do |example|
|
28
|
-
Timeout.timeout(1, &example)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
Bogus.configure do |bogus|
|
34
|
-
bogus.search_modules << Yaks
|
35
|
-
bogus.search_modules << Yaks::Mapper
|
36
|
-
end
|
data/spec/support/fixtures.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
shared_context 'fixtures' do
|
1
|
+
RSpec.shared_context 'fixtures' do
|
2
2
|
let(:john) { Friend.new(id: 1, name: 'john', pets: [boingboing, wassup], pet_peeve: regexps) }
|
3
3
|
let(:boingboing) { Pet.new(id: 2, name: 'boingboing', species: 'dog') }
|
4
4
|
let(:wassup) { Pet.new(id: 3, name: 'wassup', species: 'cat') }
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "yaks/behaviour/optional_includes"
|
2
|
+
|
3
|
+
RSpec.describe Yaks::Behaviour::OptionalIncludes do
|
4
|
+
include_context 'yaks context'
|
5
|
+
|
6
|
+
subject(:mapper) { mapper_class.new(yaks_context) }
|
7
|
+
let(:resource) { mapper.call(instance) }
|
8
|
+
|
9
|
+
let(:mapper_class) do
|
10
|
+
Class.new(Yaks::Mapper).tap do |mapper_class|
|
11
|
+
mapper_class.send :include, Yaks::Behaviour::OptionalIncludes
|
12
|
+
mapper_class.type "user"
|
13
|
+
mapper_class.has_many :posts, mapper: post_mapper_class
|
14
|
+
mapper_class.has_one :account, mapper: account_mapper_class
|
15
|
+
end
|
16
|
+
end
|
17
|
+
let(:post_mapper_class) do
|
18
|
+
Class.new(Yaks::Mapper).tap do |mapper_class|
|
19
|
+
mapper_class.type "post"
|
20
|
+
mapper_class.has_many :comments, mapper: comment_mapper_class
|
21
|
+
end
|
22
|
+
end
|
23
|
+
let(:account_mapper_class) { Class.new(Yaks::Mapper) { type "account" } }
|
24
|
+
let(:comment_mapper_class) { Class.new(Yaks::Mapper) { type "comment" } }
|
25
|
+
|
26
|
+
let(:instance) { fake(posts: [fake(comments: [fake])], account: fake) }
|
27
|
+
|
28
|
+
it "includes the associations" do
|
29
|
+
rack_env["QUERY_STRING"] = "include=posts.comments,account"
|
30
|
+
|
31
|
+
expect(resource.type).to eq "user"
|
32
|
+
expect(resource.subresources[0].type).to eq "post"
|
33
|
+
expect(resource.subresources[0].members[0].type).to eq "post"
|
34
|
+
expect(resource.subresources[0].members[0].subresources[0].type).to eq "comment"
|
35
|
+
expect(resource.subresources[0].members[0].subresources[0].members[0].type).to eq "comment"
|
36
|
+
expect(resource.subresources[1].type).to eq "account"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "excludes associations not specified in the QUERY_STRING" do
|
40
|
+
rack_env["QUERY_STRING"] = "include=posts"
|
41
|
+
|
42
|
+
expect(resource.subresources.count).to eq 1
|
43
|
+
end
|
44
|
+
|
45
|
+
it "doesn't include the associations when QUERY_STRING is empty" do
|
46
|
+
expect(resource.type).to eq "user"
|
47
|
+
expect(resource.subresources).to be_empty
|
48
|
+
end
|
49
|
+
|
50
|
+
it "allows :if to override the query parameter checking" do
|
51
|
+
mapper_class.has_one :account, mapper: account_mapper_class, if: true
|
52
|
+
|
53
|
+
expect(resource.subresources.count).to eq 1
|
54
|
+
end
|
55
|
+
|
56
|
+
it "caches parsing of the query parameter" do
|
57
|
+
rack_env["QUERY_STRING"] = "include=posts"
|
58
|
+
expect(mapper.call(instance).subresources.count).to eq 1
|
59
|
+
|
60
|
+
rack_env["QUERY_STRING"] = nil
|
61
|
+
expect(mapper.call(instance).subresources.count).to eq 1
|
62
|
+
end
|
63
|
+
end
|
@@ -30,7 +30,7 @@ RSpec.describe Yaks::DefaultPolicy, '#derive_mapper_from_collection' do
|
|
30
30
|
it 'should propagate the error' do
|
31
31
|
expect {
|
32
32
|
policy.derive_mapper_from_object([])
|
33
|
-
}.to raise_error
|
33
|
+
}.to raise_error(RuntimeError)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -38,9 +38,9 @@ RSpec.describe Yaks::DefaultPolicy, '#derive_mapper_from_collection' do
|
|
38
38
|
let(:options) { {namespace: DislikesOtherMappers} }
|
39
39
|
|
40
40
|
it 'should propagate the error' do
|
41
|
-
expect
|
41
|
+
expect {
|
42
42
|
policy.derive_mapper_from_object([Namespace::Nested::Rye.new])
|
43
|
-
|
43
|
+
}.to raise_error(RuntimeError)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -47,10 +47,12 @@ RSpec.describe Yaks::Format::JsonAPI do
|
|
47
47
|
expect(format.call(resource)).to eql(
|
48
48
|
data: {
|
49
49
|
type: 'wizards',
|
50
|
+
relationships: {
|
51
|
+
favourite_spell: {data: {type: "spells", id: "1"}}
|
52
|
+
},
|
50
53
|
links: {
|
51
54
|
self: "/the/self/link",
|
52
|
-
profile: "/the/profile/link"
|
53
|
-
'favourite_spell' => {linkage: {type: "spells", id: "1"}},
|
55
|
+
profile: "/the/profile/link"
|
54
56
|
}
|
55
57
|
},
|
56
58
|
included: [{type: 'spells', id: "1"}]
|
@@ -72,7 +74,7 @@ RSpec.describe Yaks::Format::JsonAPI do
|
|
72
74
|
expect(format.call(resource)).to eql(
|
73
75
|
data: {
|
74
76
|
type: 'wizards',
|
75
|
-
|
77
|
+
relationships: {favourite_spell: {data: {type: 'spells', id: "777"}}}
|
76
78
|
},
|
77
79
|
included: [{type: 'spells', id: "777", attributes: {name: 'Lucky Sevens'}}]
|
78
80
|
)
|
@@ -103,10 +105,10 @@ RSpec.describe Yaks::Format::JsonAPI do
|
|
103
105
|
it 'should include the each subresource only once' do
|
104
106
|
expect(format.call(resource)).to eql(
|
105
107
|
data: [
|
106
|
-
{type: 'wizards', id: '7',
|
107
|
-
{type: 'wizards', id: '3',
|
108
|
-
{type: 'wizards', id: '2',
|
109
|
-
{type: 'wizards', id: '9',
|
108
|
+
{type: 'wizards', id: '7', relationships: {favourite_spell: {data: {type: 'spells', id: '1'}}}},
|
109
|
+
{type: 'wizards', id: '3', relationships: {favourite_spell: {data: {type: 'spells', id: '1'}}}},
|
110
|
+
{type: 'wizards', id: '2', relationships: {favourite_spell: {data: {type: 'spells', id: '12'}}}},
|
111
|
+
{type: 'wizards', id: '9', relationships: {wand: {data: {type: 'wands', id: '1'}}}},
|
110
112
|
],
|
111
113
|
included: [
|
112
114
|
{type: 'spells', id: '1'},
|
@@ -121,14 +123,38 @@ RSpec.describe Yaks::Format::JsonAPI do
|
|
121
123
|
let(:resource) {
|
122
124
|
Yaks::Resource.new(
|
123
125
|
type: 'wizard',
|
124
|
-
subresources: [
|
126
|
+
subresources: [subresource]
|
125
127
|
)
|
126
128
|
}
|
127
129
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
130
|
+
context "non-collection subresouce" do
|
131
|
+
let(:subresource) { Yaks::NullResource.new.add_rel("rel:wand") }
|
132
|
+
|
133
|
+
it 'should include a nil linkage object' do
|
134
|
+
expect(format.call(resource)).to eql(
|
135
|
+
data: {
|
136
|
+
type: 'wizards',
|
137
|
+
relationships: {
|
138
|
+
wand: {data: nil}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "collection subresouce" do
|
146
|
+
let(:subresource) { Yaks::NullResource.new(collection: true).add_rel("rel:wands") }
|
147
|
+
|
148
|
+
it 'should include a nil linkage object' do
|
149
|
+
expect(format.call(resource)).to eql(
|
150
|
+
data: {
|
151
|
+
type: 'wizards',
|
152
|
+
relationships: {
|
153
|
+
wands: {data: []}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
)
|
157
|
+
end
|
132
158
|
end
|
133
159
|
end
|
134
160
|
|
data/yaks.gemspec
CHANGED
@@ -38,8 +38,10 @@ Gem::Specification.new do |gem|
|
|
38
38
|
gem.add_development_dependency 'benchmark-ips'
|
39
39
|
gem.add_development_dependency 'bogus'
|
40
40
|
gem.add_development_dependency 'hamster'
|
41
|
-
|
42
|
-
|
41
|
+
if RUBY_VERSION >= "2.1.0"
|
42
|
+
gem.add_development_dependency 'mutant'
|
43
|
+
gem.add_development_dependency 'mutant-rspec'
|
44
|
+
end
|
43
45
|
gem.add_development_dependency 'rake'
|
44
46
|
gem.add_development_dependency 'rubocop'
|
45
47
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yaks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arne Brasseur
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: abstract_type
|
@@ -318,6 +318,7 @@ files:
|
|
318
318
|
- ataru_setup.rb
|
319
319
|
- find_missing_tests.rb
|
320
320
|
- lib/yaks.rb
|
321
|
+
- lib/yaks/behaviour/optional_includes.rb
|
321
322
|
- lib/yaks/breaking_changes.rb
|
322
323
|
- lib/yaks/builder.rb
|
323
324
|
- lib/yaks/changelog.rb
|
@@ -395,6 +396,7 @@ files:
|
|
395
396
|
- spec/support/pet_peeve_mapper.rb
|
396
397
|
- spec/support/shared_contexts.rb
|
397
398
|
- spec/support/youtypeit_models_mappers.rb
|
399
|
+
- spec/unit/yaks/behaviour/optional_includes_spec.rb
|
398
400
|
- spec/unit/yaks/builder_spec.rb
|
399
401
|
- spec/unit/yaks/collection_mapper_spec.rb
|
400
402
|
- spec/unit/yaks/collection_resource_spec.rb
|
@@ -463,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
463
465
|
version: '0'
|
464
466
|
requirements: []
|
465
467
|
rubyforge_project:
|
466
|
-
rubygems_version: 2.
|
468
|
+
rubygems_version: 2.2.3
|
467
469
|
signing_key:
|
468
470
|
specification_version: 4
|
469
471
|
summary: Serialize to hypermedia. HAL, JSON-API, etc.
|
@@ -497,6 +499,7 @@ test_files:
|
|
497
499
|
- spec/support/pet_peeve_mapper.rb
|
498
500
|
- spec/support/shared_contexts.rb
|
499
501
|
- spec/support/youtypeit_models_mappers.rb
|
502
|
+
- spec/unit/yaks/behaviour/optional_includes_spec.rb
|
500
503
|
- spec/unit/yaks/builder_spec.rb
|
501
504
|
- spec/unit/yaks/collection_mapper_spec.rb
|
502
505
|
- spec/unit/yaks/collection_resource_spec.rb
|