The callback is like below.
class User
include UserSettable
before_action :set_user
def show
[Something with #user]
end
end
class Group
include UserSettable
before_action :set_user
...
end
And the set_user is like below. And this method is defined in the controller concern.
module UserSettable
extend ActiveSupport::Concern
def set_user
#user = User.find(params[:id])
end
end
I have three questions.
How do I pass params to callback method?
Can the instance variable defined in concern be used in controller?
Does this implementation deviate from the usual usage of rails?
How do I pass params to callback method?
params of request will automatically be available to action. You don't need to pass it explicitly. Your code snippet is perfect use case of how it should be used.
Can the instance variable defined in concern be used in controller?
You mean controller's concern. I think the instance variable defined there should be available. Can you give an example of what you are trying to achieve?
Does this implementation deviate from the usual usage of rails?
No
Related
I have a requirement to need to validate presence of some params in certain situations. Here is the example of that :
In my user controller, for update action, I am required to validate the presence of these params. Same deal for car controller, update action as well, you could see recurring theme here. Params are additional_info.
My base controller provides additional_info_params which pulls the right data from the request.
Here is what I tried so far. I created a AR controller concern and included it in the controller, here is some code:
module ClassMethods
def require_additional_info_for(*methods)
binding.pry
return unless methods.include?(action_name)
if additional_info_params.empty?
head 400
end
end
end
My idea was to be able to define methods that require these params on the top of controller file, just like before_action from rails or skip_authorization_check from cancan. Like so:
MyController < BaseController
include Concerns::AdditionalInformation
require_additional_info_for :update
def update
...
end
end
This code above however does not work as I intended, mainly because this fires on the request class without much knowledge about the request (where I need to derive action name from via action_name).
So how can I do something like this?
Yes, you can, but i suggest you to use the before_action callback!
In a 'abstract' controller, register your method like this:
class SameController < ApplicationController
...
protected
def require_additional_params
render status: :unprocessable_entity if additional_info_params.empty?
end
end
After this, all the controllers who will use this methods, must extends SameController, and runs before_action passing the above method for the wanted actions, for example:
class UserController < SameController
before_action :require_additional_params, only: [:action1, :action2]
end
Note: You can put the require_additional_params in a module and include in your controller, or just put it in the ApplicationController
You might also look at making these regular strong params in the respective controller. It looks something like this:
def update_params
params.require(:car).permit(:engine, :wheels, :rims).tap do |car_params|
car_params.require(:engine)
end
end
This would expect a top-level :car key params (which it strips), and require an :engine param, but allow the other 2 (:wheels and :rims). If :engine isn't present, it will raise a ActionController::ParameterMissing (just like if :cars was missing)
This is straight from the action controller strong params docs (last example at bottom)
I'll sometimes throw these into separate private methods on the respective controller, so there would also possibly be a create_params method with different requirements. I prefer this method over using a custom method as a before_action.
I use MongoDB as a database in my Rails application with MongoID gem. I want to call the helper method from the model within after_create callback method. How is it possible?My model code is:
class Department
include ApplicationHelper
after_create :create_news
private
def create_news
#user = ApplicationHelper.get_current_users
end
end
And my helper code is:
module ApplicationHelper
def get_current_users
current_user
end
end
When I create new department then following error occur.
undefined method `get_current_users' for ApplicationHelper:Module
How to remove error? Thanks in advance.
I also use mongoid and use this all the time. Shouldn't be unique to mongoid though.
ApplicationController.helpers.my_helper_method
If you want a helper method that you can use in your views to return the current user, you can do so in your ApplicationController, something like this for example:
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
Then you can use this in any view.
If you want some arbitrary method in a model to know what user it's dealing with, pass #current_user in as an argument to the method when you call it in your controller.
Your code seems incomplete so I can't really see what you're trying to accomplish, but this is pretty standard practice.
Make sure the module file is named properly, meaning in your case application_helper.rb and it's located on the helpers library.
You can also try to include the helper in the ApplicationController (app/controller/application_controller.rb).
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'm using the bitly gem and would like to have access to the bitly API inside my helper methods (which get called by views and mailers to generate URLs).
I initiate an API connection in this method in my ApplicationController:
(is there a more appropriate place to do this BTW?)
class ApplicationController < ActionController::Base
before_filter :bitly_connect
def bitly_connect
Bitly.use_api_version_3
#bitly ||= Bitly.new(APP_CONFIG['bitly_username'], APP_CONFIG['bitly_api_key'] )
end
end
By default I don't have access to #bitly in my helpers. Can you suggest a way to accomplish that?
The only related thread I found wasn't helpful:
Rails 3 and Controller Instance Variables Inside a Helper
Thanks.
Rails by convention passes instance variables set in the controller actions (and filters) along to the views. The helper methods are available in these views, and should have access to the instance variables you set inside your controller action.
Alternately, you can set a local variable inside your helper method by passing the variable to the method, or by using the Object#instance_variable_get method: http://ruby-doc.org/core/classes/Object.html#M001028
# app/controllers/example_controller.rb
class ExampleController
def index
#instance_variable = 'foo'
end
end
# app/helpers/example_helper.rb
module ExampleHelper
def foo
# instance variables set in the controller actions can be accessed here
#instance_variable # => 'foo'
# alternately using instance_variable_get
variable = instance_variable_get(:#instance_variable)
variable # => 'foo'
end
end
As for your concerns with the placement of the logic, it does not look like it belongs in the controller. Think of the controller as routing requests for your application. Most logic should be performed inside of your model classes. "Skinny controller, fat model.": http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model
If you need a controller method to be accessible as a helper, you can use helper_method
class ApplicationController < ActionController::Base
helper_method :bitly_connect
def bitly_connect
#bitly ||= begin
Bitly.use_api_version_3
Bitly.new(APP_CONFIG['bitly_username'], APP_CONFIG['bitly_api_key'] )
end
end
end
Note that I also altered the method, so that it doesn't call Bitly.use_api_version_3 each time it is called.
As Ben Simpson noted, you should probably move this into a Model though.