class Distillery::Archiver::External

Use external program to process archive Selection of external program is described in external.yaml

Public Class Methods

new(list, read, write=nil, delete=nil, extensions:, mimetypes: nil) click to toggle source
# File lib/distillery/archiver/external.rb, line 84
def initialize(list, read, write=nil, delete=nil,
               extensions:, mimetypes: nil)
    @list       = list
    @read       = read
    @write      = write
    @delete     = delete
    @extensions = extensions
    @mimetypes  = mimetypes
end
registering(list = [ '7z', 'zip' ], default_file = File.join(__dir__, 'external.yaml')) click to toggle source

Perform registration of the various archive format supported by this archiver provider

@param list [Array<String>] list of format to register @param default_file [String] default configuration file

@return [void]

# File lib/distillery/archiver/external.rb, line 23
def self.registering(list         = [ '7z', 'zip' ],
                     default_file = File.join(__dir__, 'external.yaml'))
    dflt_config = YAML.load_file(default_file)

    if Array === list
        list = Hash[list.map {|app| [ app, {} ] }]
    end

    list.each {|app, cfg|
        dflt       = dflt_config.dig(app) || {}
        extensions = Array(cfg.dig('extension')  || dflt.dig('extension'))
        mimetypes  = Array(cfg.dig('mimetype' )  || dflt.dig('mimetype' ))
        list       = {
            :cmd    => cfg .dig('list', 'cmd')   || cfg .dig('cmd') ||
                       dflt.dig('list', 'cmd')   || dflt.dig('cmd'),
            :args   => cfg .dig('list', 'args')  ||
                       dflt.dig('list', 'args'),
            :parser => if parser = (cfg .dig('list', 'parser')      ||
                                    dflt.dig('list', 'parser'))
                           Regexp.new('\A' + parser + '\Z')
                       end,
            :validator => cfg .dig('list', 'validator') ||
                          dflt.dig('list', 'validator')
        }
        read       = {
            :cmd    => cfg .dig('read', 'cmd')   || cfg .dig('cmd') ||
                       dflt.dig('read', 'cmd')   || dflt.dig('cmd'),
            :args   => cfg .dig('read', 'args')  ||
                       dflt.dig('read', 'args'),
        }
        write      = {
            :cmd    => cfg .dig('write', 'cmd')  || cfg .dig('cmd') ||
                       dflt.dig('write', 'cmd')  || dflt.dig('cmd'),
            :args   => cfg .dig('write', 'args') ||
                       dflt.dig('write', 'args'),
        }
        delete     = {
            :cmd    => cfg .dig('delete', 'cmd')  || cfg .dig('cmd') ||
                       dflt.dig('delete', 'cmd')  || dflt.dig('cmd'),
            :args   => cfg .dig('delete', 'args') ||
                       dflt.dig('delete', 'args'),
        }

        if list[:cmd].nil? || read[:cmd].nil?
            Archiver.logger&.warn {
                "#{self}: command not defined for #{app} program (SKIP)"
            }
            next
        end
        if write[:cmd].nil?
            Archiver.logger&.warn {
                "#{self}: write mode not supported for #{app} program"
            }
        end
            
        Archiver.register(External.new(list, read, write, delete,
                                       extensions: extensions,
                                        mimetypes: mimetypes))
    }
end

Public Instance Methods

delete!(file, entry) click to toggle source

(see Archiver#delete!)

# File lib/distillery/archiver/external.rb, line 140
def delete!(file, entry)
    subst = { '$(infile)' => file, '$(entry)' => entry  }
    cmd   = @delete[:cmd ]
    args  = @delete[:args].map{|e| e.gsub(/\$\(\w+\)/, subst) }

    Open3.popen2(cmd, *args) {|stdin, stdout|
        stdin.close_write
    }

    true
end
each(file, &block) click to toggle source

(see Archiver#each)

# File lib/distillery/archiver/external.rb, line 107
def each(file, &block)
    return to_enum(:each, file) if block.nil?
    entries(file).each {|entry|
        reader(file, entry) {|io|
            block.call(entry, io)
        }
    }
end
entries(file) click to toggle source

(see Archiver#entries)

# File lib/distillery/archiver/external.rb, line 153
def entries(file)
    subst     = { '$(infile)' => file }
    cmd       = @list[:cmd      ]
    args      = @list[:args     ].map{|e| e.gsub(/\$\(\w+\)/, subst) }
    parser    = @list[:parser   ]
    validator = @list[:validator]
    
    stdout, stderr, status = Open3.capture3(cmd, *args)

    if ! status.exitstatus.zero?
        raise ExecError, "running external command failed (#{stderr})"
    end

    stdout.force_encoding('BINARY').lines(chomp: true).map {|l|
        unless m = l.match(parser)
            raise ProcessingError, "unable to parse entry (#{file}) (#{l})"
        end

        if validator && validator.find {|k,v| m[k] != v }
            next
        end

        m[:entry]
    }.compact
end
extensions() click to toggle source

(see Archiver#extensions)

# File lib/distillery/archiver/external.rb, line 96
def extensions
    @extensions
end
mimetypes() click to toggle source

(see Archiver#mimetypes)

# File lib/distillery/archiver/external.rb, line 102
def mimetypes
    @mimetypes
end
reader(file, entry, &block) click to toggle source

(see Archiver#reader)

# File lib/distillery/archiver/external.rb, line 117
def reader(file, entry, &block)
    subst = { '$(infile)' => file, '$(entry)' => entry  }
    cmd   = @read[:cmd ]
    args  = @read[:args].map{|e| e.gsub(/\$\(\w+\)/, subst) }

    Open3.popen2(cmd, *args) {|stdin, stdout|
        stdin.close_write
        block.call(InputStream.new(stdout))
    }
end
writer(file, entry, &block) click to toggle source

(see Archiver#writer)

# File lib/distillery/archiver/external.rb, line 129
def writer(file, entry, &block)
    subst = { '$(infile)' => file, '$(entry)' => entry  }
    cmd   = @write[:cmd ]
    args  = @write[:args].map{|e| e.gsub(/\$\(\w+\)/, subst) }

    Open3.popen2(cmd, *args) {|stdin, stdout|
        block.call(OutputStream.new(stdin))
    }
end