编写plugin代码

在gem的lib/puma/plugin/gem_name.rb中编写如下代码,例如yabeda-puma-plugin([[yabeda-puma-plugin浅析]])

# yabeda-puma-plugin-0.6.0/lib/puma/plugin/yabeda.rb
Puma::Plugin.create do
  def start(launcher)
    # ...
  end
end

puma就会以该block创建一个Plugin子类,并以名字yabeda注册到Plugins

# puma-5.6.5/lib/puma/plugin.rb
module Puma
  class Plugin
    # Matches
    #  "C:/Ruby22/lib/ruby/gems/2.2.0/gems/puma-3.0.1/lib/puma/plugin/tmp_restart.rb:3:in `'"
    #  AS
    #  C:/Ruby22/lib/ruby/gems/2.2.0/gems/puma-3.0.1/lib/puma/plugin/tmp_restart.rb
    CALLER_FILE = /
      \A       # start of string
      .+       # file path (one or more characters)
      (?=      # stop previous match when
        :\d+     # a colon is followed by one or more digits
        :in      # followed by a colon followed by in
      )
    /x

    def self.extract_name(ary)
      path = ary.first[CALLER_FILE]
      m = %r!puma/plugin/([^/]*)\.rb$!.match(path)
      return m[1]
    end

    def self.create(&blk)
      name = extract_name(caller)
      cls = Class.new(self)
      cls.class_eval(&blk)
      Plugins.register name, cls
    end
  end
end

在项目中使用plugin

在config/puma.rb中,使用plugin命令

# puma-5.6.5/lib/puma/dsl.rb
module Puma
  class DSL
    def plugin(name)
      @plugins << @config.load_plugin(name)
    end
  end
end

例如plugin :yabeda,就会尝试从Plugins找回名为yabeda的那个插件,如果没有就会尝试require

(这个require是改写过的,会搜寻gems目录下每个gem看是否有匹配的puma/plugin/xxx.rb,有就加载该文件)

# puma-5.6.5/lib/puma/configuration.rb
module Puma
  class Configuration
    def load_plugin(name)
      @plugins.create name
    end
  end
end

# puma-5.6.5/lib/puma/plugin.rb
module Puma
  class PluginLoader
    def create(name)
      if cls = Plugins.find(name)
        plugin = cls.new
        @instances << plugin
        return plugin
      end

      raise UnknownPlugin, "File failed to register properly named plugin"
    end
  end

  class PluginRegistry
    def find(name)
      name = name.to_s

      if cls = @plugins[name]
        return cls
      end

      begin
        require "puma/plugin/#{name}"
      rescue LoadError
        raise UnknownPlugin, "Unable to find plugin: #{name}"
      end

      if cls = @plugins[name]
        return cls
      end

      raise UnknownPlugin, "file failed to register a plugin"
    end
  end

  Plugins = PluginRegistry.new
end

puma运行plugin

puma启动后,就会调用每个Plugin子类的start方法,也就是Puma::Plugin.create{ }中所编写的那个

# puma-5.6.5/lib/puma/launcher.rb
module Puma
  class Launcher
    def run
      # ...
      @config.plugins.fire_starts self
      # ...
    end
  end
end

# puma-5.6.5/lib/puma/plugin.rb
module Puma
  class PluginLoader
    def fire_starts(launcher)
      @instances.each do |i|
        if i.respond_to? :start
          i.start(launcher)
        end
      end
    end
  end
end