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

Simplify App#run by using Null Object pattern.

Welcome, Guides, Videos, Examples and Categories now all have a
static #create method to be used instead of #new for instantiation.

The #create returns either the corresponding object instance or
NullObject which behaves as if it were the needed object, but
effectively does nothing.

Categories.create is a bit different - it first uses one of two
strategies for creating categories: FileCategories when filename
present or AutoCategories when not, then feeding the resulting
categories list to actual Categories object.
parent fcae5605
Loading
Loading
Loading
Loading
+5 −27
Original line number Diff line number Diff line
@@ -47,33 +47,11 @@ module JsDuck
      Lint.new(@relations).run

      @images = Images.new(@opts.images)

      @welcome = Welcome.new
      if @opts.welcome
        @welcome.parse(@opts.welcome)
      end

      @guides = Guides.new(get_doc_formatter)
      if @opts.guides
        @guides.parse(@opts.guides)
      end

      @videos = Videos.new
      if @opts.videos
        @videos.parse(@opts.videos)
      end

      @examples = Examples.new
      if @opts.examples
        @examples.parse(@opts.examples)
      end

      @categories = Categories.new(get_doc_formatter, @relations)
      if @opts.categories_path
        @categories.parse(@opts.categories_path)
      else
        @categories.auto_generate
      end
      @welcome = Welcome.create(@opts.welcome)
      @guides = Guides.create(@opts.guides, get_doc_formatter)
      @videos = Videos.create(@opts.videos)
      @examples = Examples.create(@opts.examples)
      @categories = Categories.create(@opts.categories_path, get_doc_formatter, @relations)

      clear_output_dir unless @opts.export == :stdout
      if @opts.export == :stdout
+11 −60
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/json_duck'
require 'jsduck/file_categories'
require 'jsduck/auto_categories'

