module Overrider

Constants

VERSION

Public Class Methods

disable=(v) click to toggle source
# File lib/overrider.rb, line 48
def self.disable=(v)
  @disable = v
end
disabled?() click to toggle source
# File lib/overrider.rb, line 52
def self.disabled?
  @disable
end
enabled?() click to toggle source
# File lib/overrider.rb, line 56
def self.enabled?
  !disabled?
end
sexps() click to toggle source
# File lib/overrider.rb, line 60
def self.sexps
  @sexps ||= {}
end

Private Instance Methods

override(symbol) click to toggle source
# File lib/overrider.rb, line 124
def override(symbol)
  return symbol if Overrider.disabled?

  owner = self
  override_methods.add(instance_method(symbol))

  caller_info = caller_locations(1, 1)[0]
  unless Overrider.sexps[caller_info.absolute_path]
    Overrider.sexps[caller_info.absolute_path] ||= Ripper.sexp(File.read(caller_info.absolute_path))
  end

  trace_for_override(owner)

  symbol
end
override_methods() click to toggle source
# File lib/overrider.rb, line 66
def override_methods
  @override_methods ||= Set.new
end
override_singleton_method(symbol) click to toggle source
# File lib/overrider.rb, line 140
def override_singleton_method(symbol)
  return symbol if Overrider.disabled?

  owner = self
  override_methods.add(singleton_class.instance_method(symbol))

  caller_info = caller_locations(1, 1)[0]
  unless Overrider.sexps[caller_info.absolute_path]
    Overrider.sexps[caller_info.absolute_path] ||= Ripper.sexp(File.read(caller_info.absolute_path))
  end

  trace_for_override(owner)

  symbol
end
trace_for_override(owner) click to toggle source
# File lib/overrider.rb, line 72
def trace_for_override(owner)
  @__overrider_trace_point ||= TracePoint.trace(:end, :c_return, :return, :raise) do |t|
    if t.event == :raise
      @__overrider_trace_point.disable
      @__overrider_trace_point = nil
      next
    end

    klass = t.self

    override_at_outer = false
    if t.event == :return && klass == self && (t.method_id == :override || t.method_id == :override_singleton_method)
      c = caller_locations(2, 1)[0]
      traverser = SexpTraverser.new(Overrider.sexps[c.absolute_path])
      traverser.traverse do |n, parent|
        if n[0] == :@ident && (n[1] == "override" || n[1] == "override_singleton_method") && n[2][0] == c.lineno
          if parent[0] == :command || parent[0] == :fcall
            # override :foo
          elsif parent[0] == :command_call || parent[0] == :call
            if parent[1][0] == :var_ref && parent[1][1][0] == :@kw && parent[1][1][1] == "self"
              # self.override :foo
            else
              # unknown case
              warn "call `override` method by unknown way"
              override_at_outer = true
            end
          else
            override_at_outer = true
          end
        end
      end
    end

    target_class_end = klass == owner && t.event == :end
    target_class_new_end = (klass == Class || klass == Module) && t.event == :c_return && t.method_id == :new && t.return_value == owner

    if target_class_end || target_class_new_end || override_at_outer
      override_methods.each do |meth|
        unless meth.super_method
          @__overrider_trace_point.disable
          @__overrider_trace_point = nil
          raise NoSuperMethodError.new(self, meth, caller(4))
        end
      end
      @__overrider_trace_point.disable
      @__overrider_trace_point = nil
    end
  end
end