require 'set'

module Callback
  HOOKS = [:before, :after].freeze

  class Chain
    HOOKS.each do |cb_hook|
      define_method("#{cb_hook}_chain_of") do |mth_name|
        get_hook(cb_hook, mth_name)
      end
    end

    def initialize
      HOOKS.each do |cb_hook|
        instance_variable_set("@_#{cb_hook}_chain", new_chain)
      end
    end

    def append(hook_name, mth, hook_mth)
      chain = get_chain(hook_name)
      chain[mth] << hook_mth
    end

    private

    def get_hook(hook_name, mth)
      chain = get_chain(hook_name)
      chain[mth].dup
    end

    def get_chain(hook_name)
      instance_variable_get("@_#{hook_name}_chain")
    end

    def new_chain
      Hash.new { |h, k| h[k] = Set.new }
    end
  end
end