ivy-serializers 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 965769bbd1ab1c1e7345b7767e4ac2f35ced0890
4
- data.tar.gz: 8c8b913128925ad947f0810f05cb47d34d7c1f95
3
+ metadata.gz: 53cc58ca39399bdcc72ecb47fd0aeecac296be43
4
+ data.tar.gz: ad5208023b028606b093a3056added2c7574ba82
5
5
  SHA512:
6
- metadata.gz: 5ccc6afa7b0b08e61cbb53876c8239192c60683fd54bf707e06e02e34bf2ce2b8ea86b0ad8cc40b6d87471b1ff6e531d1f9f5aed5b0ee0303f3a27631d1bbe0d
7
- data.tar.gz: 255b0f79b33bec71c516fadcc0d471abec3e4e685a6b520b508076ac39f70033416813a6438fa54d813638dc6511ed492cd6eada2e44a91c0b4d74ff7ec7bf19
6
+ metadata.gz: 329051d45ee2fbbd118e992ffcd83e69fcac05128e96606ba2fdd64e53ee1b2b74ad75649d1c48c32fddd83fa04c121706b6f0b79b15eb67171f34e368f0cb12
7
+ data.tar.gz: 6a6540f204ccb754c0c0846924708e4ee0a5caed80827ef1abcea481176c4d7f2a8a3f8c44ed9f45deb93f203d7b450b3098fde5ab003029a75f9445b998880e
data/.travis.yml CHANGED
@@ -1,10 +1,8 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
+ - 2.2.2
4
5
  - 1.9.3
5
- - 2.0.0
6
- - 2.1
7
- - 2.2
8
6
 
9
7
  cache: bundler
10
8
 
data/README.md CHANGED
@@ -1,13 +1,10 @@
1
- # Ivy::Serializers
1
+ # ivy-serializers
2
2
 
