How do I pass a variable from one action to another in a Rails controller? - ruby

There is a situation where I need to call my update action from my create action:
def create
#product = Product.find_or_initialize_by_sku(params[:sku])
if #product.new_record?
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render json: #product, status: :created, location: #product }
else
format.html { render action: "new" }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
else
update ##*** I am calling the update action at this point ***##
end
end
def update
#product = Product.find_or_initialize_by_sku(params[:sku])
respond_to do |format|
if #product.update_attributes(params[:product])
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
But I end up making two identical queries to my database because I am not sure of the most effective way to pass a variable (in this case, the instance variable #product) from one action to another.
I am trying to get a grasp on the 'rails' way to do this. What should I do here (as I know I should not be making two identical database queries)?
Would I store it in the session as if I was performing a redirect (Am I performing a redirect when I call an action from within another action like this?)?
session[:product] = #product # in my create action
# then
#product = session[:product] # in my update action
Does caching make this all a moot point?

Consider using memoization:
#product ||= Product.find_or_initialize_by_sku(params[:sku])
The ||= operator will check if #product is nil, and if it is, Ruby will execute the finder. Otherwise, Ruby will use the current value. Therefore, when you call update from create, the update method will use the #product that you found or initialized in create.
Regarding your second question, you're not performing a redirect when you call one controller action from another. Try not to over-think it - a controller is just another Ruby class with its own instance variables (e.g. #product) and methods. To perform a redirect, you would have to call redirect_to, which would result in a second web request to your controller.

Related

Rescuing an ActiveRecord::RecordNotUnique in Rails 3.2

I am new to ruby and I want to ensure that the combination of three columns in a model will be unique. I tried using validates_uniqueness_of with scopes but it didn't work as intended. So i added a unique index to my db which raises an ActiveRecord::RecordNotUnique exception every time an already existing combination of values is saved. The problem is that i am not sure on how to handle the exception. In my controller i altered the update action to:
def update
#patient_history = PatientHistory.find(params[:id])
#patient_history.save!
respond_to do |format|
format.html { redirect_to #patient_history, notice: 'Patient history was successfully updated.' }
format.json { head :no_content }
end
rescue ActiveRecord::RecordNotUnique
respond_to do |format|
format.html { render action: "edit" }
format.json { render json: #patient_history.errors, status: :unprocessable_entity }
end
end
end
This code does not saves the changes and redirects to the show page of my model (:patient_history) with the notice "Patient history was successfully updated". What i am trying to do is for the controller to redirect to the edit page and flash a message at the top of the page about the error (much like validates_uniqueness of would do).
Thanks in advance
How did you try using validates_uniqueness_of? You should use this to make sure that this combination is unique, like this:
validates_uniqueness_of :column1, :scope => [:column2, :column3]

Rspec does not seem to be targeting the correct controller

Getting really bizarre rspec behavior in one of my controller specs.
It's best to illustrate. In rubymine, when I set a breakpoint, this happens:
#rspec test
describe Api::V1::UsersController do
let(:user) { FactoryGirl.create(:user) }
describe "#show" do
it "responds successfully" do
get 'show', id: user.id
response.should be_success
end
end
#controller
class Api::V1::UsersController < AuthenticatedController
def show # !!! RubyMine breakpoint will stop execution here !!!
user = User.find(params[:id])
user_hash = User.information(user, current_user)
respond_to do |format|
format.json { render json: user_hash.to_json }
end
end
So the above works as expected.
But, now this test fails.
#rspec test
describe UsersController do
let(:user) { FactoryGirl.create(:user, is_admin: false) }
describe "#show" do
it "redirects non-admin" do
get 'index'
response.should redirect_to user_path(user)
end
end
#controller
class UsersController < AuthenticatedController
def index # !!! Breakpoint is never hit !!!
#users = User.all
respond_to do |format|
if current_user.is_admin
format.html
format.json { render json: #users }
else
redirect_to user_path(current_user) and return
end
end
end
By the way, this is the result:
Expected response to be a redirect to <http://test.host/users/625> but was a redirect to <https://test.host/users>
None of my breakpoints in controller methods in UsersController are hit. BUT all controller methods are hit if I set breakpoints in API::V1::UsersController.
Any guidance is greatly appreciated. I'm really at a loss of how to debug this.
Sorry, this question was more out of frustation than anything. But I finally figured out what was going on. Hint: tailing the test.log is a good idea.
I was forcing ssl on the controller. The request rspec sent is http. ActionController::ForceSSL redirects the request to https and to the same controller#action. However, at this point, the rspec test was finished and failed the test because it only sees the redirection back to the same controller#action.
So in a before(:each) or something similar, use this: request.env['HTTPS'] = 'on'. All tests work as expected now.
I think maybe you're stepping outside of the domain of rspec here with regards to redirect testing. May I suggest using capybara and rspec?
My sources:
Rspec - Rails - How to follow a redirect
http://robots.thoughtbot.com/post/33771089985/rspec-integration-tests-with-capybara

Why does respond_with JSON not work?

I'm having a problem when trying to use the return to in a rails controller. This is not working:
class UsersController < ApplicationController
respond_to :json
def create
#user = User.create params[:user_info]
respond_with #user
end
end
This works:
class UsersController < ApplicationController
respond_to :json
def create
#user = User.create params[:user_info]
respond_with #user do |format|
format.json { render json: #user.to_json }
end
end
end
Why? This is the error I have in the server's log when using the one that doesn't work:
NoMethodError (undefined method `user_url' for #<UsersController:0x007fd44d83ea90>):
app/controllers/users_controller.rb:7:in `create'
My route is:
resources :users, :only => [:create]
responds_with tries to redirect to user_url, so it looks for a show method in your user controller, which you don't have, since your route is limited to the create method only. Since the create method redirects to the show method by default this doesn't work. But in your second version you are actually rendering something, so no redirection happens.
You can give a :location option to respond_with if that's what you want, like so:
respond_with(#user, :location => home_url)
or use the render version as you do in your second version.

How do I create and get an active record using ajax in Rails 3?

In Rails 3.2 I'm trying to figure out how to use ajax to create an ActiveRecord model instance. Basically I want to take form fields, send them to the server with ajax, and get the model instance back. Surprisingly, I can't find a single example of this on stackoverflow or elsewhere.
Does anyone have an example of this?
Thanks for any help.
You need to add an action in your controller, responding for both formats (html and json):
# app/controllers/bananas_controller.rb
class BananasController < ApplicationController
def create
#banana = Banana.new(params[:banana])
respond_to do |format|
if #banana.save
format.html { redirect_to #banana, notice: 'Banana was successfully created.' }
format.json { render json: #banana, status: :created, location: #banana }
else
format.html { render action: "new" }
format.json { render json: #banana.errors, status: :unprocessable_entity }
end
end
end
end
You'll also need to add the route:
# config/routes.rb
resources :bananas, :only => [:create]
For a complete example, use the scaffold command (maybe on another application) to create the model, views and controller:
rails generate scaffold banana

Couldn't find <object> without an ID rails 3.0.1

Unfortunately, this is my second post in as many days. So the application worked fine with mysql and rails 3.0.3 but I found out that I needed to use MSSQL so I had to downgrade rails to 3.0.1.
In a nutshell, I copied the show.html.erb as show2.html.erb and created a new method which is a copy of the show method. Then I created a route match.
my controller
class fathersController < ApplicationController
def show
#father= Father.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #father}
end
end
def show2
#father= Father.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #father}
end
end
end
routes.rb
resources :fathers do
match '/show2' => 'fathers#show2'
resources :kids
end
when I call
http://127.0.0.1:3000/father/1
I get the show view but when I call
http://127.0.0.1:3000/father/1/show2
I get the following error
Couldn't find father without an ID
The request Parameters come back as
{"father_id"=>"1"}
so I know that the problem is that the app is passing the id as father_id but how do I fix it? Any help would be appreciated.
There are two problems.
You're trying to use a non-resourceful route on a route that actually should be resourceful.
It looks like you're trying to send /show2 to a controller named hospitals, when your action is actually specified on the fathers controller.
This should do the trick:
resources :fathers do
get :show2, :on => :member
resources :kids
end
You can also write the above as:
resources :fathers do
member do
get :show2
end
resources :kids
end

Resources