lib/logstash-docket/artifact_plugin.rb (82 lines of code) (raw):

# encoding: utf-8 require_relative 'embedded_plugin' require_relative 'aliased_plugin' require_relative 'plugin' require_relative 'repository' require 'set' module LogstashDocket ## # An {@link ArtifactPlugin} is an implementation of {@link Plugin} that # is used to represent plugins that are directly available by name on # rubygems.org. # # It can be used to represent self-contained plugins: # - filter, # - input, # - output, OR # - codec. # # It can also be used to represent top-level "integration" plugins that # themselves contain multiple "embedded" plugins (e.g., {@link EmbeddedPlugin}). # # @api public class ArtifactPlugin include Plugin ## # Attempts to instantiate an {@link ArtifactPlugin} from the named gem, using # the optionally-provided version as a hint. # # @param gem_name [String] # @param version [String, nil]: (optional: when omitted, source's main will # be used with the latest-available published gem metadata) # # @yieldparam [Hash{String=>Object}]: gem metadata # @yieldreturn [Source] # # @return [ArtifactPlugin,nil] def self.from_rubygems(gem_name, version=nil, &source_generator) repository = Repository.from_rubygems(gem_name, version, &source_generator) repository && repository.released_plugin(version) end SUPPORTED_TYPES = Set.new(%w(input output filter codec integration).map(&:freeze)).freeze EMPTY = Array.new.freeze VALID_PLUGIN_CAPTURING_TYPE_AND_NAME = %r{\Alogstash-(?<type>[a-z]+)-(?<name>.\w+)} ## # @see Plugin#repository attr_reader :repository ## # @see Plugin#version attr_reader :version ## # @see Plugin#initialize # # @param repository [Repository] # @param version [String] def initialize(repository:,version:) if repository.name !~ VALID_PLUGIN_CAPTURING_TYPE_AND_NAME fail(ArgumentError, "invalid plugin name `#{repository.name}`") end super(type: Regexp.last_match(:type), name: Regexp.last_match(:name)) fail("#{desc} plugin type #{type} not supported as a top-level plugin") unless SUPPORTED_TYPES.include?(type) @repository = repository @version = version && Gem::Version.new(version) @embedded_plugins = Util::ThreadsafeDeferral.for(&method(:generate_embedded_plugins)) end ## # @see Plugin#release_date def release_date version && repository.release_date(version) end ## # @see Plugin#documentation def documentation repository.read_file("docs/index.asciidoc", version) end ## # @see Plugin#changelog_url def changelog_url repository.web_url("CHANGELOG.md", version) end ## # @see Plugin#tag def tag version ? "v#{version}" : "main" end ## # @see Plugin#desc def desc @desc ||= "[plugin:#{canonical_name}@#{tag}]" end ## # Returns an {@link Enumerable[Plugin]} containing itself and any integrated plugins # # @return [Enumerable[Plugin]] def with_embedded_plugins return enum_for(:with_embedded_plugins) unless block_given? yield self embedded_plugins.each do |embedded_plugin| yield embedded_plugin end end ## # Returns an {@link Enumerable[Plugin]} containing itself and any wrapped plugins, # including integration-wrapped plugins and aliases from the provided mappings # def with_wrapped_plugins(alias_definitions) return enum_for(:with_wrapped_plugins, alias_definitions) unless block_given? with_embedded_plugins do |plugin| plugin.with_alias(alias_definitions) do |plugin_or_alias| yield plugin_or_alias end end end ## # Returns zero or more {@link Plugin::Wrapped} provided by # an "integration" plugin. # def embedded_plugins @embedded_plugins.value end ## # @see Plugin#== def ==(other) return false unless super return false unless other.kind_of?(ArtifactPlugin) return false unless self.repository == other.repository return true end private ## # @api private # # @return [Array[EmbeddedPlugin]]: a frozen array of {@link EmbeddedPlugin} def generate_embedded_plugins gem_data_version = version || repository.rubygem_info.latest gem_data_version || fail("No releases on rubygems") rubygem_info = repository.rubygem_info.for_version(gem_data_version) || fail("[#{desc}]: no gem data available") embedded_plugin_canonical_names_csv = rubygem_info.dig('metadata','integration_plugins') return EMPTY if embedded_plugin_canonical_names_csv.nil? embedded_plugin_canonical_names_csv.split(',').map(&:strip).map do |wrapped_canonical_name| if wrapped_canonical_name !~ %r{\Alogstash-(?<type>[a-z]+)-(?<name>.*)} fail(ArgumentError "unsupported plugin name `#{canonical_name}`") end EmbeddedPlugin.new(artifact_plugin: self, type: Regexp.last_match(:type), name: Regexp.last_match(:name)) end.freeze end end end