json_api_ruby 0.4.0 → 0.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 018c57ac3ac726a576720f582783604eb22151a7
4
- data.tar.gz: c8d50c7117d9b54654ad484b7e011e82a8402c8b
3
+ metadata.gz: 0061d47ce650e9fcb4fb372e55830c44ffc2ee84
4
+ data.tar.gz: e21dcd60e6302c6cfc8c206fcd84cf15fad78ddb
5
5
  SHA512:
6
- metadata.gz: c40fd216bd9f2809c83bf7ecf47372b23772747fb5827c373d52d357a4a22b05b8f03a97eb62a85e1816f6c87009ea32c8e28a5ede090dda103375f428bae8d8
7
- data.tar.gz: 8ea27ee8bb37753a4a8aa098d1529a69a0feff3d49d7b859dc3d2300bc14f68c5ddeb051eb90ea12162c0532907806b5505196d16e2e8076bdf0af84895746ab
6
+ metadata.gz: 955e5c106e646ca173aa3b31b3f292abdc43d3122928aa856a52f7d09354d97f818b530a03e2cacfb7cf931341e9886f4664ea9205c4dc0525f2bcbfc8f59b94
7
+ data.tar.gz: c3e643b234cf81c68b5bbbcc0924f128e5364ec4500807c4d9b69eb71090dc84d9e67d755176ede994ec4cbe106a3628ba27925073e002d73d14b06a2a75e6e4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ 0.4.1
2
+ Some major refactoring but no backwards compatibility breaking functionality
3
+
1
4
  0.4.0
2
5
  Add support for nested includes and link enabling/disabling
3
6
 
