I'm having an issue understanding how to setup the routes in rails to pass an object into the URL helper.
I added a new method add_item
def add_item
#item = Item.create(item_params)
puts #hospital.inspect
puts #item.inspect
#hospital.items << #item
respond_to do |format|
if #hospital.save
format.html { redirect_to hospital_url(#hospital), notice: "Item was added to hospital" }
format.json { render :show, status: :ok, location: #hospital }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #hospital.errors, status: :unprocessable_entity }
end
end
end
I added the corresponding route
resources :hospitals do
member do
post :add_item
end
end
but when I run my test that uses
post add_item_hospital_url(#hospital), params: { item: { item_code: item.item_code, description: item.description, gross_charge: item.gross_charge } }
the #hospital in the test is not nil but on the controller, it is. What am I doing wrong?
My routes seem to be okay.
add_item_hospital POST /hospitals/:id/add_item(.:format)
The instance variable #hospital is nil in the controller because it has not been set. Maybe you could add a before_action to set the value of the instance variable or just add it to the add_item method.
When passing models into the URL helper the id of that object can be found within the params hash
class HospitalsController < ApplicationController
before_action :set_hospital, only: :add_item
def add_item
# ... your code here
end
private
def set_hospital
#hospital = Hospital.find(params[:id])
rescue ActiveRecord::RecordNotFound
# handle exception
end
end
Related
I am trying to pass an attribute to an object that is being created by a link. I am on the show view of another object and I want to have two links available one that will make the :attribute false and the other to make the :attribute true. I have it set up so the default value of the this attribute is false and I tried using something like below, but it just saves it as nil in the database:
<%= link_to "Yes", new_building_listing_appointment_rented_unit_path(#building, #listing, #appointment, #rented_unit, leased: true) %>>
controller
class RentedUnitsController < ApplicationController
before_action :building
before_action :listing
before_action :appointment
before_action :set_rented_unit, only: [:show, :edit, :update, :destroy]
# GET /rented_units
# GET /rented_units.json
def index
#rented_units = appointment.rented_units
end
# GET /rented_units/1
# GET /rented_units/1.json
def show
end
# GET /rented_units/new
def new
#rented_unit = appointment.rented_units.new
end
# GET /rented_units/1/edit
def edit
end
# POST /rented_units
# POST /rented_units.json
def create
#rented_unit = appointment.rented_units.new(rented_unit_params)
respond_to do |format|
if #rented_unit.save
format.html { redirect_to [building, listing, appointment, #rented_unit], notice: 'Rented unit was successfully created.' }
format.json { render :show, status: :created, location: #rented_unit }
else
format.html { render :new }
format.json { render json: #rented_unit.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /rented_units/1
# PATCH/PUT /rented_units/1.json
def update
respond_to do |format|
if #rented_unit.update(rented_unit_params)
format.html { redirect_to [building, listing, appointment, #rented_unit], notice: 'Rented unit was successfully updated.' }
format.json { render :show, status: :ok, location: #rented_unit }
else
format.html { render :edit }
format.json { render json: #rented_unit.errors, status: :unprocessable_entity }
end
end
end
# DELETE /rented_units/1
# DELETE /rented_units/1.json
def destroy
#rented_unit.destroy
respond_to do |format|
format.html { redirect_to building_listing_appointment_rented_units_path(#building, #listing, #appointment), notice: 'Rented unit was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_rented_unit
#rented_unit = appointment.rented_units.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def rented_unit_params
params.require(:rented_unit).permit(:unit_no, :unit_model, :price, :bedrooms, :bathrooms, :half_baths, :square_footage, :leased, :appointment_id)
end
def building
#building ||= Building.find(params[:building_id])
end
def listing
#listing ||= Listing.find(params[:listing_id])
end
def appointment
#appointment ||= Appointment.find(params[:appointment_id])
end
end
From what I understand you are looking to populate leased attribute auto when you open a new from from the link.
You need to give the param param to the link.
<%= link_to "Yes", new_building_listing_appointment_rented_unit_path(#building, #listing, #appointment, #rented_unit, rented_unit: { leased: true } ) %>>
In the controller then you can do some thing like
# GET /rented_units/new
def new
#rented_unit = appointment.rented_units.new(rented_unit_params)
end
Then, in the new form you will see the checkbox (or other control) selected.
I have added a module to my application called Tokened.rb and added the include Tokened to my model. However, now when I try to load that model, I get a "NameError in TestingsController#index" error... I haven't included Tokened in my TestingsController, but not sure why I should or where I should put it.
My code:
testing.rb
class Testing < ActiveRecord::Base
include Tokened
end
My Tokened.rb module:
module Tokened
extend ActiveSupport::Concern
included do
after_initialize do
self.token = generate_token if self.token.blank?
end
end
private
def generate_token
loop do
key = SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
break key unless self.class.find_by(token: key)
end
end
end
Finally, my testing controller:
class TestingsController < ApplicationController
before_action :set_testing, only: [:show, :edit, :update, :destroy]
# GET /testings
# GET /testings.json
def index
#testings = Testing.all
end
# GET /testings/1
# GET /testings/1.json
def show
end
# GET /testings/new
def new
#testing = Testing.new
end
# GET /testings/1/edit
def edit
end
# POST /testings
# POST /testings.json
def create
#testing = Testing.new(testing_params)
respond_to do |format|
if #testing.save
format.html { redirect_to #testing, notice: 'Testing was successfully created.' }
format.json { render :show, status: :created, location: #testing }
else
format.html { render :new }
format.json { render json: #testing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /testings/1
# PATCH/PUT /testings/1.json
def update
respond_to do |format|
if #testing.update(testing_params)
format.html { redirect_to #testing, notice: 'Testing was successfully updated.' }
format.json { render :show, status: :ok, location: #testing }
else
format.html { render :edit }
format.json { render json: #testing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /testings/1
# DELETE /testings/1.json
def destroy
#testing.destroy
respond_to do |format|
format.html { redirect_to testings_url, notice: 'Testing was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_testing
#testing = Testing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def testing_params
params.require(:testing).permit(:name, :address, :signature)
end
end
What gives? I am not sure what is going on here and why it needs to be included in the controller.
First of all it should be lowercased: tokened.rb. But where is your file? It should be here modules/concerns/tokened.rb.
I am following Agile Web Development with Rails 4. Chapter Cart 9 Cart Creation. When I want to update a cart, I get the following Error Notification: When assigning attributes, you must pass a hash as an arguments. CartController#update.
class CartsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:show, :edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart
def index
#carts = Cart.all
end
def show
end
def new
#cart = Cart.new
end
def edit
end
def create
#cart = Cart.new(cart_params)
respond_to do |format|
if #cart.save
format.html { redirect_to #cart, notice: 'Cart was successfully created.' }
format.json { render :show, status: :created, location: #cart }
else
format.html { render :new }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
def update
#cart = Cart.find(params[:id])
respond_to do |format|
if #cart.update_attributes(params[:cart])
format.html { redirect_to #cart, notice: 'Cart was successfully updated.' }
format.json { render :show, status: :ok, location: #cart }
else
format.html { render :edit }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
def destroy
#cart.destroy if #cart.id == session[:card_id]
session[:card_id] = nil
respond_to do |format|
format.html { redirect_to store_url, notice: 'Your cart is currently empty.' }
format.json { head :no_content }
end
end
private
def set_cart
#cart = Cart.find(params[:id])
end
def cart_params
params[:cart]
end
def invalid_cart
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to store_url, notice: 'Invalid cart'
end
end
Your params is probably an instance of ActionController::Parameters
If so, you need to permit the attributes you want to use, like so:
def cart_params
params.require(:cart).permit(:attribute1, :attribute2, :attribute3)
end
Try this:
In your update method replace
if #cart.update_attributes(params[:cart])
by
if #cart.update_attributes(cart_params)
In your cart_params private method do this:
def cart_params
params.require(:cart).permit(:attribute1, :attribute2, :attribute3)
end
With Rails 4, the concept of strong parameters has been introduced which basically forbids mass assignment of attributes in the controller. This means that the mass assingment protection that was once in model (attr_accessible) has now been moved to the controller. Hence in your models you no more need to use this:
attr_accessible :attribute1, attribute 2 #attributes that can be mass-assinged
attr_protected :attribute3 #attribute that is protected and cannot be mass-assinged
Instead you can now do this in your controller through :
params.require(:cart).permit(:attribute1, :attribute2, :attribute3)
This means that only attribute1, attribute2. attribute3 of cart are accessible while other are protected attributes
I want show a daycare details on show page but I got this error
NoMethodError : undefined method `find' for nil:NilClass
from daycare controller file and i'm not get any idea. I have mentioned below that error line.
This is my Controller file
class DayCaresController < ApplicationController
before_filter :authenticate_user!
before_action :set_day_care, only: [:show, :edit, :update, :destroy]
# GET /day_cares
# GET /day_cares.json
def index
#day_cares = DayCare.all
end
# GET /day_cares/1
# GET /day_cares/1.json
def show
end
# GET /day_cares/new
def new
#day_care = DayCare.new
end
# GET /day_cares/1/edit
def edit
end
# POST /day_cares
# POST /day_cares.json
def create
#day_care = current_user.build_day_care(day_care_params)
respond_to do |format|
if #day_care.save
UserMailer.welcome_email(#user).deliver
format.html { redirect_to #day_care, :gflash => { :success => 'Day care was successfully created.'} }
format.json { render :show, status: :created, location: #day_care }
else
format.html { render :new }
format.json { render json: #day_care.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /day_cares/1
# PATCH/PUT /day_cares/1.json
def update
respond_to do |format|
if #day_care.update(day_care_params)
format.html { redirect_to #day_care, :gflash => { :success => 'Day care was successfully updated.'} }
format.json { render :show, status: :ok, location: #day_care }
else
format.html { render :edit }
format.json { render json: #day_care.errors, status: :unprocessable_entity }
end
end
end
# DELETE /day_cares/1
# DELETE /day_cares/1.json
def destroy
#day_care.destroy
respond_to do |format|
format.html { redirect_to day_cares_url, :gflash => { :success => 'Day care was successfully destroyed.'} }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions
def set_day_care
#day_care = current_user.day_care.find(params[:id]) # => **I got error this line**
end
# Never trust parameters from the scary internet, only allow the white list through.
def day_care_params
params.require(:day_care).permit(:name, :address, :office_phone, :cell_phone, :logo, :website, :user_id)
end
def dashboard
end
def profile
end
end
If user has_many: day_cares then use this name instead of day_care:
#day_care = current_user.day_cares.where(id: params[:id]).take
or probably as you wrote:
#day_care = current_user.day_cares.find(params[:id])
But with arrays instead of single instance (day_cares).
Also you can use just:
#day_care = DayCare.find(params[:id])
If you search by id. Or if you need to check that it's users day_care:
#day_care = DayCare.where(id: params[:id], user: current_user).take
current_user.day_care.find is not available, because you can only perform queries on plural associations. So given that the model associations are setup correctly as:
class User < ActiveRecord:Base
has_many :day_cares
...
end
the solution is probably just to resolve the spelling error from
`current_user.day_care.find` #wrong!
to
`current_user.day_cares.find` #right!
I created 2 scaffolds 'mepager' and 'pimp' and linked the models like this:
class Mepager < ActiveRecord::Base
belongs_to :pimp
end
class Pimp < ActiveRecord::Base
has_one :mepager
end
I added following lines to my routing:
resources :pimps do
resources :mepagers
end
And my mepager create action looks like this:
def create
#mepager = #pimp.build_mepager(mepager_params)
respond_to do |format|
if #mepager.save
format.html { redirect_to #mepager, notice: 'Mepager was successfully created.' }
format.json { render action: 'show', status: :created, location: #mepager }
else
format.html { render action: 'new' }
format.json { render json: #mepager.errors, status: :unprocessable_entity }
end
end
end
And for #pimp to set
def setPimp
#pimp = Pimp.find_by_id(:pimp_id)
end
But if I try to create a new mepager at pimps/1/mepagers/new I get an undefined method build_mepager error.
undefined method `build_mepager' for nil:NilClass
I tried the same in the rails console and it worked with just that method. So I guess somethings still missing to make it work on my web application.
Btw I m using rails 4.0.0
Regards!
Routing
Prefix Verb URI Pattern Controller#Action
pimps GET /pimps(.:format) pimps#index
POST /pimps(.:format) pimps#create
new_pimp GET /pimps/new(.:format) pimps#new
edit_pimp GET /pimps/:id/edit(.:format) pimps#edit
pimp GET /pimps/:id(.:format) pimps#show
PATCH /pimps/:id(.:format) pimps#update
PUT /pimps/:id(.:format) pimps#update
DELETE /pimps/:id(.:format) pimps#destroy
root GET / pimps#index
pimp_mepagers GET /pimps/:pimp_id/mepagers(.:format) mepagers#index
POST /pimps/:pimp_id/mepagers(.:format) mepagers#create
new_pimp_mepager GET /pimps/:pimp_id/mepagers/new(.:format) mepagers#new
edit_pimp_mepager GET /pimps/:pimp_id/mepagers/:id/edit(.:format) mepagers#edit
pimp_mepager GET /pimps/:pimp_id/mepagers/:id(.:format) mepagers#show
PATCH /pimps/:pimp_id/mepagers/:id(.:format) mepagers#update
PUT /pimps/:pimp_id/mepagers/:id(.:format) mepagers#update
DELETE /pimps/:pimp_id/mepagers/:id(.:format) mepagers#destroy
GET /pimps(.:format) pimps#index
POST /pimps(.:format) pimps#create
GET /pimps/new(.:format) pimps#new
GET /pimps/:id/edit(.:format) pimps#edit
GET /pimps/:id(.:format) pimps#show
PATCH /pimps/:id(.:format) pimps#update
PUT /pimps/:id(.:format) pimps#update
DELETE /pimps/:id(.:format) pimps#destroy
Altered line in pimp controller
def create
#pimp = Pimp.new(pimp_params)
respond_to do |format|
if #pimp.save
format.html { redirect_to new_pimp_mepager_path, notice: 'Product Improvement was successfully created.' }
format.json { render action: 'show', status: :created, location: #pimp }
else
format.html { render action: 'new' }
format.json { render json: #pimp.errors, status: :unprocessable_entity }
end
end
end
mepager controller
class MepagersController < ApplicationController
before_action :set_mepager, only: [:show, :edit, :update, :destroy]
def setPimp
#pimp = Pimp.find(params[:pimp_id])
end
# GET /mepagers
# GET /mepagers.json
def index
#mepagers = Mepager.all
end
# GET /mepagers/1
# GET /mepagers/1.json
def show
end
# GET /mepagers/new
def new
#mepager = Mepager.new
end
# GET /mepagers/1/edit
def edit
end
# POST /mepagers
# POST /mepagers.json
def create
raise params[:pimp_id].inspect
#mepager = #pimp.build_mepager(mepager_params)
respond_to do |format|
if #mepager.save
format.html { redirect_to #mepager, notice: 'Mepager was successfully created.' }
format.json { render action: 'show', status: :created, location: #mepager }
else
format.html { render action: 'new' }
format.json { render json: #mepager.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /mepagers/1
# PATCH/PUT /mepagers/1.json
def update
respond_to do |format|
if #mepager.update(mepager_params)
format.html { redirect_to #mepager, notice: 'Mepager was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #mepager.errors, status: :unprocessable_entity }
end
end
end
# DELETE /mepagers/1
# DELETE /mepagers/1.json
def destroy
#mepager.destroy
respond_to do |format|
format.html { redirect_to mepagers_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_mepager
#mepager = Mepager.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def mepager_params
params.require(:mepager).permit(:pre, :post, :comment, :save_h, :save_c, :save_other, :save_otherv, :affect_design, :affect_stress, :affect_me, :affect_other, :affect_dno, :affect_mid, :affect_otherdoc, :owner, :pps, :reference)
end
end
The problem is probably here:
#pimp = Pimp.find_by_id(:pimp_id)
what you are doing is finding a pimp that has an id of the literal symbol :pimp_id (which is highly unlikely to exist)
Probably what you want is the parameter: params[:pimp_id]
EDIT:
and for the second part.
for the create action to work with a real #pimp (that can be found by :pimp_id), you must also do the same on the new action too!
Otherwise create won't work.
Can I recommend something like...
before_action :fetch_pimp, only: [:new, :create]
# GET /mepagers/new
def new
#mepager = Mepager.new
end
# POST /mepagers
# POST /mepagers.json
def create
#mepager = #pimp.build_mepager(mepager_params)
respond_to do |format|
# as previously...
end
end
# ...
private
def fetch_pimp
#pimp = Pimp.find(params[:pimp_id])
end