Rails: Methods Shared by Multiple Controllers

Rails: Methods shared by multiple controllers

As you are using Rails 4 (This approach should work in newer versions of Rails as well), the recommended way of sharing code among your controllers is to use Controller Concerns. Controller Concerns are modules that can be mixed into controllers to share code between them. So, you should put the common helper methods inside the controller concern and include the concern module in all of your controllers where you need to use the helper method.

In your case, as you want to share method3 between two controllers, you should put it in a concern. See this tutorial to know how to create concern and share codes/methods among controllers.

Here are some codes to help you get going:

Define you controller concern:

# app/controllers/concerns/your_controller_concern.rb
module YourControllerConcern
extend ActiveSupport::Concern

included do
helper_method :method3
end

def method3
# method code here
end
end

Then, include the concern in your controllers:

class CartsController < ApplicationController
include YourControllerConcern
# rest of the controller codes
end

class OrdersController < ApplicationController
include YourControllerConcern
# rest of the controller codes
end

Now, you should be able to use method3 in both controllers.

Where should I put methods to be used by multiple controllers in rails?

The Rails4 way is to use Concerns, even though there is some discussion going on about it. Still, I like this approach, even though most of the material you find will be more about models than about controllers.

A simple example

If you are on Rails 3 (as your tag implies), just add a concerns-folder into your controllers-folder and add it to your autoload-path:

