Class: YARD::LinkStdlib::ObjectMap

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/yard/link_stdlib/object_map.rb

Constant Summary collapse

@@data_dir =

Class Variables

LinkStdlib::ROOT.join( 'maps' ).tap { |path|
  FileUtils.mkdir_p( path ) unless path.exist?
}
@@current =
nil
@@module_aliases =

A map of module names that we know actually point to another one.

This is basically here to support YAML, which internally points to Psych in a truly annoying fashion… seems to be a remnant from when ‘Syck` was around, but that appears to have been yanked out years ago around Ruby 2.0, and I can’t think of seeing anyone use anything except Psych for about a decade.

However, it means that there is no entry in the object map for things like YAML.load, which are pretty commonly used. This functionality allows us to address that, by being aware that YAML points to Psych for practical purposes.

Returns:

  • (Hash<String, String>)
{
  "YAML" => "Psych",
}
@@name_rewrites =
{
  # The instance methods of the {JSON} module are encapsulated in a 
  # {Module#module_function} context, which adds them as module methods as
  # well, but RDoc doesn't seem to pick that up, so we just transform them
  # to make this bullshit work.
  # 
  # Creates mappings like:
  # 
  #     "JSON::load" => "JSON.html#method-i-load"
  #     "JSON::dump" => "JSON.html#method-i-dump"
  # 
  /\AJSON#(.*)\z/ => ->( match ) { "JSON::#{ match[ 1 ] }" },
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(version) ⇒ ObjectMap

Instantiate an YARD::LinkStdlib::ObjectMap for a Ruby version.

This just initialized the interface - the source may need to be downloaded and the map generated (see #make) to use it for anything.

Parameters:

  • version (String | Gem::Version)

    Ruby version.



204
205
206
# File 'lib/yard/link_stdlib/object_map.rb', line 204

def initialize version
  @version = Gem::Version.new version
end

Instance Attribute Details

#versionGem::Version (readonly)

Ruby version.

Returns:

  • (Gem::Version)


190
191
192
# File 'lib/yard/link_stdlib/object_map.rb', line 190

def version
  @version
end

Class Method Details

.add(ruby_version, force: false) ⇒ ObjectMap

Add a Ruby version (download and build map data, see #make).

Parameters:

  • ruby_version (String | Gem::Version)

    Version to add.

  • force (Boolean) (defaults to: false)

    Pass ‘true` to re-build map when already present (see #make).

Returns:



176
177
178
# File 'lib/yard/link_stdlib/object_map.rb', line 176

def self.add ruby_version, force: false
  new( ruby_version ).make force: force
end

.allArray<ObjectMap>

Get all the object maps present.

Returns:



155
156
157
158
159
160
161
162
# File 'lib/yard/link_stdlib/object_map.rb', line 155

def self.all
  data_dir.entries.
    select  { |filename| filename.to_s =~ /\Aruby\-(\d+\.)+json\.gz\z/ }.
    map     { |filename|
      new File.basename( filename.to_s, '.json.gz' ).sub( /\Aruby\-/, '' )
    }.
    sort
end

.currentObjectMap

Returns:



140
141
142
143
144
145
146
147
148
# File 'lib/yard/link_stdlib/object_map.rb', line 140

def self.current
  version = RubyVersion.get

  if @@current.nil? || @@current.version != version
    @@current = new( version ).make
  end

  @@current
end

.data_dirPathname

The directory in which to load and store object map data.

Examples:

YARD::LinkStdlib::ObjectMap.data_dir
#=> Pathname.new "#{ LinkStdlib::ROOT }/maps"

Returns:

  • (Pathname)


131
132
133
# File 'lib/yard/link_stdlib/object_map.rb', line 131

def self.data_dir
  @@data_dir
end

.data_dir=(path) ⇒ Pathname

Set the directory in which to load and store map data. Must exist.

Parameters:

  • path (String | Pathname)

    New data directory. Will be expanded.

Returns:

  • (Pathname)

Raises:

  • (ArgumentError)

    If ‘path` is not a directory.



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/yard/link_stdlib/object_map.rb', line 109

def self.data_dir= path
  expanded = Pathname.new( path ).expand_path

  unless expanded.directory?
    raise ArgumentError,
      "Custom ObjectMap.data_dir must expand to an existing directory," +
      "try creating it first? Received #{ path.inspect }, expanded to " +
      expanded.to_s.inspect
  end

  @@data_dir = expanded
end

.remove(ruby_version, remove_source: true, force: false) ⇒ Object



181
182
183
# File 'lib/yard/link_stdlib/object_map.rb', line 181

def self.remove ruby_version, remove_source: true, force: false
  raise "TODO"
end

Instance Method Details

#<=>(other) ⇒ Fixnum

Compare YARD::LinkStdlib::ObjectMap instances by their #version (used to sort them).

Returns:

  • (Fixnum)

    ‘0` is equal, negatives and positives denote order.



419
420
421
# File 'lib/yard/link_stdlib/object_map.rb', line 419

def <=> other
  version <=> other.version
end

#data(reload: false) ⇒ Object



286
287
288
289
290
291
292
293
294
295
# File 'lib/yard/link_stdlib/object_map.rb', line 286

def data reload: false
  if reload || @data.nil?
    @name_rewrites = nil
    @data = Zlib::GzipReader.open path do |gz|
      JSON.load gz.read
    end
  end

  @data
end

#filenameString

The name for this YARD::LinkStdlib::ObjectMap‘s data file.

Examples:

YARD::LinkStdlib::ObjectMap.new( '2.3.7' ).filename
#=> "ruby-2.3.7.json.gz"

Returns:

  • (String)


221
222
223
# File 'lib/yard/link_stdlib/object_map.rb', line 221

def filename
  @filename ||= "ruby-#{ version }.json.gz"
end

#make(force: false) ⇒ ObjectMap

Build the map data file (if needed or forced).

Parameters:

  • force (Boolean) (defaults to: false)

    Set to true to re-build even if the map data file is present.

Returns:



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/yard/link_stdlib/object_map.rb', line 260

def make force: false
  # Bail unless forced or the map is not present
  if force
    log.info "FORCE making object map for Ruby #{ version }..."
  elsif !present?
    log.info "Making object map for Ruby #{ version }..."
  else
    log.info "Object map for Ruby #{ version } is present."
    return self
  end

  # Make sure we have the source files in place
  source.ensure

  # Invoke the build script
  LinkStdlib.system! \
    LinkStdlib::ROOT.join( 'bin', 'make_map.rb' ).to_s,
    source.src_path.to_s,
    path.to_s
  
  log.info "Made object map for Ruby #{ version }."

  self
end

#name_rewrites(reload: false) ⇒ Object



310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/yard/link_stdlib/object_map.rb', line 310

def name_rewrites reload: false
  data( reload: true ) if reload
  
  @name_rewrites ||= \
    data.each_with_object( {} ) do |(name, rel_path), name_rewrites|
      @@name_rewrites.each do |regexp, transformer|
        if (match = regexp.match( name ))
          name_rewrites[ transformer.call match ] = rel_path
        end
      end
    end
end

#names(reload: false) ⇒ Array<String>

Names of the objects in #data (equivalent to ‘self.data.keys`).

Parameters:

  • reload (Boolean) (defaults to: false)

    When ‘true`, reload the #data from disk first.

Returns:

  • (Array<String>)


305
306
307
# File 'lib/yard/link_stdlib/object_map.rb', line 305

def names reload: false
  data( reload: reload ).keys
end

#pathPathname

Absolute path to this YARD::LinkStdlib::ObjectMap‘s data file.

Returns:

  • (Pathname)


230
231
232
# File 'lib/yard/link_stdlib/object_map.rb', line 230

def path
  @path ||= self.class.data_dir.join filename
end

#present?Boolean

Is the object map present for this #version?

Returns:

  • (Boolean)


239
240
241
# File 'lib/yard/link_stdlib/object_map.rb', line 239

def present?
  path.exist?
end

#resolve(name) ⇒ nil, Array[(String, String?)>

Get the relative path for the URL of an online stdlib document given the code object’s name.

Examples:

YARD::LinkStdlib::ObjectMap.current.resolve 'String'
#=> [ 'String', 'String.html' ]

De-Aliasing

YARD::LinkStdlib::ObjectMap.current.resolve 'YAML.load'
#=> [ 'Psych::load', 'Psych.html#method-c-load' ]

Parameters:

  • name (String)

Returns:

  • (nil)

    The (normalized) ‘name` was not found in the YARD::LinkStdlib::ObjectMap.

  • (Array[(String, String?)>)

    The normalized name (which may be totally different than the ‘name` argument due to de-aliasing) followed by the relative URL path to it’s doc.



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/yard/link_stdlib/object_map.rb', line 345

def resolve name
  name = LinkStdlib.normalize_name name
  rel_path = data[ name ]
  
  if rel_path.nil?
    split = name.split '::'
    
    if (de_aliased_module_name = @@module_aliases[ split.first ])
      de_aliased_name = \
        [ de_aliased_module_name, *split[ 1..-1 ] ].join( '::' )
      
      if (de_aliased_module_name = data[ de_aliased_name ])
        return [ de_aliased_name, de_aliased_module_name ]
      end
    end
    
    if (rewritten_rel_path = name_rewrites[ name ])
      log.debug "Found re-written relative path: " +
                "#{ name } -> #{ rewritten_rel_path.inspect }"
      
      return [ name, rewritten_rel_path ]
    end # if rewritten_rel_path
  end # if rel_path.nil?
  
  # NOTE `rel_path` may be `nil`, indicating we didn't find shit
  [ name, rel_path ]
end

#sourceRubySource

The RubySource interface for this YARD::LinkStdlib::ObjectMap.

Returns:



248
249
250
# File 'lib/yard/link_stdlib/object_map.rb', line 248

def source
  @source ||= RubySource.new version
end

#url_for(name, **url_options) ⇒ nil, String

Get the doc URL for a name.

Examples:

Using defaults

YARD::LinkStdlib::ObjectMap.current.url_for 'String'
#=> 'https://docs.ruby-lang.org/en/2.3.0/String.html'

Manually override components

YARD::LinkStdlib::ObjectMap.current.url_for 'String',
  https: false,
  domain: 'example.com',
  lang: 'ja'
#=> 'http://example.com/ja/2.3.0/String.html'

Parameters:

Returns:

  • (nil)

    The (normalized) ‘name` was not found in the YARD::LinkStdlib::ObjectMap.

  • (String)

    The fully-formed URL to the online doc.



399
400
401
402
403
404
405
406
407
408
# File 'lib/yard/link_stdlib/object_map.rb', line 399

def url_for name, **url_options
  name, rel_path = resolve name
  
  if rel_path
    LinkStdlib.build_url \
      rel_path,
      **url_options,
      version: RubyVersion.minor( version )
  end
end