Commit 2b045f55 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Extract Warnings class from Logger.

Test the warnings system through the Warnings class instead.
Switch Logger class back to the old system of including Util::Singleton.
parent 1b5d7a02
Loading
Loading
Loading
Loading
+13 −78
Original line number Diff line number Diff line
require 'jsduck/util/singleton'
require 'jsduck/util/os'
require 'jsduck/warnings'

module JsDuck

  # Central logging of JsDuck
  class LoggerCls
  class Logger
    include Util::Singleton

    # Set to true to enable verbose logging
    attr_accessor :verbose

@@ -15,51 +19,7 @@ module JsDuck
      @verbose = false
      @colors = nil

      @warning_docs = [
        [:global, "Member doesn't belong to any class"],
        [:inheritdoc, "@inheritdoc referring to unknown class or member"],
        [:extend, "@extend/mixin/requires/uses referring to unknown class"],
        [:tag, "Use of unsupported @tag"],
        [:tag_repeated, "An @tag used multiple times, but only once allowed"],
        [:tag_syntax, "@tag syntax error"],
        [:link, "{@link} to unknown class or member"],
        [:link_ambiguous, "{@link} is ambiguous"],
        [:link_auto, "Auto-detected link to unknown class or member"],
        [:html, "Unclosed HTML tag"],

        [:alt_name, "Name used as both classname and alternate classname"],
        [:name_missing, "Member or parameter has no name"],
        [:no_doc, "Public class without documentation"],
        [:no_doc_member, "Public member without documentation"],
        [:no_doc_param, "Parameter of public member without documentation"],
        [:dup_param, "Method has two parameters with the same name"],
        [:dup_member, "Class has two members with the same name"],
        [:req_after_opt, "Required parameter comes after optional"],
        [:param_count, "Less parameters documented than detected from code"],
        [:subproperty, "@param foo.bar where foo param doesn't exist"],
        [:sing_static, "Singleton class member marked as @static"],
        [:type_syntax, "Syntax error in {type definition}"],
        [:type_name, "Unknown type referenced in {type definition}"],
        [:enum, "Enum with invalid values or no values at all"],
        [:fires, "@fires references unknown event"],

        [:image, "{@img} referring to missing file"],
        [:image_unused, "An image exists in --images dir that's not used"],
        [:cat_old_format, "Categories file uses old deprecated format"],
        [:cat_no_match, "Class pattern in categories file matches nothing"],
        [:cat_class_missing, "Class is missing from categories file"],
        [:guide, "Guide is missing from --guides dir"],

        [:aside, "Problem with @aside tag"],
        [:hide, "Problem with @hide tag"],
      ]
      # Turn off all warnings by default.
      # This is good for testing.
      # When running JSDuck app, the Options class enables most warnings.
      @warnings = {}
      @warning_docs.each do |w|
        @warnings[w[0]] = {:enabled => false, :patterns => []}
      end
      @warnings = Warnings.new

      @shown_warnings = {}
    end
@@ -75,30 +35,16 @@ module JsDuck
    # or all warnings when type == :all.
    # Additionally a filename pattern can be specified.
    def set_warning(type, enabled, pattern=nil)
      if type == :all
        # When used with a pattern, only add the pattern to the rules
        # where it can have an effect - otherwise we get a warning.
        @warnings.each_key do |key|
          set_warning(key, enabled, pattern) unless pattern && @warnings[key][:enabled] == enabled
        end
      elsif @warnings.has_key?(type)
        if pattern
          if @warnings[type][:enabled] == enabled
            warn(nil, "Warning rule '#{enabled ? '+' : '-'}#{type}:#{pattern}' has no effect")
          else
            @warnings[type][:patterns] << Regexp.new(Regexp.escape(pattern))
          end
        else
          @warnings[type] = {:enabled => enabled, :patterns => []}
        end
      else
        warn(nil, "Warning of type '#{type}' doesn't exist")
      begin
        @warnings.set(type, enabled, pattern)
      rescue Exception => e
        warn(nil, e.message)
      end
    end

    # get documentation for all warnings
    def doc_warnings
      @warning_docs.map {|w| " #{@warnings[w[0]][:enabled] ? '+' : '-'}#{w[0]} - #{w[1]}" }
      @warnings.doc
    end

    # Prints warning message.