module JsDuck

  # Reads in categories and outputs them as HTML div
  class Categories
    def initialize(doc_formatter, relations={})
      @doc_formatter = doc_formatter
      @relations = relations
      @categories = []
    end

    # Automatically divides all available classes into categories
    def auto_generate
      @categories = AutoCategories.new(@relations).generate
    end

    # Parses categories in JSON file
    def parse(path)
      @categories = JsonDuck.read(path)

      # Don't crash if old syntax is used.
      if @categories.is_a?(Hash) && @categories["categories"]
        Logger.instance.warn(:old_cat_format, 'Update categories file to contain just the array inside {"categories": [...]}')
        @categories = @categories["categories"]
      end

      # Perform expansion on all class names containing * wildcard
      @categories.each do |cat|
        cat["groups"].each do |group|
          group["classes"] = group["classes"].map do |name|
            expand(name) # name =~ /\*/ ? expand(name) : name
          end.flatten
        end
      end

      validate
    end

    # Expands class name like 'Foo.*' into multiple class names.
    def expand(name)
      re = Regexp.new("^" + name.split(/\*/, -1).map {|part| Regexp.escape(part) }.join('.*') + "$")
      classes = @relations.to_a.find_all {|cls| re =~ cls[:name] && !cls[:private] }.map {|cls| cls[:name] }.sort
      if classes.length == 0
        Logger.instance.warn(:cat_no_match, "No class found matching a pattern '#{name}' in categories file.")
      end
      classes
    end

    # Prints warnings for missing classes in categories file
    def validate
      # Build a map of all classes listed in categories
      listed_classes = {}
      @categories.each do |cat|
        cat["groups"].each do |group|
          group["classes"].each do |cls_name|
            listed_classes[cls_name] = true
          end
    def self.create(filename, doc_formatter, relations)
      if filename
        categories = FileCategories.new(filename, relations)
      else
        categories = AutoCategories.new(relations)
      end
      Categories.new(categories.generate, doc_formatter, relations)
    end

      # Check that each existing non-private class is listed
      @relations.each do |cls|
        unless listed_classes[cls[:name]] || cls[:private]
          Logger.instance.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file")
        end
      end
    def initialize(categories, doc_formatter, relations={})
      @categories = categories
      @doc_formatter = doc_formatter
      @relations = relations
    end

    # Returns HTML listing of classes divided into categories
    def to_html
      return "" if @categories.length == 0

      html = @categories.map do |category|
        [
          "<div class='section'>",
+9 −5
Original line number Diff line number Diff line
require 'jsduck/json_duck'
require 'jsduck/null_object'

module JsDuck

  # Reads in examples JSON file
  class Examples
    def initialize
      @examples = []
    # Creates Examples object from filename.
    def self.create(filename)
      if filename
        Examples.new(filename)
      else
        NullObject.new(:to_array => [])
      end
    end

    # Parses examples config file
    def parse(filename)
    def initialize(filename)
      @examples = JsonDuck.read(filename)
    end

    # Writes examples JSON file to dir
    def write(dir)
      return if @examples.length == 0

      FileUtils.mkdir(dir) unless File.exists?(dir)
      # Write the JSON to output dir, so it's available in released
      # version of docs and people can use it with JSDuck by themselves.
+65 −0
Original line number Diff line number Diff line
module JsDuck

  # Reads categories info from config file
  class FileCategories
    def initialize(filename, relations)
      @filename = filename
      @relations = relations
    end

    # Parses categories in JSON file
    def generate
      @categories = JsonDuck.read(@filename)

      # Don't crash if old syntax is used.
      if @categories.is_a?(Hash) && @categories["categories"]
        Logger.instance.warn(:old_cat_format, 'Update categories file to contain just the array inside {"categories": [...]}')
        @categories = @categories["categories"]
      end

      # Perform expansion on all class names containing * wildcard
      @categories.each do |cat|
        cat["groups"].each do |group|
          group["classes"] = group["classes"].map do |name|
            expand(name) # name =~ /\*/ ? expand(name) : name
          end.flatten
        end
      end

      validate

      @categories
    end

    # Expands class name like 'Foo.*' into multiple class names.
    def expand(name)
      re = Regexp.new("^" + name.split(/\*/, -1).map {|part| Regexp.escape(part) }.join('.*') + "$")
      classes = @relations.to_a.find_all {|cls| re =~ cls[:name] && !cls[:private] }.map {|cls| cls[:name] }.sort
      if classes.length == 0
        Logger.instance.warn(:cat_no_match, "No class found matching a pattern '#{name}' in categories file.")
      end
      classes
    end

    # Prints warnings for missing classes in categories file
    def validate
      # Build a map of all classes listed in categories
      listed_classes = {}
      @categories.each do |cat|
        cat["groups"].each do |group|
          group["classes"].each do |cls_name|
            listed_classes[cls_name] = true
          end
        end
      end

      # Check that each existing non-private class is listed
      @relations.each do |cls|
        unless listed_classes[cls[:name]] || cls[:private]
          Logger.instance.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file")
        end
      end
    end
  end

end
+10 −8
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/json_duck'
require 'jsduck/null_object'
require 'fileutils'

module JsDuck

  # Reads in guides and converts them to JsonP files
  class Guides
    def initialize(formatter)
      @formatter = formatter
      @guides = []
    # Creates Guides object from filename and formatter
    def self.create(filename, formatter)
      if filename
        Guides.new(filename, formatter)
      else
        NullObject.new(:to_array => [], :to_html => "")
      end
    end

    # Parses guides config file
    def parse(filename)
    def initialize(filename, formatter)
      @path = File.dirname(filename)
      @guides = JsonDuck.read(filename)
      @formatter = formatter
    end

    # Writes all guides to given dir in JsonP format
    def write(dir)
      return if @guides.length == 0

      FileUtils.mkdir(dir) unless File.exists?(dir)
      @guides.each {|group| group["items"].each {|g| write_guide(g, dir) } }
      # Write the JSON to output dir, so it's available in released
@@ -63,8 +67,6 @@ module JsDuck

    # Returns HTML listing of guides
    def to_html
      return "" if @guides.length == 0

      html = @guides.map do |group|
        [
          "<h3>#{group['title']}</h3>",
Loading