#config/application.rb
config.autoload_paths += %W(#{config.root}/app/controllers/concerns)

For instance, I have something like this in app/controllers/concerns/can_can_sanitizer.rb

module CanCanSanitizer
extend ActiveSupport::Concern

included do
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
end
end

I include this into my application_controller just like any other module:

include CanCanSanitizer

Admittedly, not the best use-case, but it should give you a headstart.

rails 3 - code shared between multiple controllers - where to put it?

There are three options, the easiest (though, the most unclean) is the application controller. The other two options are a shared parent controller

class FooController < FooBarParentController
# code here
end

class BarController < FooBarParentController
# code here
end

Usage depends on how related these controllers are.

The final solution is a module

module FooBarModule
extend ActiveSupport::Concern

included do
# class level code
# before_filter ....
end

module ClassMethods
# all class methods here
end

# instance methods here
end

This is where the shared code required is for a handful of ad-hoc controllers, or if you are already using the inheritance above and this code doesn't quite fit into this subset (thus attempting to emulate multiple inheritance).

Where to put a before_filter shared between multiple controllers

How about putting your before_filter and method in a module and including it in each of the controllers. I'd put this file in the lib folder.

module MyFunctions

def self.included(base)
base.before_filter :my_before_filter
end

def my_before_filter
Rails.logger.info "********** YEA I WAS CALLED ***************"
end
end

Then in your controller, all you would have to do is

class MyController < ActionController::Base
include MyFunctions
end

Finally, I would ensure that lib is autoloaded. Open config/application.rb and add the following to the class for your application.

config.autoload_paths += %W(#{config.root}/lib)

Code shared by multiple controllers and models -- where is the best place to keep it?

If their are not tied to one of the three tiers, you should place them in the /lib directory.

The convention under /lib is that you should name your folders as modules, and files and classes, and that you should always try to encapsulate your additional behavior in modules. Let's say, you have some class

module MyModule

class MyHelperClass
end

end

You should put it into /lib/my_module/my_helper_class.rb

How to use the same method in 2 controllers?

Might be some typos lurking in here but:

class GenericController < ApplicationController
def index
@objects = params[:controller].singularize.camelcase.constantize.all()
end

def show
@object = params[:controller].singularize.camelcase.constantize.find(params[:id])
end

def new
@object = params[:controller].singularize.camelcase.constantize.new
end

def edit
@object = params[:controller].singularize.camelcase.constantize.find(params[:id])
end

def create
model = params[:controller].singularize.downcase
@object = params[:controller].singularize.camelcase.constantize.new(params[model])
if @object.save
redirect_to '/'+params[:controller]
else
render :action => 'new'
end
end

def update
model = params[:controller].singularize.downcase
@object = params[:controller].singularize.camelcase.constantize.find(params[:id])
if @object.update_attributes(params[model])
redirect_to :controller => params[:controller], :action => 'index'
else
render :action => 'edit'
end
end

def destroy
if @object = params[:controller].singularize.camelcase.constantize.find(params[:id])
@object.destroy
end
redirect_to :controller => params[:controller], :action => 'index'
end

end

Specific controllers can override those implementations as needed, but:

class ProjectsController < GenericController
# done!
end

class ScenariosController < GenericController
# done!
end

Two controllers for one shared view in Ruby on Rails

I have a similar situation with one of my projects. All the delete views for most controllers are styled the same way, display the same confirmation boxes, and simply renders a predictable display of whatever object is being deleted.

The solution was quite simple and elegant in my opinion. Simply put, what we (the developers) did was create a new directory in app/views called shared and put shared views in there. These could be full template files or just partials.

I would suggest using a shared template (in neither categories nor photos view directories, but rather in the shared directory) and rendering it manually from the view.

e.g. have a method as such in both controllers and a file app/views/shared/photo.html.erb:

def show
@photo = Photo.first # ... or whatever here
render :template => 'shared/photo'
end

This should successfully render the shared template. It is the DRYest route and doesn't have the feeling of pollution you get when using a more-or-less empty view in each controller's view directory just to include a shared partial, as I understand your question is suggesting.

How to Create a Sidebar with Multiple Controller Methods?

If I am understanding your question correctly: you wish to take methods from various controllers and then call upon these methods within a sidebar controller.

This would seem to me to be going against convention and sounds a bit odd. You shouldn't call on a controller's methods from another controller, if you wish to make your application restful.

If you wish to mix-in methods across multiple controllers (write a method once and then mix that method into a bunch of controllers so that all those controllers have their own copy of the method) then take that shared method and put it into a concern, then include that concern into your controller. In your case: include that module into your sidebar controller.

app/controllers/concerns/shared.rb

module Shared
def someMethod
@someVariableToPassToView = 33
end
end

app/controllers/sidebar_controller.rb

include Shared
# Your shared controller has now mixed in all the methods from within the
# Shared module ( in this case, the someMethod method)

And of course don't forget to create the corresponding view to match sidebar's mixed in method: someMethod:

app/views/sidebar/someMethod.html.erb

<p> Outputting the contents of the variable someVariableToPassToView </p>
<p><%= @someVariableToPassToView<%></p>

Also, don't forget to update your routes.rb file for the actions you are mixing into sidebar so that Rails will know how to get to the appropriate view file:

get 'sidebar/somemethod'

To respond to your question: when we want to make an variable from a controller available to a view, we pass it as an instance variable. That is what the @ is for, to make this variable an instance variable so that we can pull out its contents from the view using the <%= %> notation. This is what I did above as an example to pull the number 33 out of the variable it is stored in: @someVariableToPassToView

I would highly recommend the following for you:

Watch Kevin Skoglund's "Ruby On Rails 4 Essential Training." It is about 13hrs long and is the best rails intro that I have found online. Located here: http://www.lynda.com/Ruby-Rails-tutorials/Ruby-Rails-4-Essential-Training/139989-2.html

If you are like me, you will end of watching that training about three times through, and then refer back to sections of the training videos multiple times. After you get comfortable with that, head on over to www.codeschool.com and watch their courses: "Rails 4: Zombie Outlaws" and "Rails 4 Patterns."

Of course, before even digging into rails, you want to make sure you have a solid understanding of the ruby programming language. Rails will make a lot more since once you put it into context: Rails is just ruby code. Kevin Skoglund has an excellent training video on Ruby: http://www.lynda.com/Ruby-tutorials/essential-training/47905-2.html

Good Luck!

Share the same piece of data in multiple controllers and views

Simple Solution:

  • Normally, I just put these into application_controller.rb as a before_action.

Example

# app/controllers/application_controller.rb
before_action :set_sidebar_resources
# ...

private

def set_sidebar_resources
@sidebar_archives = Archive.all
@sidebar_categories = Category.all
end

Modular Solution:

  • Simple solution above works great until you define more and more methods and other global controller logic into ApplicationController, and then the file becomes too big to manage. The following is a less conventional approach favouring more of manageability rather than simplicity.

Example

# app/controllers/application_controller.rb
include WithSidebar

# app/controllers/concerns/with_sidebar.rb
module WithSidebar
extend ActiveModel::Concern
included do
before_action :set_sidebar_resources

private

def set_sidebar_resources
@sidebar_archives = Archive.all
@sidebar_categories = Category.all
end
end
end


Related Topics



Leave a reply



Submit