@@ -134,21 +80,13 @@ module JsDuck
      return false
    end

    # True when the warning is enabled for the given type and filename
    # combination.
    def warning_enabled?(type, filename)
      if type == nil
        true
      elsif !@warnings.has_key?(type)
      elsif !@warnings.has?(type)
        warn(nil, "Unknown warning type #{type}")
        false
      else
        rule = @warnings[type]
        if rule[:patterns].any? {|re| filename =~ re }
          !rule[:enabled]
      else
          rule[:enabled]
        end
        @warnings.enabled?(type, filename)
      end
    end

@@ -212,7 +150,4 @@ module JsDuck
    end
  end

  # The actual global Logger instance
  Logger = LoggerCls.new

end

lib/jsduck/warnings.rb

0 → 100644
+101 −0
Original line number Diff line number Diff line
module JsDuck

  # Warnings management
  class Warnings

    def initialize
      @docs = [
        [:global, "Member doesn't belong to any class"],
        [:inheritdoc, "@inheritdoc referring to unknown class or member"],
        [:extend, "@extend/mixin/requires/uses referring to unknown class"],
        [:tag, "Use of unsupported @tag"],
        [:tag_repeated, "An @tag used multiple times, but only once allowed"],
        [:tag_syntax, "@tag syntax error"],
        [:link, "{@link} to unknown class or member"],
        [:link_ambiguous, "{@link} is ambiguous"],
        [:link_auto, "Auto-detected link to unknown class or member"],
        [:html, "Unclosed HTML tag"],

        [:alt_name, "Name used as both classname and alternate classname"],
        [:name_missing, "Member or parameter has no name"],
        [:no_doc, "Public class without documentation"],
        [:no_doc_member, "Public member without documentation"],
        [:no_doc_param, "Parameter of public member without documentation"],
        [:dup_param, "Method has two parameters with the same name"],
        [:dup_member, "Class has two members with the same name"],
        [:req_after_opt, "Required parameter comes after optional"],
        [:param_count, "Less parameters documented than detected from code"],
        [:subproperty, "@param foo.bar where foo param doesn't exist"],
        [:sing_static, "Singleton class member marked as @static"],
        [:type_syntax, "Syntax error in {type definition}"],
        [:type_name, "Unknown type referenced in {type definition}"],
        [:enum, "Enum with invalid values or no values at all"],
        [:fires, "@fires references unknown event"],

        [:image, "{@img} referring to missing file"],
        [:image_unused, "An image exists in --images dir that's not used"],
        [:cat_old_format, "Categories file uses old deprecated format"],
        [:cat_no_match, "Class pattern in categories file matches nothing"],
        [:cat_class_missing, "Class is missing from categories file"],
        [:guide, "Guide is missing from --guides dir"],

        [:aside, "Problem with @aside tag"],
        [:hide, "Problem with @hide tag"],
      ]
      # Turn off all warnings by default.
      # This is good for testing.
      # When running JSDuck app, the Options class enables most warnings.
      @warnings = {}
      @docs.each do |w|
        @warnings[w[0]] = {:enabled => false, :patterns => []}
      end
    end

    # Enables or disables a particular warning
    # or all warnings when type == :all.
    # Additionally a filename pattern can be specified.
    def set(type, enabled, pattern=nil)
      if type == :all
        # When used with a pattern, only add the pattern to the rules
        # where it can have an effect - otherwise we get a warning.
        @warnings.each_key do |key|
          set(key, enabled, pattern) unless pattern && @warnings[key][:enabled] == enabled
        end
      elsif @warnings.has_key?(type)
        if pattern
          if @warnings[type][:enabled] == enabled
            raise "Warning rule '#{enabled ? '+' : '-'}#{type}:#{pattern}' has no effect"
          else
            @warnings[type][:patterns] << Regexp.new(Regexp.escape(pattern))
          end
        else
          @warnings[type] = {:enabled => enabled, :patterns => []}
        end
      else
        raise "Warning of type '#{type}' doesn't exist"
      end
    end

    # get documentation for all warnings
    def doc
      @docs.map {|w| " #{@warnings[w[0]][:enabled] ? '+' : '-'}#{w[0]} - #{w[1]}" }
    end

    # True when the warning is enabled for the given type and filename
    # combination.
    def enabled?(type, filename)
      rule = @warnings[type]
      if rule[:patterns].any? {|re| filename =~ re }
        !rule[:enabled]
      else
        rule[:enabled]
      end
    end

    def has?(type)
      @warnings.has_key?(type)
    end

  end

