Class: JsDuck::Class

Inherits:
Object
  • Object
show all
Defined in:
lib/jsduck/class.rb

Overview

Encapsulates class documentation and provides some commonly needed methods on it. Otherwise it acts like Hash, providing the [] method.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(doc, class_exists = true) ⇒ Class

Creates JSDuck class.

Pass true as second parameter to create a placeholder class.



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/jsduck/class.rb', line 14

def initialize(doc, class_exists=true)
  @doc = doc

  # Wrap classname into custom string class that allows
  # differenciating between existing and missing classes.
  @doc[:name] = ClassNameString.new(@doc[:name], class_exists)

  @doc[:members] = Class.default_members_hash if !@doc[:members]
  @doc[:statics] = Class.default_members_hash if !@doc[:statics]
  @relations = nil
end

Instance Attribute Details

#relationsObject

Returns the value of attribute relations.



9
10
11
# File 'lib/jsduck/class.rb', line 9

def relations
  @relations
end

Class Method Details

.default_members_hashObject

Returns default hash that has empty array for each member type



322
323
324
325
326
327
328
329
330
331
# File 'lib/jsduck/class.rb', line 322

def self.default_members_hash
  return {
    :cfg => [],
    :property => [],
    :method => [],
    :event => [],
    :css_var => [],
    :css_mixin => [],
  }
end

.package_name(name) ⇒ Object

Utility method that given a package or class name finds the name of its parent package.



303
304
305
# File 'lib/jsduck/class.rb', line 303

def self.package_name(name)
  name.slice(0, name.length - self.short_name(name).length - 1) || ""
end

.short_name(name) ⇒ Object

Utility method that given full package or class name extracts the “class”-part of the name.

Because we try to emulate ext-doc, it’s not as simple as just taking the last part. See class_spec.rb for details.



312
313
314
315
316
317
318
319
# File 'lib/jsduck/class.rb', line 312

def self.short_name(name)
  parts = name.split(/\./)
  short = parts.pop
  while parts.length > 1 && parts.last =~ /^[A-Z]/
    short = parts.pop + "." + short
  end
  short
end

Instance Method Details

#[](key) ⇒ Object



36
37
38
# File 'lib/jsduck/class.rb', line 36

def [](key)
  @doc[key]
end

#all_local_membersObject

Returns all local members of class



256
257
258
259
260
261
262
263
264
# File 'lib/jsduck/class.rb', line 256

def all_local_members
  all = []
  [:members, :statics].each do |group|
    @doc[group].each_value do |ms|
      all += ms
    end
  end
  all
end

#all_membersObject

Returns all members of class, including the inherited and mixed in ones



245
246
247
248
249
250
251
252
253
# File 'lib/jsduck/class.rb', line 245

def all_members
  all = []
  [:members, :statics].each do |group|
    @doc[group].each_key do |type|
      all += members(type, group)
    end
  end
  all
end

#constructor_first(ms) ⇒ Object

If methods list contains constructor, rename it with class name and move into beginning of methods list.



125
126
127
128
129
130
131
132
# File 'lib/jsduck/class.rb', line 125

def constructor_first(ms)
  constr = ms.find {|m| m[:name] == "constructor" }
  if constr
    ms.delete(constr)
    ms.unshift(constr)
  end
  ms
end

#deps(type) ⇒ Object

Returns an array of class instances this class directly depends on. Possible types are:

  • :mixins

  • :requires

  • :uses



67
68
69
# File 'lib/jsduck/class.rb', line 67

def deps(type)
  @doc[type] ? @doc[type].collect {|classname| lookup(classname) } : []
end

#full_nameObject

A way to access full class name with similar syntax to package_name and short_name



268
269
270
# File 'lib/jsduck/class.rb', line 268

def full_name
  @doc[:name]
end

#get_members(name, type_name = nil, static = false) ⇒ Object

Returns members by name. An array of one or more members, or empty array when nothing matches.

