jsduck 4.0.beta → 4.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +3 -0
- data/Rakefile +6 -0
- data/bin/compare +199 -37
- data/bin/graph +52 -0
- data/jsduck.gemspec +2 -2
- data/lib/jsduck/aggregator.rb +6 -0
- data/lib/jsduck/app.rb +7 -4
- data/lib/jsduck/ast.rb +44 -14
- data/lib/jsduck/class.rb +8 -2
- data/lib/jsduck/class_writer.rb +2 -3
- data/lib/jsduck/doc_ast.rb +10 -0
- data/lib/jsduck/doc_formatter.rb +7 -7
- data/lib/jsduck/doc_parser.rb +12 -0
- data/lib/jsduck/enum.rb +73 -0
- data/lib/jsduck/guide_writer.rb +2 -3
- data/lib/jsduck/guides.rb +11 -12
- data/lib/jsduck/importer.rb +102 -0
- data/lib/jsduck/inherit_doc.rb +26 -15
- data/lib/jsduck/lint.rb +10 -0
- data/lib/jsduck/logger.rb +3 -0
- data/lib/jsduck/markdown.rb +46 -0
- data/lib/jsduck/merger.rb +3 -1
- data/lib/jsduck/options.rb +17 -4
- data/lib/jsduck/parallel_wrap.rb +10 -9
- data/lib/jsduck/renderer.rb +21 -0
- data/lib/jsduck/source_file.rb +1 -1
- data/lib/jsduck/source_file_parser.rb +0 -2
- data/lib/jsduck/source_writer.rb +3 -3
- data/lib/jsduck/tag/new.rb +20 -0
- data/lib/jsduck/tag/since.rb +26 -0
- data/lib/jsduck/type_parser.rb +25 -1
- metadata +9 -3
data/lib/jsduck/ast.rb
CHANGED
@@ -73,7 +73,7 @@ module JsDuck
|
|
73
73
|
|
74
74
|
# Foo = ...
|
75
75
|
elsif exp && assignment?(exp) && class_name?(to_s(exp["left"]))
|
76
|
-
make_class(to_s(exp["left"]))
|
76
|
+
make_class(to_s(exp["left"]), exp["right"])
|
77
77
|
|
78
78
|
# var foo = Ext.extend("Parent", {})
|
79
79
|
elsif var && var["init"] && ext_extend?(var["init"])
|
@@ -81,7 +81,7 @@ module JsDuck
|
|
81
81
|
|
82
82
|
# var Foo = ...
|
83
83
|
elsif var && class_name?(to_s(var["id"]))
|
84
|
-
make_class(to_s(var["id"]))
|
84
|
+
make_class(to_s(var["id"]), var["right"])
|
85
85
|
|
86
86
|
# function Foo() {}
|
87
87
|
elsif function?(ast) && class_name?(to_s(ast["id"]))
|
@@ -193,12 +193,20 @@ module JsDuck
|
|
193
193
|
:name => name,
|
194
194
|
}
|
195
195
|
|
196
|
-
# apply information from Ext.extend
|
196
|
+
# apply information from Ext.extend, Ext.define, or {}
|
197
197
|
if ast
|
198
198
|
if ext_extend?(ast)
|
199
|
-
|
199
|
+
args = ast["arguments"]
|
200
|
+
cls[:extends] = to_s(args[0])
|
201
|
+
if args.length == 2 && args[1]["type"] == "ObjectExpression"
|
202
|
+
detect_class_members_from_object(cls, args[1])
|
203
|
+
end
|
200
204
|
elsif ext_define?(ast)
|
201
205
|
detect_ext_define(cls, ast)
|
206
|
+
elsif ast["type"] == "ObjectExpression"
|
207
|
+
detect_class_members_from_object(cls, ast)
|
208
|
+
elsif ast["type"] == "ArrayExpression"
|
209
|
+
detect_class_members_from_array(cls, ast)
|
202
210
|
end
|
203
211
|
end
|
204
212
|
|
@@ -217,6 +225,7 @@ module JsDuck
|
|
217
225
|
cls[:aliases] = []
|
218
226
|
cls[:members] = []
|
219
227
|
cls[:statics] = []
|
228
|
+
cls[:code_type] = :ext_define
|
220
229
|
|
221
230
|
each_pair_in_object_expression(ast["arguments"][1]) do |key, value, pair|
|
222
231
|
case key
|
@@ -247,17 +256,38 @@ module JsDuck
|
|
247
256
|
when "inheritableStatics"
|
248
257
|
cls[:statics] += make_statics(value, {:inheritable => true})
|
249
258
|
else
|
250
|
-
|
251
|
-
m = make_method(key, value)
|
252
|
-
cls[:members] << m if apply_autodetected(m, pair)
|
253
|
-
else
|
254
|
-
p = make_property(key, value)
|
255
|
-
cls[:members] << p if apply_autodetected(p, pair)
|
256
|
-
end
|
259
|
+
detect_method_or_property(cls, key, value, pair)
|
257
260
|
end
|
258
261
|
end
|
259
262
|
end
|
260
263
|
|
264
|
+
# Detects class members from object literal
|
265
|
+
def detect_class_members_from_object(cls, ast)
|
266
|
+
cls[:members] = []
|
267
|
+
each_pair_in_object_expression(ast) do |key, value, pair|
|
268
|
+
detect_method_or_property(cls, key, value, pair)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Detects class members from array literal
|
273
|
+
def detect_class_members_from_array(cls, ast)
|
274
|
+
cls[:members] = []
|
275
|
+
ast["elements"].each do |el|
|
276
|
+
detect_method_or_property(cls, key_value(el), el, el)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Detects item in object literal either as method or property
|
281
|
+
def detect_method_or_property(cls, key, value, pair)
|
282
|
+
if function?(value)
|
283
|
+
m = make_method(key, value)
|
284
|
+
cls[:members] << m if apply_autodetected(m, pair)
|
285
|
+
else
|
286
|
+
p = make_property(key, value)
|
287
|
+
cls[:members] << p if apply_autodetected(p, pair)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
261
291
|
def make_extends(cfg_value)
|
262
292
|
return nil unless cfg_value
|
263
293
|
|
@@ -325,8 +355,8 @@ module JsDuck
|
|
325
355
|
# returns false.
|
326
356
|
#
|
327
357
|
# Otherwise detects the line number of member and returns true.
|
328
|
-
def apply_autodetected(m,
|
329
|
-
docset = find_docset(
|
358
|
+
def apply_autodetected(m, ast, inheritable=true)
|
359
|
+
docset = find_docset(ast)
|
330
360
|
|
331
361
|
if !docset || docset[:type] != :doc_comment
|
332
362
|
if inheritable
|
@@ -344,7 +374,7 @@ module JsDuck
|
|
344
374
|
# Get line number from third place at range array.
|
345
375
|
# This third item exists in forked EsprimaJS at
|
346
376
|
# https://github.com/nene/esprima/tree/linenr-in-range
|
347
|
-
m[:linenr] =
|
377
|
+
m[:linenr] = ast["range"][2]
|
348
378
|
return true
|
349
379
|
end
|
350
380
|
end
|
data/lib/jsduck/class.rb
CHANGED
@@ -239,7 +239,11 @@ module JsDuck
|
|
239
239
|
#
|
240
240
|
# Optionally one can also specify type name to differenciate
|
241
241
|
# between different types of members.
|
242
|
-
|
242
|
+
#
|
243
|
+
# Finally static flag can be specified. True to look only at
|
244
|
+
# static members, false to look at instance members, and nil to
|
245
|
+
# look at both.
|
246
|
+
def get_members(name, type_name=nil, static=nil)
|
243
247
|
# build hash of all members
|
244
248
|
unless @members_map
|
245
249
|
@members_map = {}
|
@@ -254,7 +258,9 @@ module JsDuck
|
|
254
258
|
|
255
259
|
ms = @members_map[name] || []
|
256
260
|
ms = ms.find_all {|m| m[:tagname] == type_name } if type_name
|
257
|
-
|
261
|
+
# static = true | false | nil
|
262
|
+
ms = ms.find_all {|m| m[:meta][:static] } if static == true
|
263
|
+
ms = ms.find_all {|m| !m[:meta][:static] } if static == false
|
258
264
|
return ms
|
259
265
|
end
|
260
266
|
|
data/lib/jsduck/class_writer.rb
CHANGED
@@ -10,7 +10,6 @@ module JsDuck
|
|
10
10
|
def initialize(exporter_class, relations, opts)
|
11
11
|
@relations = relations
|
12
12
|
@exporter = exporter_class.new(relations, opts)
|
13
|
-
@parallel = ParallelWrap.new(:in_processes => opts.processes)
|
14
13
|
end
|
15
14
|
|
16
15
|
# Writes class data into given directory or STDOUT when dir == :stdout.
|
@@ -24,13 +23,13 @@ module JsDuck
|
|
24
23
|
private
|
25
24
|
|
26
25
|
def write_stdout
|
27
|
-
json =
|
26
|
+
json = ParallelWrap.map(@relations.classes) {|cls| @exporter.export(cls) }.compact
|
28
27
|
Stdout.instance.add(json)
|
29
28
|
end
|
30
29
|
|
31
30
|
def write_dir(dir, extension)
|
32
31
|
FileUtils.mkdir(dir)
|
33
|
-
|
32
|
+
ParallelWrap.each(@relations.classes) do |cls|
|
34
33
|
filename = dir + "/" + cls[:name] + extension
|
35
34
|
Logger.instance.log("Writing docs", filename)
|
36
35
|
json = @exporter.export(cls)
|
data/lib/jsduck/doc_ast.rb
CHANGED
@@ -51,6 +51,7 @@ module JsDuck
|
|
51
51
|
:singleton => !!doc_map[:singleton],
|
52
52
|
:requires => detect_list(:requires, doc_map),
|
53
53
|
:uses => detect_list(:uses, doc_map),
|
54
|
+
:enum => detect_enum(doc_map),
|
54
55
|
}, doc_map)
|
55
56
|
end
|
56
57
|
|
@@ -276,6 +277,15 @@ module JsDuck
|
|
276
277
|
end
|
277
278
|
end
|
278
279
|
|
280
|
+
def detect_enum(doc_map)
|
281
|
+
return nil unless extract(doc_map, :class, :enum)
|
282
|
+
|
283
|
+
return {
|
284
|
+
:type => extract(doc_map, :class, :type),
|
285
|
+
:default => extract(doc_map, :class, :default),
|
286
|
+
}
|
287
|
+
end
|
288
|
+
|
279
289
|
# Combines :doc-s of most tags
|
280
290
|
# Ignores tags that have doc comment themselves and subproperty tags
|
281
291
|
def detect_doc(docs)
|
data/lib/jsduck/doc_formatter.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require 'rubygems'
|
3
|
-
require 'rdiscount'
|
4
3
|
require 'strscan'
|
4
|
+
require 'jsduck/markdown'
|
5
5
|
require 'jsduck/logger'
|
6
6
|
require 'jsduck/inline_img'
|
7
7
|
require 'jsduck/inline_video'
|
@@ -133,12 +133,12 @@ module JsDuck
|
|
133
133
|
text = $2
|
134
134
|
if target =~ /^(.*)#(static-)?(?:(cfg|property|method|event|css_var|css_mixin)-)?(.*)$/
|
135
135
|
cls = $1.empty? ? @class_context : $1
|
136
|
-
static =
|
136
|
+
static = $2 ? true : nil
|
137
137
|
type = $3 ? $3.intern : nil
|
138
138
|
member = $4
|
139
139
|
else
|
140
140
|
cls = target
|
141
|
-
static =
|
141
|
+
static = nil
|
142
142
|
type = false
|
143
143
|
member = false
|
144
144
|
end
|
@@ -257,7 +257,7 @@ module JsDuck
|
|
257
257
|
end
|
258
258
|
|
259
259
|
# applies the link template
|
260
|
-
def link(cls, member, anchor_text, type=nil, static=
|
260
|
+
def link(cls, member, anchor_text, type=nil, static=nil)
|
261
261
|
# Use the canonical class name for link (not some alternateClassName)
|
262
262
|
cls = @relations[cls].full_name
|
263
263
|
# prepend type name to member name
|
@@ -281,7 +281,7 @@ module JsDuck
|
|
281
281
|
end
|
282
282
|
end
|
283
283
|
|
284
|
-
def get_matching_member(cls, member, type=nil, static=
|
284
|
+
def get_matching_member(cls, member, type=nil, static=nil)
|
285
285
|
ms = get_members(cls, member, type, static).find_all {|m| !m[:private] }
|
286
286
|
if ms.length > 1
|
287
287
|
instance_ms = ms.find_all {|m| !m[:meta][:static] }
|
@@ -291,7 +291,7 @@ module JsDuck
|
|
291
291
|
end
|
292
292
|
end
|
293
293
|
|
294
|
-
def get_members(cls, member, type=nil, static=
|
294
|
+
def get_members(cls, member, type=nil, static=nil)
|
295
295
|
@relations[cls] ? @relations[cls].get_members(member, type, static) : []
|
296
296
|
end
|
297
297
|
|
@@ -310,7 +310,7 @@ module JsDuck
|
|
310
310
|
# code-blocks beginning with empty line.
|
311
311
|
input.gsub!(/<pre>(<code>)?\n?/, "<pre>\\1")
|
312
312
|
|
313
|
-
replace(
|
313
|
+
replace(JsDuck::Markdown.to_html(input))
|
314
314
|
end
|
315
315
|
|
316
316
|
# Shortens text
|
data/lib/jsduck/doc_parser.rb
CHANGED
@@ -130,6 +130,8 @@ module JsDuck
|
|
130
130
|
at_var
|
131
131
|
elsif look(/@throws\b/)
|
132
132
|
at_throws
|
133
|
+
elsif look(/@enum\b/)
|
134
|
+
at_enum
|
133
135
|
elsif look(/@inheritable\b/)
|
134
136
|
boolean_at_tag(/@inheritable/, :inheritable)
|
135
137
|
elsif look(/@accessor\b/)
|
@@ -282,6 +284,16 @@ module JsDuck
|
|
282
284
|
skip_white
|
283
285
|
end
|
284
286
|
|
287
|
+
# matches @enum {type} name ...
|
288
|
+
def at_enum
|
289
|
+
match(/@enum/)
|
290
|
+
add_tag(:class)
|
291
|
+
@current_tag[:enum] = true
|
292
|
+
maybe_type
|
293
|
+
maybe_name_with_default
|
294
|
+
skip_white
|
295
|
+
end
|
296
|
+
|
285
297
|
# matches @type {type} or @type type
|
286
298
|
#
|
287
299
|
# The presence of @type implies that we are dealing with property.
|
data/lib/jsduck/enum.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module JsDuck
|
2
|
+
|
3
|
+
class Enum
|
4
|
+
def initialize(classes)
|
5
|
+
@classes = classes
|
6
|
+
end
|
7
|
+
|
8
|
+
# Applies additional processing to all enum-classes.
|
9
|
+
def process_all!
|
10
|
+
@classes.each_value do |cls|
|
11
|
+
if cls[:enum]
|
12
|
+
process(cls)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# processes single class
|
20
|
+
def process(cls)
|
21
|
+
expand_default(cls)
|
22
|
+
strip_inheritdoc(cls)
|
23
|
+
cls[:enum][:type] = infer_type(cls) unless cls[:enum][:type]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Given an enum class, returns the type infered from its values.
|
27
|
+
def infer_type(cls)
|
28
|
+
if cls[:members][:property].length > 0
|
29
|
+
types = cls[:members][:property].map {|p| p[:type] }
|
30
|
+
types.sort.uniq.join("/")
|
31
|
+
else
|
32
|
+
"Object"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Expands default value like widget.* into list of properties
|
37
|
+
def expand_default(cls)
|
38
|
+
if cls[:enum][:default] =~ /\A(.*)\.\*\Z/
|
39
|
+
each_alias($1) do |name, owner|
|
40
|
+
cls[:members][:property] << {
|
41
|
+
:tagname => :property,
|
42
|
+
:id => 'property-' + name,
|
43
|
+
:name => name,
|
44
|
+
:default => "'" + name + "'",
|
45
|
+
:type => "String",
|
46
|
+
:meta => {:private => owner[:private]},
|
47
|
+
:private => owner[:private],
|
48
|
+
:files => cls[:files],
|
49
|
+
:owner => cls[:name],
|
50
|
+
:doc => "Alias for {@link #{owner[:name]}}.",
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def each_alias(prefix)
|
57
|
+
@classes.each_value do |cls|
|
58
|
+
if cls[:aliases] && cls[:aliases][prefix]
|
59
|
+
cls[:aliases][prefix].each {|name| yield(name, cls) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Remove the auto-inserted inheritdoc tag so the auto-detected enum
|
65
|
+
# values default to being public.
|
66
|
+
def strip_inheritdoc(cls)
|
67
|
+
cls[:members][:property].each do |p|
|
68
|
+
p[:inheritdoc] = nil if p[:autodetected]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/jsduck/guide_writer.rb
CHANGED
@@ -10,7 +10,6 @@ module JsDuck
|
|
10
10
|
def initialize(exporter_class, guides, opts)
|
11
11
|
@guides = guides
|
12
12
|
@exporter = exporter_class.new(guides, opts)
|
13
|
-
@parallel = ParallelWrap.new(:in_processes => opts.processes)
|
14
13
|
end
|
15
14
|
|
16
15
|
# Writes guide data into given directory or STDOUT when dir == :stdout.
|
@@ -24,13 +23,13 @@ module JsDuck
|
|
24
23
|
private
|
25
24
|
|
26
25
|
def write_stdout
|
27
|
-
json =
|
26
|
+
json = ParallelWrap.map(all_guides) {|guide| @exporter.export_guide(guide) }.compact
|
28
27
|
Stdout.instance.add(json)
|
29
28
|
end
|
30
29
|
|
31
30
|
def write_dir(dir, extension)
|
32
31
|
FileUtils.mkdir(dir) unless File.exists?(dir)
|
33
|
-
|
32
|
+
ParallelWrap.each(all_guides) do |guide|
|
34
33
|
filename = dir + "/" + guide["name"] + extension
|
35
34
|
Logger.instance.log("Writing guide", filename)
|
36
35
|
json = @exporter.export_guide(guide)
|
data/lib/jsduck/guides.rb
CHANGED
@@ -36,15 +36,13 @@ module JsDuck
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Modified each_item that also loads HTML for each guide
|
39
|
-
def each_item
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
def each_item
|
40
|
+
super do |guide|
|
41
|
+
# Load the guide if not loaded
|
42
|
+
guide[:html] = load_guide(guide) if guide[:html] == nil
|
43
|
+
# Pass guide to block if it was successfully loaded.
|
44
|
+
yield guide if guide[:html]
|
45
45
|
end
|
46
|
-
|
47
|
-
super(&block)
|
48
46
|
end
|
49
47
|
|
50
48
|
# Modified to_array that excludes the :html from guide nodes
|
@@ -60,12 +58,13 @@ module JsDuck
|
|
60
58
|
def load_guide(guide)
|
61
59
|
in_dir = @path + "/guides/" + guide["name"]
|
62
60
|
|
63
|
-
|
64
|
-
return Logger.instance.warn(:guide, "Guide #{in_dir} not found") unless File.exists?(in_dir)
|
61
|
+
return Logger.instance.warn(:guide, "Guide #{in_dir} not found") unless File.exists?(in_dir)
|
65
62
|
|
66
|
-
|
67
|
-
return Logger.instance.warn(:guide, "README.md not found in #{in_dir}") unless File.exists?(guide_file)
|
63
|
+
guide_file = in_dir + "/README.md"
|
68
64
|
|
65
|
+
return Logger.instance.warn(:guide, "README.md not found in #{in_dir}") unless File.exists?(guide_file)
|
66
|
+
|
67
|
+
begin
|
69
68
|
@formatter.doc_context = {:filename => guide_file, :linenr => 0}
|
70
69
|
name = File.basename(in_dir)
|
71
70
|
@formatter.img_path = "guides/#{name}"
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'jsduck/json_duck'
|
2
|
+
require 'jsduck/null_object'
|
3
|
+
require 'jsduck/logger'
|
4
|
+
require 'jsduck/parallel_wrap'
|
5
|
+
|
6
|
+
module JsDuck
|
7
|
+
|
8
|
+
# Reads in JSDuck exports of different versions of docs.
|
9
|
+
module Importer
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# Loads in exported docs and generates @since and @new tags based on that data.
|
13
|
+
def import(imports, relations)
|
14
|
+
if imports.length > 0
|
15
|
+
generate_since_tags(read_all(imports), relations)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reads in data for all versions, returning array of
|
20
|
+
# version/class-data pairs. We don't use a hash to preserve the
|
21
|
+
# order of versions (from oldest to newest).
|
22
|
+
def read_all(imports)
|
23
|
+
imports.map do |ver|
|
24
|
+
{
|
25
|
+
:version => ver[:version],
|
26
|
+
:classes => ver[:path] ? read(ver) : current_version,
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_version
|
32
|
+
NullObject.new(:[] => NullObject.new(:[] => true))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Reads in data from all .json files in directory
|
36
|
+
def read(ver)
|
37
|
+
# Map list of files into pairs of (classname, members-hash)
|
38
|
+
pairs = ParallelWrap.map(Dir[ver[:path] + "/*.json"]) do |filename|
|
39
|
+
JsDuck::Logger.instance.log("Importing #{ver[:version]}", filename)
|
40
|
+
json = JsonDuck.read(filename)
|
41
|
+
[json["name"], members_id_index(json)]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Turn key-value pairs array into hash
|
45
|
+
return Hash[ pairs ]
|
46
|
+
end
|
47
|
+
|
48
|
+
# creates index of all class members
|
49
|
+
def members_id_index(json)
|
50
|
+
index = {}
|
51
|
+
["members", "statics"].each do |group_name|
|
52
|
+
json[group_name].each_pair do |tagname, members|
|
53
|
+
members.each do |m|
|
54
|
+
index[m["id"]] = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
index
|
59
|
+
end
|
60
|
+
|
61
|
+
# Using the imported versions data, adds @since tags to all
|
62
|
+
# classes/members.
|
63
|
+
def generate_since_tags(versions, relations)
|
64
|
+
last_version = versions.last[:version]
|
65
|
+
|
66
|
+
relations.each do |cls|
|
67
|
+
v = cls[:meta][:since] || class_since(versions, cls)
|
68
|
+
cls[:meta][:since] = v
|
69
|
+
cls[:meta][:new] = true if v == last_version
|
70
|
+
|
71
|
+
cls.all_local_members.each do |m|
|
72
|
+
v = m[:meta][:since] || member_since(versions, cls, m)
|
73
|
+
m[:meta][:since] = v
|
74
|
+
m[:meta][:new] = true if v == last_version
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def member_since(versions, cls, m)
|
80
|
+
versions.each do |ver|
|
81
|
+
c = ver[:classes][cls[:name]]
|
82
|
+
return ver[:version] if c && c[m[:id]]
|
83
|
+
cls[:alternateClassNames].each do |name|
|
84
|
+
c = ver[:classes][name]
|
85
|
+
return ver[:version] if c && c[m[:id]]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns name of the version since which the class is available
|
91
|
+
def class_since(versions, cls)
|
92
|
+
versions.each do |ver|
|
93
|
+
return ver[:version] if ver[:classes][cls[:name]]
|
94
|
+
cls[:alternateClassNames].each do |name|
|
95
|
+
return ver[:version] if ver[:classes][name]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|