3
3
  [![Build Status](https://travis-ci.org/IvyApp/ivy-serializers.svg?branch=master)](https://travis-ci.org/IvyApp/ivy-serializers)
4
4
 
5
- JSON serialization for client-side apps, with multiple output formats. Ships
6
- with [ActiveModel::Serializers][ams] and [JSON-API][jsonapi] RC3 support
7
- out of the box.
5
+ JSON serialization for client-side apps, with multiple output formats. Ships with [ActiveModel::Serializers](https://github.com/rails-api/active_model_serializers) and [JSON-API](http://jsonapi.org/) 1.0 support out of the box.
8
6
 
9
- [ams]: https://github.com/rails-api/active_model_serializers
10
- [jsonapi]: http://jsonapi.org/
7
+ If you're building a Rails project, take a look at [ivy-serializers-rails](https://github.com/IvyApp/ivy-serializers-rails) instead.
11
8
 
12
9
  ## Installation
13
10
 
@@ -45,40 +42,27 @@ class Comment < ActiveRecord::Base
45
42
  end
46
43
  ```
47
44
 
48
- Define a serializer in `app/serializers/serializer.rb`:
45
+ Define a serializer in `app/serializers/my_serializer.rb`:
49
46
 
50
47
  ```ruby
51
- class Serializer < Ivy::Serializers::Serializer
48
+ class MySerializer < Ivy::Serializers::Serializer
52
49
  map Post do
53
- attributes :id, :title
50
+ attributes :title
54
51
  has_many :comments
55
52
  end
56
53
 
57
54
  map Comment do
58
- attributes :id, :body
55
+ attributes :body
59
56
  belongs_to :post
60
57
  end
61
58
  end
62
59
  ```
63
60
 
64
- Then set up your controllers to use the serializer:
65
-
66
- ```ruby
67
- class ApplicationController < ActionController::Base
68
- self.serializer = Serializer
69
- end
70
- ```
71
-
72
- Note that you're not limited to a single serializer. If you have multiple
73
- serialization formats, such as one for admin and one for public-facing, you can
74
- define alternate serializers in `app/serializers` and use them as well.
61
+ **NOTE**: An `id` attribute is automatically defined for you. This is a consequence of supporting JSON-API, which requires all resources to have IDs.
75
62
 
76
63
  ### Sideloading
77
64
 
78
- The `#belongs_to` and `#has_many` methods support an optional `:embed_in_root`
79
- option, which will load the associated record into the root of the payload. For
80
- instance, if we wanted the list of comments to be included when fetching
81
- a post, we could define the `has_many` relationship like so:
65
+ The `#belongs_to` and `#has_many` methods support an optional `:embed_in_root` option, which will load the associated record into the root of the payload. For instance, if we wanted the list of comments to be included when fetching a post, we could define the `has_many` relationship like so:
82
66
 
83
67
  ```ruby
84
68
  map Post do
@@ -86,8 +70,7 @@ map Post do
86
70
  end
87
71
  ```
88
72
 
89
- The same thing also works with `belongs_to`, so if we wanted to ensure the post
90
- was included when fetching a comment:
73
+ The same thing also works with `belongs_to`, so if we wanted to ensure the post was included when fetching a comment:
91
74
 
92
75
  ```ruby
93
76
  map Comment do
@@ -97,8 +80,7 @@ end
97
80
 
98
81
  ### Polymorphic Associations
99
82
 
100
- There is also support for polymorphic associations. To use it, pass the
101
- `:polymorphic => true` option to the `#belongs_to` or `#has_many` methods:
83
+ There is also support for polymorphic associations. To use it, pass the `:polymorphic => true` option to the `#belongs_to` or `#has_many` methods:
102
84
 
103
85
  ```ruby
104
86
  map Post do
@@ -112,18 +94,15 @@ end
112
94
 
113
95
  ### Customizing Attributes
114
96
 
115
- By default, attributes are mapped directly to methods on the record being
116
- serialized. So defining:
97
+ By default, attributes are mapped directly to methods on the record being serialized. So defining:
117
98
 
118
99
  ```ruby
119
100
  map Post do
120
- attributes :id, :title
101
+ attributes :title
121
102
  end
122
103
  ```
123
104
 
124
- will read `id` and `title` from the post and write it into the hash under the
125
- `:id` and `:title` keys. If you want to customize the value, you can use the
126
- `#attribute` method instead, and pass it a block:
105
+ will read `title` from the post and write it into the hash under the `:title` key. If you want to customize the value, you can use the `#attribute` method instead, and pass it a block:
127
106
 
128
107
  ```ruby
129
108
  map Post do
@@ -131,20 +110,7 @@ map Post do
131
110
  end
132
111
  ```
133
112
 
134
- In the above example, we read the `headline` attribute from the post and write
135
- it into the payload under the `:title` key.
136
-
137
- ### Alternate Output Formats
138
-
139
- By default the ActiveModel::Serializers output format is used. If you'd rather
140
- use JSON-API (or a custom format), you can do so by setting
141
- `serialization_format` in your controller. For instance, to use JSON-API:
142
-
143
- ```ruby
144
- class ApplicationController < ActionController::Base
145
- self.serialization_format = Ivy::Serializers::Formats::JSONAPI
146
- end
147
- ```
113
+ In the above example, we read the `headline` attribute from the post and write it into the payload under the `:title` key.
148
114
 
149
115
  ## Contributing
150
116
 
@@ -17,7 +17,7 @@ module Ivy
17
17
  end
18
18
 
19
19
  def get(resource)
20
- @getter.call(resource)
20
+ @getter.call(resource) if resource
21
21
  end
22
22
  end
23
23
  end
@@ -8,46 +8,49 @@ module Ivy
8
8
  @serializer = serializer
9
9
  @primary_resource_name = primary_resource_name
10
10
  @primary_resource = primary_resource
11
- @linked_resources = Hash.new { |hash, klass| hash[klass] = Set.new }
11
+ @included_resources = Hash.new { |hash, klass| hash[klass] = Set.new }
12
12
  end
13
13
 
14
14
  def belongs_to(name, resource, options={})
15
- link(resource) if options[:embed_in_root]
15
+ include_resource(resource) if resource && options[:embed_in_root]
16
16
  end
17
17
 
18
18
  def generate(generator)
19
19
  generator.document(self)
20
20
  end
21
21
 
22
- def generate_linked(generator)
23
- generator.linked(self) unless @linked_resources.empty?
22
+ def generate_attributes(generator, resource)
23
+ @serializer.attributes(generator, resource)
24
24
  end
25
25
 
26
- def generate_linked_resources(generator)
27
- @linked_resources.each_pair { |klass, resources| generator.linked_resources(klass, resources) }
26
+ def generate_included(generator)
27
+ generator.included(self) unless @included_resources.empty?
28
28
  end
29
29
 
30
- def generate_links(generator, resource)
31
- @serializer.links(generator, resource)
30
+ def generate_included_resources(generator)
31
+ @included_resources.each_pair { |klass, resources| generator.included_resources(klass, resources) }
32
+ end
33
+
34
+ def generate_relationships(generator, resource)
35
+ @serializer.relationships(generator, resource)
32
36
  end
33
37
 
34
38
  def generate_resource(generator, resource)
35
39
  @serializer.resource(generator, resource)
36
- generator.links(resource)
37
40
  end
38
41
 
39
42
  def has_many(name, resources, options={})
40
- link_many(resources) if options[:embed_in_root]
43
+ include_resources(resources) if options[:embed_in_root]
41
44
  end
42
45
 
43
46
  private
44
47
 
45
- def link(resource)
46
- @serializer.links(self, resource) if @linked_resources[resource.class].add?(resource)
48
+ def include_resource(resource)
49
+ @serializer.relationships(self, resource) if @included_resources[resource.class].add?(resource)
47
50
  end
48
51
 
49
- def link_many(resources)
50
- resources.each { |resource| link(resource) }
52
+ def include_resources(resources)
53
+ resources.each { |resource| include_resource(resource) }
51
54
  end
52
55
  end
53
56
  end
@@ -4,8 +4,8 @@ module Ivy
4
4
  module Serializers
5
5
  module Documents
6
6
  class IndividualResource < Document
7
- def generate_linked(generator)
8
- @serializer.links(self, @primary_resource)
7
+ def generate_included(generator)
8
+ @serializer.relationships(self, @primary_resource)
9
9
  super
10
10
  end
11
11
 
@@ -4,8 +4,8 @@ module Ivy
4
4
  module Serializers
5
5
  module Documents
6
6
  class ResourceCollection < Document
7
- def generate_linked(generator)
8
- @primary_resource.each { |resource| @serializer.links(self, resource) }
7
+ def generate_included(generator)
8
+ @primary_resource.each { |resource| @serializer.relationships(self, resource) }
9
9
  super
10
10
  end
11
11
 
@@ -29,10 +29,6 @@ module Ivy
29
29
  super(singularize(primary_resource_name).to_sym, primary_resource)
30
30
  end
31
31
 
32
- def primary_resources(primary_resources_name, primary_resources)
33
- super(primary_resources_name, primary_resources)
34
- end
35
-
36
32
  private
37
33
 
38
34
  def polymorphic_resource(resource)
@@ -19,13 +19,17 @@ module Ivy
19
19
  @hash_gen.store(key, value)
20
20
  end
21
21
 
22
+ def attributes(resource)
23
+ @document.generate_attributes(self, resource)
24
+ end
25
+
22
26
  def belongs_to(name, resource, options={})
23
27
  id(name, resource)
24
28
  end
25
29
 
26
30
  def document(document)
27
31
  document.generate_primary_resource(self)
28
- document.generate_linked(self)
32
+ document.generate_included(self)
29
33
  end
30
34
 
31
35
  def has_many(name, resources, options={})
@@ -40,19 +44,15 @@ module Ivy
40
44
  attribute(key, resources.map { |resource| extract_id(resource) })
41
45
  end
42
46
 
43
- def linked(document)
44
- document.generate_linked_resources(self)
47
+ def included(document)
48
+ document.generate_included_resources(self)
45
49
  end
46
50
 
47
- def linked_resources(resource_class, resources)
51
+ def included_resources(resource_class, resources)
48
52
  key = key_for_collection(resource_class).to_sym
49
53
  @hash_gen.store_array(key) { resources(resources) }
50
54
  end
51
55
 
52
- def links(resource)
53
- @document.generate_links(self, resource)
54
- end
55
-
56
56
  def primary_resource(primary_resource_name, primary_resource)
57
57
  @hash_gen.store_object(primary_resource_name) { resource(primary_resource) }
58
58
  end
@@ -61,7 +61,12 @@ module Ivy
61
61
  @hash_gen.store_array(primary_resources_name) { resources(primary_resources) }
62
62
  end
63
63
 
64
+ def relationships(resource)
65
+ @document.generate_relationships(self, resource)
66
+ end
67
+
64
68
  def resource(resource)
69
+ id(:id, resource)
65
70
  @document.generate_resource(self, resource)
66
71
  end
67
72
 
@@ -76,11 +81,11 @@ module Ivy
76
81
  private
77
82
 
78
83
  def extract_id(resource)
79
- resource.id
84
+ resource.id if resource
80
85
  end
81
86
 
82
87
  def extract_type(resource)
83
- ActiveSupport::Inflector.dasherize(ActiveSupport::Inflector.underscore(resource.class.name))
88
+ ActiveSupport::Inflector.dasherize(key_for_individual(resource.class))
84
89
  end
85
90
 
86
91
  def key_for_collection(resource_class)
@@ -4,27 +4,26 @@ module Ivy
4
4
  module Serializers
5
5
  module Formats
6
6
  class JSONAPI < JSON
7
- def attribute(key, value)
8
- value = coerce_id(value) if key == :id
9
- super
7
+ def attributes(resource)
8
+ @hash_gen.store_object(:attributes) { super }
10
9
  end
11
10
 
12
11
  def belongs_to(name, resource, options={})
13
- @hash_gen.store_object(name) { linkage(resource) }
12
+ if resource
13
+ @hash_gen.store_object(name) { linkage(resource) }
14
+ else
15
+ @hash_gen.store(name, nil)
16
+ end
14
17
  end
15
18
 
16
19
  def has_many(name, resources, options={})
17
20
  @hash_gen.store_object(name) { linkages(resources) }
18
21
  end
19
22
 
20
- def linked(document)
23
+ def included(document)
21
24
  @hash_gen.store_object(:included) { super }
22
25
  end
23
26
 
24
- def links(document)
25
- @hash_gen.store_object(:links) { super }
26
- end
27
-
28
27
  def primary_resource(primary_resource_name, primary_resource)
29
28
  super(:data, primary_resource)
30
29
  end
@@ -33,6 +32,10 @@ module Ivy
33
32
  super(:data, primary_resources)
34
33
  end
35
34
 
35
+ def relationships(document)
36
+ @hash_gen.store_object(:relationships) { super }
37
+ end
38
+
36
39
  def resource(resource)
37
40
  super
38
41
  type(:type, resource)
@@ -49,7 +52,7 @@ module Ivy
49
52
  end
50
53
 
51
54
  def linkage(resource)
52
- @hash_gen.store_object(:linkage) { linkage_object(resource) }
55
+ @hash_gen.store_object(:data) { linkage_object(resource) }
53
56
  end
54
57
 
55
58
  def linkage_object(resource)
@@ -58,7 +61,7 @@ module Ivy
58
61
  end
59
62
 
60
63
  def linkages(resources)
61
- @hash_gen.store_array(:linkage) { linkage_objects(resources) }
64
+ @hash_gen.store_array(:data) { linkage_objects(resources) }
62
65
  end
63
66
 
64
67
  def linkage_objects(resources)
@@ -6,10 +6,8 @@ module Ivy
6
6
  class Mapping
7
7
  def initialize(klass)
8
8
  @attrs = {}
9
- @links = {}
9
+ @relationships = {}
10
10
  @klass = klass
11
-
12
- attribute(:id)
13
11
  end
14
12
 
15
13
  def attribute(name, &block)
@@ -21,19 +19,24 @@ module Ivy
21
19
  end
22
20
 
23
21
  def belongs_to(name, options={}, &block)
24
- @links[name] = Relationships::BelongsTo.new(name, options, &block)
22
+ @relationships[name] = Relationships::BelongsTo.new(name, options, &block)
23
+ end
24
+
25
+ def generate_attributes(generator, resource)
26
+ @attrs.each_value { |attr| attr.generate(generator, resource) }
25
27
  end
26
28
 
27
29
  def has_many(name, options={}, &block)
28
- @links[name] = Relationships::HasMany.new(name, options, &block)
30
+ @relationships[name] = Relationships::HasMany.new(name, options, &block)
29
31
  end
30
32
 
31
- def links(generator, resource)
32
- @links.each_value { |link| link.generate(generator, resource) }
33
+ def relationships(generator, resource)
34
+ @relationships.each_value { |relationship| relationship.generate(generator, resource) }
33
35
  end
34
36
 
35
37
  def resource(generator, resource)
36
- @attrs.each_value { |attr| attr.generate(generator, resource) }
38
+ generator.attributes(resource) unless @attrs.empty?
39
+ generator.relationships(resource) unless @relationships.empty?
37
40
  end
38
41
  end
39
42
  end
@@ -7,14 +7,18 @@ module Ivy
7
7
  @mappings = Hash.new { |hash, klass| hash[klass] = new_mapping(klass) }
8
8
  end
9
9
 
10
- def links(generator, resource)
11
- mapping_for(resource.class).links(generator, resource)
10
+ def attributes(generator, resource)
11
+ mapping_for(resource.class).generate_attributes(generator, resource)
12
12
  end
13
13
 
14
14
  def map(klass, &block)
15
15
  mapping_for(klass).instance_eval(&block)
16
16
  end
17
17
 
18
+ def relationships(generator, resource)
19
+ mapping_for(resource.class).relationships(generator, resource)
20
+ end
21
+
18
22
  def resource(generator, resource)
19
23
  mapping_for(resource.class).resource(generator, resource)
20
24
  end
@@ -6,12 +6,16 @@ module Ivy
6
6
  class << self
7
7
  attr_accessor :_registry
8
8
 
9
+ def attributes(generator, resource)
10
+ _registry.attributes(generator, resource)
11
+ end
12
+
9
13
  def inherited(base)
10
14
  base._registry = Registry.new
11
15
  end
12
16
 
13
- def links(generator, resource)
14
- _registry.links(generator, resource)
17
+ def relationships(generator, resource)
18
+ _registry.relationships(generator, resource)
15
19
  end
16
20
 
17
21
  def map(klass, &block)
@@ -23,8 +27,12 @@ module Ivy
23
27
  end
24
28
  end
25
29
 
26
- def links(generator, links)
27
- self.class.links(generator, links)
30
+ def attributes(generator, resource)
31
+ self.class.attributes(generator, resource)
32
+ end
33
+
34
+ def relationships(generator, resource)
35
+ self.class.relationships(generator, resource)
28
36
  end
29
37
 
30
38
  def resource(generator, resource)
@@ -1,5 +1,5 @@
1
1
  module Ivy
2
2
  module Serializers
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
@@ -87,6 +87,12 @@ RSpec.describe Ivy::Serializers::Formats::ActiveModelSerializers do
87
87
  let(:resource) { post }
88
88
 
89
89
  it { should eq(:post => {:author_id => 1, :id => 1}) }
90
+
91
+ context 'with no related resource' do
92
+ let(:author) { nil }
93
+
94
+ it { should eq(:post => {:author_id => nil, :id => 1}) }
95
+ end
90
96
  end
91
97
 
92
98
  context 'for a resource collection' do
@@ -107,6 +113,12 @@ RSpec.describe Ivy::Serializers::Formats::ActiveModelSerializers do
107
113
  let(:resource) { post }
108
114
 
109
115
  it { should eq(:post => {:id => 1, :user_id => 1}) }
116
+
117
+ context 'with no related resource' do
118
+ let(:author) { nil }
119
+
120
+ it { should eq(:post => {:id => 1, :user_id => nil}) }
121
+ end
110
122
  end
111
123
 
112
124
  context 'for a resource collection' do
@@ -130,6 +142,12 @@ RSpec.describe Ivy::Serializers::Formats::ActiveModelSerializers do
130
142
  :authors => [{:id => 1}],
131
143
  :post => {:author_id => 1, :id => 1}
132
144
  ) }
145
+
146
+ context 'with no related resource' do
147
+ let(:author) { nil }
148
+
149
+ it { should eq(:post => {:author_id => nil, :id => 1}) }
150
+ end
133
151
  end
134
152
 
135
153
  context 'for a resource collection' do
@@ -153,6 +171,12 @@ RSpec.describe Ivy::Serializers::Formats::ActiveModelSerializers do
153
171
  let(:resource) { post }
154
172
 
155
173
  it { should eq(:post => {:author => {:id => 1, :type => 'author'}, :id => 1}) }
174
+
175
+ context 'with no related resource' do
176
+ let(:author) { nil }
177
+
178
+ it { should eq(:post => {:author => nil, :id => 1}) }
179
+ end
156
180
  end
157
181
 
158
182
  context 'for a resource collection' do
@@ -19,8 +19,7 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
19
19
  it { should eq({
20
20
  :data => {
21
21
  :type => 'post',
22
- :id => '1',
23
- :links => {}
22
+ :id => '1'
24
23
  }
25
24
  }) }
26
25
  end
@@ -31,8 +30,7 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
31
30
  it { should eq({
32
31
  :data => [{
33
32
  :type => 'post',
34
- :id => '1',
35
- :links => {}
33
+ :id => '1'
36
34
  }]
37
35
  }) }
38
36
  end
@@ -56,8 +54,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
56
54
  :data => {
57
55
  :type => 'post',
58
56
  :id => '1',
59
- :title => 'title',
60
- :links => {}
57
+ :attributes => {
58
+ :title => 'title'
59
+ }
61
60
  }
62
61
  }) }
