I am very new to Ruby, so I might just be missing a simple thing here.
I am using the rails Ancestry gem to produce JSON output for an API I'm writing. Here is the controller:
class ParentsController < ApplicationController
def show
#vendor = Vendor.find(params[:id]).subtree
render json: #vendor, status: 200
end
end
This produces the correct output showing the ID, vendor, ancestry etc.
However when I add .arrange, as such:
class ParentsController < ApplicationController
def show
#vendor = Vendor.find(params[:id]).subtree.arrange
render json: #vendor, status: 200
end
end
The output I get is:
{"#\u003CVendor:0x4b9e628\u003E":
{"#\u003CVendor:0x4b9e3e8\u003E":
{},
"#\u003CVendor:0x4b9e1f0\u003E":
{}}}
which looks like memory locations for the objects. What fundamental thing am I missing to get this to actually output the objects in an arranged form?
I've also tried using arrange_serializable instead of arrange, this gives me a
undefined method 'arrange_serializable' for #<ActiveRecord::Relation::ActiveRecord_Relation_Vendor:0x4b9b910>
error
Related
I have a problem with rendering array properly with JBuilder on Rails 6 (API only mode).
I basically have a list of registration plates, which I want to fetch via API request
My index view looks like this:
# frozen_string_literal: true
json.array! #registration_plates,
partial: 'registration_plates/registration_plate',
as: :registration_plate
My show view looks like:
# frozen_string_literal: true
json.partial! 'registration_plates/registration_plate',
registration_plate: #registration_plate
And finally partial _registration_plate.json.jbuilder is very simple:
# frozen_string_literal: true
json.id registration_plate.id
json.plate registration_plate.plate.to_s
I do get a normal response from server:
But instead of an array, I get the series of JSON objects
Did anyone have similar problem, or do you have any idea how to solve it?
Than you in advance.
EDIT
Also my controller is configured to render the jbuilder rather than json
# GET /registration_plates
def index
#registration_plates = RegistrationPlate.all
render #registration_plates
end
# GET /registration_plates/1
def show
render #registration_plate
end
And if I change the index action to look like
def index
#registration_plates = RegistrationPlate.all
render json: #registration_plates.to_json(only: %i[id plate])
end
i do get correct output, but then, I defy the sole purpose of jbuilder
update your index to remove render #registration_plates
# GET /registration_plates
def index
#registration_plates = RegistrationPlate.all
end
render #registration_plates renders _registration_plate.json.jbuilder directly without going into index.json.jbuilder
How do I render custom error JSON in a Rails 5 API? Right now, if I perform a GET on this url http://localhost:3000/users/5, it returns the 404 not found code, and all the traces associated with it. How can I stop Rails from automatically rendering all the traces?
Example of the generated error response: https://pastebin.com/C1dQA5eL
Hi you can create a custom module and extend it in your controller. Create a method in that module with parameters of resource and value. And on the basis of that send response and after that you can extend it in your respective Controller
like this:
class MyController
include AppError
end
I think you should if....else.
def show
user = User.find_by(id: params[:id])
if user.present?
render json: user
else
render json: { status: :not_found }
end
end
I have 2 non database attributes in my model. If one of them has a value, I need to return the other one in the json response:
class Car < ApplicationRecord
attr_accessor :max_speed_on_track
attr_accessor :track
def attributes
if !self.track.nil?
super.merge('max_speed_on_track' => self.max_speed_on_track)
end
end
end
The problem is that the line 'if !self.track.nil?' throws an error when the controller tries to return the json
Perhaps there is a better way as I read that using attr_accessor is a code smell.
What I am trying to do is if the user passes me a track value as a query parameter, then I pass that value to the model and it uses it to calculate the max_speed_on_track, and return that value.
Obviously if no track is provided by the user then I don't want to return max_speed_on_track in the json.
The controller method is very basic for now (I still need to add the code that checks for the track param). The code throws the error on the save line.
def create
#car = Car.new(car_params)
if #car.save
render json: #car, status: :created
else
render json: #car.errors, status: :unprocessable_entity
end
end
Try this out:
class Car < ApplicationRecord
attr_accessor :max_speed_on_track
attr_accessor :track
def as_json(options = {})
if track.present?
options.merge!(include: [:max_speed_on_track])
end
super(options)
end
end
Since Rails uses the attributes method, and you're only needing this for json output, you can override the as_json method just like in this article. This will allow you to include your max_speed_on_track method in your json output when the track is present (not nil).
I have a model called Entity and the create action in the controller looks like this:
# enitities_controller.rb
def create
# loading params, etc...
#entity.save
respond_with #entity
end
I am using jbuilder for custom JSON views rather than rendering #entity.to_json, which works great. I have one last issue, which is when the model won't save due to validation errors I get the following response (with status 422 Unprocessable Entity):
{"errors":{"parent_share":["can't be blank","is not a number"]}}
I would like to override this json with my own. I am aware of he possibility to replace respond_with #entity with:
respond_with #entity do |format|
if #entity.errors.any?
format.json {
render "entities/create", :status => :unprocessable_entity
}
end
end
But shouldn't there be a more auto-magic way by defining some sort of errors view or something? This feels a bit dirty AND it makes me have to write more code each time I need this rather than allowing me to use respond_with. Is there another way?
Meanwhile I have found the answer:
You have to create the file lib/application_responder.rb and add the following:
class ApplicationResponder < ActionController::Responder
include Responders::FlashResponder
include Responders::HttpCacheResponder
def to_json
set_flash_message! if set_flash_message?
if !has_errors? || response_overridden?
default_render
else
controller.default_render( status: :unprocessable_entity )
end
end
end
And add the following to your application responder:
self.responder = ApplicationResponder
What this does is add a to_json method that will copy the behaviour of the to_js responder.
I want to PUT to rails and avoid getting a 204. I am using this pattern:
class SomeController < ApplicationController
respond_to :json
def update
# ...
respond_with(some_object)
end
end
However, when I do a put to update, I get a 204 back. I realize this is completely valid etc, but I explicitly want the content back. I can override it to some extent like this:
def update
respond_with(some_object) do |format|
format.json{render json: some_object}
end
end
but this seems a bit too hands-on for rails. Is there any more idiomatic way of avoiding a 204 and requesting the full content to be sent back? This is Rails 3.2.
In summary: I want maximally idiomatic rails that avoids a 204.
I made a custom responder which always returns my JSON encoded resource even on PUT/POST.
I put this file in lib/responders/json_responder.rb. Your /lib dir should be autoloaded.
module Responders::JsonResponder
protected
# simply render the resource even on POST instead of redirecting for ajax
def api_behavior(error)
if post?
display resource, :status => :created
# render resource instead of 204 no content
elsif put?
display resource, :status => :ok
else
super
end
end
end
Now, explicitly modify the controller which requires this behavior, or place it in the application controller.
class ApplicationController < ActionController::Base
protect_from_forgery
responders :json
end
You should now get JSON encoded resources back on PUT.
As a less invasive alternative, you can pass a json: option to the respond_with method invocation inside your controller update action, like this:
def update
# ...
respond_with some_object, json: some_object
end
Granted it seems a bit unDRY having to repeat the object twice in the arguments, but it'll give you what you want, the json representation of the object in the response of a PUT request, and you don't need to use the render json: way, which won't give you the benefits of responders.
However, if you have a lot of controllers with this situation, then customizing the responders, as jpfuentes2 showed in the accepted anwser, is the way to go. But for a quick single case, this alternative may be easier.
Source: https://github.com/plataformatec/responders/pull/115#issuecomment-72517532
This behavior seems intentional to fall in line with the HTTP spec, and "ideally" you should be firing off an additional GET request to see the results. However, I agree in the real world I'd rather have it return the JSON.
#jpfuentes2's solution above should do the trick (it's very similar to the pull request below), but I'm hesitant to apply anything that's patching rails internals, as it could be a real pain to upgrade between major versions, especially if you don't have tests for it (and let's face it, developers often skimp on controller tests).
References
https://github.com/rails/rails/issues/9862
https://github.com/rails/rails/pull/9887
Just to clarify, you do not need the responders gem to do this... You can just do:
config/initializers/responder_with_put_content.rb
class ResponderWithPutContent < ActionController::Responder
def api_behavior(*args, &block)
if put?
display resource, :status => :ok
else
super
end
end
end
and then either (for all updates actions to be affected):
class ApplicationController < ActionController::Base
def self.responder
ResponderWithPutContent
end
end
or in your action:
def update
foo = Foo.find(params[:id])
foo.update_attributes(params[:foo])
respond_with foo, responder: ResponderWithPutContent
end
What's wrong with simply doing:
def update
some_object = SomeObject.update()
render json: some_object
end
Not a big fan of this behavior. To get around it, I had to avoid using the respond_with method:
class SomeController < ApplicationController
respond_to :json
def update
# ...
respond_to do |format|
format.json { render(json: some_object, status: 200) }
end
end
end