Skip to content

Commit 569ae69

Browse files
author
Michael Deutsch
committed
Merge remote-tracking branch 'upstream/master'
* upstream/master: Improve specs (jsonapi-rb#33) Expose deserialization reverse mapping in controllers (jsonapi-rb#29) Update jsonapi-rb to v0.2.1. (jsonapi-rb#32) Add dummy app temp files to .gitignore (jsonapi-rb#30) Deal with jsonapi-serializable no longer calling to_json (jsonapi-rb#26)
2 parents 52019d6 + a8d47c2 commit 569ae69

14 files changed

+111
-291
lines changed

jsonapi-rails.gemspec

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ Gem::Specification.new do |spec|
1414
spec.files = Dir['README.md', 'lib/**/*']
1515
spec.require_path = 'lib'
1616

17-
spec.add_dependency 'jsonapi-rb', '~> 0.1'
17+
spec.add_dependency 'jsonapi-rb', '~> 0.2.1'
1818

1919
spec.add_development_dependency 'rails', '~> 5.0'
2020
spec.add_development_dependency 'sqlite3'
2121
spec.add_development_dependency 'rake', '~> 11.3'
2222
spec.add_development_dependency 'rspec-rails', '~> 3.5'
23-
spec.add_development_dependency 'dry-validation', '~> 0.10'
2423
end

lib/jsonapi/rails/action_controller.rb

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ module Rails
66
module ActionController
77
extend ActiveSupport::Concern
88

9-
REVERSE_MAPPING_KEY = 'jsonapi_deserializable.reverse_mapping'.freeze
9+
JSONAPI_POINTERS_KEY = 'jsonapi_deserializable.jsonapi_pointers'.freeze
1010

11-
module ClassMethods
11+
class_methods do
1212
def deserializable_resource(key, options = {}, &block)
1313
_deserializable(key, options,
1414
JSONAPI::Deserializable::Resource, &block)
@@ -26,17 +26,14 @@ def _deserializable(key, options, fallback, &block)
2626

2727
before_action(options) do |controller|
2828
resource = klass.new(controller.params[:_jsonapi].to_unsafe_hash)
29-
controller.request.env[REVERSE_MAPPING_KEY] =
29+
controller.request.env[JSONAPI_POINTERS_KEY] =
3030
resource.reverse_mapping
3131
controller.params[key.to_sym] = resource.to_hash
3232
end
3333
end
3434
end
35-
36-
private
37-
3835
def jsonapi_pointers
39-
request.env[REVERSE_MAPPING_KEY]
36+
request.env[JSONAPI_POINTERS_KEY]
4037
end
4138
end
4239
end

lib/jsonapi/rails/railtie.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ module Rails
1212
class Railtie < ::Rails::Railtie
1313
MEDIA_TYPE = 'application/vnd.api+json'.freeze
1414
RENDERERS = {
15-
jsonapi: JSONAPI::Rails.rails_renderer(SuccessRenderer),
16-
jsonapi_errors: JSONAPI::Rails.rails_renderer(ErrorsRenderer)
15+
jsonapi: SuccessRenderer.new,
16+
jsonapi_error: ErrorsRenderer.new
1717
}.freeze
1818

1919
initializer 'jsonapi-rails.action_controller' do
@@ -34,8 +34,19 @@ class Railtie < ::Rails::Railtie
3434
end
3535

3636
if JSONAPI::Rails.config.register_renderers
37-
RENDERERS.each do |key, renderer|
38-
::ActionController::Renderers.add(key, &renderer)
37+
::ActionController::Renderers.add(:jsonapi) do |resources, options|
38+
self.content_type ||= Mime[:jsonapi]
39+
40+
RENDERERS[:jsonapi].render(resources, options).to_json
41+
end
42+
43+
::ActionController::Renderers.add(:jsonapi_error) do |errors, options|
44+
# Renderer proc is evaluated in the controller context, so it
45+
# has access to the jsonapi_pointers method.
46+
options = options.merge(_jsonapi_pointers: jsonapi_pointers)
47+
self.content_type ||= Mime[:jsonapi]
48+
49+
RENDERERS[:jsonapi_error].render(errors, options).to_json
3950
end
4051
end
4152

lib/jsonapi/rails/renderer.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
module JSONAPI
55
module Rails
66
class SuccessRenderer
7-
def self.render(resources, options)
7+
def initialize(renderer = JSONAPI::Serializable::SuccessRenderer.new)
8+
@renderer = renderer
9+
10+
freeze
11+
end
12+
13+
def render(resources, options)
814
opts = options.dup
915
# TODO(beauby): Move this to a global configuration.
1016
default_exposures = {
@@ -13,12 +19,18 @@ def self.render(resources, options)
1319
opts[:expose] = default_exposures.merge!(opts[:expose] || {})
1420
opts[:jsonapi] = opts.delete(:jsonapi_object)
1521

16-
JSONAPI::Serializable::Renderer.render(resources, opts)
22+
@renderer.render(resources, opts)
1723
end
1824
end
1925

2026
class ErrorsRenderer
21-
def self.render(errors, options)
27+
def initialize(renderer = JSONAPI::Serializable::ErrorsRenderer.new)
28+
@renderer = renderer
29+
30+
freeze
31+
end
32+
33+
def render(errors, options)
2234
errors = [errors] unless errors.is_a?(Array)
2335
errors = errors.flat_map do |error|
2436
if error.respond_to?(:as_jsonapi)
@@ -32,20 +44,8 @@ def self.render(errors, options)
3244
end
3345
end
3446

35-
JSONAPI::Serializable::ErrorRenderer.render(errors, options)
36-
end
37-
end
38-
39-
module_function
40-
41-
# @api private
42-
def rails_renderer(renderer)
43-
proc do |json, options|
44-
# Renderer proc is evaluated in the controller context.
45-
options = options.merge(_jsonapi_pointers: jsonapi_pointers)
46-
json = renderer.render(json, options) unless json.is_a?(String)
47-
self.content_type ||= Mime[:jsonapi]
48-
self.response_body = json
47+
# TODO(beauby): SerializableError inference on AR validation errors.
48+
@renderer.render(errors, options)
4949
end
5050
end
5151
end

spec/action_controller_spec.rb

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,72 @@
11
require 'rails_helper'
22

3-
RSpec.describe JSONAPI::Rails::ActionController do
4-
class TestController < ActionController::Base
5-
deserializable_resource "things"
3+
describe ActionController::Base, type: :controller do
4+
describe '.deserializable_resource' do
5+
controller do
6+
deserializable_resource :user
7+
8+
def create
9+
render plain: 'ok'
10+
end
11+
end
12+
13+
let(:payload) do
14+
{
15+
_jsonapi: {
16+
'data' => {
17+
'type' => 'users',
18+
'attributes' => { 'foo' => 'bar', 'bar' => 'baz' }
19+
}
20+
}
21+
}
22+
end
23+
24+
it 'makes the deserialized resource available in params' do
25+
post :create, params: payload
26+
27+
expected = { 'type' => 'users', 'foo' => 'bar', 'bar' => 'baz' }
28+
expect(controller.params[:user]).to eq(expected)
29+
end
30+
31+
it 'makes the deserialization mapping available via #jsonapi_pointers' do
32+
post :create, params: payload
33+
34+
expected = { foo: '/data/attributes/foo',
35+
bar: '/data/attributes/bar',
36+
type: '/data/type' }
37+
expect(controller.jsonapi_pointers).to eq(expected)
38+
end
639
end
740

8-
let(:controller) { TestController.new }
41+
describe '#render jsonapi:' do
42+
controller do
43+
def index
44+
serializer = Class.new(JSONAPI::Serializable::Resource) do
45+
type :users
46+
attribute :name
47+
end
48+
user = OpenStruct.new(id: 1, name: 'Lucas')
949

10-
context 'source pointers' do
11-
it 'should fetch the mapping created during deserialization' do
12-
reverse_mapping = {id: "/data/id", type: "/data/type"}
13-
allow(controller).to receive(:request) do
14-
OpenStruct.new(env: {'jsonapi_deserializable.reverse_mapping' => reverse_mapping})
50+
render jsonapi: user, class: serializer
1551
end
16-
expect(controller.send(:jsonapi_pointers)).to equal reverse_mapping
52+
end
53+
54+
subject { JSON.parse(response.body) }
55+
let(:serialized_user) do
56+
{
57+
'data' => {
58+
'id' => '1',
59+
'type' => 'users',
60+
'attributes' => { 'name' => 'Lucas' }
61+
}
62+
}
63+
end
64+
65+
it 'renders a JSON API document' do
66+
get :index
67+
68+
expect(response.content_type).to eq('application/vnd.api+json')
69+
is_expected.to eq(serialized_user)
1770
end
1871
end
1972
end

spec/dummy/app/controllers/tweets_controller.rb

Lines changed: 0 additions & 49 deletions
This file was deleted.

spec/dummy/app/deserializable/deserializable_tweet.rb

Lines changed: 0 additions & 13 deletions
This file was deleted.

spec/dummy/app/models/tweet.rb

Lines changed: 0 additions & 6 deletions
This file was deleted.

spec/dummy/app/models/user.rb

Lines changed: 0 additions & 3 deletions
This file was deleted.

spec/dummy/app/serializable/serializable_tweet.rb

Lines changed: 0 additions & 9 deletions
This file was deleted.

spec/dummy/app/serializable/serializable_user.rb

Lines changed: 0 additions & 10 deletions
This file was deleted.

spec/rails_helper.rb

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
require 'spec_helper'
77
require 'rspec/rails'
88
# Add additional requires below this line. Rails is not loaded until this point!
9-
require 'dry-validation'
109

1110
# Requires supporting ruby files with custom matchers and macros, etc, in
1211
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
@@ -56,40 +55,3 @@
5655
# arbitrary gems may also be filtered via:
5756
# config.filter_gems_from_backtrace("gem name")
5857
end
59-
60-
# Custom RSpec matchers
61-
module JSONAPI
62-
module RSpec
63-
class BeValidJsonapiMatcher
64-
def matches?(body)
65-
JSONAPI.parse_response!(JSON.parse(body))
66-
true
67-
end
68-
end
69-
70-
def be_valid_jsonapi
71-
BeValidJsonapiMatcher.new
72-
end
73-
74-
class MatchSchemaMatcher
75-
def matches?(body, &block)
76-
schema = Dry::Validation.Schema(&block)
77-
@body = JSON.parse(body) unless body.is_a?(Hash)
78-
@result = schema.call(@body.with_indifferent_access)
79-
@result.success?
80-
end
81-
82-
def failure_message
83-
"#{@body} #{@result.errors}"
84-
end
85-
end
86-
87-
def match_schema(&block)
88-
MatchSchemaMatcher.new(&block)
89-
end
90-
end
91-
end
92-
93-
RSpec.configure do |config|
94-
config.include JSONAPI::RSpec
95-
end

spec/railtie_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require 'rails_helper'
2+
3+
describe JSONAPI::Rails::Railtie do
4+
it 'registers the JSON API MIME type' do
5+
expect(Mime[:jsonapi]).to eq('application/vnd.api+json')
6+
end
7+
8+
it 'registers the params parser for the JSON API MIME type' do
9+
expect(::ActionDispatch::Request.parameter_parsers[:jsonapi]).not_to be_nil
10+
end
11+
end

0 commit comments

Comments
 (0)