helpers.rb 4.3 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
module ActionController #:nodoc:
  module Helpers #:nodoc:
    def self.append_features(base)
      super
      base.class_eval { class << self; alias_method :inherited_without_helper, :inherited; end }
      base.extend(ClassMethods)
    end

    # The template helpers serves to relieve the templates from including the same inline code again and again. It's a
    # set of standardized methods for working with forms (FormHelper), dates (DateHelper), texts (TextHelper), and 
    # Active Records (ActiveRecordHelper) that's available to all templates by default.
    #
    # It's also really easy to make your own helpers and it's much encouraged to keep the template files free
    # from complicated logic. It's even encouraged to bundle common compositions of methods from other helpers 
    # (often the common helpers) as they're used by the specific application.
    # 
    #   module MyHelper
    #     def hello_world() "hello world" end
    #   end
    # 
    # MyHelper can now be included in a controller, like this:
    # 
    #   class MyController < ActionController::Base
    #     helper :my_helper
    #   end
    # 
    # ...and, same as above, used in any template rendered from MyController, like this:
    # 
    # Let's hear what the helper has to say: <tt><%= hello_world %></tt>
    module ClassMethods
      # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
      # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules 
      # available to the templates.
D
David Heinemeier Hansson 已提交
34
      def add_template_helper(helper_module) #:nodoc:
D
Initial  
David Heinemeier Hansson 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
        template_class.class_eval "include #{helper_module}"
      end

      # Declare a helper.  If you use this method in your controller, you don't
      # have to do the +self.append_features+ incantation in your helper class.
      #   helper :foo
      # requires 'foo_helper' and includes FooHelper in the template class.
      #   helper FooHelper
      # includes FooHelper in the template class.
      #   helper { def foo() "#{bar} is the very best" end }
      # evaluates the block in the template class, adding method #foo.
      #   helper(:three, BlindHelper) { def mice() 'mice' end }
      # does all three.
      def helper(*args, &block)
        args.flatten.each do |arg|
          case arg
51 52 53 54 55 56 57 58 59 60 61 62 63
          when Module
            add_template_helper(arg)
          when String, Symbol
            file_name  = Inflector.underscore(arg.to_s.downcase) + '_helper'
            class_name = Inflector.camelize(file_name)
            begin
              require_dependency(file_name)
            rescue LoadError => load_error
              requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
              if requiree == file_name
                raise LoadError, "Missing helper file helpers/#{file_name}.rb"
              else
                raise LoadError, "Can't load file: #{requiree}"
64
              end
65 66 67 68 69
            end
            raise ArgumentError, "Missing #{class_name} module in helpers/#{file_name}.rb" unless Object.const_defined?(class_name)
            add_template_helper(Object.const_get(class_name))
          else
            raise ArgumentError, 'helper expects String, Symbol, or Module argument'
D
Initial  
David Heinemeier Hansson 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
          end
        end

        # Evaluate block in template class if given.
        template_class.module_eval(&block) if block_given?
      end

      # Declare a controller method as a helper.  For example,
      #   helper_method :link_to
      #   def link_to(name, options) ... end
      # makes the link_to controller method available in the view.
      def helper_method(*methods)
        template_class.controller_delegate(*methods)
      end

      # Declare a controller attribute as a helper.  For example,
      #   helper_attr :name
      #   attr_accessor :name
      # makes the name and name= controller methods available in the view.
      # The is a convenience wrapper for helper_method.
      def helper_attr(*attrs)
        attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
      end

      private 
        def inherited(child)
          inherited_without_helper(child)
          begin
98
            child.helper(child.controller_name)
99
          rescue ArgumentError, LoadError
D
Initial  
David Heinemeier Hansson 已提交
100 101 102 103 104 105
            # No default helper available for this controller
          end
        end        
    end
  end
end