Why can't I refer to my #quiz variable in 'update' action? - ruby-on-rails

My QuizzesController#index action looks like this:
def index
#user = current_user
#quiz = Quiz.create(user_id: current_user.id)
end
My view draws the quiz form fine. It goes to the results/index view as intended. BUT the various attributes of the quiz are NOT updated on the Quiz instance which is pulled from the database, in the QuizzesContoller#update action:
def update
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
('update' is called in this case because the Quiz instance already exists, having been created in the 'index' action).
So, I tried changing the 'update' action to:
def update
#quiz.save
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
But this triggers the error:
undefined method 'save' for nil:NilClass
Why is that? Shouldn't my QuizzesController have access to the #quiz variable as set up in the 'index' action? Can anyone explain what the problem is there?

Others have answered this question, so I thought I would explain why the answer is what it is. In Ruby, variables that begin with the # symbol are instance variables. This means that they are created when a new instance of their parent object is instantiated and are unique to that instance of the object.
Rails based web apps, for the most part, are stateless, meaning that state is not persisted between http requests. In layman terms, the app treats each and every request independent of all other requests. Due to this, the controllers are instanced classes. Every request instantiates a new instance of the controller class.
EDIT:
More I look at your code, you aren't following proper conventions
class QuizzesController < ApplicationController
# GET index: for displaying a list of quizzes
def index
#quizzes = Quiz.where(user_id: current_user.id)
end
# GET show: for getting a single quiz record
def show
#quiz = Quiz.find(params[:id])
end
# GET new: for initializing a new quiz record
def new
#quiz = Quiz.new
end
# POST create: for saving a new quiz record
def create
#quiz = current_user.quizzes.create(quiz_params)
if #quiz.errors
render :new
else
redirect_to #quiz #or whereever
end
end
# GET edit: for initializing existing quiz for update
def edit
#quiz = Quiz.find(params[:id)
end
# PUT/PATCH update: for updating an existing quiz record
def update
#quiz = Quiz.find(params[:id])
if #quiz.update(quiz_params)
redirect_to #quiz # or whereever
else
render :edit
end
# DELETE destroy: for deleting a quiz record
def destroy
Quiz.find(params[:id]).destroy
redirect_to :index # or whereever
end
end

You have not #quiz variable in your update action. Actions in the controller does not have access to variables in other actions.

The QuizzesController instance is not persisted between requests. The real reason instance variables are used in controllers is to pass that variable to the view.
A normal update action would look something like:
def update
#quiz = current_user.quiz # I'm assuming a user has one quiz?
#quiz.update(quiz_params) # Where quiz params takes the posted parameters from your update form
if #quiz.errors.any?
render :edit
else
redirect_to results_path
end
The key is you need to reassign #quiz with each request

Related

is there any difference in using instance variables in a Rails controller other than visibility in the view

I tend to mix local variables and instance variables in Rails controllers when I don't need to use them in a view. Obviously, if I'm using in the view, I use instance variables. Is there any difference between using them in that scenario (other than class-level visibiity)?
For example:
def destroy
#micropost.find(params[:id])
#micropost.destroy
redirect_to root_url
end
or
def destroy
micropost.find(params[:id])
micropost.destroy
redirect_to root_url
end
an example of using instance variables for class level visibility would be here: https://github.com/mhartl/sample_app/blob/master/app/controllers/microposts_controller.rb ?
I think these lines of code is what your question about. Of course you don't have to instantiate that variable with # since you are not actually going to show it on your view(since it is being destroyed). The purpose of these lines of code if to first check wether #micropost exists, if it does not then redirect_to root_path else it will go to destroy method destroy the micropost and then redirect_to root_path.
Now, to answer your question, yes, there is a huge difference between #micropost and micropost. #micropost will be accessible in other methods of your controller while micropost will not(since its scope will be limited to the method you instantiate it in).
However, if you're concern about not having a # variable then you can change the code shown here to this:
class MicropostsController < ApplicationController
before_filter :signed_in_user
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
if micropost = current_user.microposts.find_by_id(params[:id])
micropost.destroy
end
redirect_to root_path
end
end
Beside, scope difference, you can access instance variable in another method of same controller, as
def method1
#var = "some value"
puts #var
end
def method2
puts #var
end
now, depending on the sequence u call these method, #var can have different values

Don't understan how to implement simple_form create action

