Rails application variable life cycle question - ruby-on-rails

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

Related

Rails: Strong parameters, Does it have to be present all the time?

As the title says. I know that strong parameters is to prevent other unauthorized attributes to be included when updating or creating new objects. I've seen codes that doesn't have strong parameters. For example in Hartl's tutorial relationships controller:
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
and others have it included such as creating new post or user etc. so my question is, when is the practice to use strong parameters?
Its ideal to use strong parameters when mass assigning values. Like creating new post or user. It may allows attackers to set any database column’s value.
Check out Rails official guide for it.
Doing like these is fine, as long as you know and mentioning model attributes.
#user = User.find(params[:followed_id])
#user = Relationship.find(params[:id]).followed
I would say - use strong params for any actions where you use mass-assignment. This means that, actions like create or update must employ strong params.
For example, instead of having:
#object.update_attributes(params[:object])
Just have a:
#object.update_attributes(object_params)
Which will whitelist params for you. Also, it allows you to pass-through different params for different actions, with methods like object_update_params and object_create_params which will whitelist params for update and params for create, respectively.
Yes, not using strong parameters will raise ActiveModel::ForbiddenAttributesError so it's not optional unless you manage to override this behavior.
In the above example he is just retrieving a record and then creating a relationship with that id in the model.
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end

Send a variable from one action to another in ruby on rails

Code in controller is
if params["type"] == "user"
respond_to do |format|
format.html { redirect_to home_user_path, notice: "abc" }
end
If I send variable with notice then work fine but I want to send with my own key like
format.html { redirect_to home_user_path, search: "abc" }
It doesn't recieve there
You must remember that you're not "sending" a variable to another action; you're invoking another action, and populating it with a variable (data):
1. Instance Variable
You can set an instance variable, which will then be available in the next action:
def your_action
#search = "abc"
respond_to do |format|
format.html { redirect_to home_user_path } #-> #search will be available in the other view
end
end
2. Sessions
You're currently trying to use sessions to populate the data:
def your_action
redirect_to home_user_path, search: "abc"
end
#app/views/controller/your_action.html.erb
<%= flash[:search] %>
3. URL
Finally, you can set the value in the URL through your routes:
def your_action
redirect_to home_user_path(search: "abc") #-> you'll probably need to set the user object too, judging from the route name
end
This should populate your route with some GET request params: url.com/action?param=value
The underpin of all of this, as mentioned, is that you're not sending a variable, you'll be initializing it in your current controller action, and then having the next action call it.
This is a problem with Ruby's optional braces: sometimes it's hard to see which method call something is being treated as an argument to.
Try this instead:
format.html { redirect_to home_user_path(search: "abc") }
this will redirect to home_user_path and set params[:search] to "abc".

Why can't I refer to my #quiz variable in 'update' action?

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

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

Instance variable vs local variable in the Create method

As far as I understand we use namely an instance variable in the method new
def new
#article = Article.new
end
because this variable is used in new.html.erb too. (Please correct me if I am wrong).
But why do we use an instance variable in the create method? Where else is it used outside the create method? Can't we just use a local variable article instead of the instance variable #article?
def create
article = Article.new(article_params)
if article.save
flash[:success] = "Article created successfully!"
redirect_to articles_url
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :body)
end
This means that you don't understand render :new.
This line means that the view for the new method will be rendered, but the method itself will NOT be invoked. That's why you need the variable to be instance variable, because if the validations fails, you will need it to pass it to form.
Take a look at the guides for more information
PS render :new is the same as render 'new'
You can't, because #article variable is also used in new.html.erb file, rendered when article isn't saved successfully.
So you'll get (as far as I remember) error undefined method 'model_name' for nil:NilClass.

Resources