respond_with having issue with Rails 5 - ruby-on-rails

I am upgrading my Rails app from 4.1.1 to 5.1.4.
I am using roar-rails gem to parsing and rendering REST documents. I am facing some issues as responders gem has been extracted to separate gem.
respond_with has been moved to 'responders' gem.
My rails 4 code lookgs like this:
PostsController:
class PostsController < ApplicationController
respond_to :json
def index
posts = current_user.posts
respond_with posts, :represent_with => PostsRepresenter, current_user: current_user
end
end
My representers for Post
module PostsRepresenter
# Rails 4 code
# include Roar::Representer::JSON::HAL
# Rails 5 code (after adding responders) --------
include Roar::JSON
include Roar::Hypermedia
# Rails 5 code --------
collection(
:post,
class: Post,
extend: PostRepresenter,
embedded: true)
link :make do |args|
p "............. #{args.inspect}"
# In rails 4, args are coming correctly what is passing from Controller
# But in rails 5, args is coming `nil`
posts_path if args[:current_user].can_create?(Post)
end
end
Post representer
module PostRepresenter
# Rails 4 code
# include Roar::Representer::JSON::HAL
# Rails 5 code (after adding responders) --------
include Roar::JSON
include Roar::Hypermedia
# Rails 5 code --------
property :title
property :description
property :author
link :self do |args|
post_path(id) if args[:current_user].can_read?(self)
end
link :remove do |args|
post_path(id) if args[:current_user].can_delete?(self)
end
link :edit do |args|
post_path(id) if args[:current_user].can_update?(self)
end
end
I am facing issue with args which are passing through Controller,
after rails 5, its coming nil.
I have debug the issue and found that in responders gem, options are coming in respond_with method, but I think it could not send it to roar-rails.
/path-to-gem/vendor/responders-master/lib/action_controller/respond_with.rb
Here is snippet:
def respond_with(*resources, &block)
if self.class.mimes_for_respond_to.empty?
raise "In order to use respond_with, first you need to declare the " \
"formats your controller responds to in the class level."
end
mimes = collect_mimes_from_class_level
collector = ActionController::MimeResponds::Collector.new(mimes, request.variant)
block.call(collector) if block_given?
if format = collector.negotiate_format(request)
_process_format(format)
options = resources.size == 1 ? {} : resources.extract_options!
options = options.clone
options[:default_response] = collector.response
p "====================== options :: #{options.inspect}"
# Options are correct till here but coming `nil` on representers
(options.delete(:responder) || self.class.responder).call(self, resources, options)
else
raise ActionController::UnknownFormat
end
end
Please let me know what needs to be done here that make args
available in representers

respond_with was deprecated from rails 4.2.1.
https://apidock.com/rails/ActionController/MimeResponds/respond_with

Related

Using Rails Path and URL Helpers with fast_jsonapi

I would like to use the rails URL helper instead of hard coding the path to access the article.
I checked into the documentation but nothing is specified.
The article_path helper method exists (I checked by running rake routes)
class V3::ArticlesController < Api::V3::BaseController
def index
articles = Article.all
render json: ::V3::ArticleItemSerializer.new(articles).serialized_json
end
end
class V3::ArticleItemSerializer
include FastJsonapi::ObjectSerializer
attributes :title
link :working_url do |object|
"http://article.com/#{object.title}"
end
# link :what_i_want_url do |object|
# article_path(object)
# end
end
What you want to do is pass in the context to your serializer from your controller:
module ContextAware
def initialize(resource, options = {})
super
#context = options[:context]
end
end
class V3::ArticleItemSerializer
include FastJsonapi::ObjectSerializer
include ContextAware
attributes :title
link :working_url do |object|
#context.article_path(object)
end
end
class V3::ArticlesController < Api::V3::BaseController
def index
articles = Article.all
render json: ::V3::ArticleItemSerializer.new(articles, context: self).serialized_json
end
end
You should also switch to the jsonapi-serializer gem which is currently maintained as fast_jsonapi was abandoned by Netflix.
I found a solution thanks to max's example.
I also changed the gem to jsonapi-serializer
class V3::ArticlesController < Api::V3::BaseController
def index
articles = Article.all
render json: ::V3::ArticleItemSerializer.new(articles, params: { context: self }).serialized_json
end
end
class V3::ArticleItemSerializer
include JSONAPI::Serializer
attributes :title
link :working_url do |object|
"http://article.com/#{object.title}"
end
link :also_working_url do |object, params|
params[:context].article_path(object)
end
end

Rails API active model serializer root node not working

