class IOStreams::Builder
Build the streams that need to be applied to a path druing reading or writing.
Attributes
Public Class Methods
# File lib/io_streams/builder.rb, line 7 def initialize(file_name = nil) @file_name = file_name @streams = nil @options = nil @format = nil @format_option = nil end
Public Instance Methods
Returns the tabular format if set, otherwise tries to autodetect the format if the file_name
has been set Returns [nil] if no format is set, or if it cannot be determined from the file_name
# File lib/io_streams/builder.rb, line 96 def format @format ||= file_name ? Tabular.format_from_file_name(file_name) : nil end
# File lib/io_streams/builder.rb, line 100 def format=(format) unless format.nil? || IOStreams::Tabular.registered_formats.include?(format) raise(ArgumentError, "Invalid format: #{format.inspect}") end @format = format end
Supply an option that is only applied once the file name extensions have been parsed. Note:
-
Cannot set both `stream` and `option`
# File lib/io_streams/builder.rb, line 18 def option(stream, **options) stream = stream.to_sym unless stream.is_a?(Symbol) raise(ArgumentError, "Invalid stream: #{stream.inspect}") unless IOStreams.extensions.include?(stream) raise(ArgumentError, "Cannot call both #option and #stream on the same streams instance}") if @streams raise(ArgumentError, "Cannot call #option unless the `file_name` was already set}") unless file_name @options ||= {} if (opts = @options[stream]) opts.merge!(options) else @options[stream] = options.dup end self end
# File lib/io_streams/builder.rb, line 53 def option_or_stream(stream, **options) if streams stream(stream, **options) elsif file_name option(stream, **options) else stream(stream, **options) end end
Returns [Hash<Symbol:Hash>] the pipeline of streams with their options that will be applied when the reader or writer is invoked.
# File lib/io_streams/builder.rb, line 80 def pipeline return streams.dup.freeze if streams build_pipeline.freeze end
# File lib/io_streams/builder.rb, line 70 def reader(io_stream, &block) execute(:reader, pipeline, io_stream, &block) end
Removes the named stream from the current pipeline. If the stream pipeline has not yet been built it will be built from the file_name
if present. Note: Any options must be set before calling this method.
# File lib/io_streams/builder.rb, line 89 def remove_from_pipeline(stream_name) @streams ||= build_pipeline @streams.delete(stream_name.to_sym) end
Return the options set for either a stream or option.
# File lib/io_streams/builder.rb, line 64 def setting(stream) return streams[stream] if streams options[stream] if options end
# File lib/io_streams/builder.rb, line 33 def stream(stream, **options) stream = stream.to_sym unless stream.is_a?(Symbol) raise(ArgumentError, "Cannot call both #option and #stream on the same streams instance}") if @options # To prevent any streams from being applied supply a stream named `:none` if stream == :none @streams = {} return self end raise(ArgumentError, "Invalid stream: #{stream.inspect}") unless IOStreams.extensions.include?(stream) @streams ||= {} if (opts = @streams[stream]) opts.merge!(options) else @streams[stream] = options.dup end self end
# File lib/io_streams/builder.rb, line 74 def writer(io_stream, &block) execute(:writer, pipeline, io_stream, &block) end
Private Instance Methods
# File lib/io_streams/builder.rb, line 110 def build_pipeline return {} unless file_name built_streams = {} # Encode stream is always first built_streams[:encode] = options[:encode] if options&.key?(:encode) opts = options || {} parse_extensions.each { |stream| built_streams[stream] = opts[stream] || {} } built_streams end
# File lib/io_streams/builder.rb, line 122 def class_for_stream(type, stream) ext = IOStreams.extensions[stream.nil? ? nil : stream.to_sym] || raise(ArgumentError, "Unknown Stream type: #{stream.inspect}") ext.send("#{type}_class") || raise(ArgumentError, "No #{type} registered for Stream type: #{stream.inspect}") end
Executes the streams that need to be executed.
# File lib/io_streams/builder.rb, line 142 def execute(type, pipeline, io_stream, &block) raise(ArgumentError, "IOStreams call is missing mandatory block") if block.nil? if pipeline.empty? block.call(io_stream) elsif pipeline.size == 1 stream, opts = pipeline.first class_for_stream(type, stream).open(io_stream, **opts, &block) else # Daisy chain multiple streams together last = pipeline.keys.inject(block) do |inner, stream_sym| ->(io) { class_for_stream(type, stream_sym).open(io, **pipeline[stream_sym], &inner) } end last.call(io_stream) end end
Returns the streams for the supplied file_name
# File lib/io_streams/builder.rb, line 129 def parse_extensions parts = ::File.basename(file_name).split(".") extensions = [] while (extension = parts.pop) sym = extension.downcase.to_sym break unless IOStreams.extensions[sym] extensions.unshift(sym) end extensions end