lib/yard/server/library_version.rb in yard-0.9.16 vs lib/yard/server/library_version.rb in yard-0.9.17
- old
+ new
@@ -1,277 +1,277 @@
-# frozen_string_literal: true
-require 'fileutils'
-require 'thread'
-
-module YARD
- module Server
- # This exception is raised when {LibraryVersion#prepare!} fails, or discovers
- # that the library is not "prepared" to be served by
- class LibraryNotPreparedError < RuntimeError; end
-
- # A library version encapsulates a library's documentation at a specific version.
- # Although the version is optional, this allows for creating multiple documentation
- # points for a specific library, each representing a unique version. The term
- # "library" used in other parts of the YARD::Server documentation refers to
- # objects of this class unless otherwise noted.
- #
- # A library points to a location where a {#yardoc_file} is located so that
- # its documentation may be loaded and served. Optionally, a {#source_path} is
- # given to point to a location where any extra files (and {YARD::CLI::Yardoc .yardopts})
- # should be loaded from. Both of these methods may not be known immediately,
- # since the yardoc file may not be built until later. Resolving the yardoc
- # file and source path are dependent on the specific library "source type" used.
- # Source types (known as "library source") are discussed in detail below.
- #
- # == Using with Adapters
- # A list of libraries need to be passed into adapters upon creation. In
- # most cases, you will never do this manually, but if you use a {RackMiddleware},
- # you will need to pass in this list yourself. To build this list of libraries,
- # you should create a hash of library names mapped to an *Array* of LibraryVersion
- # objects. For example:
- #
- # {'mylib' => [LibraryVersion.new('mylib', '1.0', ...),
- # LibraryVersion.new('mylib', '2.0', ...)]}
- #
- # Note that you can also use {Adapter#add_library} for convenience.
- #
- # The "array" part is required, even for just one library version.
- #
- # == Library Sources
- # The {#source} method represents the library source type, ie. where the
- # library "comes from". It might come from "disk", or it might come from a
- # "gem" (technically the disk, but a separate type nonetheless). In these
- # two cases, the yardoc file sits somewhere on your filesystem, though
- # it may also be built dynamically if it does not yet exist. This behaviour
- # is controlled through the {#prepare!} method, which prepares the yardoc file
- # given a specific library source. We will see how this works in detail in
- # the following section.
- #
- # == Implementing a Custom Library Source
- # YARD can be extended to support custom library sources in order to
- # build or retrieve a yardoc file at runtime from many different locations.
- #
- # To implement this behaviour, 3 methods can be added to the +LibraryVersion+
- # class, +#load_yardoc_from_SOURCE+, +#yardoc_file_for_SOURCE+, and
- # +#source_path_for_SOURCE+. In all cases, "SOURCE" represents the source
- # type used in {#source} when creating the library object. The
- # +#yardoc_file_for_SOURCE+ and +#source_path_for_SOURCE+ methods are called upon
- # creation and should return the location where the source code for the library
- # lives. The load method is called from {#prepare!} if there is no yardoc file
- # and should set {#yardoc_file}. Below is a full example for
- # implementing a custom library source, +:http+, which reads packaged .yardoc
- # databases from zipped archives off of an HTTP server.
- #
- # Note that only +#load_yardoc_from_SOURCE+ is required. The other two
- # methods are optional and can be set manually (via {#source_path=} and
- # {#yardoc_file=}) on the object at any time.
- #
- # @example Implementing a Custom Library Source
- # # Adds the source type "http" for .yardoc files zipped on HTTP servers
- # class LibraryVersion
- # def load_yardoc_from_http
- # Thread.new do
- # # zip/unzip method implementations are not shown
- # download_zip_file("http://mysite.com/yardocs/#{self}.zip")
- # unzip_file_to("/path/to/yardocs/#{self}")
- # end
- #
- # # tell the server it's not ready yet (but it might be next time)
- # raise LibraryNotPreparedError
- # end
- #
- # def yardoc_file_for_http
- # "/path/to/yardocs/#{self}/.yardoc"
- # end
- #
- # def source_path_for_http
- # File.dirname(yardoc_file)
- # end
- # end
- #
- # # Creating a library of this source type:
- # LibraryVersion.new('name', '1.0', nil, :http)
- #
- class LibraryVersion
- # @return [String] the name of the library
- attr_accessor :name
-
- # @return [String] the version of the specific library
- attr_accessor :version
-
- # @return [String] the location of the yardoc file used to load the object
- # information from.
- # @return [nil] if no yardoc file exists yet. In this case, {#prepare!} will
- # be called on this library to build the yardoc file.
- # @note To implement a custom yardoc file getter, implement
- def yardoc_file
- @yardoc_file ||= load_yardoc_file
- end
- attr_writer :yardoc_file
-
- # @return [Symbol] the source type representing where the yardoc should be
- # loaded from. Defaults are +:disk+ and +:gem+, though custom sources
- # may be implemented. This value is used to inform {#prepare!} about how
- # to load the necessary data in order to display documentation for an object.
- # @see LibraryVersion LibraryVersion documentation for "Implementing a Custom Library Source"
- attr_accessor :source
-
- # @return [String] the location of the source code for a library. This
- # value is filled by calling +#source_path_for_SOURCE+ on this class.
- # @return [nil] if there is no source code
- # @see LibraryVersion LibraryVersion documentation for "Implementing a Custom Library Source"
- def source_path
- @source_path ||= load_source_path
- end
- attr_writer :source_path
-
- # @param [String] name the name of the library
- # @param [String] version the specific (usually, but not always, numeric) library
- # version
- # @param [String] yardoc the location of the yardoc file, or nil if it is
- # generated later
- # @param [Symbol] source the location of the files used to build the yardoc.
- # Builtin source types are +:disk+ or +:gem+.
- def initialize(name, version = nil, yardoc = nil, source = :disk)
- self.name = name
- self.yardoc_file = yardoc
- self.version = version
- self.source = source
- end
-
- # @param [Boolean] url_format if true, returns the string in a URI-compatible
- # format (for appending to a URL). Otherwise, it is given in a more human
- # readable format.
- # @return [String] the string representation of the library.
- def to_s(url_format = true)
- version ? "#{name}#{url_format ? '/' : '-'}#{version}" : name.to_s
- end
-
- # @return [Fixnum] used for Hash mapping.
- def hash; to_s.hash end
-
- # @return [Boolean] whether another LibraryVersion is equal to this one
- def eql?(other)
- other.is_a?(LibraryVersion) && other.name == name &&
- other.version == version && other.yardoc_file == yardoc_file
- end
- alias == eql?
- alias equal? eql?
-
- # @return [Boolean] whether the library has been completely processed
- # and is ready to be served
- def ready?
- return false if yardoc_file.nil?
- serializer.complete?
- end
-
- # @note You should not directly override this method. Instead, implement
- # +load_yardoc_from_SOURCENAME+ when implementing loading for a specific
- # source type. See the {LibraryVersion} documentation for "Implementing
- # a Custom Library Source"
- #
- # Prepares a library to be displayed by the server. This callback is
- # performed before each request on a library to ensure that it is loaded
- # and ready to be viewed. If any steps need to be performed prior to loading,
- # they are performed through this method (though they should be implemented
- # through the +load_yardoc_from_SOURCE+ method).
- #
- # @raise [LibraryNotPreparedError] if the library is not ready to be
- # displayed. Usually when raising this error, you would simultaneously
- # begin preparing the library for subsequent requests, although this
- # is not necessary.
- def prepare!
- return if ready?
- meth = "load_yardoc_from_#{source}"
- send(meth) if respond_to?(meth, true)
- end
-
- # @return [Gem::Specification] a gemspec object for a given library. Used
- # for :gem source types.
- # @return [nil] if there is no installed gem for the library
- def gemspec
- ver = version ? "= #{version}" : ">= 0"
- YARD::GemIndex.find_all_by_name(name, ver).last
- end
-
- protected
-
- @@chdir_mutex = Mutex.new
-
- # Called when a library of source type "disk" is to be prepared. In this
- # case, the {#yardoc_file} should already be set, but the library may not
- # be prepared. Run preparation if not done.
- #
- # @raise [LibraryNotPreparedError] if the yardoc file has not been
- # prepared.
- def load_yardoc_from_disk
- return if ready?
-
- @@chdir_mutex.synchronize do
- Dir.chdir(source_path_for_disk) do
- Thread.new do
- CLI::Yardoc.run('--no-stats', '-n', '-b', yardoc_file)
- end
- end
- end
-
- raise LibraryNotPreparedError
- end
-
- # Called when a library of source type "gem" is to be prepared. In this
- # case, the {#yardoc_file} needs to point to the correct location for
- # the installed gem. The yardoc file is built if it has not been done.
- #
- # @raise [LibraryNotPreparedError] if the gem does not have an existing
- # yardoc file.
- def load_yardoc_from_gem
- return if ready?
- ver = version ? "= #{version}" : ">= 0"
-
- @@chdir_mutex.synchronize do
- Thread.new do
- # Build gem docs on demand
- log.debug "Building gem docs for #{to_s(false)}"
- CLI::Gems.run(name, ver)
- log.debug "Done building gem docs for #{to_s(false)}"
- end
- end
-
- raise LibraryNotPreparedError
- end
-
- # @return [String] the source path for a disk source
- def source_path_for_disk
- File.dirname(yardoc_file) if yardoc_file
- end
-
- # @return [String] the source path for a gem source
- def source_path_for_gem
- gemspec.full_gem_path if gemspec
- end
-
- # @return [String] the yardoc file for a gem source
- def yardoc_file_for_gem
- require 'rubygems'
- ver = version ? "= #{version}" : ">= 0"
- Registry.yardoc_file_for_gem(name, ver)
- end
-
- private
-
- def load_source_path
- meth = "source_path_for_#{source}"
- send(meth) if respond_to?(meth, true)
- end
-
- def load_yardoc_file
- meth = "yardoc_file_for_#{source}"
- send(meth) if respond_to?(meth, true)
- end
-
- def serializer
- return if yardoc_file.nil?
- Serializers::YardocSerializer.new(yardoc_file)
- end
- end
- end
-end
+# frozen_string_literal: true
+require 'fileutils'
+require 'thread'
+
+module YARD
+ module Server
+ # This exception is raised when {LibraryVersion#prepare!} fails, or discovers
+ # that the library is not "prepared" to be served by
+ class LibraryNotPreparedError < RuntimeError; end
+
+ # A library version encapsulates a library's documentation at a specific version.
+ # Although the version is optional, this allows for creating multiple documentation
+ # points for a specific library, each representing a unique version. The term
+ # "library" used in other parts of the YARD::Server documentation refers to
+ # objects of this class unless otherwise noted.
+ #
+ # A library points to a location where a {#yardoc_file} is located so that
+ # its documentation may be loaded and served. Optionally, a {#source_path} is
+ # given to point to a location where any extra files (and {YARD::CLI::Yardoc .yardopts})
+ # should be loaded from. Both of these methods may not be known immediately,
+ # since the yardoc file may not be built until later. Resolving the yardoc
+ # file and source path are dependent on the specific library "source type" used.
+ # Source types (known as "library source") are discussed in detail below.
+ #
+ # == Using with Adapters
+ # A list of libraries need to be passed into adapters upon creation. In
+ # most cases, you will never do this manually, but if you use a {RackMiddleware},
+ # you will need to pass in this list yourself. To build this list of libraries,
+ # you should create a hash of library names mapped to an *Array* of LibraryVersion
+ # objects. For example:
+ #
+ # {'mylib' => [LibraryVersion.new('mylib', '1.0', ...),
+ # LibraryVersion.new('mylib', '2.0', ...)]}
+ #
+ # Note that you can also use {Adapter#add_library} for convenience.
+ #
+ # The "array" part is required, even for just one library version.
+ #
+ # == Library Sources
+ # The {#source} method represents the library source type, ie. where the
+ # library "comes from". It might come from "disk", or it might come from a
+ # "gem" (technically the disk, but a separate type nonetheless). In these
+ # two cases, the yardoc file sits somewhere on your filesystem, though
+ # it may also be built dynamically if it does not yet exist. This behaviour
+ # is controlled through the {#prepare!} method, which prepares the yardoc file
+ # given a specific library source. We will see how this works in detail in
+ # the following section.
+ #
+ # == Implementing a Custom Library Source
+ # YARD can be extended to support custom library sources in order to
+ # build or retrieve a yardoc file at runtime from many different locations.
+ #
+ # To implement this behaviour, 3 methods can be added to the +LibraryVersion+
+ # class, +#load_yardoc_from_SOURCE+, +#yardoc_file_for_SOURCE+, and
+ # +#source_path_for_SOURCE+. In all cases, "SOURCE" represents the source
+ # type used in {#source} when creating the library object. The
+ # +#yardoc_file_for_SOURCE+ and +#source_path_for_SOURCE+ methods are called upon
+ # creation and should return the location where the source code for the library
+ # lives. The load method is called from {#prepare!} if there is no yardoc file
+ # and should set {#yardoc_file}. Below is a full example for
+ # implementing a custom library source, +:http+, which reads packaged .yardoc
+ # databases from zipped archives off of an HTTP server.
+ #
+ # Note that only +#load_yardoc_from_SOURCE+ is required. The other two
+ # methods are optional and can be set manually (via {#source_path=} and
+ # {#yardoc_file=}) on the object at any time.
+ #
+ # @example Implementing a Custom Library Source
+ # # Adds the source type "http" for .yardoc files zipped on HTTP servers
+ # class LibraryVersion
+ # def load_yardoc_from_http
+ # Thread.new do
+ # # zip/unzip method implementations are not shown
+ # download_zip_file("http://mysite.com/yardocs/#{self}.zip")
+ # unzip_file_to("/path/to/yardocs/#{self}")
+ # end
+ #
+ # # tell the server it's not ready yet (but it might be next time)
+ # raise LibraryNotPreparedError
+ # end
+ #
+ # def yardoc_file_for_http
+ # "/path/to/yardocs/#{self}/.yardoc"
+ # end
+ #
+ # def source_path_for_http
+ # File.dirname(yardoc_file)
+ # end
+ # end
+ #
+ # # Creating a library of this source type:
+ # LibraryVersion.new('name', '1.0', nil, :http)
+ #
+ class LibraryVersion
+ # @return [String] the name of the library
+ attr_accessor :name
+
+ # @return [String] the version of the specific library
+ attr_accessor :version
+
+ # @return [String] the location of the yardoc file used to load the object
+ # information from.
+ # @return [nil] if no yardoc file exists yet. In this case, {#prepare!} will
+ # be called on this library to build the yardoc file.
+ # @note To implement a custom yardoc file getter, implement
+ def yardoc_file
+ @yardoc_file ||= load_yardoc_file
+ end
+ attr_writer :yardoc_file
+
+ # @return [Symbol] the source type representing where the yardoc should be
+ # loaded from. Defaults are +:disk+ and +:gem+, though custom sources
+ # may be implemented. This value is used to inform {#prepare!} about how
+ # to load the necessary data in order to display documentation for an object.
+ # @see LibraryVersion LibraryVersion documentation for "Implementing a Custom Library Source"
+ attr_accessor :source
+
+ # @return [String] the location of the source code for a library. This
+ # value is filled by calling +#source_path_for_SOURCE+ on this class.
+ # @return [nil] if there is no source code
+ # @see LibraryVersion LibraryVersion documentation for "Implementing a Custom Library Source"
+ def source_path
+ @source_path ||= load_source_path
+ end
+ attr_writer :source_path
+
+ # @param [String] name the name of the library
+ # @param [String] version the specific (usually, but not always, numeric) library
+ # version
+ # @param [String] yardoc the location of the yardoc file, or nil if it is
+ # generated later
+ # @param [Symbol] source the location of the files used to build the yardoc.
+ # Builtin source types are +:disk+ or +:gem+.
+ def initialize(name, version = nil, yardoc = nil, source = :disk)
+ self.name = name
+ self.yardoc_file = yardoc
+ self.version = version
+ self.source = source
+ end
+
+ # @param [Boolean] url_format if true, returns the string in a URI-compatible
+ # format (for appending to a URL). Otherwise, it is given in a more human
+ # readable format.
+ # @return [String] the string representation of the library.
+ def to_s(url_format = true)
+ version ? "#{name}#{url_format ? '/' : '-'}#{version}" : name.to_s
+ end
+
+ # @return [Fixnum] used for Hash mapping.
+ def hash; to_s.hash end
+
+ # @return [Boolean] whether another LibraryVersion is equal to this one
+ def eql?(other)
+ other.is_a?(LibraryVersion) && other.name == name &&
+ other.version == version && other.yardoc_file == yardoc_file
+ end
+ alias == eql?
+ alias equal? eql?
+
+ # @return [Boolean] whether the library has been completely processed
+ # and is ready to be served
+ def ready?
+ return false if yardoc_file.nil?
+ serializer.complete?
+ end
+
+ # @note You should not directly override this method. Instead, implement
+ # +load_yardoc_from_SOURCENAME+ when implementing loading for a specific
+ # source type. See the {LibraryVersion} documentation for "Implementing
+ # a Custom Library Source"
+ #
+ # Prepares a library to be displayed by the server. This callback is
+ # performed before each request on a library to ensure that it is loaded
+ # and ready to be viewed. If any steps need to be performed prior to loading,
+ # they are performed through this method (though they should be implemented
+ # through the +load_yardoc_from_SOURCE+ method).
+ #
+ # @raise [LibraryNotPreparedError] if the library is not ready to be
+ # displayed. Usually when raising this error, you would simultaneously
+ # begin preparing the library for subsequent requests, although this
+ # is not necessary.
+ def prepare!
+ return if ready?
+ meth = "load_yardoc_from_#{source}"
+ send(meth) if respond_to?(meth, true)
+ end
+
+ # @return [Gem::Specification] a gemspec object for a given library. Used
+ # for :gem source types.
+ # @return [nil] if there is no installed gem for the library
+ def gemspec
+ ver = version ? "= #{version}" : ">= 0"
+ YARD::GemIndex.find_all_by_name(name, ver).last
+ end
+
+ protected
+
+ @@chdir_mutex = Mutex.new
+
+ # Called when a library of source type "disk" is to be prepared. In this
+ # case, the {#yardoc_file} should already be set, but the library may not
+ # be prepared. Run preparation if not done.
+ #
+ # @raise [LibraryNotPreparedError] if the yardoc file has not been
+ # prepared.
+ def load_yardoc_from_disk
+ return if ready?
+
+ @@chdir_mutex.synchronize do
+ Dir.chdir(source_path_for_disk) do
+ Thread.new do
+ CLI::Yardoc.run('--no-stats', '-n', '-b', yardoc_file)
+ end
+ end
+ end
+
+ raise LibraryNotPreparedError
+ end
+
+ # Called when a library of source type "gem" is to be prepared. In this
+ # case, the {#yardoc_file} needs to point to the correct location for
+ # the installed gem. The yardoc file is built if it has not been done.
+ #
+ # @raise [LibraryNotPreparedError] if the gem does not have an existing
+ # yardoc file.
+ def load_yardoc_from_gem
+ return if ready?
+ ver = version ? "= #{version}" : ">= 0"
+
+ @@chdir_mutex.synchronize do
+ Thread.new do
+ # Build gem docs on demand
+ log.debug "Building gem docs for #{to_s(false)}"
+ CLI::Gems.run(name, ver)
+ log.debug "Done building gem docs for #{to_s(false)}"
+ end
+ end
+
+ raise LibraryNotPreparedError
+ end
+
+ # @return [String] the source path for a disk source
+ def source_path_for_disk
+ File.dirname(yardoc_file) if yardoc_file
+ end
+
+ # @return [String] the source path for a gem source
+ def source_path_for_gem
+ gemspec.full_gem_path if gemspec
+ end
+
+ # @return [String] the yardoc file for a gem source
+ def yardoc_file_for_gem
+ require 'rubygems'
+ ver = version ? "= #{version}" : ">= 0"
+ Registry.yardoc_file_for_gem(name, ver)
+ end
+
+ private
+
+ def load_source_path
+ meth = "source_path_for_#{source}"
+ send(meth) if respond_to?(meth, true)
+ end
+
+ def load_yardoc_file
+ meth = "yardoc_file_for_#{source}"
+ send(meth) if respond_to?(meth, true)
+ end
+
+ def serializer
+ return if yardoc_file.nil?
+ Serializers::YardocSerializer.new(yardoc_file)
+ end
+ end
+ end
+end