@@ -0,0 +1,58 @@
1
+ module JsonApi
2
+ class Association
3
+ # The resource object that "owns" this relationship
4
+ #
5
+ # Example:
6
+ # class ArticleResource < JsonApi::Resource
7
+ # has_one :author
8
+ # end
9
+ #
10
+ # `ArticleResource` is the parent of the author object
11
+ attr_reader :parent
12
+ attr_reader :parent_model
13
+
14
+ # Determines whether the `data` attribute should be filled out and
15
+ # included
16
+ attr_reader :included
17
+ attr_reader :name
18
+ attr_reader :explicit_resource_class
19
+
20
+
21
+ # The resource object that represents this relationship
22
+ attr_reader :resources
23
+
24
+ def initialize(name, options)
25
+ @name = name
26
+ @resources = []
27
+ @parent = options.fetch(:parent_resource)
28
+ @parent_model = parent._model
29
+ @included = options.fetch(:included, JsonApi::Includes.new)
30
+ @explicit_resource_class = options.fetch(:explicit_resource_class)
31
+ end
32
+
33
+ def included?
34
+ included.has_name?(@name)
35
+ end
36
+
37
+ def to_hash
38
+ return_hash = {}
39
+ return_hash.merge!(relationship_links) if JsonApi.configuration.use_links
40
+ return_hash.merge!(data) if included?
41
+ return_hash
42
+ end
43
+
44
+ def relationship_links
45
+ {
46
+ 'links' => {
47
+ 'self' => JsonApi.configuration.base_url + parent.self_link_path + "/relationships/#{name}",
48
+ 'related' => JsonApi.configuration.base_url + parent.self_link_path + "/#{name}"
49
+ }
50
+ }
51
+ end
52
+ end
53
+ end
54
+
55
+ require_relative 'associations/meta'
56
+ require_relative 'associations/to_one'
57
+ require_relative 'associations/to_many'
58
+
@@ -0,0 +1,48 @@
1
+ module JsonApi
2
+ module Associations
3
+ UnknownCardinalityError = Class.new(StandardError)
4
+
5
+ class Meta
6
+ TO_ONE = :one
7
+ TO_MANY = :many
8
+ CARDINALITY = [TO_ONE, TO_MANY]
9
+
10
+ # The name of this relationship.
11
+ #
12
+ # This name comes from the resource object that defines the
13
+ # relationship. Example:
14
+ #
15
+ # class ArticleResource < JsonApi::Resource
16
+ # has_one :author # this is the name of this relationship
17
+ # end
18
+ attr_reader :name
19
+
20
+ attr_reader :cardinality
21
+
22
+ attr_reader :explicit_resource_class
23
+
24
+ def initialize(name, resource_class: nil, cardinality:)
25
+ @name = name.to_s
26
+ validate_cardinality(cardinality)
27
+ @cardinality = cardinality
28
+ @explicit_resource_class = resource_class
29
+ end
30
+
31
+ def validate_cardinality(cardinality)
32
+ unless(CARDINALITY.include?(cardinality))
33
+ fail UnknownCardinalityError.new
34
+ end
35
+ end
36
+
37
+ def build_resources(options)
38
+ if cardinality == TO_ONE
39
+ relationship = ToOne.new(name, options.merge(explicit_resource_class: explicit_resource_class))
40
+ else
41
+ relationship = ToMany.new(name, options.merge(explicit_resource_class: explicit_resource_class))
42
+ end
43
+ relationship.build_resources(options)
44
+ relationship
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ module JsonApi
2
+ module Associations
3
+ class ToMany < JsonApi::Association
4
+ def data(options={})
5
+ data = resource_objects.map do |object|
6
+ object.identifier_hash
7
+ end
8
+ {'data' => data}
9
+ end
10
+
11
+ # Build the related resources
12
+ def build_resources(options)
13
+ return unless included?
14
+
15
+ parent_model.send(name).each do |resource_model|
16
+ resource_class = JsonApi::Resources::Discovery.resource_for_name(resource_model, options.merge(parent_resource: parent, resource_class: explicit_resource_class))
17
+ @resources << resource_class.new(resource_model, options.merge({include: included.next}))
18
+ end
19
+ end
20
+
21
+ def resource_objects
22
+ @resources
23
+ end
24
+
25
+ def cardinality
26
+ :many
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ module JsonApi
2
+ module Associations
3
+ # convenience classes
4
+ class ToOne < JsonApi::Association
5
+ def data(options={})
6
+ identifier_hash = resource_object.identifier_hash if resource_object
7
+ {'data' => identifier_hash}
8
+ end
9
+
10
+ def build_resources(options)
11
+ return unless included?
12
+ resource_model = parent_model.send(name)
13
+ return if resource_model.blank?
14
+
15
+ resource_class = JsonApi::Resources::Discovery.resource_for_name(resource_model, options.merge(parent_resource: parent, resource_class: explicit_resource_class))
16
+ @resources << resource_class.new(resource_model, options.merge({include: included.next}))
17
+ end
18
+
19
+ def resource_object
20
+ @resources.first
21
+ end
22
+
23
+ def cardinality
24
+ :one
25
+ end
26
+ end
27
+ end
28
+ end
@@ -3,7 +3,7 @@ module JsonApi
3
3
  class Includes
4
4
  attr_reader :name
5
5
  attr_writer :includes
6
- attr_accessor :next
6
+ attr_writer :next
7
7
 
8
8
  # Returns:
9
9
  # #<Includes>
@@ -20,17 +20,27 @@ module JsonApi
20
20
  zipped = first_array.zip(*other_arrays)
21
21
  first_array = zipped.shift
22
22
  first_include.includes = first_array.uniq.compact
23
+
23
24
  zipped.inject(first_include) do |base_include, object_name|
24
25
  new_include = self.new
25
26
  new_include.includes = object_name.uniq.compact
26
27
  base_include.next = new_include
27
28
  new_include
28
29
  end
30
+
29
31
  first_include
30
32
  end
31
33
 
34
+ def has_name?(name)
35
+ includes.include?(name)
36
+ end
37
+
32
38
  def includes
33
39
  @includes || []
34
40
  end
41
+
42
+ def next
43
+ @next || self.class.new
44
+ end
35
45
  end
36
46
  end
@@ -1,5 +1,4 @@
1
1
  require_relative 'resources/base'
2
- require_relative 'resources/relationships'
3
2
  require_relative 'resources/dsl'
4
3
 
5
4
  module JsonApi
@@ -58,7 +57,7 @@ module JsonApi
58
57
  def initialize(model, options={})
59
58
  options.stringify_keys!
60
59
  @_model = model
61
- @includes = options.fetch('include', [])
60
+ @includes = options.fetch('include', Includes.new)
62
61
  build_object_graph # base module method