Optionally one can also specify type name to differenciate between different types of members.



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/jsduck/class.rb', line 225

def get_members(name, type_name=nil, static=false)
  # build hash of all members
  unless @members_map
    @members_map = {}
    [:members, :statics].each do |group|
      @doc[group].each_key do |type|
        members_hash(type, group).each_pair do |key, member|
          @members_map[key] = (@members_map[key] || []) + [member]
        end
      end
    end
  end

  ms = @members_map[name] || []
  ms = ms.find_all {|m| m[:tagname] == type_name } if type_name
  ms = ms.find_all {|m| m[:meta][:static] } if static
  return ms
end

#iconObject

Returns CSS icons class for the class



289
290
291
292
293
294
295
296
297
# File 'lib/jsduck/class.rb', line 289

def icon
  if @doc[:singleton]
    "icon-singleton"
  elsif inherits_from?("Ext.Component")
    "icon-component"
  else
    "icon-class"
  end
end

#inherits_from?(class_name) ⇒ Boolean

Returns true when this class inherits from the specified class. Also returns true when the class itself is the one we are asking about.

Returns:

  • (Boolean)


107
108
109
# File 'lib/jsduck/class.rb', line 107

def inherits_from?(class_name)
  return full_name == class_name || (parent ? parent.inherits_from?(class_name) : false)
end

#internal_docObject

Accessors for internal doc object. These are used to run ClassFormatter on the internal doc object and then assign it back.



29
30
31
# File 'lib/jsduck/class.rb', line 29

def internal_doc
  @doc
end

#internal_doc=(doc) ⇒ Object



32
33
34
# File 'lib/jsduck/class.rb', line 32

def internal_doc=(doc)
  @doc = doc
end

#local_members_hash(type, context) ⇒ Object

Helper method to get the direct members of this class



212
213
214
215
216
217
218
# File 'lib/jsduck/class.rb', line 212

def local_members_hash(type, context)
  local_members = {}
  (@doc[context][type] || []).each do |m|
    local_members[m[:name]] = m
  end
  local_members
end

#lookup(classname) ⇒ Object

Looks up class object by name When not found, prints warning message.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/jsduck/class.rb', line 78

def lookup(classname)
  if @relations[classname]
    @relations[classname]
  elsif @relations.ignore?(classname) || classname =~ /\*/
    # Ignore explicitly ignored classes and classnames with
    # wildcards in them.  We could expand the wildcard, but that
    # can result in a very long list of classes, like when
    # somebody requires 'Ext.form.*', so for now we do the
    # simplest thing and ignore it.
    Class.new({:name => classname}, false)
  else
    context = @doc[:files][0]
    Logger.instance.warn(:extend, "Class #{classname} not found", context[:filename], context[:linenr])
    # Create placeholder class
    Class.new({:name => classname}, false)
  end
end

#members(type, context = :members) ⇒ Object

Returns array of all public members of particular type in a class, sorted by name.

For methods the the constructor is listed first.

See members_hash for details.



117
118
119
120
121
# File 'lib/jsduck/class.rb', line 117

def members(type, context=:members)
  ms = members_hash(type, context).values #.find_all {|m| !m[:private] }
  ms.sort! {|a,b| a[:name] <=> b[:name] }
  type == :method ? constructor_first(ms) : ms
end

#members_hash(type, context = :members) ⇒ Object

Returns hash of all members in class (and of parent classes and mixin classes). Members are methods, properties, cfgs, events (member type is specified through ‘type’ parameter).

When parent and child have members with same name, member from child overrides tha parent member.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/jsduck/class.rb', line 140

