helpers.rb 4.8 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2 3 4
module ActionController #:nodoc:
  module Helpers #:nodoc:
    def self.append_features(base)
      super
5 6 7 8 9 10

      # Initialize the base module to aggregate its helpers.
      base.class_inheritable_accessor :master_helper_module
      base.master_helper_module = Module.new

      # Extend base with class methods to declare helpers.
D
Initial  
David Heinemeier Hansson 已提交
11
      base.extend(ClassMethods)
12 13 14 15 16 17 18 19

      base.class_eval do
        # Wrap inherited to create a new master helper module for subclasses.
        class << self
          alias_method :inherited_without_helper, :inherited
          alias_method :inherited, :inherited_with_helper
        end
      end
D
Initial  
David Heinemeier Hansson 已提交
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
    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 已提交
47
      def add_template_helper(helper_module) #:nodoc:
48
        master_helper_module.module_eval "include #{helper_module}"
D
Initial  
David Heinemeier Hansson 已提交
49 50
      end

D
David Heinemeier Hansson 已提交
51
      # Declare a helper:
D
Initial  
David Heinemeier Hansson 已提交
52 53 54 55 56 57 58 59 60 61 62
      #   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
63 64 65 66 67
            when Module
              add_template_helper(arg)
            when String, Symbol
              file_name  = arg.to_s.underscore + '_helper'
              class_name = file_name.camelize
68
                
69 70 71 72
              begin
                require_dependency(file_name)
              rescue LoadError => load_error
                requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
73 74
                msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
                raise LoadError.new(msg).copy_blame!(load_error)
75
              end
76 77 78 79

              add_template_helper(class_name.constantize)
            else
              raise ArgumentError, 'helper expects String, Symbol, or Module argument'
D
Initial  
David Heinemeier Hansson 已提交
80 81 82 83
          end
        end

        # Evaluate block in template class if given.
84
        master_helper_module.module_eval(&block) if block_given?
D
Initial  
David Heinemeier Hansson 已提交
85 86 87 88 89 90 91
      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)
92 93 94 95 96 97 98
        methods.flatten.each do |method|
          master_helper_module.module_eval <<-end_eval
            def #{method}(*args, &block)
              controller.send(%(#{method}), *args, &block)
            end
          end_eval
        end
D
Initial  
David Heinemeier Hansson 已提交
99 100 101 102 103 104 105 106 107 108 109 110
      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 
111
        def inherited_with_helper(child)
D
Initial  
David Heinemeier Hansson 已提交
112
          inherited_without_helper(child)
113 114 115 116
          begin
            child.master_helper_module = Module.new
            child.master_helper_module.send :include, master_helper_module
            child.helper child.controller_path
117
          rescue MissingSourceFile => e
118
            raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
D
Initial  
David Heinemeier Hansson 已提交
119 120 121 122 123
          end
        end        
    end
  end
end