Customise as_json response in ruby - ruby-on-rails

I am building rest APIs in ruby
I am using #object.as_json() for the response in json format
I am getting stuck in the mid while transfer the data in as_json. I need to more customize the response
I am listing what exactly I want and where I am getting stuck
I am using below code
#videos = Video.all.map{|m| m.as_json(
only: [:title],
method: [:watch_count],
include: {
user: {
only: [:first_name],
method: [:role]
}
}
).merge(
is_upvoted: m.is_upvoted(current_user)
) }
But I need to customise it.
1- I need to pass manual key and value (which is not in db) in video and user object, currently it can be possible with video but not with user. It should e possible with parent object and its child associate object. I don't want to to use :method for the same
2-I need to pass parameter in :method, like video model has a method is_upvoted but having a parameter like current user. So I am unable to pass parameter in :method. currently I can do with merge as I am doing but It can not be possible with user object.
3- I need to change key name while use include: {user: {method: []}}.
I need to use key "owner" at the place of "user"
Many Thanks in Advance

You can do it with JBuilder, which much more flexible and allow to make all things from your list. Result code will be like this:
#videos = JBuilder.encode do |json|
json.array! Video.all do |video|
json.(video, :title, :watch_count)
json.is_upvoted video.is_upvoted(current_user)
json.owner do
json.(video.user, :first_name, :role)
json.field video.user.with_param(some_param)
end
end
end
You may find details about JBuilder in documentation.

Related

Add Non Persistent attribute In Active Record JSON response

Is is possible to create a custom model attribute and get its value included by default, whenever that model is called?
I have a model named Video. It has an attribute named name. It contains the name of the video sample-video.mp4. I want to create two custom attributes named iphone_url and android_url for model.
Both attributes will have different urls concatenated with name. So, iphone_url will have http://link1/+name+/playlist.m3u8 where as android_url will have http://link2/+name
Is it possible that whenevr i call that model, both attributes are automatically added(in JSON response)?
I tried solutions mentioned here.
I was able to add custom attributes by using attr_accessor, but their value is always null. May be because their value needs to be set manually first.
So how to do this?
Edit: Currently, i'm doing like this:
videos = Array.new
# Dirty, but works
Video.all.each do |video|
video = video.attributes
wowza_server = "X.X.X.X:XXXX/AppName/"
custom_attributes = {:wowza_urls => {:ios => "http://"+wowza_server+"mp4:"+video[:name])+"/playlist.m3u8", :android => "rtsp://"+wowza_server+video[:name])}}
videos << video.merge(custom_attributes)
end
render :json => videos, status: :ok
This is really what jbuilder was made for.
Jbuilder gives you a simple DSL for declaring JSON structures that beats massaging giant hash structures.
You're starting to massage a giant hash structure.
I'm assuming this is an index view, in which case it look something like this:
json.array! #videos do |video|
json.my_field video.my_field
json.iphone_url video.iphone_url
json.android_url video.android_url
end
On your app/model/video.rb
class Video
def iphone_url
"some_url_to_build"
end
...
end

How can I configure my API to send an ID rather than a name via JSON?

