lib/html2rss/item_extractors.rb in html2rss-0.9.0 vs lib/html2rss/item_extractors.rb in html2rss-0.10.0

- old
+ new

@@ -1,23 +1,88 @@ +# frozen_string_literal: true + module Html2rss ## # Provides a namespace for item extractors. module ItemExtractors - DEFAULT = 'text'.freeze - private_constant :DEFAULT + ## + # The Error class to be thrown when an unknown extractor name is requested. + class UnknownExtractorName < StandardError; end - def self.get_extractor(name) - @get_extractor ||= Hash.new do |extractors, key| - extractors[key] = Utils.get_class_from_name(key || DEFAULT, 'ItemExtractors') - end + ## + # Maps the extractor name to the class implementing the extractor. + # + # The key is the name to use in the feed config. + NAME_TO_CLASS = { + attribute: Attribute, + href: Href, + html: Html, + static: Static, + text: Text + }.freeze - @get_extractor[name] + ## + # Maps the extractor class to its corresponding options class. + ITEM_OPTION_CLASSES = Hash.new do |hash, klass| + hash[klass] = klass.const_get(:Options) end + DEFAULT_EXTRACTOR = :text + ## - # @return [Nokogiri::XML::Element] - def self.element(xml, options) - selector = options[:selector] + # Retrieves an element from Nokogiri XML based on the selector. + # + # @param xml [Nokogiri::XML::Document] + # @param selector [String, nil] + # @return [Nokogiri::XML::ElementSet] selected XML elements + def self.element(xml, selector) selector ? xml.css(selector) : xml + end + + ## + # Creates an instance of the requested item extractor. + # + # @param attribute_options [Hash<Symbol, Object>] + # Should contain at least `:extractor` (the name) and required options for that extractor. + # @param xml [Nokogiri::XML::Document] + # @return [Object] instance of the specified item extractor class + def self.item_extractor_factory(attribute_options, xml) + extractor_name = attribute_options[:extractor]&.to_sym || DEFAULT_EXTRACTOR + extractor_class = find_extractor_class(extractor_name) + options_instance = build_options_instance(extractor_class, attribute_options) + create_extractor_instance(extractor_class, xml, options_instance) + end + + ## + # Finds the extractor class based on the name. + # + # @param extractor_name [Symbol] the name of the extractor + # @return [Class] the class implementing the extractor + # @raise [UnknownExtractorName] if the extractor class is not found + def self.find_extractor_class(extractor_name) + NAME_TO_CLASS[extractor_name] || raise(UnknownExtractorName, + "Unknown extractor name '#{extractor_name}' requested in NAME_TO_CLASS") + end + + ## + # Builds the options instance for the extractor class. + # + # @param extractor_class [Class] the class implementing the extractor + # @param attribute_options [Hash<Symbol, Object>] the attribute options + # @return [Object] an instance of the options class for the extractor + def self.build_options_instance(extractor_class, attribute_options) + options = attribute_options.slice(*extractor_class::Options.members) + ITEM_OPTION_CLASSES[extractor_class].new(options) + end + + ## + # Creates an instance of the extractor class. + # + # @param extractor_class [Class] the class implementing the extractor + # @param xml [Nokogiri::XML::Document] the XML document + # @param options_instance [Object] the options instance + # @return [Object] an instance of the extractor class + def self.create_extractor_instance(extractor_class, xml, options_instance) + extractor_class.new(xml, options_instance) end end end