Updated README.rdoc again
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / helpers.rb
1 require 'active_support/dependencies'
2
3 # FIXME: helper { ... } is broken on Ruby 1.9
4 module ActionController #:nodoc:
5 module Helpers #:nodoc:
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
10
11 # Set the default directory for helpers
12 base.class_inheritable_accessor :helpers_dir
13 base.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
14
15 # Extend base with class methods to declare helpers.
16 base.extend(ClassMethods)
17
18 base.class_eval do
19 # Wrap inherited to create a new master helper module for subclasses.
20 class << self
21 alias_method_chain :inherited, :helper
22 end
23 end
24 end
25
26 # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
27 # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates
28 # by default.
29 #
30 # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
31 # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
32 # include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
33 # include <tt>MyHelper</tt>.
34 #
35 # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
36 # controller which inherits from it.
37 #
38 # ==== Examples
39 # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
40 # the Time object is blank:
41 #
42 # module FormattedTimeHelper
43 # def format_time(time, format=:long, blank_message="&nbsp;")
44 # time.blank? ? blank_message : time.to_s(format)
45 # end
46 # end
47 #
48 # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
49 #
50 # class EventsController < ActionController::Base
51 # helper FormattedTimeHelper
52 # def index
53 # @events = Event.find(:all)
54 # end
55 # end
56 #
57 # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
58 #
59 # <% @events.each do |event| -%>
60 # <p>
61 # <% format_time(event.time, :short, "N/A") %> | <%= event.name %>
62 # </p>
63 # <% end -%>
64 #
65 # Finally, assuming we have two event instances, one which has a time and one which does not,
66 # the output might look like this:
67 #
68 # 23 Aug 11:30 | Carolina Railhawks Soccer Match
69 # N/A | Carolina Railhaws Training Workshop
70 #
71 module ClassMethods
72 # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
73 # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
74 # available to the templates.
75 def add_template_helper(helper_module) #:nodoc:
76 master_helper_module.module_eval { include helper_module }
77 end
78
79 # The +helper+ class method can take a series of helper module names, a block, or both.
80 #
81 # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
82 # * <tt>&block</tt>: A block defining helper methods.
83 #
84 # ==== Examples
85 # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
86 # and include the module in the template class. The second form illustrates how to include custom helpers
87 # when working with namespaced controllers, or other cases where the file containing the helper definition is not
88 # in one of Rails' standard load paths:
89 # helper :foo # => requires 'foo_helper' and includes FooHelper
90 # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
91 #
92 # When the argument is a module it will be included directly in the template class.
93 # helper FooHelper # => includes FooHelper
94 #
95 # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers beneath
96 # <tt>ActionController::Base.helpers_dir</tt> (defaults to <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT).
97 # helper :all
98 #
99 # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
100 # to the template.
101 # # One line
102 # helper { def hello() "Hello, world!" end }
103 # # Multi-line
104 # helper do
105 # def foo(bar)
106 # "#{bar} is the very best"
107 # end
108 # end
109 #
110 # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
111 # +symbols+, +strings+, +modules+ and blocks.
112 # helper(:three, BlindHelper) { def mice() 'mice' end }
113 #
114 def helper(*args, &block)
115 args.flatten.each do |arg|
116 case arg
117 when Module
118 add_template_helper(arg)
119 when :all
120 helper(all_application_helpers)
121 when String, Symbol
122 file_name = arg.to_s.underscore + '_helper'
123 class_name = file_name.camelize
124
125 begin
126 require_dependency(file_name)
127 rescue LoadError => load_error
128 requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
129 if requiree == file_name
130 msg = "Missing helper file helpers/#{file_name}.rb"
131 raise LoadError.new(msg).copy_blame!(load_error)
132 else
133 raise
134 end
135 end
136
137 add_template_helper(class_name.constantize)
138 else
139 raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
140 end
141 end
142
143 # Evaluate block in template class if given.
144 master_helper_module.module_eval(&block) if block_given?
145 end
146
147 # Declare a controller method as a helper. For example, the following
148 # makes the +current_user+ controller method available to the view:
149 # class ApplicationController < ActionController::Base
150 # helper_method :current_user, :logged_in?
151 #
152 # def current_user
153 # @current_user ||= User.find_by_id(session[:user])
154 # end
155 #
156 # def logged_in?
157 # current_user != nil
158 # end
159 # end
160 #
161 # In a view:
162 # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
163 def helper_method(*methods)
164 methods.flatten.each do |method|
165 master_helper_module.module_eval <<-end_eval
166 def #{method}(*args, &block) # def current_user(*args, &block)
167 controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block)
168 end # end
169 end_eval
170 end
171 end
172
173 # Declares helper accessors for controller attributes. For example, the
174 # following adds new +name+ and <tt>name=</tt> instance methods to a
175 # controller and makes them available to the view:
176 # helper_attr :name
177 # attr_accessor :name
178 def helper_attr(*attrs)
179 attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
180 end
181
182 # Provides a proxy to access helpers methods from outside the view.
183 def helpers
184 unless @helper_proxy
185 @helper_proxy = ActionView::Base.new
186 @helper_proxy.extend master_helper_module
187 else
188 @helper_proxy
189 end
190 end
191
192 private
193 def default_helper_module!
194 unless name.blank?
195 module_name = name.sub(/Controller$|$/, 'Helper')
196 module_path = module_name.split('::').map { |m| m.underscore }.join('/')
197 require_dependency module_path
198 helper module_name.constantize
199 end
200 rescue MissingSourceFile => e
201 raise unless e.is_missing? module_path
202 rescue NameError => e
203 raise unless e.missing_name? module_name
204 end
205
206 def inherited_with_helper(child)
207 inherited_without_helper(child)
208
209 begin
210 child.master_helper_module = Module.new
211 child.master_helper_module.__send__ :include, master_helper_module
212 child.__send__ :default_helper_module!
213 rescue MissingSourceFile => e
214 raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
215 end
216 end
217
218 # Extract helper names from files in app/helpers/**/*.rb
219 def all_application_helpers
220 extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/
221 Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }
222 end
223 end
224 end
225 end