想观察下helper是如何加载的,于是加个断点

From: /home/z/test_rails/depot/app/helpers/application_helper.rb @ line 3 :

    1: binding.pry
    2:
 => 3: module ApplicationHelper
    4:   def something_in_cart?
    5:     @cart and not @cart.line_items.empty?
    6:   end
    7: end

[1] pry(main)> _bsi_
=> {0=>#<binding:70142525638460 object#<top="" (required)=""> /home/z/test_rails/depot/app/helpers/application_helper.rb:1>,
 1=>#<binding:70142997020320 activesupport::dependencies.block="" in="" load_file="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:477="">,
 2=>#<binding:70142525785720 activesupport::dependencies.new_constants_in="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:662="">,
 3=>#<binding:70142525861020 activesupport::dependencies.load_file="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:476="">,
 4=>#<binding:70142996807940 activesupport::dependencies.block="" in="" require_or_load="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:375="">,
 5=>#<binding:70142996741920 activesupport::dependencies.block="" in="" load_interlock="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:37="">,
 6=>#<binding:70142526081600 activesupport::dependencies::interlock#block="" in="" loading="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies="" interlock.rb:12="">,
 7=>#<binding:70142996611320 activesupport::concurrency::sharelock#exclusive="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" concurrency="" share_lock.rb:150="">,
 8=>#<binding:70142996431800 activesupport::dependencies::interlock#loading="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies="" interlock.rb:11="">,
 9=>#<binding:70142996261040 activesupport::dependencies.load_interlock="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:37="">,
 10=>#<binding:70142996019940 activesupport::dependencies.require_or_load="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:358="">,
 11=>#<binding:70142995857360 activesupport::dependencies.depend_on="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:336="">,
 12=>#<binding:70142995630260 applicationcontroller.require_dependency="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:252="">,
 13=>#<binding:70142995556120 applicationcontroller.block="" in="" modules_for_helpers="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" helpers.rb:150="">,
 14=>#<binding:70142995421940 applicationcontroller.modules_for_helpers="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" helpers.rb:145="">,
 15=>#<binding:70142995222820 applicationcontroller.modules_for_helpers="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_controller="" metal="" helpers.rb:93="">,
 16=>#<binding:70142995114560 applicationcontroller.helper="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" helpers.rb:109="">,
 17=>#<binding:70142990394940 applicationcontroller.default_helper_module!="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" helpers.rb:187="">,
 18=>#<binding:70142990015780 applicationcontroller.block="" in="" inherited="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" helpers.rb:36="">,
 19=>#<binding:70142989367180 actioncontroller::base.inherited="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" helpers.rb:36="">,
 20=>#<binding:70142988773160 actioncontroller::base.inherited="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionview-5.0.2="" lib="" action_view="" layouts.rb:217="">,
 21=>#<binding:70142988435320 actioncontroller::base.inherited="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_controller="" metal="" rendering.rb:23="">,
 22=>#<binding:70142985277140 actioncontroller::base.inherited="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_controller="" metal="" params_wrapper.rb:224="">,
 23=>#<binding:70142984428220 actioncontroller::base.block="" (2="" levels)="" in="" with="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" abstract_controller="" railties="" routes_helpers.rb:7="">,
 24=>#<binding:70142981935200 actioncontroller::base.inherited="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_controller="" railties="" helpers.rb:5="">,
 25=>#<binding:70142976438520 object#<top="" (required)=""> /home/z/test_rails/depot/app/controllers/application_controller.rb:1>,
 26=>#<binding:70142526053680 activesupport::dependencies.block="" in="" load_file="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:477="">,
 27=>#<binding:70142525963520 activesupport::dependencies.new_constants_in="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:662="">,
 28=>#<binding:70142525848780 activesupport::dependencies.load_file="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:476="">,
 29=>#<binding:70142525742220 activesupport::dependencies.block="" in="" require_or_load="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:375="">,
 30=>#<binding:70142525643900 activesupport::dependencies.block="" in="" load_interlock="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:37="">,
 31=>#<binding:70142525545500 activesupport::dependencies::interlock#block="" in="" loading="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies="" interlock.rb:12="">,
 32=>#<binding:70142525446900 activesupport::concurrency::sharelock#exclusive="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" concurrency="" share_lock.rb:150="">,
 33=>#<binding:70142525361420 activesupport::dependencies::interlock#loading="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies="" interlock.rb:11="">,
 34=>#<binding:70142525239600 activesupport::dependencies.load_interlock="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:37="">,
 35=>#<binding:70142525138400 activesupport::dependencies.require_or_load="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:358="">,
 36=>#<binding:70142525025400 activesupport::dependencies.load_missing_constant="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:511="">,
 37=>#<binding:70142524939660 object.const_missing="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:203="">,
 38=>#<binding:70142524828120 object#<top="" (required)=""> /home/z/test_rails/depot/app/controllers/store_controller.rb:1>,
 39=>#<binding:70142524682320 activesupport::dependencies.block="" in="" load_file="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:477="">,
 40=>#<binding:70142524647060 activesupport::dependencies.new_constants_in="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:662="">,
 41=>#<binding:70142524575880 activesupport::dependencies.load_file="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:476="">,
 42=>#<binding:70142524494820 activesupport::dependencies.block="" in="" require_or_load="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:375="">,
 43=>#<binding:70142524409620 activesupport::dependencies.block="" in="" load_interlock="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:37="">,
 44=>#<binding:70142524308840 activesupport::dependencies::interlock#block="" in="" loading="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies="" interlock.rb:12="">,
 45=>#<binding:70142524258380 activesupport::concurrency::sharelock#exclusive="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" concurrency="" share_lock.rb:150="">,
 46=>#<binding:70142524085460 activesupport::dependencies::interlock#loading="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies="" interlock.rb:11="">,
 47=>#<binding:70142523974600 activesupport::dependencies.load_interlock="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:37="">,
 48=>#<binding:70142523875140 activesupport::dependencies.require_or_load="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:358="">,
 49=>#<binding:70142523766560 activesupport::dependencies.load_missing_constant="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:511="">,
 50=>#<binding:70142523602100 object.const_missing="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:203="">,
 51=>#<binding:70142523557000 activesupport::inflector.block="" in="" constantize="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" inflector="" methods.rb:268="">,
 52=>#<binding:70142523471880 activesupport::inflector.constantize="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" inflector="" methods.rb:266="">,
 53=>#<binding:70142523367100 activesupport::dependencies::classcache#get="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:583="">,
 54=>#<binding:70142523290820 activesupport::dependencies.constantize="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" activesupport-5.0.2="" lib="" active_support="" dependencies.rb:614="">,
 55=>#<binding:70142523212960 actiondispatch::request#controller_class="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" http="" request.rb:81="">,
 56=>#<binding:70142523060760 actiondispatch::routing::routeset::dispatcher#controller="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" routing="" route_set.rb:44="">,
 57=>#<binding:70142523018800 actiondispatch::routing::routeset::dispatcher#serve="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" routing="" route_set.rb:30="">,
 58=>#<binding:70142522925400 actiondispatch::journey::router#block="" in="" serve="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" journey="" router.rb:39="">,
 59=>#<binding:70142522801580 actiondispatch::journey::router#serve="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" journey="" router.rb:26="">,
 60=>#<binding:70142522699960 actiondispatch::routing::routeset#call="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" routing="" route_set.rb:725="">,
 61=>#<binding:70142522594200 rack::etag#call="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" rack-2.0.1="" lib="" rack="" etag.rb:25="">,
 # ...............</binding:70142522594200></binding:70142522699960></binding:70142522801580></binding:70142522925400></binding:70142523018800></binding:70142523060760></binding:70142523212960></binding:70142523290820></binding:70142523367100></binding:70142523471880></binding:70142523557000></binding:70142523602100></binding:70142523766560></binding:70142523875140></binding:70142523974600></binding:70142524085460></binding:70142524258380></binding:70142524308840></binding:70142524409620></binding:70142524494820></binding:70142524575880></binding:70142524647060></binding:70142524682320></binding:70142524828120></binding:70142524939660></binding:70142525025400></binding:70142525138400></binding:70142525239600></binding:70142525361420></binding:70142525446900></binding:70142525545500></binding:70142525643900></binding:70142525742220></binding:70142525848780></binding:70142525963520></binding:70142526053680></binding:70142976438520></binding:70142981935200></binding:70142984428220></binding:70142985277140></binding:70142988435320></binding:70142988773160></binding:70142989367180></binding:70142990015780></binding:70142990394940></binding:70142995114560></binding:70142995222820></binding:70142995421940></binding:70142995556120></binding:70142995630260></binding:70142995857360></binding:70142996019940></binding:70142996261040></binding:70142996431800></binding:70142996611320></binding:70142526081600></binding:70142996741920></binding:70142996807940></binding:70142525861020></binding:70142525785720></binding:70142997020320></binding:70142525638460>


如无意外,应该是53到56之间的动作会加载controller,于是细看下55干的是什么

def controller_class
  params = path_parameters

  if params.key?(:controller)
    controller_param = params[:controller].underscore
    params[:action] ||= 'index'
    const_name = "#{controller_param.camelize}Controller"
    ActiveSupport::Dependencies.constantize(const_name)
  else
    PASS_NOT_FOUND
  end
end


要想知道加载哪个controller,需要从url中解析。而path_parameters这个方法并没有定义在当前类中,所以,进入该binding,检查path_parameters的source_location。不过,在当前类Request中method被重新定义过,所以需要拿Kernel的method来绑定当前binding来执行

[2] pry(main)> _bsi_[55]
=> #<binding:70142523212960 actiondispatch::request#controller_class="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.2="" lib="" action_dispatch="" http="" request.rb:81="">
[3] pry(main)> _bsi_[55].eval('method(:path_parameters).source_location')
ArgumentError: wrong number of arguments (given 1, expected 0)
from /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.0.2/lib/action_dispatch/http/request.rb:177:in `method'
[4] pry(main)> _bsi_[55].eval('method = Kernel.instance_method(:method); method.bind(self).call(:path_parameters).source_location')
=> ["/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.0.2/lib/action_dispatch/http/parameters.rb", 71]</binding:70142523212960>


源码如下,但PARAMETERS_KEY也没定义在该源码文件中

# Returns a hash with the \parameters used to form the \path of the request.
# Returned hash keys are strings:
#
#   {'action' => 'my_action', 'controller' => 'my_controller'}
def path_parameters
  get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
end


其实仅凭注释也知道它干啥,那索性执行下这个方法吧,应该没有副作用

[8] pry(main)> cd _bsi_[55]
[9] pry(#):1> path_parameters
=> {:controller=>"store", :action=>"index"}


因为目前访问的就是root,而root在route中被指向到store#index,所以也就这样了

接着再看回ActiveSupport::Dependencies.constantize

[12] pry(#):1> ActiveSupport::Dependencies.method(:constantize).source_location
=> ["/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/activesupport-5.0.2/lib/active_support/dependencies.rb", 613]


简单来说,它就是用Inflector来查找类,然后cache起来

require 'active_support/inflector'

module ActiveSupport
  module Dependencies
    extend self

    class ClassCache
      def initialize
        @store = Concurrent::Map.new
      end

      def get(key)
        key = key.name if key.respond_to?(:name)
        @store[key] ||= Inflector.constantize(key)
      end
    end

    Reference = ClassCache.new

    # Get the reference for class named +name+.
    # Raises an exception if referenced class does not exist.
    def constantize(name)
      Reference.get(name)
    end
  end
end


而在调用栈中,仅出现两次const_missing,它们的参数分别是:StoreController和:ApplicationController

[15] pry(main)> cd _bsi_[50]
[16] pry(Object):1> const_name
=> :StoreController
[17] pry(Object):1> exit
=> Object
[18] pry(main)> cd _bsi_[37]
[19] pry(Object):1> const_name
=> :ApplicationController


那么helper是在哪里加载呢?再检查下require_or_load,直接从最近的查起

[21] pry(main)> cd _bsi_[10]
[22] pry(ActiveSupport::Dependencies):1> file_name
=> "/home/z/test_rails/depot/app/helpers/application_helper"
[23] pry(ActiveSupport::Dependencies):1> exit
=> ActiveSupport::Dependencies
[31] pry(main)> cd _bsi_[11]
[32] pry(ActiveSupport::Dependencies):1> file_name
=> "application_helper"


再往上,modules_for_helpers,其源码如下

def modules_for_helpers(args)
  args += all_application_helpers if args.delete(:all)
  super(args)
end

# Returns a list of helper names in a given path.
#
#   ActionController::Base.all_helpers_from_path 'app/helpers'
#   # => ["application", "chart", "rubygems"]
def all_helpers_from_path(path)
  helpers = Array(path).flat_map do |_path|
    extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
    names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
    names.sort!
  end
  helpers.uniq!
  helpers
end

private
# Extract helper names from files in app/helpers/**/*_helper.rb
def all_application_helpers
  all_helpers_from_path(helpers_path)
end


从这里可确定,它接受了值为"application"的参数

[34] pry(main)> cd _bs_[15]
[35] pry(ApplicationController):1> all_application_helpers
=> []
[36] pry(ApplicationController):1> args
=> ["application"]
[37] pry(ApplicationController):1> helpers_path
=> []


再super的话,就转化为文件名了,"#{arg.to_s.underscore}_helper"

def modules_for_helpers(args)
  args.flatten.map! do |arg|
    case arg
    when String, Symbol
      file_name = "#{arg.to_s.underscore}_helper"
      begin
        require_dependency(file_name)
      rescue LoadError => e
        raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
      end

      mod_name = file_name.camelize
      begin
        mod_name.constantize
      rescue LoadError
        # dependencies.rb gives a similar error message but its wording is
        # not as clear because it mentions autoloading. To the user all it
        # matters is that a helper module couldn't be loaded, autoloading
        # is an internal mechanism that should not leak.
        raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
      end
    when Module
      arg
    else
      raise ArgumentError, "helper must be a String, Symbol, or Module"
    end
  end
end


再往上,18和17是这样的,default_helper_module!会被调用,而所谓默认helper,就是当前controller名字转underscore,因此,helpers/目录中会有需有同名的helper的文件

def default_helper_module!
  module_name = name.sub(/Controller$/, ''.freeze)
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
# and include the module in the template class. The second form illustrates how to include custom helpers
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
# in one of Rails' standard load paths:
#   helper :foo             # => requires 'foo_helper' and includes FooHelper
#   helper 'resources/foo'  # => requires 'resources/foo_helper' and includes Resources::FooHelper
def helper(*args, &block)
  modules_for_helpers(args).each do |mod|
    add_template_helper(mod)
  end

  _helpers.module_eval(&block) if block_given?
end


而default_helper_module!是在inherited时调用的

def inherited(klass)
  helpers = _helpers
  klass._helpers = Module.new { include helpers }
  klass.class_eval { default_helper_module! } unless klass.anonymous?
  super
end


此时的子类是ApplicationController

[41] pry(main)> cd _bs_[19]
[42] pry(ActionController::Base):1> klass
=> ApplicationController


总结起来,加载StoreController时,因需要继承ApplicationController,所以加载它,而ApplicationController的继承链上有AbstractController::Helpers,它会导致在inherited时,根据子类的名称去helpers/下找helper,因此,ApplicationController会找到application_helper.rb,StoreController作为ApplicationController的子类,也会去找store_helper.rb

简单来说,按rails的约定,每个controller可有对应的helper在helpers/中,没有也可以。