{"record":{"id":"c025e51c84e37c25","repo":"github/markup","slug":"subclasses-of-github-markup-implementation-must","errorCode":null,"errorMessage":"subclasses of GitHub::Markup::Implementation must define #render","messagePattern":"subclasses of GitHub::Markup::Implementation must define #render","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"lib/github/markup/implementation.rb","lineNumber":24,"sourceCode":"\n      def initialize(regexp, languages)\n        @regexp = regexp\n\n        if defined?(::Linguist)\n          @languages = languages.map do |l|\n            lang = Linguist::Language[l]\n            raise \"no match for language #{l.inspect}\" if lang.nil?\n            lang\n          end\n        end\n      end\n\n      def load\n        # no-op by default\n      end\n\n      def render(filename, content, options: {})\n        raise NotImplementedError, \"subclasses of GitHub::Markup::Implementation must define #render\"\n      end\n\n      def match?(filename, language)\n        if defined?(::Linguist)\n          languages.include? language\n        else\n          file_ext_regexp =~ filename\n        end\n      end\n\n      private\n\n      def file_ext_regexp\n        @file_ext_regexp ||= /\\.(#{regexp})\\z/\n      end\n    end\n  end\nend","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/github/markup/blob/76e2682193828b98471b3a071edf4db0590ccacb/lib/github/markup/implementation.rb#L6-L42","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Define the method with the exact expected signature in your subclass: def render(filename, content, options: {}) ... end.","Prefer subclassing a concrete class (CommandImplementation for shell-out renderers, GemImplementation for gem-backed ones) so render is provided.","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."],"exampleFix":"# before\nclass WikiImpl < GitHub::Markup::Implementation\n  def initialize\n    super(/wiki\\z/, [])\n  end\n  # forgot #render\nend\nGitHub::Markup.markup_impl(:wiki, WikiImpl.new)\nWikiImpl.new.render('a.wiki', 'text')\n# => NotImplementedError: subclasses of GitHub::Markup::Implementation must define #render\n\n# after\nclass WikiImpl < GitHub::Markup::Implementation\n  def initialize\n    super(/wiki\\z/, [])\n  end\n\n  def render(filename, content, options: {})\n    WikiConverter.to_html(content)\n  end\nend","handlingStrategy":"type-guard","validationCode":"# Fail at registration time, not at first render:\ndef register_impl(symbol, impl)\n  if impl.method(:render).owner == GitHub::Markup::Implementation\n    raise ArgumentError, \"#{impl.class} does not implement #render\"\n  end\n  GitHub::Markup.markup_impl(symbol, impl)\nend","typeGuard":"# Ruby method-owner guard: detects a subclass riding on the base class's abstract render.\ndef implements_render?(impl)\n  impl.is_a?(GitHub::Markup::Implementation) &&\n    impl.method(:render).owner != GitHub::Markup::Implementation\nend","tryCatchPattern":"begin\n  impl.render(filename, content)\nrescue NotImplementedError => e\n  raise ArgumentError, \"#{impl.class} is incomplete: #{e.message}\"\nend","preventionTips":["Subclass a concrete class (CommandImplementation, GemImplementation, Markdown) whenever possible so #render is inherited.","Add an abstract-method unit test per custom implementation: assert it responds with real output for a sample document.","Run a boot-time render probe for every registered implementation to catch missing overrides before traffic."],"tags":["subclass-contract","not-implemented","ruby","github-markup"],"backgroundTag":"abstract-method-not-implemented","analyzedSha":"76e2682193828b98471b3a071edf4db0590ccacb","analyzedAt":"2026-08-21T19:16:29.859Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}