From f11622ca1ba99cb35713398c5520abe8f2f508e2 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Sun, 20 Nov 2016 05:00:17 +0100 Subject: [PATCH 1/2] Add basic support for ActiveModel::Errors. --- Gemfile | 3 +++ lib/jsonapi/rails/action_controller.rb | 24 +++++++++++++++++++++--- lib/jsonapi/rails/railtie.rb | 5 ++--- lib/jsonapi/rails/renderer.rb | 18 +++++++++++++++++- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index fa75df1..b589d48 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,6 @@ source 'https://rubygems.org' +gem 'jsonapi-deserializable', '0.1.1.beta3', + github: 'jsonapi-rb/deserializable', branch: 'reverse-mapping' + gemspec diff --git a/lib/jsonapi/rails/action_controller.rb b/lib/jsonapi/rails/action_controller.rb index 320ecad..e6083fe 100644 --- a/lib/jsonapi/rails/action_controller.rb +++ b/lib/jsonapi/rails/action_controller.rb @@ -7,6 +7,17 @@ module ActionController def self.included(base) base.class_eval do extend ClassMethods + prepend InstanceMethods + end + end + + module InstanceMethods + def render(opts = {}) + if opts.key?(:jsonapi_error) + mapping = params.to_unsafe_h[:_jsonapi_mapping] + opts = opts.merge(_jsonapi_mapping: mapping) + end + super(opts) end end @@ -42,16 +53,23 @@ def call(env) request = Rack::Request.new(env) body = request.params.slice(*JSONAPI_KEYS) parser.parse!(body) - deserialized_hash = @deserializable_class.call(body) + deserialize!(request, body) + + @app.call(env) + end + + def deserialize!(request, body) + deserialized = @deserializable_class.new(body) + deserialized_hash = deserialized.to_h + mapping = deserialized.mapping jsonapi = {} JSONAPI_KEYS.each do |key| next unless request.params.key?(key) jsonapi[key.to_sym] = request.delete_param(key) end request.update_param(:_jsonapi, jsonapi) + request.update_param(:_jsonapi_mapping, mapping) request.update_param(@deserializable_key, deserialized_hash) - - @app.call(env) end end diff --git a/lib/jsonapi/rails/railtie.rb b/lib/jsonapi/rails/railtie.rb index f94e7fd..37afac0 100644 --- a/lib/jsonapi/rails/railtie.rb +++ b/lib/jsonapi/rails/railtie.rb @@ -2,13 +2,11 @@ require 'action_controller' require 'active_support' -require 'jsonapi/rails/parser' -require 'jsonapi/rails/renderer' - module JSONAPI module Rails class Railtie < ::Rails::Railtie MEDIA_TYPE = 'application/vnd.api+json'.freeze + require 'jsonapi/rails/parser' PARSER = JSONAPI::Rails.parser initializer 'jsonapi-rails.action_controller' do @@ -23,6 +21,7 @@ class Railtie < ::Rails::Railtie ::ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime[:jsonapi]] = PARSER end + require 'jsonapi/rails/renderer' ::ActionController::Renderers.add :jsonapi do |json, options| unless json.is_a?(String) json = JSONAPI::Rails::Renderer.render(json, options) diff --git a/lib/jsonapi/rails/renderer.rb b/lib/jsonapi/rails/renderer.rb index eda9c9a..b47eb20 100644 --- a/lib/jsonapi/rails/renderer.rb +++ b/lib/jsonapi/rails/renderer.rb @@ -18,9 +18,25 @@ def self.render(resources, options) class ErrorRenderer def self.render(errors, options) - # TODO(beauby): SerializableError inference on AR validation errors. + if errors.is_a?(ActiveModel::Errors) + errors = build_active_model_errors(errors, options[:_jsonapi_mapping]) + end JSONAPI::Serializable::ErrorRenderer.render(errors, options) end + + def self.build_active_model_errors(errors, mapping) + error_class = Class.new(JSONAPI::Serializable::Error) do + detail { @detail } + source do + path, name = @attribute + pointer "#{path}/#{name}" + end + end + errors.map! do |attr, error| + attr = mapping[attr] + error_class.new(attribute: attr, detail: error) + end + end end end end From cd54809eb6e3ddc3b877e49d17d64ad44ce3ed93 Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Sun, 20 Nov 2016 22:09:31 +0100 Subject: [PATCH 2/2] Fix error renderer call. --- lib/jsonapi/rails/railtie.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jsonapi/rails/railtie.rb b/lib/jsonapi/rails/railtie.rb index 37afac0..cfe07b4 100644 --- a/lib/jsonapi/rails/railtie.rb +++ b/lib/jsonapi/rails/railtie.rb @@ -32,7 +32,7 @@ class Railtie < ::Rails::Railtie ::ActionController::Renderers.add :jsonapi_errors do |json, options| unless json.is_a?(String) - json = JSONAPI::Rails::ErrorRender.render_errors(json, options) + json = JSONAPI::Rails::ErrorRenderer.render(json, options) end self.content_type ||= Mime[:jsonapi] self.response_body = json