63
62
  end
@@ -69,8 +68,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
69
68
  :data => [{
70
69
  :type => 'post',
71
70
  :id => '1',
72
- :title => 'title',
73
- :links => {}
71
+ :attributes => {
72
+ :title => 'title'
73
+ }
74
74
  }]
75
75
  }) }
76
76
  end
@@ -90,8 +90,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
90
90
  :data => {
91
91
  :type => 'post',
92
92
  :id => '1',
93
- :headline => 'title',
94
- :links => {}
93
+ :attributes => {
94
+ :headline => 'title'
95
+ }
95
96
  }
96
97
  }) }
97
98
  end
@@ -103,8 +104,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
103
104
  :data => [{
104
105
  :type => 'post',
105
106
  :id => '1',
106
- :headline => 'title',
107
- :links => {}
107
+ :attributes => {
108
+ :headline => 'title'
109
+ }
108
110
  }]
109
111
  }) }
110
112
  end
@@ -131,13 +133,27 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
131
133
  :data => {
132
134
  :type => 'post',
133
135
  :id => '1',
134
- :links => {
136
+ :relationships => {
135
137
  :author => {
136
- :linkage => {:id => '1', :type => 'author'}
138
+ :data => {:id => '1', :type => 'author'}
137
139
  }
138
140
  }
139
141
  }
140
142
  }) }