I have a rails 4.2.5 API app. For some reason, the JSON root node is not included in the response and I don't understand why.
curl http://localhost:3000/api/v1/category/science
returns
{"title":"science","sub_categories":34}%
instead of
{"category": {"title":"science","sub_categories":34}%}
The code:
controller
class Api::V1::CategoryController < ApplicationController
def show
category = params[:category] || "sports"
#category = Category.where(cat_title: category.capitalize).first
respond_to do |format|
format.json { render json: #category, serializer: CategorySerializer, root: "category" }
end
end
end
serializer
class CategorySerializer < ActiveModel::Serializer
attributes :title, :sub_categories
def title
URI::encode(object.cat_title.force_encoding("ISO-8859-1").encode("utf-8", replace: nil).downcase.tr(" ", "_"))
end
def sub_categories
object.cat_subcats
end
end
have a look into your initializers, this should be commented out in wrap_paramters.rb:
# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = true
# end
Rails 5.0.0.1
Using ActiveMovelSerializers (0.10.2) you just need to add an initializer:
app/config/initializers/json_api.rb
require 'active_model_serializers/register_jsonapi_renderer'
ActiveModelSerializers.config.adapter = :json_api

How to test an unrouted controller action or exception_app in Rails

We have a custom exception app that has been raising (fail safe) exceptions (the application equivalent of having an exception in a rescue block).
I think I've fixed it, but am finding it hard to test. It's an unrouted controller, so I can't use controller tests (require routing).
i.e. I have Rails.configuration.exceptions_app = ExceptionController.action(:show), not Rails.configuration.exceptions_app = self.routes.
Basically what I think I need to do is
Generate a test request request = ActionDispatch::TestRequest.new
include Rack::Test or maybe mimic behavior in ActiveSupport::IntegrationTest
Set #app = ExceptionsController.action(:show)
Fake an exception request.env.merge! 'action_dispatch.exception' => ActionController::RoutingError.new(:foo)
Test response = #app.call(request.env)
Assert no exception is raised and correct response body and status
Problems:
The env needs
a warden / devise session with current_user request.env['warden'] = spy(Warden) and request.session = ActionDispatch::Integration::Session.new(#app)
to manipulate request formats so that I can check that a request without an accept defaults to json request.any?(:json)? constraints: { default: :json } ? `request.accept = "application/javascript"
work work with the respond_with responder
set action_dispatch.show_exceptions, consider all requests local, etc request.env["action_dispatch.show_detailed_exceptions"] = true
Also, I considered building a ActionDispatch::ShowException.new(app, ExceptionController.new) or a small rack app
But our gem has no tests and I haven't been able to apply anything that I've read in exception handling gems (most work at the rescue_action_in_public level or mix in to ShowException) or in the Rails source code
This is a Rails 4.2 app tested via Rspec and Capybara.
Thoughts, links, halp?
Example code and tests
RSpec.describe 'ExceptionController' do
class ExceptionController < ActionController::Base
use ActionDispatch::ShowExceptions, Rails.configuration.exceptions_app
use ActionDispatch::DebugExceptions
#Response
respond_to :html, :json
#Layout
layout :layout_status
#Dependencies
before_action :status, :app_name, :log_exception
def show
respond_with details, status: #status, location: nil
end
def show_detailed_exceptions?
request.local?
end
protected
####################
# Dependencies #
####################
#Info
def status
#exception = env['action_dispatch.exception']
#status = ActionDispatch::ExceptionWrapper.new(env, #exception).status_code
#response = ActionDispatch::ExceptionWrapper.rescue_responses[#exception.class.name]
end
#Format
def details
#details ||= {}.tap do |h|
I18n.with_options scope: [:exception, :show, #response], exception_name: #exception.class.name, exception_message: #exception.message do |i18n|
h[:name] = i18n.t "#{#exception.class.name.underscore}.title", default: i18n.t(:title, default: #exception.class.name)
h[:message] = i18n.t "#{#exception.class.name.underscore}.description", default: i18n.t(:description, default: #exception.message)
end
end
end
helper_method :details
####################
# Layout #
####################
private
def log_exception
if #status.to_s == '500'
request.env[:exception_details] = details
request.env[:exception_details][:location] = ActionDispatch::ExceptionWrapper.new(env, #exception).application_trace[0]
end
end
#Layout
def layout_status
#status.to_s != '404' ? 'error' : 'application'
end
#App
def app_name
#app_name = Rails.application.class.parent_name
end
end
include Rack::Test::Methods
include ActionDispatch::Integration::Runner
include ActionController::TemplateAssertions
include ActionDispatch::Routing::UrlFor
let(:exception) { ActionController::RoutingError.new(:foo) }
let(:request) { ActionDispatch::TestRequest.new }
def app
# Rails.application.config.exceptions_app
#app ||= ExceptionController.action(:show)
end
it 'logs unknown format errors' do
request.env['action_dispatch.show_exceptions'] = true
request.env['consider_all_requests_local'] = true
request.env['warden'] = spy(Warden)
request.session = ActionDispatch::Integration::Session.new(app)
exception = ActionController::RoutingError.new(:foo)
request.env.merge! 'action_dispatch.exception' => exception
post '/whatever'
expect(response.body).to eq("dunno?")
end
end
refs:
https://github.com/richpeck/exception_handler
https://github.com/plataformatec/responders/blob/8f03848a2f50d4685c15a31254a1f600af947bd7/test/action_controller/respond_with_test.rb#L265-L275
https://github.com/rails/rails/blob/1d43458c148f9532a81b92ee3a247da4f1c0b7ad/actionpack/test/dispatch/show_exceptions_test.rb#L92-L99
https://github.com/rails/rails/blob/3e36db4406beea32772b1db1e9a16cc1e8aea14c/railties/test/application/middleware/exceptions_test.rb#L86-L91
https://github.com/rails/rails/blob/34fa6658dd1b779b21e586f01ee64c6f59ca1537/actionpack/lib/action_dispatch/testing/integration.rb#L647-L674
https://github.com/rails/rails/blob/ef8d09d932e36b0614905ea5bc3fb6af318b6ce2/actionview/test/abstract_unit.rb#L146-L184
https://github.com/plataformatec/responders/blob/8f03848a2f50d4685c15a31254a1f600af947bd7/lib/action_controller/respond_with.rb#L196-L207
http://blog.plataformatec.com.br/2012/01/my-five-favorite-hidden-features-in-rails-3-2/
https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/middleware/warden_user.rb
https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/rails/action_controller_rescue.rb#L3-L33
https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/rails/active_record_rescue.rb
https://github.com/rollbar/rollbar-gem/blob/master/spec/rollbar_spec.rb
http://andre.arko.net/2011/12/10/make-rails-3-stop-trying-to-serve-html/
https://github.com/rails/rails/blob/9503e65b9718e4f01860cf017c1cdcdccdfffde7/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L46-L49
Update 2015-08-27
It has been suggested that this question may be a duplicate of Testing error pages in Rails with Rspec + Capybara, however, that question addresses testing exception responses when the exceptions_app is set to routes.
As I wrote above, I'm using a Controller as the exceptions_app, so though I could use capybara to visit non-existing pages, I'd like to test Controller's action directly, rather than include the rest of the show exceptions stack. This is important because my problem is when the exceptions app is called with an unhandled content type, which I cannot easily test via capybara.
More generally, what I need to test is when the Exceptions app raises and exception, that I have fixed it.
I'm open to seeing some example code, though.

Internal Error 500 jbuilder

I'm trying to render some json in my rails app using jbuilder, but the output is showing up as:
{"status":"500","error":"Internal Server Error"}
Here's the url:
http://localhost:3000/api/v1/appusers/10
Here's the controller:
module Api
module V1
class AppusersController < ApplicationController
respond_to :json
skip_before_action :verify_authenticity_token
def show
#appuser = Appuser.find(params[:id])
end
And my show.json.jbuilder file:
json.extract! #appuser, :user_auth_token, :id
end
I've never encountered this before with jbuilder and all of my other jbuilder files work just fine. Any idea what I'm missing?
According to documentation, you don't need the end in the jbuilder template, just:
json.extract! #appuser, :user_auth_token, :id
Alternatively, if you are using a Ruby version major than 1.9, you can use this syntax:
json.(#appuser, :user_auth_token, :id)

Expiration of controller action from Sweeper does not work

Having a controller handling rendering of large XML feeds
module Spree
class FeedsController < Spree::StoreController
...
caches_action :products_out
cache_sweeper FeedSweeper
# XML feed in format of `xxxxxxx.com'
def products_out
#products = Product.all
respond_to do |format|
format.xml
end
end
end
Bellow is the corresponding sweeper's sublass:
module Spree
class FeedSweeper< ActionController::Caching::Sweeper
observe Product
def after_update(product)
# cache_configured? is nil, #controller is nil here, why ?
expire_action(:controller => :feeds,
:action => :products_out,
:format => :xml)
end
end
Above Spree::FeedSweeper is called when Spree::Product gets updated, however it seems expire_action silently dies and cache won't get invalidated.
Can somebody explain the issue ? Even better suggest some solution ?
Thanks.
Which Rails version are you using? expire_action seems to be deprecated after Rails 3.2.14.
Maybe you can try to find out the key then directly clear it with Rails.cache.delete(key).

Resources