Rails actions and jbuilder, why they have to be as difficult? - json

Rails drive me crazy. I'm trying to respond to with an action with JSON.
My goal is to let be the JSON the only format for a response to a URL.
Let's see some code.
The Model is a Devise user, with some added field.
The Controller is my UsersController that has this action
# /app/controllers/users_controller.rb
def static
render json: current_user
end
I got also this jbuilder view
# /app/views/users/static.json.jbuilder
json.content format_content(#user.content)
json.author do
json.name #user.name
json.email_address #user.email
end
if current_user.admin?
json.someValue "foo"
end
this View doesn't do some interesting stuff, but It's just a try.
Anyway I'll never get the static.json.jbuildercontent. I always get all Devise user's content as a JSON.
Am I doing something wrong? (or better: where I done the epic fail?)

Anyway found the solution:
# /config/route.rb
get 'my-static-json' => 'mycontroller#static', defaults: {format: :json}
# /app/controllers/mycontrollers_controller.rb
def my-static-json
end
# /app/views/mycontrollers/my-static-json.json.jbuolder
json.content "some static content"
this is only an example but gives have all the information that I needed

Related

Need help returning expected JSON output

I am trying to append authorIds into the post map. I am not sure if this is possible to do since I have close to no experience with Ruby. I have tried using multiple methods on the post map, such as merge, store, and others, however, nothing seems to work. I would appreciate any help I can receive, Thank you in advance!
def update
post = current_user.posts.find_by(id: params[:id])
# postMap = {post: post}
# post.merge!("authorIds": params[:authorIds])
# newPost = post.merge!('authorIds', params[:authorIds]
if post.update(post_params)
render json: {post: post}, status: :ok
else
render json: {error: post.errors}, status: :unprocessable_entity
end
end
Route function
Image to test case
For the most part of your update method, you deal with an instance of Post and not with a hash object. When you just want to return a value in the response then you need to add it to the object that will be returned as close to the end as possible.
Because the translation from an instance of Post to the returned JSON structure is done automatically you need to break those automatical steps and there add your new value.
def update
post = current_user.posts.find_by(id: params[:id])
if post.update(post_params) # `post` is an instance of `Post`
post_hash = post.as_json # translate into a Ruby hash
post_hash.merge!(authorIds: params[:authorIds]) # merge the additional value
render json: { post: post_hash }, status: :ok # return the modified hash
else
render json: { error: post.errors }, status: :unprocessable_entity
end
end
Notes: When you have a line like json: { post: post } then Ruby on Rails will first call as_json on post which will translate the instance of Post into a Ruby hash representation of a Post, then Rails will dump that hash into a JSON string. By breaking into those steps we are able to inject additional values into the hash.
Btw: the authorIds key in the params and in the returned hash is not following Ruby conventions and might confuse other developers working on the same project in the future. I suggest naming it author_ids instead.

undefined method `head?' using rabl

I am trying to get around this weird error for some time now. This is the basic controller I have:
get :index, provides: :json do
#requests = Request.order(:created_at)
render 'requests/index'
end
get :show, with: :id, provides: :json do
#request = Request.find_by(id: params[:id])
render 'requests/show'
end
This is how my rabl json files look like in each case:
index.json.rabl:
collection #requests
attributes :created_at, :updated_at, :user_id, :request_type
show.json.rabl:
object #request
attributes :id
The first route i.e. :index returns the array of Request objects nicely but the second route i.e :show throws the following error:
NoMethodError at /show/1
undefined method `head?' for #<Request:0x007f6814485840>
Can someone point at what this error could be about? Why is it looking for head? function?
Don't use #request instance variable. Rack stack uses it to store HTTP request there.

How do you set all of your Sinatra responses to be JSON?

I've been able to set all of my content types to be JSON in a before block, but is there a sinatra after filter that allows me to run to_json on all of the responses (instead of writing to_json 3 times in my example below)?
require 'sinatra'
require 'json'
before do
content_type :json
end
get '/' do
{ song: "Hello" }.to_json
end
get '/go' do
{ song: "Go Yo Ho" }.to_json
end
get '/hi' do
{ song: "Wake me Up" }.to_json
end
Thanks!
You can do that in an after block:
before do
content_type :json
end
get '/' do
{ a: 1 }
end
after do
response.body = JSON.dump(response.body)
end
Sinatra will re-calculate the correct content length for the updated body value.
An alternate way would be to use a helper:
helper do
def j(data)
JSON.dump(data)
end
end
get '/' do
j({ a: 1 })
end
The Sinatra::JSON project does the same thing. Also, you might want to look at libraries designed for building APIs like Grape or Goliath. These two libraries provide a easy way to attach decoders and encoders to handle this type of automatic conversion.
Put set :default_content_type, 'application/json' and all your responses will include a Content-Type: application/json header.

Rails routing - how to add .html extension to the routing rule?

I have this rule:
match '*urlnames' => 'home#searching_names'
The URL address looks like website.com/john.html.
The problem is, that in the log I see
Parameters: {"urlnames"=>"john"}
without the .html extension. Text extension is important, I would need to test it in the controller.
I tried to add to the routing rule this part:
match '*urlnames' => 'home#searching_names', :defaults => { :format => "html" }
But still the same, in the log is
Parameters: {"urlnames"=>"john"}
How can I catch the extension in the controller?
You have access to the requested format via request.parameters[:format] or (as a MIME type) via request.format.
However, you can also use a respond_to block:
def show
file = params[:urlnames]
respond_to do |format|
format.html { ... }
format.txt { ... }
end
end
where ... is code to render some text, or send some data or a file.
If you're just trying to show some static files, just place them in the public dir, and bypass Rails entirely.

respond_with redirecting when format=json

I'm encountering a strange behavior in my controllers. They seem to occasionally want to redirect instead of render a json response.
respond_to :json, :html, :js
def create
#favorite = current_user.favorites.build(:location_id=>params[:location_id])
if #favorite.save
respond_with(#favorite)
else
respond_with(#favorite.errors)
end
end
I think it works most of the time but today I was notified of this error:
NoMethodError: undefined method `favorite_url' for #<FavoritesController:0x00000006171dc0>
The params hash was logged as:
{"format"=>"json",
"action"=>"create",
"user_id"=>"56",
"auth_token"=>"iGSty8CMIaWsbShYZEtw",
"location_id"=>"47943",
"controller"=>"favorites"}
Especially strange since it seems to work most of the time... I have changed a few of my other controllers to use the old format.json { render :json => #object } syntax but I'd like to avoid that if possible.
How could this be?
On paths that are not GETs, respond_with tries to redirect to the url for whatever it is given. You can override this with a custom Responder