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.
Related
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
I'm comunicating my Rails API with my AngularJS application.
Everything is working great and normal up until the point I have to send parameters in a GET request. This is the Rails controller
def cats
if cat_params[:color]
#cats = Cat.where(... #you know
else
//Do something else
end
private
def cat_params
params.require(:cat).permit(:color)
end
Then from Angular
var kitty = {
cat: {
color: "red"
}
}
$http.get('some URL', { params: kitty }).success.....
By this time, the params hash looks like a stringify JSON
Started GET "some URL?cat=%7B%22color%22:%22red%22%7D" for 127.0.0.1 at 2015-01-28 23:10:24 -0300
Processing by Some_controller as JSON
Parameters: {"cat"=>"{\"color\":\"red\"}", "cat_id"=>"19"}
Cat Load (0.5ms) SELECT "cat".* FROM "cats" WHERE "cat"."id" = 19 LIMIT 1
{"cat"=>"{\"color\":\"red\"}", "format"=>"json", "controller"=>"some_controller", "action"=>"some_action", "cat_id"=>"19"}
Completed 500 Internal Server Error in 95ms
NoMethodError - undefined method `permit' for "{\"cat\":\"red\"}":String:
I'm also sending the Content-Type header, set to 'application/json'.
From Angular's $http.get documentation, I read that if the value of params is something other than a string, it will stringify the JSON object, so the issue is not in the front-end.
I don't think the solution begins with starting JSON parsing the params hash, since I've had no need to do it in the past. It seems to me that strong_parameters is playing dirty, or Rails is ignoring the JSON string. Any ideas what to do next?
I just ran into the same issue. Changing the param serializer solved the issue for me:
$http.get('someURL', {
paramSerializer: '$httpParamSerializerJQLike',
params: {cat: {color: 'red}}
})
Also adding the 'Content-Type': 'application/json' header will not help since it applies to the the body the request.
I used to meet a $http.get problem that when call $http.get('some url', {params:SOME_PARAMS}), SOME_PARAMS could be transformd to key-value params when it is a simple key-value data like {color:'red'}, and it would transform params to json string when it is a complex type like {cat:{color:'red'}}.
So to solve your question, I suggest that add params behind url like:
$http.get('some URL?cat[color]=red').success.....
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.
I am creating an API that uses JSON to communicate back/forth with external view apps (Angular). In a lot of API actions, I return a JSON response that is 99% the same as the error one below:
# controller
def create
#record = Record.new(record_params)
if #record.save
#record
else
render json: {
error: {
type: "invalid_request",
message: "Could not create record. Params: #{record_params}",
errors: #record.errors.messages
}
}, status: 404
end
end
Is there a convenient way to DRY this up? I ask specifically because I know certain methods such as the render only work in controller classes because they are inherited.
I'm thinking about something like the following:
render json: API::ErrorObject.call(#record, record_params), status: 404
And in that class it would be:
class API::ErrorObject
self.call(object, params)
{
error: {
type: "invalid_request",
message: "Could not create record. Params: #{record_params}",
errors: object.errors.messages
}
}
end
end
I think that would work, but is there an even cleaner way to abstract away some of this behavior? The API is fairly large, so there are 30+ places where very similar code will reside. I know that someday someone will request an addition to the API responses, and having a single place to update this would be a lot better than 30...
The best solution I've found so far is to bring the V back into MVC by using a tool like Jbuilder.
Using this you can really DRY up your code similar to what you do using partials in ERB.
I have been looking all over for how to properly check respond to a application/json type as well as a submitted form. I finally got it working on my own with the following code. Can someone explain why it works? Or offer advice on a better solution to achieve the same thing?
post '/login', provides: :json do
p = params
if request.content_type == 'application/json'
params = JSON.parse(request.body.read, :symbolize_names => true)
else
params = p
end
requires(params, :email, :password)
if #user = User.find_by_email(params[:email])
if #user.authenticate(params[:password])
log_user_in(#user)
rabl :login, object: #user
else
error 404, {error: "incorrect credentials"}.to_json
end
else
error 404, {error: "user not found"}.to_json
end
end
JSON requests are submitted in the body of the html request so this works but overriding the params hash is not advisable if using RESTful routes.
Hey Curtis.
Just use ::Rack::JSONBodyParser from rack-contrib:
A Rack middleware that makes JSON-encoded request bodies available in
the request.params hash. By default it parses POST, PATCH, and PUT
requests whose media type is application/json. You can
configure it to match any verb or media type via the :verbs
and :media options.
Examples:
Parse POST and GET requests only
use Rack::JSONBodyParser, verbs: ['POST', 'GET']
Parse POST|PATCH|PUT requests whose Content-Type matches 'json'
use Rack::JSONBodyParser, media: /json/
Parse POST requests whose Content-Type is 'application/json' or 'application/vnd+json'
use Rack::JSONBodyParser, verbs: ['POST'], media: ['application/json', 'application/vnd.api+json']