1 # FIXME: helper { ... } is broken on Ruby 1.9
2 module ActionController
#:nodoc:
3 module Helpers
#:nodoc:
4 HELPERS_DIR
= (defined?(RAILS_ROOT
) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
6 def self.included(base
)
7 # Initialize the base module to aggregate its helpers.
8 base
.class_inheritable_accessor
:master_helper_module
9 base
.master_helper_module
= Module
.new
11 # Extend base with class methods to declare helpers.
12 base
.extend(ClassMethods
)
15 # Wrap inherited to create a new master helper module for subclasses.
17 alias_method_chain
:inherited, :helper
22 # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
23 # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates
26 # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
27 # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
28 # include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
29 # include <tt>MyHelper</tt>.
31 # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
32 # controller which inherits from it.
35 # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
36 # the Time object is blank:
38 # module FormattedTimeHelper
39 # def format_time(time, format=:long, blank_message=" ")
40 # time.blank? ? blank_message : time.to_s(format)
44 # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
46 # class EventsController < ActionController::Base
47 # helper FormattedTimeHelper
49 # @events = Event.find(:all)
53 # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
55 # <% @events.each do |event| -%>
57 # <% format_time(event.time, :short, "N/A") %> | <%= event.name %>
61 # Finally, assuming we have two event instances, one which has a time and one which does not,
62 # the output might look like this:
64 # 23 Aug 11:30 | Carolina Railhawks Soccer Match
65 # N/A | Carolina Railhaws Training Workshop
68 # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
69 # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
70 # available to the templates.
71 def add_template_helper(helper_module
) #:nodoc:
72 master_helper_module
.module_eval
{ include helper_module
}
75 # The +helper+ class method can take a series of helper module names, a block, or both.
77 # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
78 # * <tt>&block</tt>: A block defining helper methods.
81 # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
82 # and include the module in the template class. The second form illustrates how to include custom helpers
83 # when working with namespaced controllers, or other cases where the file containing the helper definition is not
84 # in one of Rails' standard load paths:
85 # helper :foo # => requires 'foo_helper' and includes FooHelper
86 # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
88 # When the argument is a module it will be included directly in the template class.
89 # helper FooHelper # => includes FooHelper
91 # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers from
92 # <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT.
95 # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
98 # helper { def hello() "Hello, world!" end }
102 # "#{bar} is the very best"
106 # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
107 # +symbols+, +strings+, +modules+ and blocks.
108 # helper(:three, BlindHelper) { def mice() 'mice' end }
110 def helper(*args
, &block
)
111 args
.flatten
.each
do |arg
|
114 add_template_helper(arg
)
116 helper(all_application_helpers
)
118 file_name
= arg
.to_s
.underscore
+ '_helper'
119 class_name
= file_name
.camelize
122 require_dependency(file_name
)
123 rescue LoadError
=> load_error
124 requiree
= / -- (.*?)(\.rb)?$/.match(load_error
.message
).to_a
[1]
125 if requiree
== file_name
126 msg
= "Missing helper file helpers/#{file_name}.rb"
127 raise LoadError
.new(msg
).copy_blame
!(load_error
)
133 add_template_helper(class_name
.constantize
)
135 raise ArgumentError
, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
139 # Evaluate block in template class if given.
140 master_helper_module
.module_eval(&block
) if block_given
?
143 # Declare a controller method as a helper. For example, the following
144 # makes the +current_user+ controller method available to the view:
145 # class ApplicationController < ActionController::Base
146 # helper_method :current_user, :logged_in?
149 # @current_user ||= User.find_by_id(session[:user])
153 # current_user != nil
158 # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
159 def helper_method(*methods
)
160 methods
.flatten
.each
do |method
|
161 master_helper_module
.module_eval
<<-end_eval
162 def #{method}(*args, &block)
163 controller.send(%(#{method}), *args, &block)
169 # Declares helper accessors for controller attributes. For example, the
170 # following adds new +name+ and <tt>name=</tt> instance methods to a
171 # controller and makes them available to the view:
173 # attr_accessor :name
174 def helper_attr(*attrs
)
175 attrs
.flatten
.each
{ |attr
| helper_method(attr
, "#{attr}=") }
178 # Provides a proxy to access helpers methods from outside the view.
181 @helper_proxy = ActionView
::Base.new
182 @helper_proxy.extend master_helper_module
189 def default_helper_module
!
191 module_name
= name
.sub(/Controller$|$/, 'Helper')
192 module_path
= module_name
.split('::').map
{ |m
| m
.underscore
}.join('/')
193 require_dependency module_path
194 helper module_name
.constantize
196 rescue MissingSourceFile
=> e
197 raise unless e
.is_missing
? module_path
198 rescue NameError
=> e
199 raise unless e
.missing_name
? module_name
202 def inherited_with_helper(child
)
203 inherited_without_helper(child
)
206 child
.master_helper_module
= Module
.new
207 child
.master_helper_module
.__send__
:include, master_helper_module
208 child
.__send__
:default_helper_module!
209 rescue MissingSourceFile
=> e
210 raise unless e
.is_missing
?("helpers/#{child.controller_path}_helper")
214 # Extract helper names from files in app/helpers/**/*.rb
215 def all_application_helpers
216 extract
= /^#{Regexp.quote(HELPERS_DIR)}\/?(.*)_helper
.rb
$/
217 Dir
["#{HELPERS_DIR}/**/*_helper.rb"].map
{ |file
| file
.sub extract
, '\1' }