63
62
  end
64
63
  end
@@ -37,7 +37,7 @@ module JsonApi
37
37
  private
38
38
  def add_relationship(object_name, cardinality, options)
39
39
  @relationships ||= []
40
- @relationships << RelationshipMeta.new(object_name, options.merge(cardinality: cardinality))
40
+ @relationships << JsonApi::Associations::Meta.new(object_name, options.merge(cardinality: cardinality))
41
41
  create_accessor_methods(object_name)
42
42
  end
43
43
 
@@ -25,7 +25,7 @@ module JsonApi
25
25
  def initialize(object, options)
26
26
  @meta = options.fetch('meta', Hash.new).stringify_keys
27
27
  @object = object
28
- @includes = options.fetch('include', [])
28
+ @includes = options.fetch('include', Includes.new)
29
29
  @resource_class = options.fetch('resource_class', nil)
30
30
  end
31
31
 
@@ -1,3 +1,3 @@
1
1
  module JsonApi
2
- VERSION = '0.4.0'
2
+ VERSION = '0.4.1'
3
3
  end
data/lib/json_api_ruby.rb CHANGED
@@ -3,6 +3,7 @@ end
3
3
 
4
4
  require_relative 'json_api_ruby/error_resource'
5
5
  require_relative 'json_api_ruby/serializer'
6
+ require_relative 'json_api_ruby/association'
6
7
  require_relative 'json_api_ruby/resource'
7
8
  require_relative 'json_api_ruby/exceptions'
