class JsDuck::Js::Class

Auto-detection of classes.

Public Instance Methods

detect(ast, docs) click to toggle source

Checks if AST node is a class, and if so, returns doc-hash with class name and various auto-detected attributes. When not a class returns nil.

# File lib/jsduck/js/class.rb, line 16
def detect(ast, docs)
  @docs = docs

  exp = ast.expression_statement? ? ast["expression"] : nil
  var = ast.variable_declaration? ? ast["declarations"][0] : nil

  # Ext.define("Class", {})
  if exp && exp.ext_define?
    make(exp["arguments"][0].to_value, exp)

    # Ext.override(Class, {})
  elsif exp && exp.ext_override?
    make("", exp)

    # foo = Ext.extend(Parent, {})
  elsif exp && exp.assignment_expression? && exp["right"].ext_extend?
    make(exp["left"].to_s, exp["right"])

    # Foo = ...
  elsif exp && exp.assignment_expression? && class_name?(exp["left"].to_s)
    make(exp["left"].to_s, exp["right"])

    # var foo = Ext.extend(Parent, {})
  elsif var && var["init"].ext_extend?
    make(var["id"].to_s, var["init"])

    # var Foo = ...
  elsif var && class_name?(var["id"].to_s)
    make(var["id"].to_s, var["right"])

    # function Foo() {}
  elsif ast.function? && class_name?(ast["id"].to_s || "")
    make(ast["id"].to_s)

    # { ... }
  elsif ast.object_expression?
    make("", ast)

  else
    nil
  end
end
make(name, ast=nil) click to toggle source

Produces a doc-hash for a class.

# File lib/jsduck/js/class.rb, line 60
def make(name, ast=nil)
  cls = {
    :tagname => :class,
    :name => name,
  }

  # apply information from Ext.extend, Ext.define, or {}
  if ast
    if ast.ext_define?
      detect_ext_define(cls, ast)
    elsif ast.ext_extend?
      detect_ext_something(:extends, cls, ast)
    elsif ast.ext_override?
      detect_ext_something(:override, cls, ast)
    elsif ast.object_expression?
      detect_class_members_from_object(cls, ast)
    elsif ast.array_expression?
      detect_class_members_from_array(cls, ast)
    end
  end

  return cls
end

Private Instance Methods

apply_autodetected(m, ast, inheritable=true) click to toggle source

Sets auto-detection related properties :autodetected and :inheritdoc on the given member Hash.

When member has a comment, adds code to the related docset and returns false.

Otherwise detects the line number of member and returns true.

# File lib/jsduck/js/class.rb, line 205
def apply_autodetected(m, ast, inheritable=true)
  docset = find_docset(ast.raw)

  if !docset || docset[:type] != :doc_comment
    if inheritable
      m[:inheritdoc] = {}
    else
      m[:private] = true
    end
    m[:autodetected] = {:tagname => m[:tagname]}
  end

  if docset
    docset[:code] = m
    return false
  else
    m[:linenr] = ast.linenr
    return true
  end
end
class_name?(name) click to toggle source

Class name begins with upcase char

# File lib/jsduck/js/class.rb, line 87
def class_name?(name)
  return name.split(/\./).last =~ /\A[A-Z]/
end
detect_class_members_from_array(cls, ast) click to toggle source

Detects class members from array literal

# File lib/jsduck/js/class.rb, line 148
def detect_class_members_from_array(cls, ast)
  cls[:members] = []

  # This will most likely be an @enum class, in which case the
  # enum will be for documentation purposes only.
  cls[:enum] = {:doc_only => true}

  ast["elements"].each do |el|
    detect_method_or_property(cls, el.key_value, el, el)
  end
end
detect_class_members_from_object(cls, ast) click to toggle source

Detects class members from object literal

# File lib/jsduck/js/class.rb, line 140
def detect_class_members_from_object(cls, ast)
  cls[:members] = []
  ast.each_property do |key, value, pair|
    detect_method_or_property(cls, key, value, pair) if pair.raw["kind"] == "init"
  end
end
detect_ext_define(cls, ast) click to toggle source

Inspects Ext.define() and copies detected properties over to the given cls Hash

# File lib/jsduck/js/class.rb, line 103
def detect_ext_define(cls, ast)
  # defaults
  cls.merge!(Js::ExtDefine.defaults)
  cls[:members] = []
  cls[:code_type] = :ext_define

  definition_node = ast["arguments"][1]

  # Support both function and object notation
  if definition_node.function_expression?
    definition_node = definition_node.return_statement_expression
    return if definition_node.nil?
  end

  definition_node.each_property do |key, value, pair|
    if tag = Js::ExtDefine.get_tag_by_pattern(key)
      tag.parse_ext_define(cls, value)
    else
      case key
      when "config"
        cls[:members] += make_configs(value, {:accessor => true})
      when "cachedConfig"
        cls[:members] += make_configs(value, {:accessor => true})
      when "eventedConfig"
        cls[:members] += make_configs(value, {:accessor => true, :evented => true})
      when "statics"
        cls[:members] += make_statics(value)
      when "inheritableStatics"
        cls[:members] += make_statics(value, {:inheritable => true})
      else
        detect_method_or_property(cls, key, value, pair) if pair.raw["kind"] == "init"
      end
    end
  end
end
detect_ext_something(type, cls, ast) click to toggle source

Detection of Ext.extend() or Ext.override(). The type parameter must be correspondingly either :extend or :override.

# File lib/jsduck/js/class.rb, line 93
def detect_ext_something(type, cls, ast)
  args = ast["arguments"]
  cls[type] = args[0].to_s
  if args.length == 2 && args[1].object_expression?
    detect_class_members_from_object(cls, args[1])
  end
end
detect_method_or_property(cls, key, value, pair) click to toggle source

Detects item in object literal either as method or property

# File lib/jsduck/js/class.rb, line 161
def detect_method_or_property(cls, key, value, pair)
  member = value.function? ? make_method(key, value) : make_property(key, value)
  cls[:members] << member if apply_autodetected(member, pair)
end
find_docset(raw_ast) click to toggle source

Looks up docset associated with given AST node. A dead-stupid and -slow implementation, but works.

The comparison needs to be done between raw AST nodes - multiple Node instances can be created to wrap a single raw AST node, and they will then not be equal.

# File lib/jsduck/js/class.rb, line 232
def find_docset(raw_ast)
  @docs.find do |docset|
    docset[:code] == raw_ast
  end
end
make_configs(ast, defaults={}) click to toggle source
# File lib/jsduck/js/class.rb, line 166
def make_configs(ast, defaults={})
  configs = []

  ast.each_property do |name, value, pair|
    cfg = make_property(name, value)
    cfg[:tagname] = :cfg
    cfg.merge!(defaults)
    configs << cfg if apply_autodetected(cfg, pair)
  end

  configs
end
make_method(name, ast) click to toggle source
# File lib/jsduck/js/class.rb, line 238
def make_method(name, ast)
  Js::Method.make(name, ast)
end
make_property(name=nil, ast=nil) click to toggle source
# File lib/jsduck/js/class.rb, line 242
def make_property(name=nil, ast=nil)
  Js::Property.make(name, ast)
end
make_statics(ast, defaults={}) click to toggle source
# File lib/jsduck/js/class.rb, line 179
def make_statics(ast, defaults={})
  statics = []

  ast.each_property do |name, value, pair|
    if value.function?
      s = make_method(name, value)
    else
      s = make_property(name, value)
    end

    s[:static] = true
    s.merge!(defaults)

    statics << s if apply_autodetected(s, pair, defaults[:inheritable])
  end

  statics
end