143
+
144
+ context 'with no related resource' do
145
+ let(:author) { nil }
146
+
147
+ it { should eq({
148
+ :data => {
149
+ :type => 'post',
150
+ :id => '1',
151
+ :relationships => {
152
+ :author => nil
153
+ }
154
+ }
155
+ }) }
156
+ end
141
157
  end
142
158
 
143
159
  context 'for a resource collection' do
@@ -147,9 +163,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
147
163
  :data => [{
148
164
  :type => 'post',
149
165
  :id => '1',
150
- :links => {
166
+ :relationships => {
151
167
  :author => {
152
- :linkage => {:id => '1', :type => 'author'}
168
+ :data => {:id => '1', :type => 'author'}
153
169
  }
154
170
  }
155
171
  }]
@@ -171,13 +187,27 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
171
187
  :data => {
172
188
  :type => 'post',
173
189
  :id => '1',
174
- :links => {
190
+ :relationships => {
175
191
  :user => {
176
- :linkage => {:id => '1', :type => 'author'}
192
+ :data => {:id => '1', :type => 'author'}
177
193
  }
178
194
  }
179
195
  }
180
196
  }) }
197
+
198
+ context 'with no related resource' do
199
+ let(:author) { nil }
200
+
201
+ it { should eq({
202
+ :data => {
203
+ :type => 'post',
204
+ :id => '1',
205
+ :relationships => {
206
+ :user => nil
207
+ }
208
+ }
209
+ }) }
210
+ end
181
211
  end
