Class: Shomen::YardAdaptor

Inherits:
Object
  • Object
show all
Defined in:
lib/shomen/yard.rb

Overview

This adapter is used to convert YARD’s documentation extracted from a local store (‘.yardoc`) to Shomen’s pure-data format.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ YardAdaptor

New adaptor.



17
18
19
20
# File 'lib/shomen/yard.rb', line 17

def initialize(options)
  @db    = options[:db]    || '.yardoc'
  @files = options[:files] || ['lib', 'README*']
end

Instance Attribute Details

#tableObject (readonly)

The hash object that is used to store the generated documentation.



14
15
16
# File 'lib/shomen/yard.rb', line 14

def table
  @table
end

Instance Method Details

#collect_filesObject (private)

Collect files given list of globs.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/shomen/yard.rb', line 74

def collect_files
  globs = @files
  globs = globs.map{ |glob| Dir[glob] }.flatten.uniq
  globs = globs.map do |glob|
    if File.directory?(glob)
      Dir[File.join(glob, '**/*')]
    else
      glob
    end
  end
  list = globs.flatten.uniq.compact
  list = list.reject{ |path| File.extname(path) == '.html' }
  list = list.select{ |path| File.file?(path) }
  list
end

#debug_msg(msg) ⇒ Object (private)

Output progress information if debugging is enabled



356
357
358
359
360
361
362
363
364
# File 'lib/shomen/yard.rb', line 356

def debug_msg(msg)
  return unless $DEBUG
  case msg[-1,1]
    when '.' then tab = "= "
    when ':' then tab = "== "
    else          tab = "* "
  end
  $stderr.puts(tab + msg)
end

#generateObject

Generate the shomen data structure.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/shomen/yard.rb', line 23

def generate
  if not File.exist?(@db)
    $stderr.puts "ERROR: YARD database not found -- '#{@db}`."
    exit -1
  end

  @table = {}

  

  @registry = YARD::Registry.load!(@db)
  @registry.each do |object|
    case object.type
    when :constant
      generate_constant(object)
    when :class, :module
      generate_class(object)
      # TODO: is this needed?
      object.constants.each do |c|
        generate_constant(c)
      end
    #when :module
    #  generate_module(object)
    #  # TODO: is this needed?
    #  object.constants.each do |c|
    #    generate_constant(c)
    #  end
    when :method
      generate_method(object)
    else
      $stderr.puts "What is an #{object.type}? Ignored!"
    end
  end

  # TODO: Are c/c++ sourse files working okay?
  # TODO: Add a generator for non-ruby script (e.g. .js)?
  collect_files.each do |file|
    case File.extname(file)
    when '.rb', '.rbx', '.c', '.cpp'
      generate_script(file)
    when '.rdoc', '.md', '.markdown', '.txt'
      generate_document(file)
    else
      generate_document(file)
    end
  end
end

#generate_class(yard_class) ⇒ Hash (private)

Note:

As to whether ‘methods` also contains the accessor methods listed in `accessors` is left to YARD to determine.

Generate a class or module structure.

Returns:

  • (Hash)

    class data that has been placed in the table



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/shomen/yard.rb', line 105

def generate_class(yard_class)
  debug_msg(yard_class.path.to_s)

  meths = yard_class.meths(:included=>false, :inherited=>false)

  if yard_class.type == :class
    model = Model::Class.new
    model.superclass = yard_class.superclass ? yard_class.superclass.path : 'Object'
  else 
    model = Model::Module.new
  end

  model.path            = yard_class.path
  model.name            = yard_class.name.to_s
  model.namespace       = yard_class.namespace.path  #full_name.split('::')[0...-1].join('::'),
  model.comment         = yard_class.docstring.to_s
  model.format          = 'rdoc'  #TODO: how to determine? rdoc, markdown or plaintext ?
  model.constants       = yard_class.constants.map{ |x| x.path }  #TODO: complete_name(x.name, c.full_name) }
  model.includes        = yard_class.instance_mixins.map{ |x| x.path }
  model.extensions      = yard_class.class_mixins.map{ |x| x.path }
  model.modules         = yard_class.children.select{ |x| x.type == :module }.map{ |x| x.path }
                          #yard_class.modules.map{ |x| complete_name(x.name, c.full_name) }
  model.classes         = yard_class.children.select{ |x| x.type == :class }.map{ |x| x.path }
                          #yard_class.classes.map{ |x| complete_name(x.name, c.full_name) }

  model.methods         = meths.select.map{ |m| m.path }
  #model.methods         = meths.select{ |m| m.scope == :instance }.map{ |m| m.path }
  #model.class_methods   = meths.select{ |m| m.scope == :class }.map{ |m| m.path }

  model.accessors       = yard_class.attributes[:class].map{ |k, rw| yard_class.path + '.' + k.to_s } +
                          yard_class.attributes[:instance].map{ |k, rw| yard_class.path + '#' + k.to_s }
  #model.class_accessors = yard_class.attributes[:class].map{ |k, rw| yard_class.path + '.' + k.to_s }

  model.files           = yard_class.files.map{ |f, l| "/#{f}" } # :#{l}" }

  model.tags            = translate_tags(yard_class)

  #@files.concat(yard_class.files.map{ |f, l| f })

  @table[model.path] = model.to_h
end

#generate_constant(yard_constant) ⇒ Object (private)

Generate a constant.



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/shomen/yard.rb', line 272

def generate_constant(yard_constant)
  debug_msg(yard_constant.path.to_s)

  model = Model::Constant.new

  model.path      = yard_constant.path
  model.name      = yard_constant.name.to_s
  model.namespace = yard_constant.namespace.path  
  model.comment   = yard_constant.docstring.to_s
  model.format    = 'rdoc'  #  TODO: how to determine? rdoc, markdown or plain 
  model.value     = yard_constant.value
  model.tags      = translate_tags(yard_constant)
  model.files     = yard_constant.files.map{|f,l| "/#{f}"}  # or "#{f}:#{l}" ?

  @table[model.path] = model.to_h
end

#generate_document(yard_document) ⇒ Object (private)

Generate a file.



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/shomen/yard.rb', line 291

def generate_document(yard_document)
  debug_msg(yard_document)

  model = Model::Document.new

  # FIXME: make absolute
  absolute_path = yard_document.to_s

  model.path   = yard_document.to_s
  model.name   = File.basename(absolute_path)
  model.mtime  = File.mtime(absolute_path)
  model.text   = File.read(absolute_path)
  model.format = mime_type(absolute_path)

  @table['/'+model.path] = model.to_h
end

#generate_metadataHash (private)

Generate project metadata entry.

Returns:

  • (Hash)

    metadata added to the documentation table



93
94
95
96
97
# File 'lib/shomen/yard.rb', line 93

def 
   = Metadata.new

  @table['(metadata)'] = .to_h
end

#generate_method(yard_method) ⇒ Object (private)

Generate a method structure.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/shomen/yard.rb', line 184

def generate_method(yard_method)
  debug_msg(yard_method.to_s)

  model = Model::Method.new
  #class_model = object.scope == :instance ? Shomen::Module::Method : Shomen::Model::Function

  model.path        = yard_method.path
  model.name        = yard_method.name.to_s
  model.namespace   = yard_method.parent.path  
  model.comment     = yard_method.docstring.to_s
  model.format      = 'rdoc'  # TODO: how to determine? rdoc, markdown or plain 
  model.aliases     = yard_method.aliases.map{ |a| a.path }  #method_name(a) }
  # TODO: how to get alias_for from YARD?
  #model.alias_for = method_name(yard_method.alias_for)
  model.singleton   = (yard_method.scope == :class)

  model.declarations << yard_method.scope.to_s
  model.declarations << yard_method.visibility.to_s
  # FIXME
  #model.declarations << yard_method.attr_info

  model.interfaces = []
  yard_method.tags.each do |tag|
    case tag
    when ::YARD::Tags::OverloadTag
      model.interfaces << parse_interface(tag)
    end
  end
  model.interfaces << parse_interface(yard_method)

  model.returns = (
    rtns = []
    yard_method.tags(:return).each do |tag|
      tag.types.each do |t|
        rtns << {'type'=>t, 'comment'=>tag.text}
      end
    end
    rtns
  )

  model.file     = '/'+yard_method.file
  model.line     = yard_method.line.to_i
  model.source   = yard_method.source
  model.language = yard_method.source_type.to_s
  model.dynamic  = yard_method.dynamic

  model.tags     = translate_tags(yard_method)

  @table[model.path] = model.to_h
end

#generate_script(yard_script) ⇒ Object (private)

Generate a script entry.



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/shomen/yard.rb', line 310

def generate_script(yard_script)
  debug_msg(yard_script)

  model = Model::Script.new

  # FIXME: make absolute
  absolute_path = yard_script.to_s

  model.path     = yard_script.to_s
  model.name     = File.basename(absolute_path)
  model.mtime    = File.mtime(absolute_path)
  model.source   = File.read(absolute_path)
  model.language = mime_type(absolute_path)

  #  model.header        = ""
  #  model.footer        = ""
  #  model.requires      =
  #  model.constants     =
  #  model.modules       =
  #  model.classes       =
  #  model.methods       =
  #  model.class_methods =

  @table['/'+model.path] = model.to_h

  #table[index] = Shomen::Model::Script.new(
  #  "name"        => File.basename(object),
  #  "path"        => object,
  #  #"loadpath"    => "lib",
  #  "mtime"       => File.mtime(object),
  #  "header"      => "",
  #  "footer"      => "",
  #  # "requires"    : ["fileutils"],
  #  # "constants"   : ["MusicStore::CONFIG_DIRECTORY"],
  #  # "modules"     : ["MusicStore", "MusicStore::MusicMixin"],
  #  # "classes"     : ["MusicStore::Song"],
  #  # "functions"   : ["MusicStore.config_directory"],
  #  # "methods"     : ["MusicStore::MusicMixin#play", "MusicStore::MusicMixin#artist"]
  #  "source"      => File.read(object)
  #).to_h

  @table['/'+model.path] = model.to_h
end

#mime_type(path) ⇒ Object (private)

Given a file return offical mime-type basic on file extension.

FIXME: official mime types?



369
370
371
372
373
374
375
376
377
378
# File 'lib/shomen/yard.rb', line 369

def mime_type(path)
  case File.extname(path)
  when '.rb', '.rbx'      then 'text/x-ruby'
  when '.c'               then 'text/c-source'  # x-c-code
  when '.js'              then 'text/ecmascript'
  when '.rdoc'            then 'text/rdoc'
  when '.md', '.markdown' then 'text/markdown'
  else 'text/plain'
  end
end

#parse_interface(yard_method) ⇒ Object (private)

Parse a yard method’s interface.



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/shomen/yard.rb', line 236

def parse_interface(yard_method)
  args, block = [], {}

  image, returns = yard_method.signature.split(/[=-]\>/)

  image = image.strip
  if i = image.index(/\)\s*\{/)
    block['image'] = image[i+1..-1].strip
    image          = image[0..i].strip
  end
  image = image.sub(/^def\s*/, '')
  image = image.sub(/^self\./, '')
  image = image.sub('( )','()')

  yard_method.parameters.each do |n,v|
    n = n.to_s
    case n
    when /^\&/
      block['name'] = n
    else
      args << (v ? {'name'=>n,'default'=>v} : {'name'=>n})
    end
  end

  result = {}
  result['signature']  = image
  result['arguments']  = args
  #result['parameters'] = params
  result['block']      = block unless block.empty?
  result['returns']    = returns.strip if returns
  result
end

#translate_tags(yard_object) ⇒ Object (private)

Convert YARD Tags to simple Hash.

TODO: Remove param tags?



383
384
385
386
387
388
389
390
# File 'lib/shomen/yard.rb', line 383

def translate_tags(yard_object)
  tags = {}
  yard_object.tags.each do |tag|
    next if tag.tag_name == 'return'
    tags[tag.tag_name] = tag.text
  end
  return tags
end