I am configuring a basic API in Ruby. It contains two simple tables and an associative, but my join table is giving me a problem that should be easy to solve. When I open my view in the browser, it shows the object name (in this case, people). However, in view.json it shows the id. I want the API to send the name as JSON and not the id. How can I do this? The configuration of my API is below:
json.array!(#leituras) do |leitura|
json.extract! leitura, :id, :pessoa_id, :livro_id
json.url leitura_url(leitura, format: :json)
end
You should try RABL https://github.com/nesquena/rabl. RABL implements a new template to render Json, xml, ... as a view. I'm using for my API and it's very usefull.
I would recommend Active Model Serializers.
I wouldn't recommend RABL, you might end up with a bunch of n+1 queries, personally the DSL is a little weird.
Add a serializer for your object.
class LeituraSerializer < ActiveModel::Serializer
attributes :id, pessoa_id, :livro_id
end
And in your controller you can do
class LeiturasController < ApplicationController
def show
#leituras = Leitura.find(params[:id])
render json: #leituras
end
end
Read more Here : https://github.com/rails-api/active_model_serializers/blob/master/README.md

Using RABL (or other API builder) to handle model creation/update?

I'm using RABL right now to generate JSON responses of an API in Rails, but I'm finding that while RABL is super handy for mapping models to responses, to create a consistent API I'm having to to duplicate that mapping logic in the update and create functions of my controller.
As a simple example, if I just want to change the attribute names in the response to a POST request, I can do this in RABL:
create.rabl
object #car
attributes car_id: :id, badly_named_legacy_column_that_means_color: :color
But if I want the client to be able to use these same "cleaned up" attributes in the JSON POST/PUT request itself (i.e. be able to send { "id": 1, "color": "red" } instead of { "car_id": 1, "badly_named_legacy_column_that_means_color": "red" }), I have to manually do this mapping again in the controller:
cars_controller.rb
def create
params[:car_id] = params.delete(:id)
params[:badly_named_legacy_column_that_means_color] = params.delete(:color)
#car = Car.create(params)
end
Now there are two places that I need to map car_id to badly_named_legacy_column_that_means_color. Not very DRY.
So far I haven't come across any way to handle this using RABL. Is there one that I'm missing? I also realize this might be outside the scope of RABL, which bills itself specifically as a templating system, so maybe is there another API builder that would allow me to do this? I love the idea of mapping messy database columns to a clean API but having to specify this mapping in both the view and the controller isn't very DRY. Any thoughts appreciated.
Update
The original answer is all about Ruby/Rails => JSON, the question is JSON => Ruby/Rails. This answer about associating columns should explain an approach:
alias_attribute :new_column_name, :column_name_in_db
Then you can just reference new_column_name in the RABL and Rails will handle the association on the create/update.
You should be able to call render from the create method and render any view. You could customize a response with a create specific template or reuse the generic show template. The trick is to re-use the object rabl template (app/views/car/car.rabl in this case), for example:
# POST /cars
def create
#car = Car.new(params)
if #car.save
render action: 'show'
else
respond_with #car
end
end
Where app/views/cars/car.rabl is
attributes :id, ...
and app/views/cars/show.rabl is
object #car
extends "cars/car"

Inserting Array with Form/JSON to Rails API

So I'm currently working on an API, based on this other question that I asked:
I would like to be able to add an array of genre_ids to my user upon creation.
Currently in my API Users Controller, I have my function:
def create
respond_with User.create(user_params)
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [])
end
So I go to create my user with a RESTful extension I have, and creating a user normally works perfectly without genres.
But as soon as I add the genres, it doesn't fail, but it also doesn't save correctly.
I'm just trying to simulate how it would be created with a form or with url params. Would it be something like:
1&2&3
1,2,3
[1,2,3]?
Any suggestions on the proper way to do this?
Thanks!
Your two choices in this case would either to POST raw JSON:
{
"genre_ids": [
1,
2,
3
]
}
Or, if reworking your back end to handle that doesn't make sense, simply:
1,2,3 will work in the URL-encoded form value. If you find the comma is causing you trouble, you could urlencode the value before it is POSTed, which would convert , to %2C.

how do I return model parent values when I make a query from the database

So I have a model called Image that belongs_to :user. Each user has a first and last name.
I have a flash app that I am returning a json object back to of Images.
the service I will be calling on the Images controller would look something like this
def getimages
#images = Image.all
render :json => #images
end
My json would look something like this
[{"image":{"created_at":"2011-01-22T19:04:30Z","img_path":"assets/img/bowl_93847566_3_0.png","updated_at":"2011-01-22T19:04:30Z","id":9,"user_id":3}}]
what I would like to do is also include the users first and last name with in the image object that gets passed back.
once I have an image object I am able to do something like image.user.first_name but I am not clear how I would return something like an array of image objects and include the user along with it.
what would be great is if I could get my array of images to look like the following.
[{"image":{"created_at":"2011-01-22T19:04:30Z","img_path":"assets/img/bowl_93847566_3_0.png","updated_at":"2011-01-22T19:04:30Z","id":9,"user_id":3, "first_name":"Matthew", "last_name":"Wallace"}}]
I am thinking this may include adding some kind of model method or somthing that I am not familiar with.
What would be the best practice for achieving this?
You could:
render :json => #images.to_json(:include => :users)
See http://apidock.com/rails/ActiveRecord/Serialization/to_json (and http://apidock.com/rails/Array/to_json shows it works on Arrays). Finally, http://apidock.com/rails/ActionController/Base/render describes using to_json in a json render as optional and not required, which implies it should cause no harm (I couldn't see another way to pass the required options in).
Perhaps cleaner json:
render :json => #images.to_json(:include => { :user => { :only => [:first_name, :last_name] } })
Besides the answer provided by #apneadiving, you can also override the Image's to_json method and return a string containing whatever JSON you need.

Resources