182
212
 
183
213
  context 'for a resource collection' do
@@ -187,9 +217,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
187
217
  :data => [{
188
218
  :type => 'post',
189
219
  :id => '1',
190
- :links => {
220
+ :relationships => {
191
221
  :user => {
192
- :linkage => {:id => '1', :type => 'author'}
222
+ :data => {:id => '1', :type => 'author'}
193
223
  }
194
224
  }
195
225
  }]
@@ -211,9 +241,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
211
241
  :data => {
212
242
  :type => 'post',
213
243
  :id => '1',
214
- :links => {
244
+ :relationships => {
215
245
  :author => {
216
- :linkage => {:id => '1', :type => 'author'}
246
+ :data => {:id => '1', :type => 'author'}
217
247
  }
218
248
  }
219
249
  },
@@ -221,11 +251,24 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
221
251
  :included => {
222
252
  :authors => [{
223
253
  :id => '1',
224
- :links => {},
225
254
  :type => 'author'
226
255
  }]
227
256
  }
228
257
  }) }
258
+
259
+ context 'with no related resource' do
260
+ let(:author) { nil }
261
+
262
+ it { should eq({
263
+ :data => {
264
+ :type => 'post',
265
+ :id => '1',
266
+ :relationships => {
267
+ :author => nil
268
+ }
269
+ }
270
+ }) }
271
+ end
229
272
  end