8
9
  require_relative 'json_api_ruby/configuration'
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ describe JsonApi::Association do
4
+ let(:article) do
5
+ Article.new('How to raise Triops', 'Triops are hardy little creatures whose eggs can be frozen for as long as 40 years')
6
+ end
7
+
8
+ let(:article_resource) do
9
+ ArticleResource.new(article)
10
+ end
11
+
12
+ let(:author_relation) do
13
+ article_resource.class.relationships.find do |rel|
14
+ rel.name == 'author'
15
+ end
16
+ end
17
+
18
+ let(:comments_relation) do
19
+ article_resource.class.relationships.find do |rel|
20
+ rel.name == 'comments'
21
+ end
22
+ end
23
+
24
+ it 'exposes its cardinality' do
25
+ expect(author_relation.cardinality).to eq :one
26
+ expect(comments_relation.cardinality).to eq :many
27
+ end
28
+
29
+ describe 'link serialization' do
30
+ subject(:links_object) do
31
+ rel = author_relation.build_resources(parent_resource: article_resource)
32
+ rel.to_hash
33
+ end
34
+
35
+ it 'includes a links object' do
36
+ expect(links_object).to include('links')
37
+ end
38
+
39
+ it 'has a "self" object' do
40
+ expect(links_object['links']).to include('self')
41
+ end
42
+
43
+ it 'has a "related" object' do
44
+ expect(links_object['links']).to include('related')
45
+ end
46
+
47
+ describe 'self object' do
48
+ it 'is a link to the relationship on the parent object' do
49
+ expect(links_object['links']['self']).to eq "http://localhost:3000/articles/#{article.uuid}/relationships/author"
50
+ end
51
+ end
52
+
53
+ describe 'related object' do
54
+ it 'is a link to the base resource of the related object' do
55
+ expect(links_object['links']['related']).to eq "http://localhost:3000/articles/#{article.uuid}/author"
56
+ end
57
+ end
58
+
59
+ context 'with links turned off' do
60
+ before { JsonApi.configuration.use_links = false }
61
+ after { JsonApi.configuration.use_links = true }
62
+ it 'returns an empty hash' do
63
+ expect(links_object['links']).to be_blank
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'data serialization' do
69
+ let(:included_data) { JsonApi::Includes.parse_includes('author') }
70
+ subject(:serialized_object) do
71
+ rel = author_relation.build_resources(parent_resource: article_resource, included: included_data)
72
+ rel.to_hash
73
+ end
74
+
75
+ it 'has a data top level object' do
76
+ expect(serialized_object).to include('data')
77
+ end
78
+
79
+ describe 'data object' do
80
+ context 'when an array' do
81
+ let(:included_data) { JsonApi::Includes.parse_includes('comments') }
82
+ subject(:serialized_object) do
83
+ rel = comments_relation.build_resources(parent_resource: article_resource, included: included_data)
84
+ rel.to_hash
85
+ end
86
+
87
+ it 'has an identity hash for each object' do
88
+ expect(serialized_object['data'].flat_map(&:keys).uniq).to eq ['id', 'type']
89
+ end
90
+ end
91
+
92
+ context 'when a single object' do
93
+ it 'has an identity hash' do
94
+ expect(serialized_object['data'].keys).to eq ['id', 'type']
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'identity hash' do
101
+ let(:included_data) { JsonApi::Includes.parse_includes('author') }
102
+ it 'returns an identity hash given a model and parent resource' do
103
+ rel = author_relation.build_resources(parent_resource: article_resource, included: included_data)
104
+ serialized_object = rel.to_hash
105
+ expect(serialized_object['data'].keys).to eq ['id', 'type']
106
+ end
107
+ end
108
+
109
+ describe 'relationship serialization' do
110
+ context 'when the option "include" is true' do
111
+ let(:included_data) { JsonApi::Includes.parse_includes('author') }
112
+ it 'includes the data object' do
113
+ rel = author_relation.build_resources(parent_resource: article_resource, included: included_data)
114
+ serialized_data = rel.to_hash
115
+ expect(serialized_data).to include('data')
116
+ end
117
+ end
118
+
119
+ context 'when the option "include" is falsey' do
120
+ it 'does not include data object' do
121
+ rel = author_relation.build_resources(parent_resource: article_resource)
122
+ serialized_data = rel.to_hash
123
+ expect(serialized_data).to_not include('data')
124
+ end
125
+ end
126
+ end
127
+
128
+ describe 'passing in a resource class' do
129
+ let(:simple_article_resource) do
130
+ SimpleArticleResource.new(article, include: JsonApi::Includes.parse_includes(['author', 'comments']))
131
+ end
132
+
133
+ let(:author_relation) do
134
+ simple_article_resource.relationships.find do |rel|
135
+ rel.name == 'author'
136
+ end
137
+ end
138
+
139
+ let(:comment_relation) do
140
+ simple_article_resource.relationships.find do |rel|
141
+ rel.name == 'comments'
142
+ end
143
+ end
144
+
145
+ it 'uses the passed in resource_class for author' do
146
+ expect(author_relation.resource_object).to be_an_instance_of(SimplePersonResource)
147
+ end
148
+
149
+ it 'uses the passed in resource_class for each comment' do
150
+ comment_relation.resource_objects.each do |resource_object|
151
+ expect(resource_object).to be_an_instance_of(SimpleCommentResource)
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe JsonApi::Associations::Meta do
4
+ let(:parent_resource) { PersonResource.new(:person) }
5
+ it 'fails on an invalid cardinality' do
6
+ expect { JsonApi::Associations::Meta.new('person', resource_class: PersonResource, cardinality: :yeahbuddy) }.to raise_error(JsonApi::Associations::UnknownCardinalityError)
7
+ end
8
+
9
+ it 'builds a ToOne association' do
10
+ new_meta = JsonApi::Associations::Meta.new('person', resource_class: PersonResource, cardinality: :one)
11
+ expect(new_meta.build_resources({parent_resource: parent_resource})).to be_a(JsonApi::Associations::ToOne)
12
+ end
13
+
14
+ it 'builds a ToMany association' do
15
+ new_meta = JsonApi::Associations::Meta.new('person', resource_class: PersonResource, cardinality: :many)
16
+ expect(new_meta.build_resources({parent_resource: parent_resource})).to be_a(JsonApi::Associations::ToMany)
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ class ResourceObject
4
+ def identifier_hash
5
+ {}
6
+ end
7
+ end
8
+
9
+ describe JsonApi::Associations::ToMany do
10
+ subject(:assoc_instance) do
11
+ assoc = described_class.new('tomanytest', parent_resource: double('person_resource', _model: nil), explicit_resource_class: nil)
12
+ assoc.instance_variable_set(:@resources, [ResourceObject.new])
13
+ assoc
14
+ end
15
+
16
+ it 'builds a data hash' do
17
+ expect(assoc_instance.data).to eq({'data' => [{}]})
18
+ end
19
+
20
+ it 'returns nil when no resources are present' do
21
+ assoc = described_class.new('toonetest', parent_resource: double('person_resource', _model: nil), explicit_resource_class: nil)
22
+ expect(assoc.data).to eq({'data' => []})
23
+ end
24
+
25
+ it 'returns its resource objects' do
26
+ expect(assoc_instance.resource_objects).to be_an Array
27
+ end
28
+
29
+ it 'returns `many` for its cardinality' do
30
+ expect(assoc_instance.cardinality).to eq :many
31
+ end
32
+
33
+ context 'is included' do
34
+ it 'builds its included resources' do
35
+ article = Article.new('How to raise Triops', 'Triops are hardy little creatures whose eggs can be frozen for as long as 40 years')
36
+ article_resource = ArticleResource.new(article)
37
+ options = {
38
+ parent_resource: article_resource,
39
+ explicit_resource_class: CommentResource,
40
+ included: JsonApi::Includes.parse_includes(['comments'])
41
+ }
42
+ comment_assoc = described_class.new('comments', options)
43
+ comment_assoc.build_resources(options)
44
+ expect(comment_assoc.resource_objects.count).to eq article.comments.count
45
+ end
46
+ end
47
+
48
+ context 'is not included' do
49
+ it 'returns nil' do
50
+ article = Article.new('How to raise Triops', 'Triops are hardy little creatures whose eggs can be frozen for as long as 40 years')
51
+ article_resource = ArticleResource.new(article)
52
+ options = {
53
+ parent_resource: article_resource,
54
+ explicit_resource_class: CommentResource
55
+ }
56
+ comment_assoc = described_class.new('comments', options)
57
+ comment_assoc.build_resources(options)
58
+ expect(comment_assoc.resource_objects.count).to eq 0
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ class ResourceObject
4
+ def identifier_hash
5
+ {}
6
+ end
7
+ end
8
+
9
+ describe JsonApi::Associations::ToOne do
10
+ subject(:assoc_instance) do
11
+ assoc = described_class.new('toonetest', parent_resource: double('person_resource', _model: nil), explicit_resource_class: nil)
12
+ assoc.instance_variable_set(:@resources, [ResourceObject.new])
13
+ assoc
14
+ end
15
+
16
+ it 'builds a data hash' do
17
+ expect(assoc_instance.data).to eq({'data' => {}})
18
+ end
19
+
20
+ it 'returns nil when no resources are present' do
21
+ assoc = described_class.new('toonetest', parent_resource: double('person_resource', _model: nil), explicit_resource_class: nil)
22
+ expect(assoc.data).to eq({'data' => nil})
23
+ end
24
+
25
+ it 'returns its resource objects' do
26
+ expect(assoc_instance.resource_object).to be_a ResourceObject
27
+ end
28
+
29
+ it 'returns `many` for its cardinality' do
30
+ expect(assoc_instance.cardinality).to eq :one
31
+ end
32
+
33
+ context 'is included' do
34
+ it 'builds its included resources' do
35
+ article = Article.new('How to raise Triops', 'Triops are hardy little creatures whose eggs can be frozen for as long as 40 years')
36
+ article_resource = ArticleResource.new(article)
37
+ options = {
38
+ parent_resource: article_resource,
39
+ explicit_resource_class: CommentResource,
40
+ included: JsonApi::Includes.parse_includes(['comments'])
41
+ }
42
+ comment_assoc = described_class.new('comments', options)
43
+ comment_assoc.build_resources(options)
44
+ expect(comment_assoc.resource_object).to be_a CommentResource
45
+ end
46
+ end
47
+
48
+ context 'is not included' do
49
+ it 'returns nil' do
50
+ article = Article.new('How to raise Triops', 'Triops are hardy little creatures whose eggs can be frozen for as long as 40 years')
51
+ article_resource = ArticleResource.new(article)
52
+ options = {
53
+ parent_resource: article_resource,
54
+ explicit_resource_class: CommentResource
55
+ }
56
+ comment_assoc = described_class.new('comments', options)
57
+ comment_assoc.build_resources(options)
58
+ expect(comment_assoc.resource_object).to be_nil
59
+ end
60
+ end
61
+ end
@@ -4,7 +4,7 @@ describe JsonApi::Includes do
4
4
  context 'with nil' do
