Im working with a medium sized Rails application and I do this in every controller:
def create
#object = Model.new(params[:model].merge(editing_user: current_user))
...
end
def update
#object = Model.find(params[:id])
#object.editing_user = current_user
...
end
Setting the editing user over and over again is not DRY. I thought about cleaning this up with an observer but it would need access to the current user. Observers do not have access to the current user, neither should they (Law of Demeter).
Any suggestions how to DRY this up between controllers?
class ApplicationController < ActionController::Base
before_filter :init_request
def init_request
params[:editing_user] = current_user
end
end
I like using decent_exposure to dry up my controllers. It automatically finds or initializes a model instance, based on whether an :id was passed as a param, and it assigns the attributes from params[:model].
To finish drying up your code, you could use the new strategy support (see the end of the readme) to automatically set the editing_user attribute on your model.
You could try an after_filter for this. Perhaps something like so:
class ApplicationController < ActionController::Base
after_filter :set_editing_user
def set_editing_user
#object.update_attribute(:editing_user, current_user) if #object && current_user
end
The difficulty, of course, is that you'll be saving the object twice per call. Generally though creations and updates don't happen so frequently that two database commits is a serious problem, but if you expect to be the next Twitter -- with massive database insertion load -- it could be an issue.
You could also possibly set this in a before_filter, but then you'd have to find or set the object in a previous before_filter. Otherwise #object will always be nil and the before_filter will never fire. You can use the filter ordering methods prepend_before_filter and append_before_filter to ensure the correct sequencing of these filters.
Related
In laravel I am use to having
Auth::user()->id
which I can reference for setting up data-id's or something in views. I am working in a ruby on rails app and cannot for the life of me find an answer to how to achieve this in rails. I found a lot of answers talking about current_user but I cannot get any data in the view.
To be clear what I am try to set up exactly is
Enroll
Here the "current_user.id" would be that users id. With the code above (and any variation of it I can think of) I am getting nothing, no errors but no data either. Do I really have to set this up in every controller method to access it somehow? Does anyone have a solution to this that they can point me towards?
Thanks so much for any help.
If you're going to access any instance variable in a view, you need to define it first (either in a controller or in the view).
Depending on how you have auth set up, you probably have a current_user method somewhere.
It could be defined in ApplicationController (which has functionality shared by all controllers);
class ApplicationController < ActionController::Base
def current_user
User.find_by id: session["current_user_id"]
# or whatever
end
helper_method :current_user
end
The helper_method line makes it accessible in your views, so you can write <%= current_user.id %>.
You could also write some code so that the #current_user instance variable is available in all your views:
class ApplicationController < ActionController::Base
before_action :define_current_user
def define_current_user
#current_user = current_user # call the 'current_user' method defined elsewhere
end
end
I'm in the process of creating my own simple blog application and I want to include a 'latest posts' section on the sidebar, so my posts model needs to be accessible by the entire app. I'm looking for the best way of doing so.
I'm thinking a before_filter in the application controller followed up a private method to call the scope I have:
class ApplicationController < ActionController::Base
before_filter :latest_news
private
def latest_news
#latest = News.latest.limit(5)
end
end
Is this the best way?
Instead of a before_filter, I'd recommend using a lazy-load approach that does basically the same thing.
class ApplicationController < ActionController::Base
helper_method :latest_news
def latest_news
#latest_news ||= News.latest.limit(5)
end
end
This way you can call latest_news from any controller or view (which is what the helper_method macro does for you) and then it'll load it if it's not loaded already the first time it's called and any subsequent calls will be cached. This is a pretty common pattern for getting things like the current user record, etc.
My question is about controller methods (possibly included from an outside class) that work with instance variables. I frequently use a before_filter in controllers to set up certain variables, e.g.:
class DocumentController < ApplicationController
before_filter :fetch_document
def action
#document.do_something
end
private
def fetch_document
#document = Document.find(params[:id])
end
end
I've been working on a project in which a few controllers will share some functionality, say, document editing. My first thought was to extract the relevant methods, and get them from application_controller.rb or a separate module. But then I noticed I was writing code that looks like this:
def fetch_document
#document = Document.find(params[:id])
end
def do_something_to_document
#document.do_something
end
This sets off warning bells: do_something_to_document is essentially assuming the existence of #document, rather than taking it as an argument. Is this, in your sage opinions, a bad coding practice? Or am I being paranoid?
Assuming it is an issue, I see two general approaches to deal with it:
Check for the instance var and bail unless it's set:
def do_something_to_document
raise "no doc!" unless #document
[...]
end
Call the action with the instance var as an argument:
def do_something_to_document(document)
[...]
end
2 looks better, because it hides the context of the calling object. But do_something_to_doc will only be called by controllers that have already set up #document, and taking #document as a method argument incurs the overhead of object creation. (Right?) 1 seems hackish, but should cover all of the cases.
I'm inclined to go with 1 (assuming I'm right about the performance issue), even though seeing a list of methods referencing mysterious instance vars gives me hives. Thoughts? Let me know if I can be more clear. (And of course, if this is answered somewhere I didn't see it, just point me in the right direction...)
Thanks,
-Erik
If you really need document in different controllers, I'd do something like this:
class ApplicationController < ActionController::Base
private
def document
#document ||= Document.find(params[:document_id])
end
end
class FooController < ApplicationController
before_filter :ensure_document, :only => [:foo]
def foo
document.do_something
end
private
# TODO: not sure if controller_name/action_name still exists
def ensure_document
raise "#{controller_name}##{action_name} needs a document" unless document
end
end
As #variable are session/instance variable you will get a Nil exception in do_something_to_document method.
The first code is fine, because before_filter will always load your #document.
I suggest you to write something like that
def fetch_document(doc_id)
#document ||= Document.find(doc_id)
end
def do_something_to_document
my_doc = fetch_document(params[:id])
end
where do_something_to_document is in the controller (if not, dont use params[:id], even if you know you can access this global, use another explicit parameter). The ||= thing, will asssure that you call the base only once by request.
I have a ChatController and an #user variable in it. On the main page I display #user.name. I also have destroy and create methods that work with ajax, so when I delete a message from my chat, #user becomes nil. To prevent problems from calling name on a nil object, I can add #user=User.find_by_id(:user_id) to every method. But this becomes tedious if I have many methods. Can I declare #user=User.find_by_id(:user_id) once and DRY up my code?
Yes, this is done in a before_filter (Documentation).
Something like:
class ChatController < ActionController::Base
before_filter :find_user
private
def find_user
#user ||= User.find_by_id(params[:user_id])
end
end
You may also consider using Inherited Resources which automates this for you.
I have an object in ruby on rails for #user which contains username, password, etc
How can I ensure that the values are kept throughout all views?
Thanks
If you set it up as follows:
class ApplicationController < ActionController::Base
before_filter :set_user
protected
def set_user
#user = User.find_by_id(session[:user_id])
end
end
Then in all of controller, since they all inherits from ApplicationController, will have the #user value set.
Note: this will set the #user to nil if the session[:user_id] as not been set for this session.
For more on filters and the :before_filter, check this link out: Module:ActionController::Filters::ClassMethods
I take it you want some sort of user sustem? logged in and tracking all over your system?
AuthenticatedSystem is something that can help you. there is a lot of documentation out their that will tell you exactly how to setup an environment that uses it. I personally use if for several systems I've made.
In your ApplicationController, add your object to the session and create a variable for it. Add a before_filter that calls the method that does that.