Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / actionpack / lib / action_controller / helpers.rb
bloba1fba0c8a0c034debc63a306217a401aad451417
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)
13       base.class_eval do
14         # Wrap inherited to create a new master helper module for subclasses.
15         class << self
16           alias_method_chain :inherited, :helper
17         end
18       end
19     end
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
23     # by default.
24     #
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>.
29     # 
30     # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
31     # controller which inherits from it.
32     #
33     # ==== Examples
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:
36     #
37     #   module FormattedTimeHelper
38     #     def format_time(time, format=:long, blank_message="&nbsp;")
39     #       time.blank? ? blank_message : time.to_s(format)
40     #     end
41     #   end
42     #
43     # +FormattedTimeHelper+ can now be included in a controller, using the +helper+ class method:
44     #
45     #   class EventsController < ActionController::Base
46     #     helper FormattedTimeHelper
47     #     def index
48     #       @events = Event.find(:all)
49     #     end
50     #   end
51     #
52     # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
53     #
54     #   <% @events.each do |event| -%>
55     #     <p>
56     #       <% format_time(event.time, :short, "N/A") %> | <%= event.name %> 
57     #     </p>
58     #   <% end -%>
59     #
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:
62     #
63     #   23 Aug 11:30 | Carolina Railhawks Soccer Match 
64     #   N/A | Carolina Railhaws Training Workshop
65     #
66     module ClassMethods
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 }
72       end
74       # The +helper+ class method can take a series of helper module names, a block, or both.
75       #
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.
78       # 
79       # ==== Examples
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
86       #
87       # When the argument is a +Module+, it will be included directly in the template class.
88       #   helper FooHelper # => includes FooHelper
89       #
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+.
92       #   helper :all
93       #
94       # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available 
95       # to the template.
96       #   # One line
97       #   helper { def hello() "Hello, world!" end }
98       #   # Multi-line
99       #   helper do
100       #     def foo(bar) 
101       #       "#{bar} is the very best" 
102       #     end
103       #   end
104       # 
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 }
108       #
109       def helper(*args, &block)
110         args.flatten.each do |arg|
111           case arg
112             when Module
113               add_template_helper(arg)
114             when :all
115               helper(all_application_helpers)
116             when String, Symbol
117               file_name  = arg.to_s.underscore + '_helper'
118               class_name = file_name.camelize
120               begin
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)
127                 else
128                   raise
129                 end
130               end
132               add_template_helper(class_name.constantize)
133             else
134               raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
135           end
136         end
138         # Evaluate block in template class if given.
139         master_helper_module.module_eval(&block) if block_given?
140       end
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
146       #     def current_user
147       #       @current_user ||= User.find(session[:user])
148       #     end
149       #   end
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)
155             end
156           end_eval
157         end
158       end
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:
163       #   helper_attr :name
164       #   attr_accessor :name
165       def helper_attr(*attrs)
166         attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
167       end
170       private
171         def default_helper_module!
172           unless name.blank?
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
177           end
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
184         end
186         def inherited_with_helper(child)
187           inherited_without_helper(child)
189           begin
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")
195           end
196         end
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' }
202         end
203     end
204   end