~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/actionpack/lib/action_controller/helpers.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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