230
273
 
231
274
  context 'for a resource collection' do
@@ -235,9 +278,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
235
278
  :data => [{
236
279
  :type => 'post',
237
280
  :id => '1',
238
- :links => {
281
+ :relationships => {
239
282
  :author => {
240
- :linkage => {:id => '1', :type => 'author'}
283
+ :data => {:id => '1', :type => 'author'}
241
284
  }
242
285
  }
243
286
  }],
@@ -245,7 +288,6 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
245
288
  :included => {
246
289
  :authors => [{
247
290
  :id => '1',
248
- :links => {},
249
291
  :type => 'author'
250
292
  }]
251
293
  }
@@ -267,13 +309,27 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
267
309
  :data => {
268
310
  :type => 'post',
269
311
  :id => '1',
270
- :links => {
312
+ :relationships => {
271
313
  :author => {
272
- :linkage => {:id => '1', :type => 'author'}
314
+ :data => {:id => '1', :type => 'author'}
273
315
  }
274
316
  }
275
317
  }
276
318
  }) }
319
+
320
+ context 'with no related resource' do
321
+ let(:author) { nil }
322
+
323
+ it { should eq({
324
+ :data => {
325
+ :type => 'post',
326
+ :id => '1',
327
+ :relationships => {
328
+ :author => nil
329
+ }
330
+ }
331
+ }) }
332
+ end
277
333
  end
278
334
 
279
335
  context 'for a resource collection' do