5
5
  subject(:parsed_with_nil) { described_class.parse_includes(nil) }
6
6
  specify { expect(parsed_with_nil.includes).to eq [] }
7
- specify { expect(parsed_with_nil.next).to be_nil }
7
+ specify { expect(parsed_with_nil.next).to be_an_instance_of(JsonApi::Includes) }
8
8
  end
9
9
 
10
10
  context 'with nested resources using the "." syntax' do
@@ -1,155 +1,4 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe JsonApi::Resources::RelationshipMeta do
4
- let(:article) do
5
- Article.new('How to raise Triops', 'Triops are hardy little creatures whose eggs can be frozen for as long as 40 years')
6
- end
7
-
8
- let(:article_resource) do
9
- ArticleResource.new(article)
10
- end
11
-
12
- let(:author_relation) do
13
- article_resource.class.relationships.find do |rel|
14
- rel.name == 'author'
15
- end
16
- end
17
-
18
- let(:comments_relation) do
19
- article_resource.class.relationships.find do |rel|
20
- rel.name == 'comments'
21
- end
22
- end
23
-
24
- it 'exposes its cardinality' do
25
- expect(author_relation.cardinality).to eq :one
26
- expect(comments_relation.cardinality).to eq :many
27
- end
28
-
29
- describe 'link serialization' do
30
- subject(:links_object) do
31
- rel = author_relation.build_resources(parent_resource: article_resource)
32
- rel.to_hash
33
- end
34
-
35
- it 'includes a links object' do
36
- expect(links_object).to include('links')
37
- end
38
-
39
- it 'has a "self" object' do
40
- expect(links_object['links']).to include('self')
41
- end
42
-
43
- it 'has a "related" object' do
44
- expect(links_object['links']).to include('related')
45
- end
46
-
47
- describe 'self object' do
48
- it 'is a link to the relationship on the parent object' do
49
- expect(links_object['links']['self']).to eq "http://localhost:3000/articles/#{article.uuid}/relationships/author"
50
- end
51
- end
52
-
53
- describe 'related object' do
54
- it 'is a link to the base resource of the related object' do
55
- expect(links_object['links']['related']).to eq "http://localhost:3000/articles/#{article.uuid}/author"
56
- end
57
- end
58
-
59
- context 'with links turned off' do
60
- before { JsonApi.configuration.use_links = false }
61
- after { JsonApi.configuration.use_links = true }
62
- it 'returns an empty hash' do
63
- expect(links_object['links']).to be_blank
64
- end
65
- end
66
- end
67
-
68
- describe 'data serialization' do
69
- let(:included_data) { JsonApi::Includes.parse_includes('author') }
70
- subject(:serialized_object) do
71
- rel = author_relation.build_resources(parent_resource: article_resource, included: included_data)
72
- rel.to_hash
73
- end
74
-
75
- it 'has a data top level object' do
76
- expect(serialized_object).to include('data')
77
- end
78
-
79
- describe 'data object' do
80
- context 'when an array' do
81
- let(:included_data) { JsonApi::Includes.parse_includes('comments') }
82
- subject(:serialized_object) do
83
- rel = comments_relation.build_resources(parent_resource: article_resource, included: included_data)
84
- rel.to_hash
85
- end
86
-
87
- it 'has an identity hash for each object' do
88
- expect(serialized_object['data'].flat_map(&:keys).uniq).to eq ['id', 'type']
89
- end
90
- end
91
-
92
- context 'when a single object' do
93
- it 'has an identity hash' do
94
- expect(serialized_object['data'].keys).to eq ['id', 'type']
95
- end
96
- end
97
- end
98
- end
99
-
100
- describe 'identity hash' do
101
- let(:included_data) { JsonApi::Includes.parse_includes('author') }
102
- it 'returns an identity hash given a model and parent resource' do
103
- rel = author_relation.build_resources(parent_resource: article_resource, included: included_data)
104
- serialized_object = rel.to_hash
105
- expect(serialized_object['data'].keys).to eq ['id', 'type']
106
- end
107
- end
108
-
109
- describe 'relationship serialization' do
110
- context 'when the option "include" is true' do
111
- let(:included_data) { JsonApi::Includes.parse_includes('author') }
112
- it 'includes the data object' do
113
- rel = author_relation.build_resources(parent_resource: article_resource, included: included_data)
114
- serialized_data = rel.to_hash
115
- expect(serialized_data).to include('data')
116
- end
117
- end
118
-
119
- context 'when the option "include" is falsey' do
120
- it 'does not include data object' do
121
- rel = author_relation.build_resources(parent_resource: article_resource)
122
- serialized_data = rel.to_hash
123
- expect(serialized_data).to_not include('data')
124
- end
125
- end
126
- end
127
-
128
- describe 'passing in a resource class' do
129
- let(:simple_article_resource) do
130
- SimpleArticleResource.new(article, include: JsonApi::Includes.parse_includes(['author', 'comments']))
131
- end
132
-
133
- let(:author_relation) do
134
- simple_article_resource.relationships.find do |rel|
135
- rel.name == 'author'
136
- end
137
- end
138
-
139
- let(:comment_relation) do
140
- simple_article_resource.relationships.find do |rel|
141
- rel.name == 'comments'
142
- end
143
- end
144
-
145
- it 'uses the passed in resource_class for author' do
146
- expect(author_relation.resource_object).to be_an_instance_of(SimplePersonResource)
147
- end
148
-
149
- it 'uses the passed in resource_class for each comment' do
150
- comment_relation.resource_objects.each do |resource_object|
151
- expect(resource_object).to be_an_instance_of(SimpleCommentResource)
152
- end
153
- end
154
- end
155
- end
3
+ # RSpec.describe JsonApi::Resources::RelationshipMeta do
4
+ # end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_api_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tracey Eubanks
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-27 00:00:00.000000000 Z
11
+ date: 2016-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -111,6 +111,10 @@ files:
111
111
  - Rakefile
