helpers.rb 4.1 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
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.
      def add_template_helper(helper_module)
        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
          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
57
              require_dependency(file_name)
D
Initial  
David Heinemeier Hansson 已提交
58 59 60 61 62 63 64 65 66 67 68 69 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 98 99 100
            rescue LoadError
              raise LoadError, "Missing helper file helpers/#{file_name}.rb"
            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'
          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
            child.helper(child.controller_name)
          rescue LoadError
            # No default helper available for this controller
          end
        end        
    end
  end
end