以下基于2.5.3

在bin/jekyll中,先将lib置顶于PATH,然后执行其中的jekyll.rb


$:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })

require 'jekyll'


然后,在lib/jekyll.rb中有require command.rb 和 commands/*


def require_all(path)
  glob = File.join(File.dirname(__FILE__), path, '*.rb')
  Dir[glob].each do |f|
    require f
  end
end

module Jekyll
  require 'jekyll/command'
end

require_all 'jekyll/commands'


而command.rb和commands/分别是这样:

在lib/jekyll/command.rb中,重写类方法、回调方法inherited,将子类塞到类的@subclass中


module Jekyll
  class Command

    class << self

      def subclasses
        @subclasses ||= []
      end

      def inherited(base)
        subclasses << base
        super(base)
      end

      #...


在lib/jekyll/commands/*中,定义鸭子方法init_with_program,该方法接受“Mercenary.program(:jekyll) do |p|”的“p” ,以build.rb为例


module Jekyll
  module Commands
    class Build < Command

      class << self

        # Create the Mercenary command for the Jekyll CLI for this Command
        def init_with_program(prog)
          prog.command(:build) do |c|
            c.syntax      'build [options]'
            c.description 'Build your site'
            c.alias :b

            add_build_options(c)

            c.action do |args, options|
              options["serving"] = false
              Jekyll::Commands::Build.process(options)
            end
          end
        end


最后,回到bin/jekyll,为@subclass中每个命令子类定义选项,实质上选项的具体已在init_with_program中写好


Mercenary.program(:jekyll) do |p|
  #...
  Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }


这个框架使得想扩展命令时,只需扩展commands中的内容即可