112
112
  - json_api_ruby.gemspec
113
113
  - lib/json_api_ruby.rb
114
+ - lib/json_api_ruby/association.rb
115
+ - lib/json_api_ruby/associations/meta.rb
116
+ - lib/json_api_ruby/associations/to_many.rb
117
+ - lib/json_api_ruby/associations/to_one.rb
114
118
  - lib/json_api_ruby/configuration.rb
115
119
  - lib/json_api_ruby/error_resource.rb
116
120
  - lib/json_api_ruby/exceptions.rb
@@ -119,9 +123,12 @@ files:
119
123
  - lib/json_api_ruby/resources/base.rb
120
124
  - lib/json_api_ruby/resources/discovery.rb
121
125
  - lib/json_api_ruby/resources/dsl.rb
122
- - lib/json_api_ruby/resources/relationships.rb
123
126
  - lib/json_api_ruby/serializer.rb
124
127
  - lib/json_api_ruby/version.rb
128
+ - spec/json_api_ruby/association_spec.rb
129
+ - spec/json_api_ruby/associations/meta_spec.rb
130
+ - spec/json_api_ruby/associations/to_many_spec.rb
131
+ - spec/json_api_ruby/associations/to_one_spec.rb
125
132
  - spec/json_api_ruby/configuration_spec.rb
