Rspec - GET show with respond_to on specific json view - json

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.

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

How do I delete records saved with collection_select in Rails?

My goal is to be able to update a saved record when items are de-selected from a collection_select (and then that record is resubmitted.) Thanks in advance for your help!
Details
I've got a form for Newsavedmaps. Newsavedmaps can have many waypoints. Users can select waypoints in a collection_select, and when they save the Newsavedmap, these waypoints are saved to their own database table.
The problem: when users open the Newsavedmap they've saved, I want them to be able to de-select a waypoint. When they save the Newsavedmap again, I want the de-selected waypoint to be deleted.
This is a Rails 2.3X app I'm maintaining, which is why the collection_select uses a different format below.
Model
class Newsavedmap < ActiveRecord::Base
belongs_to :itinerary
has_many :waypoints, :dependent => :destroy
accepts_nested_attributes_for :waypoints, :reject_if => lambda { |a| a[:waypointaddress].blank? }, :allow_destroy => true
end
View
<% form_for #newsavedmap, :html => { :id => 'createaMap' } do |f| %>
<%= f.error_messages %>
<%= f.text_field :name, {:id=>"savemap_name", :size=>30 }%></p>
<%= collection_select :waypoints, :waypointaddress, #newsavedmap.waypoints, :waypointaddress, :waypointaddress, {}, { :multiple => true, :class => "mobile-waypoints-remove", :id =>"waypoints" } %>
<% end %>
Newsavedmaps Controller
def create
#newsavedmap = Newsavedmap.new(params[:newsavedmap])
waypoint = #newsavedmap.waypoints.build
respond_to do |format|
if #newsavedmap.save
flash[:notice] = 'The new map was successfully created.'
format.html { redirect_to "MYURL"}
format.xml { render :xml => #newsavedmap, :status => :created, :location => #newsavedmap }
else
format.html { render :action => "new" }
format.xml { render :xml => #newsavedmap.errors, :status => :unprocessable_entity }
end
end
end
def update
#newsavedmap = Newsavedmap.find(params[:id])
if #newsavedmap.itinerary.user_id == current_user.id
respond_to do |format|
if #newsavedmap.update_attributes(params[:newsavedmap])
flash[:notice] = 'Newsavedmap was successfully updated.'
format.html { redirect_to "MYURL" }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #newsavedmap.errors, :status => :unprocessable_entity }
end
end
else
redirect_to '/'
end
end
Params when creating new record
Parameters: {"newsavedmap"=>{"name"=>"Name of my map", OTHER FIELDS NOT SHOWN ABOVE, "waypoints"=>{"waypointaddress"=>["1600 Pennsylvania Ave NW, Washington, DC 20500", "350 5th Ave, New York, NY 10118"]}}
I think your problem is in correctly built form (and params which come from it).
I suggest to
look at gem cocoon or nested_form
add waypoints_attributes to attr_accessible
implement helpers from gem (from point #1) in your form
And Rails magic should done other job
Another variant (without using gems) (I think much more difficult way!)
You can remove accept_nested_attributes at all and work with your params directly. But in this case you should manage manually all process: correct inserting records, correct destroying them.
In your case it should smth like this (it is not tested!). The example based on your params which posted in the question.
def create
# delete params 'waypoints' it will be manage manually
waypoints = params[:newsavedmap].delete(:waypoints)
#newsavedmap = Newsavedmap.new(params[:newsavedmap])
waypoints.each do |waypoint|
#newsavedmap.waypoints.build(:waypointaddress => waypoint)
end
if #newsavedmap.save
...
end
end
the main troubles will be in method update
def update
# delete params 'waypoints' it will be manage manually
waypoints = params[:newsavedmap].delete(:waypoints)
# find and setup attributes
#newsavedmap = Newsavedmap.find(params[:id])
#newsavedmap.attributes = params[:newsavedmap]
# TROUBLES start here
# destroy not checked (but existed in DB) waypoints
existed_waypoints = #newsavedmap.waypoints
existed_waypoint_addresses = existed_waypoints.map(&:waypointaddress)
new_waypoints = []
waypoints.each do |waypoint|
if existed_waypoint_addresses.include?(waypoint)
# existed waypoint was selected on the form
# find it and add to new_waypoints
new_waypoints << #newsavedmap.waypoints.find_by_waypointaddress(waypoint)
else
# new waypoint was selected on the form
# build it and add to new_waypoints
new_waypoints << #newsavedmap.waypoints.build(:waypointaddress => waypoint)
end
end
# setup new records for waypoints
#newsavedmap.waypoints = new_waypoints
if #newsavedmap.save
# destroy existed but not selected waypoints
(existed_waypoints - new_waypoints).map(&:destroy)
...
end
end

Ruby on Rails: a weird no method error in my controller?

Here's what I got in my logs:
Started POST "/video_votes.437?type=up" for 127.0.0.1 at Fri Mar 18 01:11:14 -0700 2011
Processing by VideoVotesController#create as
Parameters: {"authenticity_token"=>"DLyDcc4MJxK7gk4URiyyjvsLLl9hjtDChAyQRGVawKg=", "type"=>"up"}
Completed in 83ms
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]):
app/controllers/video_votes_controller.rb:3:in `create'
I get this error when I click the button that calls on the create method.
Here's the code in my create method (note that line three is the #video = Video.find(params[:video_vote][:video_id]) line):
def create
#video = Video.find(params[:video_vote][:video_id])
#vote = #video.video_votes.new
if params[:type] = "up"
#vote.value = 1
else
#vote.value = -1
end
if #vote.save
respond_to do |format|
format.html { redirect_to #video }
format.js
end
else
respond_to do |format|
format.html { redirect_to #video }
format.js {render 'fail_create.js.erb'}
end
end
And here's my code for the button in my view that calls the create method:
<div id='voting_div'>
<%= button_to "+1", video_votes_path(video, :type=> "up"), :remote => true %>
<div id='vote_display'>
<p id='votes'><%= pluralize video.vote_sum, 'Votes' %></p>
</div>
<%= button_to "-1", video_votes_path(video, :type=> "down"), :remote => true %>
</div>
What's going on here? How do I fix this error?
You params hash is
{"authenticity_token"=>"DLyDcc4MJxK7gk4URiyyjvsLLl9hjtDChAyQRGVawKg=", "type"=>"up"}
In this hash there is no key called "video_vote" , so when you try to access params[:video_vote][:video_id], because params[:video_vote] is nil. it will throw this error.
Check your routes or if you need to "GET" more values with the button, because your params hash doesn't know about the "video_vote" key.