@@ -283,9 +339,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
283
339
  :data => [{
284
340
  :type => 'post',
285
341
  :id => '1',
286
- :links => {
342
+ :relationships => {
287
343
  :author => {
288
- :linkage => {:id => '1', :type => 'author'}
344
+ :data => {:id => '1', :type => 'author'}
289
345
  }
290
346
  }
291
347
  }]
@@ -314,9 +370,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
314
370
  :data => {
315
371
  :type => 'post',
316
372
  :id => '1',
317
- :links => {
373
+ :relationships => {
318
374
  :comments => {
319
- :linkage => [{:id => '1', :type => 'comment'}]
375
+ :data => [{:id => '1', :type => 'comment'}]
320
376
  }
321
377
  }
322
378
  }
@@ -330,9 +386,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
330
386
  :data => [{
331
387
  :type => 'post',
332
388
  :id => '1',
333
- :links => {
389
+ :relationships => {
334
390
  :comments => {
335
- :linkage => [{:id => '1', :type => 'comment'}]
391
+ :data => [{:id => '1', :type => 'comment'}]
336
392
  }
337
393
  }
338
394
  }]
@@ -354,9 +410,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
354
410
  :data => {
355
411
  :type => 'post',
356
412
  :id => '1',
357
- :links => {
413
+ :relationships => {
358
414
  :replies => {
359
- :linkage => [{:id => '1', :type => 'comment'}]
415
+ :data => [{:id => '1', :type => 'comment'}]
360
416
  }
361
417
  }
362
418
  }
@@ -370,9 +426,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
370
426
  :data => [{
371
427
  :type => 'post',
372
428
  :id => '1',
373
- :links => {
429
+ :relationships => {
374
430
  :replies => {
375
- :linkage => [{:id => '1', :type => 'comment'}]
431
+ :data => [{:id => '1', :type => 'comment'}]
376
432
  }
377
433
  }
378
434
  }]
@@ -394,9 +450,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
394
450
  :data => {
395
451
  :type => 'post',
396
452
  :id => '1',
397
- :links => {
453
+ :relationships => {
398
454
  :comments => {
399
- :linkage => [{:id => '1', :type => 'comment'}]
455
+ :data => [{:id => '1', :type => 'comment'}]
400
456
  }
401
457
  }
402
458
  },
@@ -404,8 +460,7 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
404
460
  :included => {
405
461
  :comments => [{
406
462
  :type => 'comment',
407
- :id => '1',
408
- :links => {}
463
+ :id => '1'
409
464
  }]
410
465
  }
411
466
  }) }
@@ -418,9 +473,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
418
473
  :data => [{
419
474
  :type => 'post',
420
475
  :id => '1',
421
- :links => {
476
+ :relationships => {
422
477
  :comments => {
423
- :linkage => [{:id => '1', :type => 'comment'}]
478
+ :data => [{:id => '1', :type => 'comment'}]
424
479
  }
425
480
  }
426
481
  }],
@@ -428,8 +483,7 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
428
483
  :included => {
429
484
  :comments => [{
430
485
  :type => 'comment',
431
- :id => '1',
432
- :links => {}
486
+ :id => '1'
433
487
  }]
434
488
  }
435
489
  }) }
@@ -450,9 +504,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
450
504
  :data => {
451
505
  :type => 'post',
452
506
  :id => '1',
453
- :links => {
507
+ :relationships => {
454
508
  :comments => {
455
- :linkage => [{:id => '1', :type => 'comment'}]
509
+ :data => [{:id => '1', :type => 'comment'}]
456
510
  }
457
511
  }
458
512
  }
@@ -466,9 +520,9 @@ RSpec.describe Ivy::Serializers::Formats::JSONAPI do
466
520
  :data => [{
467
521
  :type => 'post',
468
522
  :id => '1',
469
- :links => {
523
+ :relationships => {
470
524
  :comments => {
471
- :linkage => [{:id => '1', :type => 'comment'}]
525
+ :data => [{:id => '1', :type => 'comment'}]
472
526
  }
473
527
  }
474
528
  }]
@@ -0,0 +1,283 @@
1
+ require 'ivy/serializers'
2
+
3
+ RSpec.describe Ivy::Serializers::Formats::JSON do
4
+ let(:format) { described_class.new(document) }
5
+
6
+ describe '#as_json' do
7
+ let(:registry) { Ivy::Serializers::Registry.new }
8
+ let(:document) { Ivy::Serializers::Documents.create(registry, :posts, resource) }
9
+
10
+ subject { format.as_json }
11
+
12
+ context 'with default mapping' do
13
+ let(:post) { double('post', :id => 1) }
14
+
15
+ context 'for an individual resource' do
16
+ let(:resource) { post }
17
+
18
+ it { should eq(:posts => {:id => 1}) }
19
+ end
20
+
21
+ context 'for a resource collection' do
22
+ let(:resource) { [post] }
23
+
24
+ it { should eq(:posts => [{:id => 1}]) }
25
+ end
26
+ end
27
+
28
+ context 'with an attribute' do
29
+ let(:post_class) { double('Post') }
30
+ let(:post) { double('post', :class => post_class, :id => 1, :title => 'title') }
31
+
32
+ context 'with default options' do
33
+ before do
34
+ registry.map post_class do
35
+ attribute :title
36
+ end
37
+ end
38
+
39
+ context 'for an individual resource' do
40
+ let(:resource) { post }
41
+
42
+ it { should eq(:posts => {:id => 1, :title => 'title'}) }
43
+ end
44
+
45
+ context 'for a resource collection' do
46
+ let(:resource) { [post] }
47
+
48
+ it { should eq(:posts => [{:id => 1, :title => 'title'}]) }
49
+ end
50
+ end
51
+
52
+ context 'with a block provided' do
53
+ before do
54
+ registry.map post_class do
55
+ attribute(:headline) { |post| post.title }
56
+ end
57
+ end
58
+
59
+ context 'for an individual resource' do
60
+ let(:resource) { post }
61
+
62
+ it { should eq(:posts => {:id => 1, :headline => 'title'}) }
63
+ end
64
+
65
+ context 'for a resource collection' do
66
+ let(:resource) { [post] }
67
+
68
+ it { should eq(:posts => [{:id => 1, :headline => 'title'}]) }
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'with a belongs_to relationship' do
74
+ let(:author_class) { double('Author', :name => 'Author') }
75
+ let(:author) { double('author', :class => author_class, :id => 1) }
76
+ let(:post_class) { double('Post') }
77
+ let(:post) { double('post', :author => author, :class => post_class, :id => 1) }
78
+
79
+ context 'with default options' do
80
+ before do
81
+ registry.map post_class do
82
+ belongs_to :author
83
+ end
84
+ end
85
+
86
+ context 'for an individual resource' do
87
+ let(:resource) { post }
88
+
89
+ it { should eq(:posts => {:author => 1, :id => 1}) }
90
+
91
+ context 'with no related resource' do
92
+ let(:author) { nil }
93
+
94
+ it { should eq(:posts => {:author => nil, :id => 1}) }
95
+ end
96
+ end
97
+
98
+ context 'for a resource collection' do
99
+ let(:resource) { [post] }
100
+
101
+ it { should eq(:posts => [{:author => 1, :id => 1}]) }
102
+ end
103
+ end
104
+
105
+ context 'with a block provided' do
106
+ before do
107
+ registry.map post_class do
108
+ belongs_to(:user) { |post| post.author }
109
+ end
110
+ end
111
+
112
+ context 'for an individual resource' do
113
+ let(:resource) { post }
114
+
115
+ it { should eq(:posts => {:id => 1, :user => 1}) }
116
+
117
+ context 'with no related resource' do
118
+ let(:author) { nil }
119
+
120
+ it { should eq(:posts => {:id => 1, :user => nil}) }
121
+ end
122
+ end
123
+
124
+ context 'for a resource collection' do
125
+ let(:resource) { [post] }
126
+
127
+ it { should eq(:posts => [{:id => 1, :user => 1}]) }
128
+ end
129
+ end
130
+
131
+ context 'with the :embed_in_root option' do
132
+ before do
133
+ registry.map post_class do
134
+ belongs_to :author, :embed_in_root => true
135
+ end
136
+ end
137
+
138
+ context 'for an individual resource' do
139
+ let(:resource) { post }
140
+
141
+ it { should eq(
142
+ :authors => [{:id => 1}],
143
+ :posts => {:author => 1, :id => 1}
144
+ ) }
145
+
146
+ context 'with no related resource' do
147
+ let(:author) { nil }
148
+
149
+ it { should eq(:posts => {:author => nil, :id => 1}) }
150
+ end
151
+ end
152
+
153
+ context 'for a resource collection' do
154
+ let(:resource) { [post] }
155
+
156
+ it { should eq(
157
+ :authors => [{:id => 1}],
158
+ :posts => [{:author => 1, :id => 1}]
159
+ ) }
160
+ end
161
+ end
162
+
163
+ context 'with the :polymorphic option' do
164
+ before do
165
+ registry.map post_class do
166
+ belongs_to :author, :polymorphic => true
167
+ end
168
+ end
169
+
170
+ context 'for an individual resource' do
171
+ let(:resource) { post }
172
+
173
+ it { should eq(:posts => {:author => 1, :id => 1}) }
174
+
175
+ context 'with no related resource' do
176
+ let(:author) { nil }
177
+
178
+ it { should eq(:posts => {:author => nil, :id => 1}) }
179
+ end
180
+ end
181
+
182
+ context 'for a resource collection' do
183
+ let(:resource) { [post] }
184
+
185
+ it { should eq(:posts => [{:author => 1, :id => 1}]) }
186
+ end
187
+ end
188
+ end
189
+
190
+ context 'with a has_many relationship' do
191
+ let(:comment_class) { double('Comment', :name => 'Comment') }
192
+ let(:comment) { double('comment', :class => comment_class, :id => 1) }
193
+ let(:post_class) { double('Post') }
194
+ let(:post) { double('post', :class => post_class, :comments => [comment], :id => 1) }
195
+
196
+ context 'with default options' do
197
+ before do
198
+ registry.map post_class do
199
+ has_many :comments
200
+ end
201
+ end
202
+
203
+ context 'for an individual resource' do
204
+ let(:resource) { post }
205
+
206
+ it { should eq(:posts => {:comments => [1], :id => 1}) }
207
+ end
208
+
209
+ context 'for a resource collection' do
210
+ let(:resource) { [post] }
211
+
212
+ it { should eq(:posts => [{:comments => [1], :id => 1}]) }
213
+ end
214
+ end
215
+
216
+ context 'with a block provided' do
217
+ before do
218
+ registry.map post_class do
219
+ has_many(:replies) { |post| post.comments }
220
+ end
221
+ end
222
+
223
+ context 'for an individual resource' do
224
+ let(:resource) { post }
225
+
226
+ it { should eq(:posts => {:id => 1, :replies => [1]}) }
227
+ end
228
+
229
+ context 'for a resource collection' do
230
+ let(:resource) { [post] }
231
+
232
+ it { should eq(:posts => [{:id => 1, :replies => [1]}]) }
233
+ end
234
+ end
235
+
236
+ context 'with the :embed_in_root option' do
237
+ before do
238
+ registry.map post_class do
239
+ has_many :comments, :embed_in_root => true
240
+ end
241
+ end
242
+
243
+ context 'for an individual resource' do
244
+ let(:resource) { post }
245
+
246
+ it { should eq(
247
+ :comments => [{:id => 1}],
248
+ :posts => {:comments => [1], :id => 1}
249
+ ) }
250
+ end
251
+
252
+ context 'for a resource collection' do
253
+ let(:resource) { [post] }
254
+
255
+ it { should eq(
256
+ :comments => [{:id => 1}],
257
+ :posts => [{:comments => [1], :id => 1}]
258
+ ) }
259
+ end
260
+ end
261
+
262
+ context 'with the :polymorphic option' do
263
+ before do
264
+ registry.map post_class do
265
+ has_many :comments, :polymorphic => true
266
+ end
267
+ end
268
+
269
+ context 'for an individual resource' do
270
+ let(:resource) { post }
271
+
272
+ it { should eq(:posts => {:comments => [1], :id => 1}) }
273
+ end
274
+
275
+ context 'for a resource collection' do
276
+ let(:resource) { [post] }
277
+
278
+ it { should eq(:posts => [{:comments => [1], :id => 1}]) }
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ivy-serializers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dray Lacy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-05 00:00:00.000000000 Z
11
+ date: 2015-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -130,6 +130,7 @@ files:
130
130
  - lib/ivy/serializers/version.rb
131
131
  - spec/integration/formats/active_model_serializers_spec.rb
132
132
  - spec/integration/formats/json_api_spec.rb
133
+ - spec/integration/formats/json_spec.rb
133
134
  - spec/integration/serializer_spec.rb
134
135
  - spec/spec_helper.rb
135
136
  homepage: ''
@@ -159,5 +160,6 @@ summary: JSON serialization for client-side apps.
159
160
  test_files:
160
161
  - spec/integration/formats/active_model_serializers_spec.rb
161
162
  - spec/integration/formats/json_api_spec.rb
163
+ - spec/integration/formats/json_spec.rb
162
164
  - spec/integration/serializer_spec.rb
163
165
  - spec/spec_helper.rb