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



339
340
341
342
343
344
345
346
347
348
# File 'lib/jsduck/class.rb', line 339

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.



320
321
322
# File 'lib/jsduck/class.rb', line 320

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.



329
330
331
332
333
334
335
336
# File 'lib/jsduck/class.rb', line 329

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

Accessor to internal hash



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

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

#[]=(key, value) ⇒ Object

Assignment to internal hash keys



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

def []=(key, value)
  @doc[key] = value
end

#all_local_membersObject

Returns all local members of class



273
274
275
276
277
278
279
280
281
# File 'lib/jsduck/class.rb', line 273

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



262
263
264
265
266
267
268
269
270
# File 'lib/jsduck/class.rb', line 262

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.



131
132
133
134
135
136
137
138
# File 'lib/jsduck/class.rb', line 131

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



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

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



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

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.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/jsduck/class.rb', line 242

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



306
307
308
309
310
311
312
313
314
# File 'lib/jsduck/class.rb', line 306

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)


113
114
115
# File 'lib/jsduck/class.rb', line 113

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



229
230
231
232
233
234
235
# File 'lib/jsduck/class.rb', line 229

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.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/jsduck/class.rb', line 84

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.



123
124
125
126
127
# File 'lib/jsduck/class.rb', line 123

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.



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
173
174
175
176
177
178
# File 'lib/jsduck/class.rb', line 146

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, skip_overrides = false) ⇒ Object

merges second members hash into first one



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/jsduck/class.rb', line 181

def merge!(hash1, hash2, skip_overrides=false)
  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).



62
63
64
# File 'lib/jsduck/class.rb', line 62

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”



294
295
296
# File 'lib/jsduck/class.rb', line 294

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

#parentObject

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



47
48
49
# File 'lib/jsduck/class.rb', line 47

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.



78
79
80
# File 'lib/jsduck/class.rb', line 78

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”



301
302
303
# File 'lib/jsduck/class.rb', line 301

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.



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/jsduck/class.rb', line 207

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]
    unless new[:overrides].any? {|m| m[:owner] == old[:owner] }
      # Make a copy of the important properties for us.  We can't
      # just push the actual `old` member itself, because there
      # can be circular overrides (notably with Ext.Base), which
      # will result in infinite loop when we try to convert our
      # class into JSON.
      new[:overrides] << {
        :name => old[:name],
        :owner => old[:owner],
        :id => old[:id],
      }
    end
  end
end

#superclassesObject

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

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


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

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

#to_hashObject

Returns copy of @doc hash



103
104
105
# File 'lib/jsduck/class.rb', line 103

def to_hash
  @doc.clone
end

#to_json(*a) ⇒ Object



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

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