-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
198 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
require 'jsonapi/deserializable' | ||
require 'jsonapi/serializable' | ||
require 'jsonapi/rails/configuration' | ||
require 'jsonapi/rails/railtie' | ||
|
||
module JSONAPI | ||
module Rails | ||
require 'jsonapi/rails/configuration' | ||
require 'jsonapi/rails/logging' | ||
|
||
extend Configurable | ||
extend Logging | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,163 +1,13 @@ | ||
require 'jsonapi/deserializable' | ||
require 'jsonapi/parser' | ||
require 'jsonapi/rails/configuration' | ||
require 'jsonapi/rails/controller/deserialization' | ||
require 'jsonapi/rails/controller/hooks' | ||
|
||
module JSONAPI | ||
module Rails | ||
module Deserializable | ||
# @private | ||
class Resource < JSONAPI::Deserializable::Resource | ||
id | ||
type | ||
attributes | ||
has_one do |_rel, id, type, key| | ||
type = type.to_s.singularize.camelize | ||
{ "#{key}_id".to_sym => id, "#{key}_type".to_sym => type } | ||
end | ||
has_many do |_rel, ids, types, key| | ||
key = key.to_s.singularize | ||
types = types.map { |t| t.to_s.singularize.camelize } | ||
{ "#{key}_ids".to_sym => ids, "#{key}_types".to_sym => types } | ||
end | ||
end | ||
end | ||
|
||
# ActionController methods and hooks for JSON API deserialization and | ||
# rendering. | ||
module Controller | ||
extend ActiveSupport::Concern | ||
|
||
JSONAPI_POINTERS_KEY = 'jsonapi-rails.jsonapi_pointers'.freeze | ||
|
||
class_methods do | ||
# Declare a deserializable resource. | ||
# | ||
# @param key [Symbol] The key under which the deserialized hash will be | ||
# available within the `params` hash. | ||
# @param options [Hash] | ||
# @option class [Class] A custom deserializer class. Optional. | ||
# @option only List of actions for which deserialization should happen. | ||
# Optional. | ||
# @option except List of actions for which deserialization should not | ||
# happen. Optional. | ||
# @yieldreturn Optional block for in-line definition of custom | ||
# deserializers. | ||
# | ||
# @example | ||
# class ArticlesController < ActionController::Base | ||
# deserializable_resource :article, only: [:create, :update] | ||
# | ||
# def create | ||
# article = Article.new(params[:article]) | ||
# | ||
# if article.save | ||
# render jsonapi: article | ||
# else | ||
# render jsonapi_errors: article.errors | ||
# end | ||
# end | ||
# | ||
# # ... | ||
# end | ||
# | ||
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize | ||
def deserializable_resource(key, options = {}, &block) | ||
options = options.dup | ||
klass = options.delete(:class) || | ||
Class.new(JSONAPI::Rails::Deserializable::Resource, &block) | ||
|
||
before_action(options) do |controller| | ||
hash = controller.params.to_unsafe_hash[:_jsonapi] | ||
if hash.nil? | ||
JSONAPI::Rails.config[:logger].warn do | ||
"Unable to deserialize #{key} because no JSON API payload was" \ | ||
" found. (#{controller.controller_name}##{params[:action]})" | ||
end | ||
next | ||
end | ||
|
||
ActiveSupport::Notifications | ||
.instrument('parse.jsonapi-rails', | ||
key: key, payload: hash, class: klass) do | ||
JSONAPI::Parser::Resource.parse!(hash) | ||
resource = klass.new(hash[:data]) | ||
controller.request.env[JSONAPI_POINTERS_KEY] = | ||
resource.reverse_mapping | ||
controller.params[key.to_sym] = resource.to_hash | ||
end | ||
end | ||
end | ||
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize | ||
end | ||
|
||
# Hook for serializable class mapping (for resources). | ||
# Overridden by the `class` renderer option. | ||
# @return [Hash{Symbol=>Class}] | ||
def jsonapi_class | ||
JSONAPI::Rails.config[:jsonapi_class].dup | ||
end | ||
|
||
# Hook for serializable class mapping (for errors). | ||
# Overridden by the `class` renderer option. | ||
# @return [Hash{Symbol=>Class}] | ||
def jsonapi_errors_class | ||
JSONAPI::Rails.config[:jsonapi_errors_class].dup | ||
end | ||
|
||
# Hook for the jsonapi object. | ||
# Overridden by the `jsonapi_object` renderer option. | ||
# @return [Hash,nil] | ||
def jsonapi_object | ||
JSONAPI::Rails.config[:jsonapi_object] | ||
end | ||
|
||
# Hook for default exposures. | ||
# @return [Hash] | ||
def jsonapi_expose | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_expose]) | ||
end | ||
|
||
# Hook for default cache. | ||
# @return [#fetch_multi] | ||
def jsonapi_cache | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_cache]) | ||
end | ||
|
||
# Hook for default fields. | ||
# @return [Hash{Symbol=>Array<Symbol>},nil] | ||
def jsonapi_fields | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_fields]) | ||
end | ||
|
||
# Hook for default includes. | ||
# @return [IncludeDirective] | ||
def jsonapi_include | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_include]) | ||
end | ||
|
||
# Hook for default links. | ||
# @return [Hash] | ||
def jsonapi_links | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_links]) | ||
end | ||
|
||
# Hook for default meta. | ||
# @return [Hash,nil] | ||
def jsonapi_meta | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_meta]) | ||
end | ||
|
||
# Hook for pagination scheme. | ||
# @return [Hash] | ||
def jsonapi_pagination(resources) | ||
instance_exec(resources, &JSONAPI::Rails.config[:jsonapi_pagination]) | ||
end | ||
|
||
# JSON pointers for deserialized fields. | ||
# @return [Hash{Symbol=>String}] | ||
def jsonapi_pointers | ||
request.env[JSONAPI_POINTERS_KEY] || {} | ||
end | ||
include Deserialization | ||
include Hooks | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
require 'jsonapi/parser' | ||
require 'jsonapi/rails/deserializable_resource' | ||
|
||
module JSONAPI | ||
module Rails | ||
module Controller | ||
# Controller class and instance methods for deserialization of incoming | ||
# JSON API payloads. | ||
module Deserialization | ||
extend ActiveSupport::Concern | ||
|
||
JSONAPI_POINTERS_KEY = 'jsonapi-rails.jsonapi_pointers'.freeze | ||
|
||
class_methods do | ||
# Declare a deserializable resource. | ||
# | ||
# @param key [Symbol] The key under which the deserialized hash will be | ||
# available within the `params` hash. | ||
# @param options [Hash] | ||
# @option class [Class] A custom deserializer class. Optional. | ||
# @option only List of actions for which deserialization should happen. | ||
# Optional. | ||
# @option except List of actions for which deserialization should not | ||
# happen. Optional. | ||
# @yieldreturn Optional block for in-line definition of custom | ||
# deserializers. | ||
# | ||
# @example | ||
# class ArticlesController < ActionController::Base | ||
# deserializable_resource :article, only: [:create, :update] | ||
# | ||
# def create | ||
# article = Article.new(params[:article]) | ||
# | ||
# if article.save | ||
# render jsonapi: article | ||
# else | ||
# render jsonapi_errors: article.errors | ||
# end | ||
# end | ||
# | ||
# # ... | ||
# end | ||
# | ||
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize | ||
def deserializable_resource(key, options = {}, &block) | ||
options = options.dup | ||
klass = options.delete(:class) || | ||
Class.new(JSONAPI::Rails::DeserializableResource, &block) | ||
|
||
before_action(options) do |controller| | ||
hash = controller.params.to_unsafe_hash[:_jsonapi] | ||
if hash.nil? | ||
JSONAPI::Rails.logger.warn do | ||
"Unable to deserialize #{key} because no JSON API payload was" \ | ||
" found. (#{controller.controller_name}##{params[:action]})" | ||
end | ||
next | ||
end | ||
|
||
ActiveSupport::Notifications | ||
.instrument('parse.jsonapi-rails', | ||
key: key, payload: hash, class: klass) do | ||
JSONAPI::Parser::Resource.parse!(hash) | ||
resource = klass.new(hash[:data]) | ||
controller.request.env[JSONAPI_POINTERS_KEY] = | ||
resource.reverse_mapping | ||
controller.params[key.to_sym] = resource.to_hash | ||
end | ||
end | ||
end | ||
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize | ||
end | ||
|
||
# JSON pointers for deserialized fields. | ||
# @return [Hash{Symbol=>String}] | ||
def jsonapi_pointers | ||
request.env[JSONAPI_POINTERS_KEY] || {} | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
require 'jsonapi/rails/configuration' | ||
|
||
module JSONAPI | ||
module Rails | ||
module Controller | ||
extend ActiveSupport::Concern | ||
|
||
# Hooks for customizing rendering default options at controller-level. | ||
module Hooks | ||
# Hook for serializable class mapping (for resources). | ||
# Overridden by the `class` renderer option. | ||
# @return [Hash{Symbol=>Class}] | ||
def jsonapi_class | ||
JSONAPI::Rails.config[:jsonapi_class].dup | ||
end | ||
|
||
# Hook for serializable class mapping (for errors). | ||
# Overridden by the `class` renderer option. | ||
# @return [Hash{Symbol=>Class}] | ||
def jsonapi_errors_class | ||
JSONAPI::Rails.config[:jsonapi_errors_class].dup | ||
end | ||
|
||
# Hook for the jsonapi object. | ||
# Overridden by the `jsonapi_object` renderer option. | ||
# @return [Hash,nil] | ||
def jsonapi_object | ||
JSONAPI::Rails.config[:jsonapi_object] | ||
end | ||
|
||
# Hook for default exposures. | ||
# @return [Hash] | ||
def jsonapi_expose | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_expose]) | ||
end | ||
|
||
# Hook for default cache. | ||
# @return [#fetch_multi] | ||
def jsonapi_cache | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_cache]) | ||
end | ||
|
||
# Hook for default fields. | ||
# @return [Hash{Symbol=>Array<Symbol>},nil] | ||
def jsonapi_fields | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_fields]) | ||
end | ||
|
||
# Hook for default includes. | ||
# @return [IncludeDirective] | ||
def jsonapi_include | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_include]) | ||
end | ||
|
||
# Hook for default links. | ||
# @return [Hash] | ||
def jsonapi_links | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_links]) | ||
end | ||
|
||
# Hook for default meta. | ||
# @return [Hash,nil] | ||
def jsonapi_meta | ||
instance_exec(&JSONAPI::Rails.config[:jsonapi_meta]) | ||
end | ||
|
||
# Hook for pagination scheme. | ||
# @return [Hash] | ||
def jsonapi_pagination(resources) | ||
instance_exec(resources, &JSONAPI::Rails.config[:jsonapi_pagination]) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
require 'jsonapi/deserializable/resource' | ||
|
||
module JSONAPI | ||
module Rails | ||
# Customized deserializable resource class to match ActiveRecord's API. | ||
class DeserializableResource < JSONAPI::Deserializable::Resource | ||
id | ||
type | ||
attributes | ||
has_one do |_rel, id, type, key| | ||
type = type.to_s.singularize.camelize | ||
{ "#{key}_id".to_sym => id, "#{key}_type".to_sym => type } | ||
end | ||
has_many do |_rel, ids, types, key| | ||
key = key.to_s.singularize | ||
types = types.map { |t| t.to_s.singularize.camelize } | ||
{ "#{key}_ids".to_sym => ids, "#{key}_types".to_sym => types } | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ def parse(event) | |
end | ||
|
||
def logger | ||
JSONAPI::Rails.config[:logger] | ||
JSONAPI::Rails.logger | ||
end | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module JSONAPI | ||
module Rails | ||
# @private | ||
module Logging | ||
def logger | ||
config[:logger] | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters