Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add include_data :via_include_parameter #1797

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ Breaking changes:
Fixes:

- [#1887](https://github.com/rails-api/active_model_serializers/pull/1887) Make the comment reflect what the function does (@johnnymo87)
- [#1890](https://github.com/rails-api/active_model_serializers/issues/1890) Ensure generator inherits from ApplicationSerializer when available (@richmolj)
- [#1922](https://github.com/rails-api/active_model_serializers/pull/1922) Make railtie an optional dependency in runtime (@ggpasqualino)

Features:

- [#1791](https://github.com/rails-api/active_model_serializers/pull/1791) (@bf4, @youroff, @NullVoxPopuli)
- Added `jsonapi_namespace_separator` config option.
- [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee)
- [#1797](https://github.com/rails-api/active_model_serializers/pull/1797) Only include 'relationships' when sideloading (@richmolj)
- [#1917](https://github.com/rails-api/active_model_serializers/pull/1917) Add `jsonapi_pagination_links_enabled` configuration option (@richmolj)

Fixes:

Expand All @@ -26,6 +30,7 @@ Misc:
- [#1878](https://github.com/rails-api/active_model_serializers/pull/1878) Cache key generation for serializers now uses `ActiveSupport::Cache.expand_cache_key` instead of `Array#join` by default and is also overridable. This change should be backward-compatible. (@markiz)

- [#1799](https://github.com/rails-api/active_model_serializers/pull/1799) Add documentation for setting the adapter. (@ScottKbka)
- [#1909](https://github.com/rails-api/active_model_serializers/pull/1909) Add documentation for relationship links. (@vasilakisfil, @NullVoxPopuli)

### [v0.10.2 (2016-07-05)](https://github.com/rails-api/active_model_serializers/compare/v0.10.1...v0.10.2)

Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ group :test do
gem 'sqlite3', platform: (@windows_platforms + [:ruby])
gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
gem 'codeclimate-test-reporter', require: false
gem 'm', '~> 1.5'
gem 'pry', '~> 0.10'
gem 'pry-byebug', '~> 3.4', platform: :ruby
end

group :development, :test do
Expand Down
2 changes: 1 addition & 1 deletion active_model_serializers.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
# 'rack'
# 'rack-test', '~> 0.6.2'

spec.add_runtime_dependency 'railties', rails_versions
spec.add_development_dependency 'railties', rails_versions
# 'activesupport', rails_versions
# 'actionpack', rails_versions
# 'rake', '>= 0.8.7'
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10.

- [How to add root key](howto/add_root_key.md)
- [How to add pagination links](howto/add_pagination_links.md)
- [How to add relationship links](howto/add_relationship_links.md)
- [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md)
- [Testing ActiveModelSerializers](howto/test.md)
- [Passing Arbitrary Options](howto/passing_arbitrary_options.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/general/serializers.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class PictureSerializer < ActiveModel::Serializer
end
```

For more context, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter.
You can specify the serializers by [overriding serializer_for](serializers.md#overriding-association-serializer-lookup). For more context about polymorphic relationships, see the [tests](../../test/adapter/polymorphic_test.rb) for each adapter.

### Caching

Expand Down
4 changes: 2 additions & 2 deletions docs/howto/add_pagination_links.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ the resource is paginated and if you are using the ```JsonApi``` adapter.
If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari)
or [WillPaginate](https://github.com/mislav/will_paginate).

Although the others adapters does not have this feature, it is possible to
Although the other adapters do not have this feature, it is possible to
implement pagination links to `JSON` adapter. For more information about it,
please see in our docs
please check our docs.

###### Kaminari examples

Expand Down
137 changes: 137 additions & 0 deletions docs/howto/add_relationship_links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
[Back to Guides](../README.md)

# How to add relationship links

ActiveModelSerializers offers you many ways to add links in your JSON, depending on your needs.
The most common use case for links is supporting nested resources.

The following examples are without included relationship data (`include` param is empty),
specifically the following Rails controller was used for these examples:

```ruby
class Api::V1::UsersController < ApplicationController
def show
render jsonapi: User.find(params[:id]),
serializer: Api::V1::UserSerializer,
include: []
end
```

Bear in mind though that ActiveModelSerializers are [framework-agnostic](outside_controller_use.md), Rails is just a common example here.

### Links as an attribute of a resource
**This is applicable to JSONAPI, JSON and Attributes adapters**

You can define an attribute in the resource, named `links`.

```ruby
class Api::V1::UserSerializer < ActiveModel::Serializer
attributes :id, :name, :links

def links
{
self: api_v1_user_path(object.id),
microposts: api_v1_microposts_path(user_id: object.id)
}
end
end
```

This will resilt in (example is in jsonapi adapter):
```json
{
"data": {
"id": "1",
"type": "users",
"attributes": {
"name": "Example User",
"links": {
"self": "/api/v1/users/1",
"microposts": "/api/v1/microposts?user_id=1"
}
}
}
}
```


### Links as a property of the resource definiton
**This is only applicable to JSONAPI adapter**

You can use the `links` class method to define the links you need in the resource's primary data.

```ruby
class Api::V1::UserSerializer < ActiveModel::Serializer
attributes :id, :name

link(:self) { api_v1_user_path(object.id) }
link(:microposts) { api_v1_microposts_path(user_id: object.id) }
end
```

This will resilt in (example is in jsonapi adapter):
```json
{
"data": {
"id": "1",
"type": "users",
"attributes": {
"name": "Example User"
},
"links": {
"self": "/api/v1/users/1",
"microposts": "/api/v1/microposts?user_id=1"
}
}
}
```

### Links that follow the JSONAPI spec
**This is only applicable to JSONAPI adapter**

If you have a JSONAPI-strict client that you are working with (like `ember-data`)
you need to construct the links inside the relationships. Also the link to fetch the
relationship data must be under the `related` attribute, whereas to manipulate the
relationship (in case of many-to-many relationship) must be under the `self` attribute.

You can find more info in the [spec](http://jsonapi.org/format/#document-resource-object-relationships).

Here is how you can do this:

```ruby
class Api::V1::UserSerializer < ActiveModel::Serializer
attributes :id, :name

has_many :microposts, serializer: Api::V1::MicropostSerializer do
link(:related) { api_v1_microposts_path(user_id: object.id) }
end

#this is needed to avoid n+1, gem core devs are working to remove this necessity
#more on: https://github.com/rails-api/active_model_serializers/issues/1325
def microposts
object.microposts.loaded ? object.microposts : object.microposts.none
end
end
```

This will result in:

```json
{
"data": {
"id": "1",
"type": "users",
"attributes": {
"name": "Example User"
},
"relationships": {
"microposts": {
"data": [],
"links": {
"related": "/api/v1/microposts?user_id=1"
}
}
}
}
}
```
53 changes: 33 additions & 20 deletions docs/integrations/ember-and-json-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,42 @@ You will also want to set the `key_transform` to `:unaltered` since you will adj
ActiveModelSerializers.config.key_transform = :unaltered
```

Lastly, in order to properly handle JSON API responses, we need to register a JSON API renderer, like so:
In order to properly handle JSON API responses, we need to register a JSON API renderer, like so:

```ruby
# config/initializers/active_model_serializers.rb
ActiveSupport.on_load(:action_controller) do
require 'active_model_serializers/register_jsonapi_renderer'
end
```
Rails also requires your controller to tell it that you accept and generate JSONAPI data. To do that, you use `respond_to` in your controller handlers to tell rails you are consuming and returning jsonapi format data. Without this, Rails will refuse to parse the request body into params. You can add `ActionController::MimeResponds` to your application controller to enable this:

```ruby
class ApplicationController < ActionController::API
include ActionController::MimeResponds
end
```
Then, in your controller you can tell rails you're accepting and rendering the jsonapi format:
```ruby
# POST /post
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.jsonapi { render jsonapi: @post, status: :created, location: @post }
else
format.jsonapi { render jsonapi: @post.errors, status: :unprocessable_entity }
end
end
end

# Only allow a trusted parameter "white list" through.
def post_params
ActiveModelSerializers::Deserialization.jsonapi_parse!(params, only: [:title, :body] )
end
end
```


### Adapter Changes

Expand All @@ -69,18 +97,6 @@ export default DS.JSONAPIAdapter.extend({
return pluralize(underscored);
},

// allows queries to be sent along with a findRecord
// hopefully Ember / EmberData will soon have this built in
// ember-data issue tracked here:
// https://github.com/emberjs/data/issues/3596
urlForFindRecord(id, modelName, snapshot) {
let url = this._super(...arguments);
let query = Ember.get(snapshot, 'adapterOptions.query');
if(query) {
url += '?' + Ember.$.param(query);
}
return url;
}
});
```

Expand All @@ -104,18 +120,15 @@ export default DS.JSONAPISerializer.extend({

```

## Including Nested Resources

Previously, `store.find` and `store.findRecord` did not allow specification of any query params.
The ActiveModelSerializers default for the `include` parameter is to be `nil` meaning that if any associations are defined in your serializer, only the `id` and `type` will be in the `relationships` structure of the JSON API response.
For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API)
## Including Nested Resources

With the above modifications, you can execute code as below in order to include nested resources while doing a find query.
Ember Data can request related records by using `include`. Below are some examples of how to make Ember Data request the inclusion of related records. For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API)

```javascript
store.findRecord('post', postId, { adapterOptions: { query: { include: 'comments' } } });
store.findRecord('post', postId, { include: 'comments' } );
```
will generate the path `/posts/{postId}?include='comments'`
which will generate the path /posts/{postId}?include='comments'

So then in your controller, you'll want to be sure to have something like:
```ruby
Expand Down
3 changes: 2 additions & 1 deletion lib/active_model/serializer/collection_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def json_key
# rubocop:enable Metrics/CyclomaticComplexity

def paginated?
object.respond_to?(:current_page) &&
ActiveModelSerializers.config.jsonapi_pagination_links_enabled &&
object.respond_to?(:current_page) &&
object.respond_to?(:total_pages) &&
object.respond_to?(:size)
end
Expand Down
6 changes: 4 additions & 2 deletions lib/active_model/serializer/concerns/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,17 @@ def associate(reflection)
# +default_include_directive+ config value when not provided)
# @return [Enumerator<Association>]
#
def associations(include_directive = ActiveModelSerializers.default_include_directive)
def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
include_slice ||= include_directive
return unless object

Enumerator.new do |y|
self.class._reflections.values.each do |reflection|
next if reflection.excluded?(self)
key = reflection.options.fetch(:key, reflection.name)
next unless include_directive.key?(key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this context, when would include_directive.key?(key) not be equal to include_slice.key?(key)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one of the gross aspects of the current adapter but

That's opposed to include_slice, which is always the slice of the real include directive passed to render.

In other words, the current code has two 'include directives'. One real one that is what you think it is, and a separate one that is derived from a relationship's fields

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this case, for instance, let's say your include directive was:

{ posts: { comments: {} } }

When you are iterating the relations and you hit the comments portion, the include_directive (the current code) is actually '*', assuming no sparse fieldsets requested. But the include_slice would be {}.

y.yield reflection.build_association(self, instance_options)

y.yield reflection.build_association(self, instance_options, include_slice)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/active_model/serializer/concerns/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ def config.array_serializer
config.default_includes = '*'
config.adapter = :attributes
config.key_transform = nil
config.jsonapi_pagination_links_enabled = true
config.jsonapi_resource_type = :plural
config.jsonapi_namespace_separator = '-'.freeze
config.jsonapi_version = '1.0'
config.jsonapi_toplevel_meta = {}
# Make JSON API top-level jsonapi member opt-in
# ref: http://jsonapi.org/format/#document-top-level
config.jsonapi_include_toplevel_object = false
config.include_data_default = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsonapi adapter should default to false, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current default is true. per the PR description, I vote we change that default but up to you guys


config.schema_path = 'test/support/schemas'
end
Expand Down
Loading