rails - making a reusable User.find - ruby-on-rails

So I have many places in my program where I use the: #user = User.find(params[:id])
Now, I wanted to make a new method in ApplicationController so that all my controllers would be able to use the method so that I would not have to repeat my self so much.
def find_user(params[:id])
#user = User.find(params[:id])
end
So now when I want to display users in some controller, I just type the find_user(params[:id]) in an action. But this doesn't seem to work for some reason.

First I'd like to say that although it's a common call, I would not make a method for it, because it is already essentially just calling one method, but here:
You're making it a little too complicated:
In the application controller
def find_user(user_id)
#user = User.find(user_id)
end
In the controller you're using it in
find_user(params[:id])
Alternatively if for some reason you don't want to write params everytime
In the application controller
def find_user(paramsicle) # params might be reserved
#user = User.find(paramsicle[:id])
end
In the controller you're using it in
find_user(params)
EDIT: It'd probably be helpful if I explained why yours doesn't work...
Yours is fine, except that method arguments (the stuff in the parenthesis) should just be a identifier except in special cases (optional arguments, the *args things). Read more about it here. The problem wasn't with params, but with trying to access id while still in the arguments section. That's why it was find to call paramsicle[:id] afterwards.

Related

Explanation of User.new in Ruby on Rails? (From Michael Hartl's tutorial)

I have searched everywhere to try to find an explanation of how this works/what its purpose is, but I cant find anything helpful.
I am doing Michael Hartl's tutorial, and my question is mainly about the two actions: 'new' and 'create'.
The new action has the following:
def new
#user = User.new
end
In the view corresponding to the 'new' action, there is a form_for helper, where users can type in their attributes and hit submit. As expected, the beginning of the form_for helper looks like this:
form_for(#user)
However here is where I am stumped... In the create action, there is the following code:
def create
#user = User.new(user_params)
#user_params is a function we defined which simply returns the permitted params from the user.
What is the purpose of #user = User.new in the 'new' action? What does User.new even accomplish? I am assuming that the instance variable #user is necessary to pass to the form, but in that case, why do we have to redeclare an #user isntance variable in 'create'? Isn't it sufficient to have only #user = User.new(user_params) in our 'create' action? Is the User.new somehow necessary to make the form function properly?
I am mainly just trying to figure out what #user = User.new accomplishes in our 'new' action and its corresponding 'new' view (with the form), and why it is necessary when we have a 'create' action which actually CREATES the object. ANY help is SO GREATLY APPRECIATED. Thank you all for always doing your best to explain. Thank you ahead of time to anyone who answers this.
The new and create are different actions. New is called when you get the new route. Create is called when you post to the new route. So, you have to create the user in new so they're available in the form. You have to create the user with the form contents in create so you can save it to the database.
You can't assume that the request to new will go to the same rails instance as the request to create. It's common to run multiple instances of your app behind a proxy.
It's called object orientated programming
HTTP
In Ruby, each variable you define is an object. These objects are then manipulated throughout each instance of the app.
In traditional (stateful) applications, your computer is able to store a number of objects in memory, and since your application is always in state, you'll be able to manipulate them from a single invocation.
In HTTP (stateless) applications, you have to rebuild the objects with each call. Because your application doesn't retain state (memory) between each request, you have to build the objects again.
This is why Rails "variables" are called with a class function on the model (class): User.find ...
--
Thus, when using the following:
#app/controllers/your_controller.rb
class YourController < ApplicationController
def new
#user = User.new #-> invokes a new user object
end
def create
#user = User.new user_params #-> invokes a new user object & populates with your params
#user.save #-> "saves" the new record
end
def show
#user = User.find params[:id] #-> stateless means you have to rebuild the object again
end
end
... what you're doing is rebuilding the object each time your actions are invoked.
This is one of the pitfalls of using HTTP - your server is "dumb" and cannot retain state between requests. Although Rails does a great job at making it a seamless process, it can be difficult if you haven't got your head around it yet.
Most generally, users enter data and we programmer-types traditionally store it in a relational database.
This creates an "impedance" between a relational model (i.e., tables and rows) and an object-oriented one (roughly, classes and instances).
ORMs like ActiveRecord help abstract much of this tedium, and in this way model instances--such as those we're creating in controller actions--serve as helpful containers for data.
This lets us easily represent models in views when gathering user input, and bind inputs to model attributes when persisting it (basic CRUD).
The separate controller actions merely represent these two steps in the process, as any Web-based app ultimately speaks HTTP.
This is really the whole benefit and genesis of Rails and similar MVC frameworks, born in a time of relational databases and server-side rendering. (Though they are increasingly coping with and adapting to an environment that now includes document/object-oriented databases and client-scripted front-ends.)

Preferred way to use sessions to avoid hitting the database in rails

I try to optimise a Rails app with big load that currently hit the databsae on every request. I try now to optimise this by saving some info on the session so I don't need to go the database every time. I'm currently doing something like this.
def set_audience
if current_user
session[:audience] ||= current_user.audience
else
session[:audience] ||= 'general'
end
end
And then calling session[:audience] anywhere on my controller and views. Seems fine except that I'm seeing on the New Relic logs that sometimes the session is not set and therefore the app get a nasty nil.
Im thinking better I should use instance variables, maybe more something like this.
def set_audience
if current_user
session[:audience] ||= current_user.audience
end
#audience = session[:audience]
#audience = 'general' if #audience.empty?
end
And then calling #audience in my app.
Is this correct? I would like to make sure I'm used the preferred approach to this.
I think the standard approach here would be to use a helper method on ApplicationContoller:
class ApplicationController < ActionController::Base
private
def current_audience
#current_audience ||= current_user.audience
end
helper_method :current_audience
end
This will work pretty much exactly like the current_user helper method in your controllers and views. Depending on the specifics of your application, you may want to add some more robust nil handling, but this is the basic idea.

Understand "current_user" concept when creating a login session in ruby

I am going through the great Michael Hartl tutorial to build ruby app here.
I am trying to understand the concept of how to create a session and I am stuck in understanding this line:
self.current_user = user
in this method:
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
end
I understand the whole concept of creating a cookie with the user_token.
But I don't understand what does self.current_user = user means and why is it even necessary to keep this line of code - I have the cookie with the token - why do I need to know the current user?
Also, where does this "self" is being stored - it is not like a flash[:success] parameter I can see in one of my views. so I don't understand where it is.
there are also these 2 methods in the same module:
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
And still I am trying to connect the dots of the purpose for this mysterious current user - is its purpose is to create #current_user global variable to use in the views?
If so - why there are there these 2 duplicated functions def current_user=(user) and def current_user
A few things.
First, you're reading the method names wrong (which is not surprising given how cryptic ruby method naming can be). def current_user=(user) is actually read as defining the method current_user= that takes an argument user, whereas def current_user defines a method current_user that takes no arguments. These are referred to respectively as setters and getters.
Here's a reference: Ruby (programming language): What are setters and getters in Ruby?
So that explains the duplication. On to your next question.
I don't understand what does self.current_user = user means
self is a topic unto itself, worthy of its own discussion, so I won't even try to explain it (here's one reference out of many). For the purposes of this question it's just important to remember that in order to set instance variables, you need to prefix your assignment with self, even within the class (where for other purposes it would be implicit). The rest of the line is a call to the current_user= setter method I mentioned above, with the argument user.
why is it even necessary to keep this line of code - I have the cookie with the token - why do I need to know the current user?
The reason it's necessary is that you don't want to be looking up the user from the token every time you need to get the current user. Take a look at the getter method:
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
What this says is: if I haven't looked up and set the instance variable #current_user yet, then look it up; if I have already set it, then just return it. That saves a lot of looking up.
I think that answers your questions. There are a lot of deeper issues (self, etc.) which you can find more information about elsewhere. Here's one discussion of why you need to include self in setters on SO: Why do Ruby setters need "self." qualification within the class?
UPDATE: Small clarification, that last link about using self for setters within the class is actually a bit off-topic, since you're calling it within a module and not directly from a class. In the context of a module, the self in self.current_user = user will become the class that the module is included inside of, e.g. User.current_user if it was called within the class User, etc. Again, another topic of discussion unto itself...
The method def current_user=(user) is basically a setter that the sign_in method uses in order to set the current_user.
def current_user will return the #current_user or if it is not set it will find it in the Users table by the remember_token. This basically allows you get the current_user at any point in time.
self.current_user in the context of the sign_in method will refer to the calling class or module in this case. It will be calling current_user from the Session Helper module.

Where do I put the Current user query so as to not repeat per controller?

I have a standard query that gets the current user object:
#user = User.find_by_email(session[:email])
but I'm putting it as the first line in every single controller action which is obviously not the best way to do this. What is the best way to refactor this?
Do I put this as a method in the Application controller (and if so, can you just show me a quick example)?
Do I put the entire #user object into the session (has about 50 columns and some sensitive ones like is_admin)?
Or is there another way to remove this kind of redundancy?
I suggest making it into a helper placed in the ApplicationHelper module
def current_user
return nil if #user === false
#This ensures that the find method is only called once
#user = #user || User.find_by_email(session[:email]) || false
end
I prefer the above usage instead of the standard #user ||= User.find... because it prevents repetitive queries if the user record isn't found the first time. You could also just bang the find method: find_by_email! to make it throw an exception when the user can't be found
You could specify a before_filter, which is automatically called at the beginning of every controller action. Read up on it to see how to use it.

What's the correct way to run one controller action from another controller action without an HTTP redirect?

I'd like to be able to dispatch from one controller action to another conditionally, based on a combination of query parameters and data in the database.
What I have right now is something like:
class OldController < ApplicationController
def old_controller_action
if should_use_new_controller
new_params = params.dup
new_params[:controller] = "new_controller_action"
redirect_to new_params
return
end
# rest of old and busted
end
end
class NewController < ApplicationController
def new_controller_action
# new hotness
end
end
This works just fine, but it issues an HTTP redirect, which is slow. I'd like to be able to do this same thing, but within the same HTTP request.
Is there a clean way to do this?
Edit: The bounty will go to someone who can show me a clean way to do this that leaves the controllers and their actions relatively untouched (other than the redirect code itself).
Instead of calling code across actions, extract the code to lib/ or something, and call that code from both controllers.
# lib/foo.rb
module Foo
def self.bar
# ...
end
end
# posts_controller
def index
Foo.bar
end
# things_controller
def index
Foo.bar
end
Create an instance of the controller class:
#my_other_controller = MyOtherController.new
Then call methods on it:
#my_other_controller.some_method(params[:id])
I prefer the module idea, but this should do the trick.
You can also pass parameters as a whole from another controller:
#my_other_controller.params = params
I suspect you want option 3, but lets go through the some alternatives first
Option 1 - Push the controller selection logic into a helper that inserts the right link into your view. Benifits - controllers remain clean, Cons - if decision logic depending on submitted values this approach won't work. If URL is being called by external websites then this won't work.
Option 2 - Push the logic back into your model. Pro's - keeps controller clean. Cons - doesn't work well if you've got lots of sesson, params or render / redirect_to interaction.
Option 3 - Stay within the same controller. I suspect you are trying to replace some existing functionality with some new functionality, but only in some cases. Pro's - Simple and have access to everything you need. Cons - only works if it makes sense to use the same controller i.e. you're working with the same entity such as user, place or company.
Lets look an an example for option 3. My links controller has totally diferent behavour for admins than other users ...
class LinksController < ApplicationController
#...
def new
#Check params and db values to make a choice here
admin? ? new_admin : new_user
end
#...
private
def new_admin
#All of the good stuff - can use params, flash, etc
render :action => 'new_admin'
end
def new_user
#All of the good stuff - can use params, flash, etc
render :action => 'new_user'
end
end
If two controllers are trying to do the same thing, there's a very good chance this should be in a model. Take a good look at your design and -- I'm sorry I don't know your experience level with MVC -- read up on thin controller techniques:
http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model
http://www.robbyonrails.com/articles/2007/06/19/put-your-controllers-on-a-diet-already
http://andrzejonsoftware.blogspot.com/2008/07/mvc-how-to-write-controllers.html
If the problem is that you need the other controller to do the render, then maybe the route should have pointed there to begin with, and still the skinny controller technique should save the day.
If extracting the common code between controllers into a module doesn't work for you, I would use Rack middleware. I haven't seen code that uses ActiveRecord within middleware but I don't know of any reason why it shouldn't be possible since people have used Redis and the like.
Otherwise I think your only option would be to restart processing of the request with something like (untested, pseudo example):
env['REQUEST_URI'] = new_controller_uri_with_your_params
call(env)
This is similar to how integration tests are implemented. But I don't know if everything from call until you hit a controller is idempotent and safe to rerun like this. You could trace through the source and see. But even if it's ok now, it might break in any future version of rails or rack.
Using middleware would avoid this by letting you intercept the request before it's been run. You should still be able to share code with your rails application by extracting it out into common modules included in both places.
Honestly I think just doing the simple thing of factoring the common controller code is likely cleaner, but it's hard to know without the details of your situation so I thought I'd go ahead and suggest this.
Do this:
class OldController < ApplicationController
def old_controller_action
if should_use_new_controller
new_controller_action
end
# rest of old and busted
end
end
and the new controller
class NewController < OldController
def new_controller_action
# new hotness
end
end

Resources