Froze rails gems
[depot.git] / vendor / rails / actionpack / lib / action_controller / helpers.rb
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")
5
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 # Extend base with class methods to declare helpers.
12 base.extend(ClassMethods)
13
14 base.class_eval do
15 # Wrap inherited to create a new master helper module for subclasses.
16 class << self
17 alias_method_chain :inherited, :helper
18 end
19 end
20 end
21
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
24 # by default.
25 #
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>.
30 #
31 # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
32 # controller which inherits from it.
33 #
34 # ==== Examples
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:
37 #
38 # module FormattedTimeHelper
39 # def format_time(time, format=:long, blank_message="&nbsp;")
40 # time.blank? ? blank_message : time.to_s(format)
41 # end
42 # end
43 #
44 # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
45 #
46 # class EventsController < ActionController::Base
47 # helper FormattedTimeHelper
48 # def index
49 # @events = Event.find(:all)
50 # end
51 # end
52 #
53 # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
54 #
55 # <% @events.each do |event| -%>
56 # <p>
57 # <% format_time(event.time, :short, "N/A") %> | <%= event.name %>
58 # </p>
59 # <% end -%>
60 #
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:
63 #
64 # 23 Aug 11:30 | Carolina Railhawks Soccer Match
65 # N/A | Carolina Railhaws Training Workshop
66 #
67 module ClassMethods
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 }
73 end
74
75 # The +helper+ class method can take a series of helper module names, a block, or both.
76 #
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.
79 #
80 # ==== Examples
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
87 #
88 # When the argument is a module it will be included directly in the template class.
89 # helper FooHelper # => includes FooHelper
90 #
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.
93 # helper :all
94 #
95 # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
96 # to the template.
97 # # One line
98 # helper { def hello() "Hello, world!" end }
99 # # Multi-line
100 # helper do
101 # def foo(bar)
102 # "#{bar} is the very best"
103 # end
104 # end
105 #
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 }
109 #
110 def helper(*args, &block)
111 args.flatten.each do |arg|
112 case arg
113 when Module
114 add_template_helper(arg)
115 when :all
116 helper(all_application_helpers)
117 when String, Symbol
118 file_name = arg.to_s.underscore + '_helper'
119 class_name = file_name.camelize
120
121 begin
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)
128 else
129 raise
130 end
131 end
132
133 add_template_helper(class_name.constantize)
134 else
135 raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
136 end
137 end
138
139 # Evaluate block in template class if given.
140 master_helper_module.module_eval(&block) if block_given?
141 end
142
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?
147 #
148 # def current_user
149 # @current_user ||= User.find_by_id(session[:user])
150 # end
151 #
152 # def logged_in?
153 # current_user != nil
154 # end
155 # end
156 #
157 # In a view:
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)
164 end
165 end_eval
166 end
167 end
168
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:
172 # helper_attr :name
173 # attr_accessor :name
174 def helper_attr(*attrs)
175 attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
176 end
177
178 # Provides a proxy to access helpers methods from outside the view.
179 def helpers
180 unless @helper_proxy
181 @helper_proxy = ActionView::Base.new
182 @helper_proxy.extend master_helper_module
183 else
184 @helper_proxy
185 end
186 end
187
188 private
189 def default_helper_module!
190 unless name.blank?
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
195 end
196 rescue MissingSourceFile => e
197 raise unless e.is_missing? module_path
198 rescue NameError => e
199 raise unless e.missing_name? module_name
200 end
201
202 def inherited_with_helper(child)
203 inherited_without_helper(child)
204
205 begin
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")
211 end
212 end
213
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' }
218 end
219 end
220 end
221 end