lib/alf/reader/class_methods.rb in alf-0.10.1 vs lib/alf/reader/class_methods.rb in alf-0.11.0

- old
+ new

@@ -1,82 +1,91 @@ module Alf class Reader module ClassMethods - - # - # Returns registered readers - # + + # @return [Array<Reader>] registered readers def readers @readers ||= [] end - - # - # Registers a reader class associated with specific file extensions + + # Registers a reader class associated with specific file extensions. # # Registered class must provide a constructor with the following signature - # <code>new(path_or_io, environment = nil)</code>. The name must be a symbol - # which can safely be used as a ruby method name. A factory class method of - # that name and same signature is automatically installed on the Reader - # class. + # `new(path_or_io, environment = nil, options = {})`. The registration + # name must be a symbol which can safely be used as a ruby method name. + # A factory method of that name and signature is automatically installed + # on the Reader class. # # @param [Symbol] name a name for the kind of data decoded # @param [Array] extensions file extensions mapped to the registered reader # class (should include the '.', e.g. '.foo') - # @param [Class] class Reader subclass used to decode this kind of files - # + # @param [Class] class Reader subclass used to decode this kind of files def register(name, extensions, clazz) readers << [name, extensions, clazz] (class << self; self; end). send(:define_method, name) do |*args| clazz.new(*args) end end - + + # When filepath looks like a String or a path, returns a reader instance + # for the source it denotes. Otherwise, delegates the call to + # `coerce(filepath)`. # - # When filepath is a String, returns a reader instance for a specific file - # whose path is given as argument. Otherwise, delegate the call to - # <code>coerce(filepath)</code> - # - # @param [String] filepath path to a file for which extension is recognized + # @param [:to_str|:to_path] filepath path to a file for which extension + # is recognized # @param [Array] args optional additional arguments that must be passed at # reader's class new method. - # @return [Reader] a reader instance - # + # @return [Reader] a reader instance built from arguments + # @raise [ArgumentError] if `filepath` is not recognized or no reader can + # be found for this extension. def reader(filepath, *args) - if filepath.is_a?(String) - ext = File.extname(filepath) - if registered = readers.find{|r| r[1].include?(ext)} - registered[2].new(filepath, *args) - else - raise "No registered reader for #{ext} (#{filepath})" - end - elsif args.empty? + if looks_a_path?(filepath) + ext = File.extname(filepath.to_s) + has_reader_for_ext!(ext).new(filepath.to_s, *args) + elsif args.empty? coerce(filepath) else raise ArgumentError, "Unable to return a reader for #{filepath} and #{args}" end end - + + # Coerces `arg` to a Reader instance, using an optional environment to + # convert named datasets. # - # Coerces an argument to a reader, using an optional environment to convert - # named datasets. + # This method automatically provides readers for Strings and Symbols + # through `environment` (**not** through the reader factory) and for IO + # objects through a Rash reader. # - # This method automatically provides readers for Strings and Symbols through - # passed environment (**not** through the reader factory) and for IO objects - # (through Rash reader). It is part if Alf's internals and should be used - # with care. - # + # @param [Object] arg any value to coerce to a Reader instance + # @param [Environment] environment to resolved named datasets (optional) def coerce(arg, environment = nil) case arg when Reader arg when IO, StringIO rash(arg, environment) else raise ArgumentError, "Unable to coerce #{arg.inspect} to a reader" end end - + + private + + # Checks if `path` looks like a usable path + def looks_a_path?(path) + return false if path.is_a?(StringIO) + path.respond_to?(:to_str) or path.respond_to?(:to_path) + end + + # @return [Class] the reader class to use for `ext` file extension. + # @raise [ArgumentError] if no such reader class can be found + def has_reader_for_ext!(ext) + entry = readers.find{|r| r[1].include?(ext)} + return entry[2] if entry + raise ArgumentError, "No registered reader for #{ext}" + end + end # module ClassMethods extend(ClassMethods) end # class Reader end # module Alf