Module: APIHelper::Includable
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/api_helper/includable.rb
Overview
Helper To Make Resource APIs Includable
Inclusion of related resource lets your API return resources related to the primary data. This endpoint will support an include request parameter to allow the client to customize which related resources should be returned.
This design made references to the rules of Inclusion of Related Resources in JSON API: jsonapi.org/format/#fetching-includes
For instance, comments could be requested with articles:
GET /articles?include=comments
The server will respond
[
{
"id": 1,
"title": "First Post",
"content": "...",
"comments": [
{
"id": 1,
"content": "..."
},
{
"id": 3,
"content": "..."
},
{
"id": 6,
"content": "..."
}
]
},
{
"id": 2,
"title": "Second Post",
"content": "...",
"comments": [
{
"id": 2,
"content": "..."
},
{
"id": 4,
"content": "..."
},
{
"id": 5,
"content": "..."
}
]
}
]
instead of just:
[
{
"id": 1,
"title": "First Post",
"content": "...",
"comments": [1, 3, 6]
},
{
"id": 2,
"title": "Second Post",
"content": "...",
"comments": [2, 4, 5]
}
]
If requesting multiple related resources is needed, they can be stated in a comma-separated list:
GET /articles/12?include=,comments
Usage
Include this Concern in your Action Controller:
SamplesController < ApplicationController
include APIHelper::Includable
end
or in your Grape API class:
class SampleAPI < Grape::API
include APIHelper::Includable
end
then set the options for the inclusion in the grape method:
resources :posts do
get do
inclusion_for :post, root: true
# ...
end
end
This helper parses the include and include[object_type] parameters to determine what the API caller wants, and save the results into instance variables for further usage.
After this you can use the inclusion helper method to get the inclusion data that the request specifies, and do something like this in your controller:
resource = resource.includes(:author) if inclusion(:post, :author)
The inclusion helper method returns data like this:
inclusion #=> { post: [:author] }
inclusion(:post) #=> [:author]
inclusion(:post, :author) #=> true
API View with RABL
If you’re using RABL as the API view, it can be setup like this:
# set the includable and default inclusion fields of the view
set_inclusion :post, default_includes: [:author]
# set the details for all includable fields
set_inclusion_field :post, :author, :author_id
# extends the partial to show included fields
extends('extensions/includable_childs', locals: { self_resource: :post })
Class Method Summary collapse
-
.include_param_desc(example: nil, default: nil) ⇒ Object
Return the ‘include’ param description.
Instance Method Summary collapse
-
#inclusion(resource = nil, field = nil) ⇒ Object
Getter for the inclusion data.
-
#inclusion_for(resource, root: false, default_includes: []) ⇒ Object
Gets the include parameters, organize them into a @inclusion hash for model to use inner-join queries and/or templates to render relation attributes included.
-
#set_inclusion(resource, default_includes: []) ⇒ Object
View Helper to set the inclusion and default_inclusion.
-
#set_inclusion_field(self_resource, field, id_field, class_name: nil, url: nil) ⇒ Object
View Helper to set the inclusion details.
Class Method Details
.include_param_desc(example: nil, default: nil) ⇒ Object
Return the ‘include’ param description
195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/api_helper/includable.rb', line 195 def self.include_param_desc(example: nil, default: nil) if default.present? desc = "Returning compound documents that include specific associated objects, defaults to '#{default}'." else desc = "Returning compound documents that include specific associated objects." end if example.present? "#{desc} Example value: '#{example}'" else desc end end |
Instance Method Details
#inclusion(resource = nil, field = nil) ⇒ Object
Getter for the inclusion data.
183 184 185 186 187 188 189 190 191 192 |
# File 'lib/api_helper/includable.rb', line 183 def inclusion(resource = nil, field = nil) if resource.blank? @inclusion ||= {} elsif field.blank? (@inclusion ||= {})[resource] ||= [] else return false if (try(:fieldset, resource).present? && !fieldset(resource, field)) inclusion(resource).include?(field) end end |
#inclusion_for(resource, root: false, default_includes: []) ⇒ Object
Gets the include parameters, organize them into a @inclusion hash for model to use inner-join queries and/or templates to render relation attributes included. Following the URL rules of JSON API: jsonapi.org/format/#fetching-includes
Params:
resource-
Symbolname of resource to receive the inclusion
144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/api_helper/includable.rb', line 144 def inclusion_for(resource, root: false, default_includes: []) @inclusion ||= Hashie::Mash.new ||= Hashie::Mash.new # put the includes in place if params[:include].is_a? Hash @inclusion[resource] = params[:include][resource] || params[:include][resource] elsif root @inclusion[resource] = params[:include] end # splits the string into array of symbles @inclusion[resource] = @inclusion[resource] ? @inclusion[resource].split(',').map(&:to_sym) : default_includes end |
#set_inclusion(resource, default_includes: []) ⇒ Object
View Helper to set the inclusion and default_inclusion.
160 161 162 163 164 |
# File 'lib/api_helper/includable.rb', line 160 def set_inclusion(resource, default_includes: []) @inclusion ||= {} @inclusion_field ||= {} @inclusion[resource] = default_includes if @inclusion[resource].blank? end |
#set_inclusion_field(self_resource, field, id_field, class_name: nil, url: nil) ⇒ Object
View Helper to set the inclusion details.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/api_helper/includable.rb', line 167 def set_inclusion_field(self_resource, field, id_field, class_name: nil, url: nil) return if (@fieldset.present? && @fieldset[self_resource].present? && !@fieldset[self_resource].include?(field)) @inclusion_field ||= {} @inclusion_field[self_resource] ||= [] field_data = { field: field, id_field: id_field, class_name: class_name, url: url } @inclusion_field[self_resource] << field_data @fieldset[self_resource].delete(field) if @fieldset[self_resource].present? end |