end
+15 −19
Original line number Diff line number Diff line
require "jsduck/logger"
require "jsduck/warnings"

describe JsDuck::Logger do
  let(:logger) do
    JsDuck::LoggerCls.new
describe JsDuck::Warnings do
  let(:warnings) do
    JsDuck::Warnings.new
  end

  let(:usual_warnings) do
@@ -10,46 +10,42 @@ describe JsDuck::Logger do
  end

  describe "by default" do
    it "has the nil warning enabled" do
      logger.warning_enabled?(nil, "").should == true
    end

    it "has the usual warnings disabled" do
      usual_warnings.each do |type|
        logger.warning_enabled?(type, "").should == false
        warnings.enabled?(type, "").should == false
      end
    end
  end

  describe "after enabling all warnings" do
    before do
      logger.set_warning(:all, true)
      warnings.set(:all, true)
    end

    it "has the usual warnings disabled" do
      usual_warnings.each do |type|
        logger.warning_enabled?(type, "").should == true
        warnings.enabled?(type, "").should == true
      end
    end
  end

  shared_examples_for "limited to a path" do
    it "has the :tag warning disabled for /other/path/file.js" do
      logger.warning_enabled?(:tag, "/other/path/file.js").should == false
      warnings.enabled?(:tag, "/other/path/file.js").should == false
    end

    it "has the :tag warning enabled for /some/path/file.js" do
      logger.warning_enabled?(:tag, "/some/path/file.js").should == true
      warnings.enabled?(:tag, "/some/path/file.js").should == true
    end

    it "has the :tag warning enabled for /within/some/path/file.js" do
      logger.warning_enabled?(:tag, "/within/some/path/file.js").should == true
      warnings.enabled?(:tag, "/within/some/path/file.js").should == true
    end
  end

  describe "after enabling all warnings in /some/path" do
    before do
      logger.set_warning(:all, true, "/some/path")
      warnings.set(:all, true, "/some/path")
    end

    it_should_behave_like "limited to a path"
@@ -57,22 +53,22 @@ describe JsDuck::Logger do

  describe "after enabling :tag warning in /some/path" do
    before do
      logger.set_warning(:tag, true, "/some/path")
      warnings.set(:tag, true, "/some/path")
    end

    it_should_behave_like "limited to a path"

    describe "and also enabling it in /other/path" do
      before do
        logger.set_warning(:tag, true, "/other/path")
        warnings.set(:tag, true, "/other/path")
      end

      it "has the :tag warning enabled for /some/path/file.js" do
        logger.warning_enabled?(:tag, "/some/path/file.js").should == true
        warnings.enabled?(:tag, "/some/path/file.js").should == true
      end

      it "has the :tag warning enabled for /other/path/file.js" do
        logger.warning_enabled?(:tag, "/other/path/file.js").should == true
        warnings.enabled?(:tag, "/other/path/file.js").should == true
      end
    end
  end