126
133
  - spec/json_api_ruby/includes_spec.rb
127
134
  - spec/json_api_ruby/resource_spec.rb
@@ -157,6 +164,10 @@ signing_key:
157
164
  specification_version: 4
158
165
  summary: Extremely lightweight implementation of JSON API
159
166
  test_files:
167
+ - spec/json_api_ruby/association_spec.rb
168
+ - spec/json_api_ruby/associations/meta_spec.rb
169
+ - spec/json_api_ruby/associations/to_many_spec.rb
170
+ - spec/json_api_ruby/associations/to_one_spec.rb
160
171
  - spec/json_api_ruby/configuration_spec.rb
161
172
  - spec/json_api_ruby/includes_spec.rb
162
173
  - spec/json_api_ruby/resource_spec.rb
@@ -1,140 +0,0 @@
1
- module JsonApi
2
- module Resources
3
-
4
- class RelationshipMeta
5
- # The name of this relationship.
6
- #
7
- # This name comes from the resource object that defines the
8
- # relationship. Example:
9
- #
10
- # class ArticleResource < JsonApi::Resource
11
- # has_one :author # this is the name of this relationship
12
- # end
13
- attr_reader :name
14
-
15
- attr_reader :cardinality
16
-
17
- attr_reader :explicit_resource_class
18
-
19
- def initialize(name, options)
20
- @name = name.to_s
21
- @cardinality = options.fetch(:cardinality)
22
- @explicit_resource_class = options.fetch(:resource_class, nil)
23
- end
24
-
25
- def build_resources(options)
26
- if cardinality == :one
27
- relationship = ToOneRelationship.new(name, options.merge(explicit_resource_class: explicit_resource_class))
28
- else
29
- relationship = ToManyRelationship.new(name, options.merge(explicit_resource_class: explicit_resource_class))
30
- end
31
- relationship.build_resources(options)
32
- relationship
33
- end
34
- end
35
-
36
- class Relationship
37
- # The resource object that "owns" this relationship
38
- #
39
- # Example:
40
- # class ArticleResource < JsonApi::Resource
41
- # has_one :author
42
- # end
43
- #
44
- # `ArticleResource` is the parent of the author object
45
- attr_reader :parent
46
- attr_reader :parent_model
47
-
48
- # Determines whether the `data` attribute should be filled out and
49
- # included
50
- attr_reader :included
51
- attr_reader :name
52
- attr_reader :explicit_resource_class
53
-
54
-
55
- # The resource object that represents this relationship
56
- attr_reader :resources
57
-
58
- def initialize(name, options)
59
- @name = name
60
- @resources = []
61
- @parent = options.fetch(:parent_resource)
62
- @parent_model = parent._model
63
- @included = options.fetch(:included, {})
64
- @explicit_resource_class = options.fetch(:explicit_resource_class)
65
- end
66
-
67
- def included?
68
- included.present? && included.includes.include?(@name)
69
- end
70
-
71
- def to_hash
72
- return_hash = {}
73
- return_hash.merge!(relationship_links) if JsonApi.configuration.use_links
74
- return_hash.merge!(data) if included?
75
- return_hash
76
- end
77
-
78
- def relationship_links
79
- {
80
- 'links' => {
81
- 'self' => JsonApi.configuration.base_url + parent.self_link_path + "/relationships/#{name}",
82
- 'related' => JsonApi.configuration.base_url + parent.self_link_path + "/#{name}"
83
- }
84
- }
85
- end
86
- end
87
-
88
- # convenience classes
89
- class ToOneRelationship < Relationship
90
- def data(options={})
91
- identifier_hash = resource_object.identifier_hash if resource_object
92
- {'data' => identifier_hash}
93
- end
94
-
95
- def build_resources(options)
96
- return unless included?
97
- resource_model = parent_model.send(name)
98
- return if resource_model.blank?
99
-
100
- resource_class = Discovery.resource_for_name(resource_model, options.merge(parent_resource: parent, resource_class: explicit_resource_class))
101
- @resources << resource_class.new(resource_model, options.merge({include: included.next}))
102
- end
103
-
104
- def resource_object
105
- @resources.first
106
- end
107
-
108
- def cardinality
109
- :one
110
- end
111
- end
112
-
113
- class ToManyRelationship < Relationship
114
- def data(options={})
115
- data = resource_objects.map do |object|
116
- object.identifier_hash
117
- end
118
- {'data' => data}
119
- end
120
-
121
- # Build the related resources
122
- def build_resources(options)
123
- return unless included?
124
-
125
- parent_model.send(name).each do |resource_model|
126
- resource_class = Discovery.resource_for_name(resource_model, options.merge(parent_resource: parent, resource_class: explicit_resource_class))
127
- @resources << resource_class.new(resource_model, options.merge({include: included.next}))
128
- end
129
- end
130
-
131
- def resource_objects
132
- @resources
133
- end
134
-
135
- def cardinality
136
- :many
137
- end
138
- end
139
- end
140
- end