github/markup · error · NotImplementedError

subclasses of GitHub::Markup::Implementation must define #re

Error message

subclasses of GitHub::Markup::Implementation must define #render

What it means

GitHub::Markup::Implementation is an abstract base class; its #render unconditionally raises NotImplementedError. Every concrete renderer (Markdown, GemImplementation, CommandImplementation, and your own subclasses) must override render(filename, content, options: {}). The base raise exists so that a forgotten override fails loudly at render time instead of silently returning garbage.

Source

Thrown at lib/github/markup/implementation.rb:24

      def initialize(regexp, languages)
        @regexp = regexp

        if defined?(::Linguist)
          @languages = languages.map do |l|
            lang = Linguist::Language[l]
            raise "no match for language #{l.inspect}" if lang.nil?
            lang
          end
        end
      end

      def load
        # no-op by default
      end

      def render(filename, content, options: {})
        raise NotImplementedError, "subclasses of GitHub::Markup::Implementation must define #render"
      end

      def match?(filename, language)
        if defined?(::Linguist)
          languages.include? language
        else
          file_ext_regexp =~ filename
        end
      end

      private

      def file_ext_regexp
        @file_ext_regexp ||= /\.(#{regexp})\z/
      end
    end
  end
end

View on GitHub (pinned to 76e2682193)

Solutions

  1. Define the method with the exact expected signature in your subclass: def render(filename, content, options: {}) ... end.
  2. Prefer subclassing a concrete class (CommandImplementation for shell-out renderers, GemImplementation for gem-backed ones) so render is provided.
  3. Add an abstractness assertion when registering: raise if impl.method(:render).owner == GitHub::Markup::Implementation, so the error surfaces at boot rather than mid-request.

Example fix

# before
class WikiImpl < GitHub::Markup::Implementation
  def initialize
    super(/wiki\z/, [])
  end
  # forgot #render
end
GitHub::Markup.markup_impl(:wiki, WikiImpl.new)
WikiImpl.new.render('a.wiki', 'text')
# => NotImplementedError: subclasses of GitHub::Markup::Implementation must define #render

# after
class WikiImpl < GitHub::Markup::Implementation
  def initialize
    super(/wiki\z/, [])
  end

  def render(filename, content, options: {})
    WikiConverter.to_html(content)
  end
end
Defensive patterns

Strategy: type-guard

Validate before calling

# Fail at registration time, not at first render:
def register_impl(symbol, impl)
  if impl.method(:render).owner == GitHub::Markup::Implementation
    raise ArgumentError, "#{impl.class} does not implement #render"
  end
  GitHub::Markup.markup_impl(symbol, impl)
end

Type guard

# Ruby method-owner guard: detects a subclass riding on the base class's abstract render.
def implements_render?(impl)
  impl.is_a?(GitHub::Markup::Implementation) &&
    impl.method(:render).owner != GitHub::Markup::Implementation
end

Try / catch

begin
  impl.render(filename, content)
rescue NotImplementedError => e
  raise ArgumentError, "#{impl.class} is incomplete: #{e.message}"
end

Prevention

When it happens

Trigger: Defining a class < GitHub::Markup::Implementation with only match?/regexp customized and then calling impl.render(...); registering a custom implementation via markup_impl that lacks a #render method; instantiating GitHub::Markup::Implementation.new directly and calling render on it.

Common situations: Writing a custom in-process renderer for a new markup format and forgetting the render method; refactoring a subclass and accidentally renaming render to to_html; test doubles that subclass Implementation without implementing the contract.

Related errors


AI-assisted analysis of github/markup@76e2682193 (2026-08-21). Data as JSON: /api/errors/c025e51c84e37c25. Report an issue: GitHub.