85

I have some controller methods I'd like to share. What is the best practice for doing this in ruby on rails? Should I create an abstract class that my controllers extend, or should I create module and add it in to each controller? Below are the controller methods I want to share:

def driving_directions
  @address_to = params[:address_to]
  @address_from = params[:address_from]
  @map_center = params[:map_center_start]

  # if we were not given a center point to start our map on
  # let's create one.
  if !@map_center && @address_to
    @map_center = GeoKit::Geocoders::MultiGeocoder.geocode(@address_to).ll
  elsif !@map_center && @address_from
    @map_center = GeoKit::Geocoders::MultiGeocoder.geocode(@address_from).ll
  end
end

def printer_friendly
  starting_point = params[:starting_point].split(',').collect{|e|e.to_f}
  ne = params[:ne].split(',').collect{|e|e.to_f}
  sw = params[:sw].split(',').collect{|e|e.to_f}
  size = params[:size].split(',').collect{|e|e.to_f}
  address = params[:address]

  @markers = retrieve_points(ne,sw,size,false)
  @map = initialize_map([[sw[0],sw[1]],[ne[0],ne[1]]],[starting_point[0],starting_point[1]],false,@markers,true)
  @address_string = address
end
2
  • 1
    Is there a particular reason for not using application.rb in this case? Commented Sep 24, 2008 at 19:07
  • 4
    Only that some controllers will use the code, but not all. Commented Sep 25, 2008 at 2:20

7 Answers 7

116

In my opinion, normal OO design principles apply:

  • If the code is really a set of utilities that doesn't need access to object state, I would consider putting it in a module to be called separately. For instance, if the code is all mapping utilities, create a module Maps, and access the methods like: Maps::driving_directions.
  • If the code needs state and is used or could be used in every controller, put the code in ApplicationController.
  • If the code needs state and is used in a subset of all controllers that are closely and logically related (i.e. all about maps) then create a base class (class MapController < ApplicationController) and put the shared code there.
  • If the code needs state and is used in a subset of all controllers that are not very closely related, put it in a module and include it in necessary controllers.

In your case, the methods need state (params), so the choice depends on the logical relationship between the controllers that need it. In addition:

Also:

  • Use partials when possible for repeated code and either place in a common 'partials' directory or include via a specific path.
  • Stick to a RESTful approach when possible (for methods) and if you find yourself creating a lot of non-RESTful methods consider extracting them to their own controller.
Sign up to request clarification or add additional context in comments.

2 Comments

Great answer. Do you think any of the points above would change now that concerns are available? (I notice the question was asked a few years back).
I took a decade off from Rails, but coincidentally have found myself working in it full time again. I'd extend or amend each bullet point: separate types abiding by the SRP should be used when possible, consider plain Ruby objects in addition to modules; only the most fully cross cutting concerns should live in your base ApplicationController; avoid implementation inheritance unless there truly is a very strong "is a" relationship; concerns make mixins easier, and are generally nice to work with.
33

I know this question was asked 6 years ago. Just want to point out that in Rails 4, there're now Controller Concerns that're a more out of the box solution.

Comments

16

I actually think a module is the best way to share code amongst controllers. Helpers are good if you want to share code amongst views. Helpers are basically glorified modules, so if you don't need view level access, I suggest placing a module in your lib folder.

Once you create the module, you'll have to use the include statement to include it in the desired controllers.

http://www.rubyist.net/~slagell/ruby/modules.html

Comments

1

I agree with the module approach. Create a separate Ruby file in your lib directory and put the module in the new file.

The most obvious way would be to add the methods to your ApplicationController, but I am sure you know that already.

Comments

1

if you want to share codes between controller and helpers, then you should try creating a module in library. You can use @template and @controller for accessing method in controller and helper as well. Check this for more details http://www.shanison.com/?p=305

Comments

0

Another possibility:

If your common code needs state and you want to share the behavior amongst controllers, you could put it in a plain old ruby class in either your model or lib directory. Remember that model classes don't have to be persistent even though all ActiveRecord classes are persistent. In other words, it's acceptable to have transient model classes.

Comments

0

I found that one effective way to share identical code across controllers is to have one controller inherit from the other (where the code lives). I used this approach to share identical methods defined in my controllers with another set of namespaced controllers.

1 Comment

Using inheritance to share code is considered as a code smell. You should avoid it. See programmers.stackexchange.com/a/12446 . Use instead, modules, concerns or even services object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.