Note: I'm using Rails 3.2
I'm trying to implement a simple_form by following this sample code: https://github.com/rafaelfranca/simple_form-bootstrap/blob/master/app/controllers/articles_controller.rb. I have a Summary model instead of an Article model.
I understand most of what's going on, except for the two private methods. I tried basically copying the code except for the two private methods, and what it does is it creates a new Summary, but the attributes are all nil.
1) How are attributes saved? What's the difference between .new and .save?
2) What is available in the create action? When you reach the create action, you've just filled out a form, so something must be available, but I don't know what it is, or how it becomes available.
3) What's going on with the before_action and the two private methods?
Summaries Controller
class SummariesController < ApplicationController
before_filter :set_summary, only: [:show, :edit, :update, :destroy]
def index
#summaries = Summary.all
end
def show
end
def new
#summary = Summary.new
end
def edit
end
def create
#summary = Summary.new(params[:summary])
if #summary.save
redirect_to #summary, notice: 'Summary created.'
else
render :new
end
end
def update
if #summary.update(params[:summary])
redirect_to #summary, notice: 'Summary updated.'
else
render :edit
end
end
def destroy
#summary.destroy
redirect_to summaries_url, notice: 'Summary destroyed'
end
private
# sets #summary to make available for show, edit, update
# and destroy actions so code isn't repetitive
def set_summary
#summary = Summary.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
# def summary_params
# params[:summary].permit!
#end
end
To answer your questions:
1) new is a Ruby method for initializing a new instance of an object. So when you call Article.new, you get a new instance of the Article class.
save is a Rails method for saving a record to the database. If your object instance is instantiated with new, it calls create under the hood. If you loaded the object through a finder (find, all, where, etc.), then it will call update under the hood instead of create.
2) A hash named params is available in all actions (and in the view). In the case of the create action, params[:article], which is the data posted by the form. At the top of create, try calling raise params[:article].to_yaml to see what's going on in there.
Heck, even try raise params.to_yaml to see what's in there.
3) The call to before_action runs the set_article private method before the show, edit, update, and destroy actions. Without that, you'd need to manually call #article = Article.find(params[:id]) in every single one of those actions. So this eliminates quite a bit of repetition!
You'll notice that the first line of the create action calls the article_params method. This is a common way of implementing what are called strong parameters in Rails. Strong parameters are new in Rails 4 but can be added to earlier versions of Rails via the strong_parameters gem.

Rails render issue

Ok, another annoying problem.
I have a GuestsController that with an index action like this:
def index
#booking = Booking.find(session[:booking_id]) #i have also hard coded values to make sure the session isn't the issue
#guest = Guest.find(session[:guest_id])
end
and a personal action (to perform updates) as follows:
def personal
#guest = Guest.find(session[:guest_id])
if #guest.update(post_params)
redirect_to :controller => 'guests', :action => 'cards'
else
render 'index'
end
end
My index.html.erb view uses the #booking variable:
<%= #booking.friendly_id %> #this is one example
and also contains the form to submit the "name" field to the personal action. It updates fine if the data is valid but the #booking variable doesn't exist if it's invalid???
I need to show validation errors so I can't just use redirect_to.
The error I get is: NoMethodError in Guests#personal and undefined method `friendly_id' for nil:NilClass
Any ideas?
Just initialize the object in else part
else
#booking = Booking.find(session[:booking_id])
render 'index'
end
How about moving #booking and #guest definitions to before_filter?
before_filter do
#booking = Booking.find(session[:booking_id]) #i have also hard coded values to make sure the session isn't the issue
#guest = Guest.find(session[:guest_id])
end
There needs to be something to handle when #booking is nil - which can happen if I'm reading this right.
def index
if Booking.find(session[:booking_id])?
#booking = Booking.find(session[:booking_id])
else
#booking = Booking.build #or whatever you want here
end
#guest = Guest.find(session[:guest_id])
end

Build an object after user registers in rails

So i'm having this issue trying to figure out how to use the build method in rails to create an object once a user completely registers and still have that object connected to the users id. I'm using devise for authentication and the model that needs to be created is called "app".
This is the create method for "app".
def create
#app = App.new(app_params)
#app.id = current_user.id
respond_to do |format|
if #app.save
format.html { redirect_to #app, notice: 'Application successfully created.'}
else
format.html { render action: 'new' }
end
end
end
Im getting this error:
Couldn't find App with id=1
from my multi step form controller:
def show
#user = User.find(current_user)
case step
when :school, :grades, :extra_activity, :paragraph, :submit
#app = App.find(current_user)
end
render_wizard
end
You need an after_create callback in the User model. It makes no sense to mess with the AppController because no forms have been filled up for the app and you have no app_params.
class User < ActiveRecord::Base
after_create :build_initial_app
protected
def build_initial_app
self.create_app
end
end
You can read more about this at the Rails Guides page for ActiveRecord Callbacks.
The problem line in your code is here:
#app.id = current_user.id
Setting an ActiveRecord object's id is a no-no. Think of the id attribute like you would a pointer in C. The system creates it for you, and you can use it to refer to a unique model object.
What you probably want is something along the lines of:
#app.user_id = current_user.id
Or, even better:
#app.user = current_user
To do that, you need to set up an association between your App model and your User model. There's a good tutorial on that here.

Rails application variable life cycle question

Assume that I have a global variable user in application....like this:
# GET /users.xml
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users }
end
end
Is that mean every request, a new #user is created? If every request , an object is created, when will it destroyed? Also, if vistorA go to the web site, a #userA is created, and vistorB go to the web site #userB is created. Will the vistorA have a chance to get the vistorB 's object (#userB)? Also, when will the object release? Thank you.
****Update: the #users is not the global variable, it is a instance variable. So, a question to follow up. How does the server know which #user is belong to which request? Thank you.
#users is not a global variable it is an instance variable. A new instance of your controller is created to handle each request so the #users for visitor A and visitor B are independent.
1] #users is not a Global Variable it is a instance variable.its scopre remain upto that method only.
def index
#some_variable= "Hello"
other_method
redirect_to :action=>'redirect_method'
end
def other_method
#here you get #some_variable ="Hello" as you called this method in index where variable is initialise
end
def redirect_method
#here you get #some_variable as you not called this method in index but redirected to this method
end
2] for every user #users will be different as each request handle by server independently

Resources