Commit c9300d3c authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Allow overriding of builtin tags.

Previously when --tags option was used all the builtin tags got
loaded first and registered, after that the tags from additional
paths got loaded and registered, but loading an override for a tag
didn't trigger re-registration, effectively meaning that one could
override all the other methods of a builtin class except the
constructor - but that's the most useful thing to override.

Now when --tags option is used, the TagRegistry is reinstantiated
with list of additional load paths, and on first access to the
registry, the classes from all paths get loaded in and only after
all tags are loaded will they get instantiated, so one can also
override the constructor.
parent 289b8a72
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -155,7 +155,9 @@ module JsDuck
      end
      validate

      @custom_tag_paths.each {|path| TagRegistry.load_from(path) }
      if @custom_tag_paths.length > 0
        TagRegistry.reconfigure(@custom_tag_paths)
      end
    end

    def create_option_parser
+18 −14
Original line number Diff line number Diff line
@@ -2,35 +2,39 @@ require "jsduck/tag/tag"

module JsDuck

  # Loads Tag classes from the builtin tags/ dir and optionally also
  # from additional paths.
  class TagLoader
    def initialize
      @already_loaded = {}
    def initialize(extra_paths=[])
      @paths = [File.dirname(__FILE__) + "/tag"]
      @paths += extra_paths
    end

    # Loads tag classes from given dir or single file.
    # Loads Tag classes from all supplied paths.
    #
    # Returns the tag classes that got loaded, sorted alphabetically
    # by class name. This ensures attributes in member signatures are
    # always rendered in the same order.
    def load_from(path)
    # by class name.
    def load_all
      @paths.each {|path| load(path) }
      tag_classes
    end

    private

    # Loads tag classes from given dir or single file.
    def load(path)
      if File.directory?(path)
        Dir[path+"/**/*.rb"].each {|file| require(file) }
      else
        require(path)
      end

      tag_classes
    end

    private

    def tag_classes
      classes = JsDuck::Tag::Tag.descendants
      # exclude already loaded classes
      classes.reject! {|cls| @already_loaded[cls.name] }
      # remember these classes as loaded
      classes.each {|cls| @already_loaded[cls.name] = true }
      # sort by classname
      # This ensures attributes in member signatures are
      # always rendered in the same order.
      classes.sort {|a, b| a.name <=> b.name }
    end

+19 −10
Original line number Diff line number Diff line
require "jsduck/tag_loader"
require "jsduck/util/singleton"

module JsDuck

  # Access to builtin @tags
  class TagRegistry
    include Util::Singleton
    # Access to the singleton instance (only used internally)
    def self.instance
      @instance = TagRegistry.new unless @instance
      @instance
    end

    # Reconfigures the registry with additional load paths.
    # Used in Options class.
    def self.reconfigure(load_paths)
      @instance = TagRegistry.new(load_paths)
    end

    def initialize
    # Redirect calls from TagRegistry.method to TagRegistry.instance.method,
    # making it behave like other Singleton classes.
    def self.method_missing(meth, *args, &block)
      self.instance.send(meth, *args, &block)
    end

    def initialize(load_paths=[])
      @patterns = {}
      @ext_define_patterns = {}
      @ext_define_defaults = {}
@@ -19,13 +34,7 @@ module JsDuck
      @member_types = []
      @css = []

      @loader = TagLoader.new
      load_from(File.dirname(__FILE__) + "/tag")
    end

    # Loads and instantiates tags from the given file or dir.
    def load_from(path)
      instantiate_tags(@loader.load_from(path))
      instantiate_tags(TagLoader.new(load_paths).load_all)
    end

    # Instantiates all descendants of JsDuck::Tag::Tag