parallel_sftp 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parallel_sftp/version"
4
+ require "parallel_sftp/errors"
5
+ require "parallel_sftp/configuration"
6
+ require "parallel_sftp/lftp_command"
7
+ require "parallel_sftp/progress_parser"
8
+ require "parallel_sftp/segment_progress_parser"
9
+ require "parallel_sftp/time_estimator"
10
+ require "parallel_sftp/download"
11
+ require "parallel_sftp/client"
12
+
13
+ module ParallelSftp
14
+ class << self
15
+ attr_writer :configuration
16
+
17
+ def configuration
18
+ @configuration ||= Configuration.new
19
+ end
20
+
21
+ # Configure ParallelSftp globally
22
+ #
23
+ # @example
24
+ # ParallelSftp.configure do |config|
25
+ # config.default_segments = 8
26
+ # config.timeout = 60
27
+ # config.max_retries = 15
28
+ # end
29
+ def configure
30
+ yield(configuration)
31
+ end
32
+
33
+ # Reset configuration to defaults
34
+ def reset_configuration!
35
+ @configuration = Configuration.new
36
+ end
37
+
38
+ # Check if lftp is available on the system
39
+ #
40
+ # @return [Boolean] true if lftp is installed and accessible
41
+ def lftp_available?
42
+ system("which lftp > /dev/null 2>&1")
43
+ end
44
+
45
+ # Get the installed lftp version
46
+ #
47
+ # @return [String, nil] Version string or nil if not installed
48
+ def lftp_version
49
+ return nil unless lftp_available?
50
+ `lftp --version`.lines.first&.strip
51
+ end
52
+
53
+ # Raise an error if lftp is not available
54
+ #
55
+ # @raise [LftpNotFoundError] if lftp is not installed
56
+ def ensure_lftp_available!
57
+ raise LftpNotFoundError unless lftp_available?
58
+ end
59
+
60
+ # Simple one-liner for downloading a file
61
+ #
62
+ # @param options [Hash] Connection and download options
63
+ # @option options [String] :host SFTP host
64
+ # @option options [String] :user Username
65
+ # @option options [String] :password Password
66
+ # @option options [Integer] :port SFTP port (default: 22)
67
+ # @option options [String] :remote_path Path to file on server
68
+ # @option options [String] :local_path Local destination path
69
+ # @option options [Integer] :segments Parallel connections (default: 4)
70
+ # @option options [Boolean] :resume Continue interrupted downloads (default: true)
71
+ # @option options [Integer] :timeout Connection timeout in seconds
72
+ # @option options [Integer] :max_retries Maximum retry attempts
73
+ # @option options [Proc] :on_progress Progress callback
74
+ # @option options [Proc] :on_segment_progress Per-segment progress callback
75
+ #
76
+ # @return [String] Local path to downloaded file
77
+ # @raise [DownloadError] if download fails
78
+ #
79
+ # @example Basic progress
80
+ # ParallelSftp.download(
81
+ # host: 'ftp.example.com',
82
+ # user: 'username',
83
+ # password: 'secret',
84
+ # remote_path: '/path/to/large_file.zip',
85
+ # local_path: '/tmp/large_file.zip',
86
+ # segments: 8,
87
+ # on_progress: ->(info) { puts "#{info[:percent]}%" }
88
+ # )
89
+ #
90
+ # @example With per-segment progress
91
+ # ParallelSftp.download(
92
+ # host: 'ftp.example.com',
93
+ # user: 'username',
94
+ # password: 'secret',
95
+ # remote_path: '/path/to/large_file.zip',
96
+ # local_path: '/tmp/large_file.zip',
97
+ # segments: 8,
98
+ # on_segment_progress: lambda do |info|
99
+ # puts "Overall: #{info[:overall_percent]}% (#{info[:eta]} remaining)"
100
+ # puts "Speed: #{info[:speed] / 1_000_000.0} MB/s" if info[:speed]
101
+ # info[:segments].each do |seg|
102
+ # puts " Segment #{seg[:index]}: #{seg[:percent]}%"
103
+ # end
104
+ # end
105
+ # )
106
+ def download(options = {})
107
+ client = Client.new(
108
+ host: options.fetch(:host),
109
+ user: options.fetch(:user),
110
+ password: options.fetch(:password),
111
+ port: options.fetch(:port, configuration.default_port)
112
+ )
113
+
114
+ client.download(
115
+ options.fetch(:remote_path),
116
+ options.fetch(:local_path),
117
+ segments: options[:segments],
118
+ resume: options.fetch(:resume, true),
119
+ timeout: options[:timeout],
120
+ max_retries: options[:max_retries],
121
+ reconnect_interval: options[:reconnect_interval],
122
+ sftp_connect_program: options[:sftp_connect_program],
123
+ on_progress: options[:on_progress],
124
+ on_segment_progress: options[:on_segment_progress]
125
+ )
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "parallel_sftp/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "parallel_sftp"
9
+ spec.version = ParallelSftp::VERSION
10
+ spec.authors = ["Nestor G Pestelos Jr"]
11
+ spec.email = ["ngpestelos@gmail.com"]
12
+
13
+ spec.summary = "Fast parallel SFTP downloads using lftp's segmented transfer"
14
+ spec.description = "A Ruby gem that wraps lftp for parallel/segmented SFTP downloads of large files. " \
15
+ "Supports resume, progress callbacks, and configurable retry settings."
16
+ spec.homepage = "https://github.com/ngpestelos/parallel_sftp"
17
+ spec.license = "MIT"
18
+
19
+ spec.required_ruby_version = ">= 2.5.0"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = spec.homepage
23
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
24
+
25
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_development_dependency "bundler", ">= 1.17"
33
+ spec.add_development_dependency "rake", ">= 12.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ end
data/shell.nix ADDED
@@ -0,0 +1,29 @@
1
+ { pkgs ? import <nixpkgs> {} }:
2
+
3
+ pkgs.mkShell {
4
+ buildInputs = with pkgs; [
5
+ ruby_3_3
6
+ bundler
7
+ lftp
8
+ curl
9
+ ];
10
+
11
+ shellHook = ''
12
+ export GEM_HOME="$PWD/.gems"
13
+ export PATH="$GEM_HOME/bin:$PATH"
14
+ mkdir -p "$GEM_HOME"
15
+ # Check if Claude Code is installed natively
16
+ if [ ! -f "$HOME/.local/bin/claude" ]; then
17
+ echo "Installing Claude Code (native)..."
18
+ curl -fsSL https://claude.ai/install.sh | bash
19
+ fi
20
+
21
+ # Add Claude to PATH if not already there
22
+ export PATH="$HOME/.local/bin:$PATH"
23
+
24
+ # Indicate we're in a nix-shell with Claude Code
25
+ export PS1="\[\033[1;32m\][nix-shell:claude]\[\033[0m\] $PS1"
26
+
27
+ echo "Claude Code environment activated. Run 'claude' to use the CLI."
28
+ '';
29
+ }
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parallel_sftp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Nestor G Pestelos Jr
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-01 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: bundler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.17'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '1.17'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '12.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '12.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ description: A Ruby gem that wraps lftp for parallel/segmented SFTP downloads of large
55
+ files. Supports resume, progress callbacks, and configurable retry settings.
56
+ email:
57
+ - ngpestelos@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".envrc"
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - CHANGELOG.md
66
+ - CLAUDE.md
67
+ - Gemfile
68
+ - LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - bin/console
72
+ - bin/setup
73
+ - lib/parallel_sftp.rb
74
+ - lib/parallel_sftp/client.rb
75
+ - lib/parallel_sftp/configuration.rb
76
+ - lib/parallel_sftp/download.rb
77
+ - lib/parallel_sftp/errors.rb
78
+ - lib/parallel_sftp/lftp_command.rb
79
+ - lib/parallel_sftp/progress_parser.rb
80
+ - lib/parallel_sftp/segment_progress_parser.rb
81
+ - lib/parallel_sftp/time_estimator.rb
82
+ - lib/parallel_sftp/version.rb
83
+ - parallel_sftp.gemspec
84
+ - shell.nix
85
+ homepage: https://github.com/ngpestelos/parallel_sftp
86
+ licenses:
87
+ - MIT
88
+ metadata:
89
+ homepage_uri: https://github.com/ngpestelos/parallel_sftp
90
+ source_code_uri: https://github.com/ngpestelos/parallel_sftp
91
+ changelog_uri: https://github.com/ngpestelos/parallel_sftp/blob/master/CHANGELOG.md
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 2.5.0
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.7.2
107
+ specification_version: 4
108
+ summary: Fast parallel SFTP downloads using lftp's segmented transfer
109
+ test_files: []