def members_hash(type, context=:members)
  # Singletons have no static members
  if @doc[:singleton] && context == :statics
    # Warn if singleton has static members
    if @doc[context][type].length > 0
      Logger.instance.warn(:sing_static, "Singleton class #{@doc[:name]} can't have static members, remove the @static tag.")
    end
    return {}
  end

  ms = parent ? parent.members_hash(type, context) : {}

  mixins.each do |mix|
    merge!(ms, mix.members_hash(type, context))
  end

  # For static members, exclude everything not explicitly marked as inheritable
  if context == :statics
    ms.delete_if {|key, member| !member[:inheritable] }
  end

  merge!(ms, local_members_hash(type, context))

  # If singleton has static members, include them as if they were
  # instance members.  Otherwise they will be completely excluded
  # from the docs, as the static members block is not created for
  # singletons.
  if @doc[:singleton] && @doc[:statics][type].length > 0
    merge!(ms, local_members_hash(type, :statics))
  end

  ms
end

#merge!(hash1, hash2) ⇒ Object

merges second members hash into first one



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/jsduck/class.rb', line 175

def merge!(hash1, hash2)
  hash2.each_pair do |name, m|
    if m[:meta] && m[:meta][:hide]
      if hash1[name]
        hash1.delete(name)
      else
        ctx = m[:files][0]
        Logger.instance.warn(:hide, "@hide used but #{m[:tagname]} #{m[:name]} not found in parent class", ctx[:filename], ctx[:linenr])
      end
    else
      if hash1[name]
        store_overrides(hash1[name], m)
      end
      hash1[name] = m
    end
  end
end

#mixinsObject

Returns all direct mixins of this class. Same as #deps(:mixins).



56
57
58
# File 'lib/jsduck/class.rb', line 56

def mixins
  deps(:mixins)
end

#package_nameObject

Returns package name of the class.

That is the namespace part of full class name.

For example “My.package” is package_name of “My.package.Class”



277
278
279
# File 'lib/jsduck/class.rb', line 277

def package_name
  Class.package_name(@doc[:name])
end

#parentObject

Returns instance of parent class, or nil if there is none



41
42
43
# File 'lib/jsduck/class.rb', line 41

def parent
  @doc[:extends] ? lookup(@doc[:extends]) : nil
end

#parent_deps(type) ⇒ Object

Same ase #deps, but pulls out the dependencies from all parent classes.



72
73
74
# File 'lib/jsduck/class.rb', line 72

def parent_deps(type)
  parent ? parent.deps(type) + parent.parent_deps(type) : []
end

#short_nameObject

Returns last part of full class name

For example for “My.package.Class” it is “Class”



284
285
286
# File 'lib/jsduck/class.rb', line 284

def short_name
  Class.short_name(@doc[:name])
end

#store_overrides(old, new) ⇒ Object

Invoked when merge! finds two members with the same name. New member always overrides the old, but inside new we keep a list of members it overrides. Normally one member will override one other member, but a member from mixin can override multiple members - although there’s not a single such case in ExtJS, we have to handle it.

Every overridden member is listed just once.



201
202
203
204
205
206
207
208
209
# File 'lib/jsduck/class.rb', line 201

def store_overrides(old, new)
  # Sometimes a class is included multiple times (like Ext.Base)
  # resulting in its members overriding themselves.  Because of
  # this, ignore overriding itself.
  if new[:owner] != old[:owner]
    new[:overrides] = [] unless new[:overrides]
    new[:overrides] << old unless new[:overrides].any? {|m| m[:owner] == old[:owner] }
  end
end

#superclassesObject

Returns array of ancestor classes. Example result when asking ancestors of MyPanel might be:

[Ext.util.Observable, Ext.Component, Ext.Panel]


50
51
52
53
# File 'lib/jsduck/class.rb', line 50

def superclasses
  p = parent
  p ? p.superclasses + [p]  : []
end

#to_hashObject

Returns copy of @doc hash



97
98
99
# File 'lib/jsduck/class.rb', line 97

def to_hash
  @doc.clone
end

#to_json(*a) ⇒ Object



101
102
103
# File 'lib/jsduck/class.rb', line 101

def to_json(*a)
  to_hash.to_json(*a)
end