rails 3 json custom json formatting - json

I have a collection of #clients with attributes id and email
I want to render this json format
[
{"id":" 1","label":"johndoe#yahoo.com","value":"1"},{"id":" 2","label":"paulsmith#gmail.com.com","value":"2"}
]
in clients_controller I defined the following method
def search
#clients = Client.where(:user_id => current_user.id).select('id','email')
render :partial => "clients/search"
end
and here is the view _search.json.erb
[
<%= raw #client.map{|client| '{"id":"' +" #{client.id}" +'","label":"' + "#{client.email}" + '","value":"' +"#{client.id}" +'"}' }.join(",") %>
]
this is working, but I found it fugly...is there a more elegant way to generate a custom json format in a view?

Use a helper function you call from the view to format the output or a library function you call from the controller. Example (of later):
def search
#clients = Client.where(:user_id => current_user.id).select('id','email')
respond_to do |format|
format.html
format.json do
render :json => custom_json_for(#clients)
end
end
end
private
def custom_json_for(value)
list = value.map do |client|
{ :id => " #{client.id}",
:label => client.email.to_s,
:value => client.id.to_s
}
end
list.to_json
end

You just need use the to_json method. In you case it's
#client.to_json(:only => [:id, :label, :value])

You could use jBuilder gem from GitHub
for clients_controller
def search
#clients = Client.where(:user_id => current_user.id)
end
and search.json.jbuilder
json.id #clients.id
json.label #clients.email
json.value #clients.id
For more info you can visit Jbuilder on RailsCast

You can use https://github.com/dewski/json_builder/ to customize your json response in the view and separate it from the controller. It's good when you need to add some "current user" depending attributes like
[{:attending => event.attending?(current_user)}]

Related

rendering json gets error no template found for controller rendering head no_content 204

I have a controller that render a json with data from models. When i enter route to get it it do nothing and just show and error in console.
Controller:
class Api::ForecastController < ApplicationController
before_action :set_hourly_forecast, only: %i[ show edit update destroy ]
def index
respond_to do |format|
format.html do
#hourly_forecasts = HourlyForecast.where(forecast_location_id: params[:forecast_location_id]).paginate(:page => params[:page], :per_page=>24) if params[:forecast_location_id].present?
end
format.json do
weather_service = WeatherService.where(name: params[:name])
#forecast_location = ForecastLocation.where(weather_service_id: weather_service)#& municipality: #municipality.name)
#hourly_forecasts = HourlyForecast.where(forecast_location_id: forecast_location.id ).paginate(:page => params[:page], :per_page=>24) if params[:forecast_location_id].present?
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_hourly_forecast
#hourly_forecast = HourlyForecast.find(params[:id])
end
# Only allow a list of trusted parameters through.
def hourly_forecast_params
params.require(:hourly_forecast).permit(:forecast_location_id, :date, :temperature, :humidity, :rain, :rain_probability, :wind_speed, :wind_direction)
end
end
Error:
> Started GET "/api/forecast.json?name=dark_sky" for 127.0.0.1 at 2022-04-20 18:33:29 +0200
Processing by Api::ForecastController#index as JSON
Parameters: {"name"=>"dark_sky"}
No template found for Api::ForecastController#index, rendering head :no_content
Completed 204 No Content in 53ms (ActiveRecord: 6.2ms | Allocations: 4983)
The route i use its
127.0.0.1:3000/api/forecast.json?name=dark_sky
So the output should be a json with all hourly model.
But it just do nothing and on console it does get and the select but jumps this error of template, i dont understand it since im new on rails.
If need more controllers, models or anything ask in comments.
You have to have a separate template file to render json index.json.jbuilder for example.
# app/views/api/forecasts/index.json.jbuilder
json.array! #hourly_forecasts do |hourly_forecast|
json.extract! hourly_forecast, :id, :forecast_location_id, :date, :temperature, :humidity, :rain, :rain_probability, :wind_speed, :wind_direction
json.url api_forecast_url(hourly_forecast, format: :json)
end
https://github.com/rails/jbuilder
If you don't need to customize rendered json too much, render json inline in the controller
format.json do
weather_service = WeatherService.where(name: params[:name])
#forecast_location = ForecastLocation.where(weather_service_id: weather_service)#& municipality: #municipality.name)
#hourly_forecasts = HourlyForecast.where(forecast_location_id: forecast_location.id ).paginate(:page => params[:page], :per_page=>24) if params[:forecast_location_id].present?
render json: #hourly_forecasts
end
https://guides.rubyonrails.org/layouts_and_rendering.html#rendering-json
https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html

acts_as_api and bullet N+1 queries

I am using acts_as_api to provide a JSON response for some models in my system. My API related code is (reduced to make the example easier):
# File app/modes/item.rb
# API
acts_as_api
api_accessible :v1_list do |template|
template.add :id
template.add :client_name
end
This API is working as expected. It is important to know that client_name is a method containing:
def client_name
client.name
end
That is, the client name is not included in the item model but in the client model. Thus, this info is not contained in the items table.
Using Bullet gem I have noticed that a N+1 query is being performed in the clients table. For each item, a SQL query to the clients table is also performed.
I know that ActiveRecord has some utilities in the API that avoids N+1 queries, and I would like to know if there is a way to use that ActiveRecord feature together with acts_as_api gem.
The gem documentation shows this
def index
#users = User.all
#Note that it’s wise to add a root param when rendering lists.
respond_to do |format|
format.xml { render_for_api :name_only, :xml => #users, :root => :users }
format.json { render_for_api :name_only, :json => #users, :root => :users }
end
end
So for your case you should simply eager load the client association
def index
#items = Item.includes(:client).all
# Note that it’s wise to add a root param when rendering lists.
respond_to do |format|
format.xml { render_for_api :name_only, :xml => #items, :root => :items }
format.json { render_for_api :name_only, :json => #items, :root => :items }
end
end

Rails 4 and Mongoid: Retrieving embedded document as json

I want to retrieve all the embedded documents of a document to return as a "list" of json elements.
I have documents as follows:
class Parent
include Mongoid::Document
field :name, :type => String
embeds_many :kids
class Kid
include Mongoid::Document
field :kidname, :type => String
embedded_in :parent, :inverse_of => :kids
I have defined two routes
get 'parents/:kidname' => 'parents#getparents'
where getparents is defined as
#retval = Parent.where("kids.kidname" => params[:kidname])
respond_to do |format|
format.html # index.html.erb
format.json { render json: #retval}
end
This gives me the correct output, i.e., the parent whose kid's name is params[:kidname]
However when I try to do the reverse, i.e., retrieve the list of all kids whose parent's name is params[:name], that doesn't work! The route is
get 'kids/:name' => 'parents#getkids'
and getkids is defined as
def getkids
#parent = Parent.where("name" => params[:name])
#kids = #parent.kids
respond_to do |format|
format.html # index.html.erb
format.json { render json: #kids}
end
end
What am I doing wrong? Does it matter where getkids is defined .. I defined it in parents_controller, should it be in kids_controller? Please help!
Thanks.
if you re not getting the results or getting some error change this line #parent = Parent.where("name" => params[:name]) to #parent = Parent.where("name" => params[:name]).first

Rspec - GET show with respond_to on specific json view

I'm trying to use Rspec-rails on my application but I fail when I try to verify the response of my request.
This is my code :
# app/controllers/user_controller.rb
# GET api/users/:id
def show
#user = User.find(:id)
respond_to do |format|
format.json {render template: "/api/users/detailed"}
end
end
...
# spec/controllers/api/users_controller_spec.rb
require "spec_helper"
describe Api::UsersController do
include Devise::TestHelpers
describe "GET #show" do
user = FactoryGirl.build(:user)
it "assigns the requested user to #user" do
get :show, :format => 'json', :id => user.id
assigns(:user).should eq(#user)
end
it "renders the #detailed view" do
get :show, :format => 'json', :id => user.id
response.should be_respond_to("api/users/detailed")
#expect(response).to render_template("api/users/detailed")
#expect(subject).to render_template("/api/users/detailed")
#render(:template => "/api/users/detailed.json.rabl", :format => "json")
#response.should render_template("/api/users/detailed.json.rabl")
end
end
I commented a part of what I have tested.
I just want to be sure that my controller respond_to a template called "api/users/detailed".
Most often, I have the following error :
expecting <"api/users/detailed"> but rendering with <"">
I'm pretty sure my request called the controller because the first describe "assigns the requested user to #user" works.

Ignore modules from Model

I have in my application a few controllers that i want to use as a api. In this api i need to use versioning.
in my routes.rb i`m using this:
require 'api_constraints'
(...)
scope '/:target/:version', :module => :api, :constraints => { :version => /[0-z\.]+/ } , :defaults => { :format => 'json' } do
scope :module => :v1, :constraints => ApiConstraints.new(:version => 1, :default => true) do
match '/list' => 'sample#list'
end
end
my api_constraints.rb:
class ApiConstraints
def initialize(options)
#version = options[:version]
#default = options[:default]
end
def matches?(req)
#default || req.headers['Accept'].include?("application/waytaxi.api.v#{#version}")
end
def self.version
#version
end
end
in my SampleController.rb:
module Api
module V1
class SampleController < ApiBaseController
def list
render json: Model.find_by_id(params[:id])
end
end
end
end
the ApiBaseController:
module Api
class ApiBaseController < ApplicationController
before_filter :authenticate
skip_before_filter :verify_authenticity_token
private
def authenticate
# if params[:target] == "ios"
# render :json => {status: 404}
# return false
# end
end
end
end
the problem is:
whenever i try to call Model i get this error:
uninitialized constant Api::V1::SampleController::Model
If i use: ::Model i get this error:
uninitialized constant Model
And yes, i do have this models on my database. If i use Model.all outside the SampleController i get the objects.
P.S.: I'm using rails 3.2.8
Found my problem.
My Model was in plural and on my controller i was calling it in singular