1 module ActionController #:nodoc:
2 module Helpers #:nodoc:
3 HELPERS_DIR = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
5 def self.included(base)
6 # Initialize the base module to aggregate its helpers.
7 base.class_inheritable_accessor :master_helper_module
8 base.master_helper_module = Module.new
10 # Extend base with class methods to declare helpers.
11 base.extend(ClassMethods)
14 # Wrap inherited to create a new master helper module for subclasses.
16 alias_method_chain :inherited, :helper
21 # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
22 # +numbers+ and +ActiveRecord+ objects, to name a few. These helpers are available to all templates
25 # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
26 # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
27 # include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
28 # include <tt>MyHelper</tt>.
30 # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
31 # controller which inherits from it.
34 # The +to_s+ method from the +Time+ class can be wrapped in a helper method to display a custom message if
35 # the Time object is blank:
37 # module FormattedTimeHelper
38 # def format_time(time, format=:long, blank_message=" ")
39 # time.blank? ? blank_message : time.to_s(format)
43 # +FormattedTimeHelper+ can now be included in a controller, using the +helper+ class method:
45 # class EventsController < ActionController::Base
46 # helper FormattedTimeHelper
48 # @events = Event.find(:all)
52 # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
54 # <% @events.each do |event| -%>
56 # <% format_time(event.time, :short, "N/A") %> | <%= event.name %>
60 # Finally, assuming we have two event instances, one which has a time and one which does not,
61 # the output might look like this:
63 # 23 Aug 11:30 | Carolina Railhawks Soccer Match
64 # N/A | Carolina Railhaws Training Workshop
67 # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
68 # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
69 # available to the templates.
70 def add_template_helper(helper_module) #:nodoc:
71 master_helper_module.module_eval { include helper_module }
74 # The +helper+ class method can take a series of helper module names, a block, or both.
76 # * <tt>*args</tt>: One or more +Modules+, +Strings+ or +Symbols+, or the special symbol <tt>:all</tt>.
77 # * <tt>&block</tt>: A block defining helper methods.
80 # When the argument is a +String+ or +Symbol+, the method will provide the "_helper" suffix, require the file
81 # and include the module in the template class. The second form illustrates how to include custom helpers
82 # when working with namespaced controllers, or other cases where the file containing the helper definition is not
83 # in one of Rails' standard load paths:
84 # helper :foo # => requires 'foo_helper' and includes FooHelper
85 # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
87 # When the argument is a +Module+, it will be included directly in the template class.
88 # helper FooHelper # => includes FooHelper
90 # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers from
91 # <tt>app/helpers/**/*.rb</tt> under +RAILS_ROOT+.
94 # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
97 # helper { def hello() "Hello, world!" end }
101 # "#{bar} is the very best"
105 # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
106 # +symbols+, +strings+, +modules+ and blocks.
107 # helper(:three, BlindHelper) { def mice() 'mice' end }
109 def helper(*args, &block)
110 args.flatten.each do |arg|
113 add_template_helper(arg)
115 helper(all_application_helpers)
117 file_name = arg.to_s.underscore + '_helper'
118 class_name = file_name.camelize
121 require_dependency(file_name)
122 rescue LoadError => load_error
123 requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
124 if requiree == file_name
125 msg = "Missing helper file helpers/#{file_name}.rb"
126 raise LoadError.new(msg).copy_blame!(load_error)
132 add_template_helper(class_name.constantize)
134 raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
138 # Evaluate block in template class if given.
139 master_helper_module.module_eval(&block) if block_given?
142 # Declare a controller method as a helper. For example, the following
143 # makes the +current_user+ controller method available to the view:
144 # class ApplicationController < ActionController::Base
145 # helper_method :current_user
147 # @current_user ||= User.find(session[:user])
150 def helper_method(*methods)
151 methods.flatten.each do |method|
152 master_helper_module.module_eval <<-end_eval
153 def #{method}(*args, &block)
154 controller.send(%(#{method}), *args, &block)
160 # Declares helper accessors for controller attributes. For example, the
161 # following adds new +name+ and <tt>name=</tt> instance methods to a
162 # controller and makes them available to the view:
164 # attr_accessor :name
165 def helper_attr(*attrs)
166 attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
171 def default_helper_module!
173 module_name = name.sub(/Controller$|$/, 'Helper')
174 module_path = module_name.split('::').map { |m| m.underscore }.join('/')
175 require_dependency module_path
176 helper module_name.constantize
178 rescue MissingSourceFile => e
179 raise unless e.is_missing? module_path
180 logger.debug("#{name}: missing default helper path #{module_path}") if logger
181 rescue NameError => e
182 raise unless e.missing_name? module_name
183 logger.debug("#{name}: missing default helper module #{module_name}") if logger
186 def inherited_with_helper(child)
187 inherited_without_helper(child)
190 child.master_helper_module = Module.new
191 child.master_helper_module.send! :include, master_helper_module
192 child.send! :default_helper_module!
193 rescue MissingSourceFile => e
194 raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
198 # Extract helper names from files in app/helpers/**/*.rb
199 def all_application_helpers
200 extract = /^#{Regexp.quote(HELPERS_DIR)}\/?(.*)_helper.rb$/
201 Dir["#{HELPERS_DIR}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }