lib/logstash-docket/repository.rb (79 lines of code) (raw):
# encoding: utf-8
require_relative 'util/threadsafe_index'
require_relative 'util/threadsafe_deferral'
require_relative 'artifact_plugin'
require_relative 'source'
require_relative 'rubygem_info'
module LogstashDocket
##
# A `Repository` contains the complete source and release history of a named artefact
# that is pushed to rubygems. It has a {@link Source}, which allows us to extract contents
# of the versioned artefacts, and provides methods for listing releases.
#
class Repository
##
# Construct a new {@link Repository} from a gem name and optional version, using metatata
# from the latest release on rubygems.
#
# @param gem_name [String]: the name of the gem on rubygems.org
# @param version [String]: an X.Y.Z version specifier (default: latest)
#
# @yieldparam gem_data [Hash{String=>Object}]: the gem data from rubygems.org
# @yieldreturn [Source]: a {@link Source}, which will be used to get artifact contents etc.
#
# @return [Repository,nil]: returns a {@link Repository} IFF one could be deduced from gem metadata
def self.from_rubygems(gem_name, gem_version=nil, &source_generator)
rubygem_info = RubygemInfo.new(gem_name)
if rubygem_info.nil?
$stderr.puts("[gem:#{gem_name}]: release metadata unavailable from rubygems.org")
return nil
end
gem_version ||= rubygem_info.latest
gemdata = rubygem_info.for_version(gem_version)
if gemdata.nil?
$stderr.puts("[gem:#{gem_name}]: release `#{gem_version}` not published to rubygems.org")
return nil
end
source = source_generator.call(gemdata)
new(gem_name, source, rubygem_info)
end
##
# Construct a new {@link Repository} from a known {@link Source}
#
# @param name [String]: the name of the artefact as registered on rubygems.org
# @param source [Source]: a {@link Source}, which will be used to get artifact contents etc.
def self.from_source(name, source)
new(name, source)
end
private_class_method(:new)
attr_reader :name
attr_reader :source
##
# @api private
def initialize(name, source, rubygem_info = nil)
@name = name
@source = source
@plugin_versions = Util::ThreadsafeIndex.new { |version| ArtifactPlugin.new(repository: self, version: version) }
@rubygem_info = Util::ThreadsafeDeferral.for { rubygem_info || RubygemInfo.new(name) }
end
##
# @return [String]: a short description suitable for logging
def desc
@desc ||= "[repository:#{name}]"
end
##
# Returns an {@link Enumerable} containing all {@link ReleasePackage}s from
# rubygems.org that have a corresponding release tag in our `source`.
#
# @param include_prerelease [Boolean]: (default false)
#
# @return Enumerable[ArtifactPlugin]
def source_tagged_releases(include_prerelease=false)
return enum_for(:source_tagged_releases, include_prerelease) unless block_given?
released_plugins(include_prerelease).each do |released_plugin|
next unless source.release_tags.include?(released_plugin.tag)
yield released_plugin
end
end
##
# Returns an {@link Enumerable} containing all {@link ReleasePackage}s from
# rubygems.org
#
# @param include_prerelease [Boolean]: (default false)
#
# @return Enumerable[ArtifactPlugin]
def released_plugins(include_prerelease=false)
return enum_for(:released_plugins, include_prerelease) unless block_given?
rubygem_info.versions.each do |version|
next unless include_prerelease || !Gem::Version.new(version).prerelease?
yield released_plugin(version)
end
end
##
# Returns a {@link Plugin} representing a release artefact from this repository
# from rubygems.org
#
# @param version [String]: the version to fetch from rubygems (optional: when
# omitted or provided explicitly as `nil`, the resulting {@link Plugin} will
# be an approximation of the repository's `main` using the latest release's\
# metadata).
def released_plugin(version)
@plugin_versions.fetch(version)
end
##
# Returns the {@link Time} of the latest release of a plugin from this
# {@link Repository}, without instantiating a {@link Plugin}.
#
# @return [Time]
def last_release_date
latest_release_version = rubygem_info.latest
latest_release_version && release_date(latest_release_version)
end
##
# @return [Plugin]: the last available release, or nil if there are no releases
def last_release
latest_version = rubygem_info.latest
latest_version && released_plugin(latest_version)
end
##
# @api private
# @return [String]
def read_file(filename, version=nil)
@source.read_file(filename, version)
end
##
# @api private
# @return [String]
def web_url(filename, version)
@source.web_url(filename, version)
end
##
# Fetch the release date of a version, without instantiating a {@link Plugin}
#
# @return [Time]
def release_date(version)
return nil if version.nil?
gem_info = rubygem_info.for_version(version)
created_at = gem_info && gem_info.dig('created_at')
created_at && Time.parse(created_at)
end
##
# @api private
# @return [RubygemInfo]
def rubygem_info
@rubygem_info.value
end
end
end