net-ftp-list 3.2.11 → 3.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.
@@ -9,27 +9,25 @@ require 'time'
9
9
  class Net::FTP::List::Netware < Net::FTP::List::Parser
10
10
  # Stolen straight from the ASF's commons Java FTP LIST parser library.
11
11
  # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
12
- REGEXP = %r!^
12
+ REGEXP = /^
13
13
  (d|-){1}\s+
14
14
  \[(.*?)\]\s+
15
15
  (\S+)\s+(\d+)\s+
16
16
  (\S+\s+\S+\s+((\d+:\d+)|(\d{4})))
17
17
  \s+(.*)
18
- $!x
18
+ $/x.freeze
19
19
 
20
20
  # Parse a Netware like FTP LIST entries.
21
- def self.parse(raw)
21
+ def self.parse(raw, timezone: :utc)
22
22
  match = REGEXP.match(raw.strip) or return false
23
-
24
- is_dir = match[1] == 'd'
23
+ type = match[1] == 'd' ? :dir : :file
25
24
 
26
25
  emit_entry(
27
26
  raw,
28
- :mtime => Time.parse(match[5]),
29
- :filesize => match[4].to_i,
30
- :dir => is_dir,
31
- :file => !is_dir,
32
- :basename => match[9]
27
+ mtime: parse_time(match[5], timezone: timezone),
28
+ filesize: match[4].to_i,
29
+ type: type,
30
+ basename: match[9],
33
31
  )
34
32
  end
35
33
  end
@@ -1,28 +1,45 @@
1
+ require 'date'
2
+
1
3
  # Abstract FTP LIST parser. It really just defines and documents the interface.
2
4
  class Net::FTP::List::Parser
5
+ class << self
6
+ # Returns registered parsers.
7
+ def parsers
8
+ @parsers ||= []
9
+ end
3
10
 
4
- @@parsers = []
11
+ # Manuall register a parser.
12
+ def register(parser)
13
+ parsers.push(parser) unless parsers.include?(parser)
14
+ end
5
15
 
6
- # Run a passed block with each parser in succession, from the most specific to the least
7
- # specific. Will return the result of the block.
8
- def self.with_each_parser(&blk) #:yields: parser
9
- @@parsers.each(&blk)
10
- end
16
+ # Automatically add an inheriting parser to the list of known parsers.
17
+ def inherited(klass)
18
+ super
19
+ register(klass)
20
+ end
11
21
 
12
- # Automatically add an inheriting parser to the list of known parsers.
13
- def self.inherited(klass) #:nodoc:
14
- @@parsers.push(klass)
15
- end
22
+ # The main parse method. Return false from it if parsing fails (this is cheaper than raising an exception)
23
+ def parse(_raw, **)
24
+ false
25
+ end
16
26
 
17
- # The main parse method. Return false from it if parsing fails (this is cheaper than raising an exception)
18
- def self.parse(raw)
19
- return false
20
- end
27
+ private
21
28
 
22
- private
29
+ # Automatically adds the name of the parser class to the server_type field
30
+ def emit_entry(raw, **extra)
31
+ Net::FTP::List::Entry.new raw, **extra, server_type: to_s.split('::').pop
32
+ end
23
33
 
24
- # Automatically adds the name of the parser class to the server_type field
25
- def self.emit_entry(raw, extra_attributes)
26
- Net::FTP::List::Entry.new raw, extra_attributes.merge(:server_type => to_s.split('::').pop)
34
+ def parse_time(str, timezone:, format: nil)
35
+ case timezone
36
+ when :utc
37
+ (format ? DateTime.strptime(str, format) : DateTime.parse(str)).to_time
38
+ when :local
39
+ format ? Time.strptime(str, format) : Time.parse(str)
40
+ else
41
+ raise ArgumentError, "invalid timezone #{timezone}, only :utc and :local are allowed"
42
+ end
43
+ end
27
44
  end
28
45
  end
@@ -6,28 +6,27 @@ require 'net/ftp/list/parser'
6
6
  # drwxr-xr-x folder 0 Nov 30 10:03 houdini
7
7
  # -rw-r--r-- 0 101426 101426 Jun 7 2008 imap with spaces.rb
8
8
  class Net::FTP::List::Rumpus < Net::FTP::List::Parser
9
-
10
- REGEXP = %r!^
11
- ([drwxr-]{10})\s+
9
+ REGEXP = /^
10
+ ([drwx-]{10})\s+
12
11
  (folder|0\s+\d+)\s+
13
12
  (\d+)\s+
14
13
  (\w+)\s+
15
14
  (\d{1,2})\s+
16
15
  (\d{2}:\d{2}|\d{4})\s+
17
16
  (.+)
18
- $!x
17
+ $/x.freeze
19
18
 
20
19
  # Parse a Rumpus FTP LIST entry.
21
- def self.parse(raw)
20
+ def self.parse(raw, timezone: :utc)
22
21
  match = REGEXP.match(raw.strip) or return false
22
+ type = match[2] == 'folder' ? :dir : :file
23
23
 
24
24
  emit_entry(
25
25
  raw,
26
- :basename => match[7],
27
- :mtime => Time.parse([match[4], match[5], match[6]].join(" ")),
28
- :file => !(match[2] == "folder"),
29
- :dir => (match[2] == "folder"),
30
- :filesize => match[3].to_i
26
+ basename: match[7],
27
+ mtime: parse_time(match[4..6].join(' '), timezone: timezone),
28
+ type: type,
29
+ filesize: match[3].to_i,
31
30
  )
32
31
  end
33
32
  end
@@ -8,11 +8,10 @@ require 'net/ftp/list/parser'
8
8
  # drwxr-xr-x 4 steve group 4096 Dec 10 20:23 etc
9
9
  # -rw-r--r-- 1 root other 531 Jan 29 03:26 README.txt
10
10
  class Net::FTP::List::Unix < Net::FTP::List::Parser
11
-
12
11
  # Stolen straight from the ASF's commons Java FTP LIST parser library.
13
12
  # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
14
13
  REGEXP = %r{
15
- ([pbcdlfmpSs-])
14
+ ([pbcdlfmSs-])
16
15
  (((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\+?\s+
17
16
  (?:(\d+)\s+)?
18
17
  (\S+)?\s+
@@ -22,69 +21,63 @@ class Net::FTP::List::Unix < Net::FTP::List::Parser
22
21
  ((?:\d+[-/]\d+[-/]\d+)|(?:\S+\s+\S+))\s+
23
22
  (\d+(?::\d+)?)\s+
24
23
  (\S*)(\s*.*)
25
- }x
24
+ }x.freeze
26
25
 
27
26
  ONE_YEAR = (60 * 60 * 24 * 365)
28
27
 
29
28
  # Parse a Unix like FTP LIST entries.
30
- def self.parse(raw)
29
+ def self.parse(raw, timezone: :utc)
31
30
  match = REGEXP.match(raw.strip) or return false
32
-
33
- dir, symlink, file, device = false, false, false, false
34
- case match[1]
35
- when /d/ then dir = true
36
- when /l/ then symlink = true
37
- when /[f-]/ then file = true
38
- when /[psbc]/ then device = true
39
- end
40
- return false unless dir or symlink or file or device
31
+ type = case match[1]
32
+ when /d/ then :dir
33
+ when /l/ then :symlink
34
+ when /[f-]/ then :file
35
+ when /[psbc]/ then :device
36
+ end
37
+ return false if type.nil?
41
38
 
42
39
  # Don't match on rumpus (which looks very similar to unix)
43
- return false if match[17].nil? and ((match[15].nil? and match[16].to_s == 'folder') or match[15].to_s == '0')
40
+ return false if match[17].nil? && ((match[15].nil? && (match[16].to_s == 'folder')) || (match[15].to_s == '0'))
44
41
 
45
42
  # TODO: Permissions, users, groups, date/time.
46
43
  filesize = match[18].to_i
47
-
48
44
  mtime_month_and_day = match[19]
49
45
  mtime_time_or_year = match[20]
50
46
 
51
47
  # Unix mtimes specify a 4 digit year unless the data is within the past 180
52
- # days or so. Future dates always specify a 4 digit year.
48
+ # days or so. Future dates always specify a 4 digit year.
53
49
  # If the parsed date, with today's year, could be in the future, then
54
50
  # the date must be for the previous year
55
- mtime_string = if mtime_time_or_year.match(/^[0-9]{1,2}:[0-9]{2}$/)
56
- if Time.parse("#{mtime_month_and_day} #{Time.now.year}") > Time.now
57
- "#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year - 1}"
58
- else
59
- "#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year}"
60
- end
61
- elsif mtime_time_or_year.match(/^[0-9]{4}$/)
62
- "#{mtime_month_and_day} #{mtime_time_or_year}"
63
- end
64
-
65
- mtime = Time.parse(mtime_string)
51
+ mtime_string = case mtime_time_or_year
52
+ when /^[0-9]{1,2}:[0-9]{2}$/
53
+ if parse_time("#{mtime_month_and_day} #{Time.now.year}", timezone: timezone) > Time.now
54
+ "#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year - 1}"
55
+ else
56
+ "#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year}"
57
+ end
58
+ when /^[0-9]{4}$/
59
+ "#{mtime_month_and_day} #{mtime_time_or_year}"
60
+ end
66
61
 
62
+ mtime = parse_time(mtime_string, timezone: timezone)
67
63
  basename = match[21].strip
68
64
 
69
65
  # filenames with spaces will end up in the last match
70
66
  basename += match[22] unless match[22].nil?
71
67
 
72
68
  # strip the symlink stuff we don't care about
73
- if symlink
74
- basename.sub!(/\s+\->(.+)$/, '')
75
- symlink_destination = $1.strip if $1
69
+ if type == :symlink
70
+ basename.sub!(/\s+->(.+)$/, '')
71
+ symlink_destination = Regexp.last_match(1).strip if Regexp.last_match(1)
76
72
  end
77
73
 
78
74
  emit_entry(
79
75
  raw,
80
- :dir => dir,
81
- :file => file,
82
- :device => device,
83
- :symlink => symlink,
84
- :filesize => filesize,
85
- :basename => basename,
86
- :symlink_destination => symlink_destination,
87
- :mtime => mtime
76
+ type: type,
77
+ filesize: filesize,
78
+ basename: basename,
79
+ symlink_destination: symlink_destination,
80
+ mtime: mtime,
88
81
  )
89
82
  end
90
83
  end
@@ -3,12 +3,9 @@ require 'net/ftp/list/parser'
3
3
  # If all other attempts to parse the entry fail this is the parser that is going to be used.
4
4
  # It might be a good idea to fail loudly.
5
5
  class Net::FTP::List::Unknown < Net::FTP::List::Parser
6
- def self.parse(raw)
7
- if Net::FTP::List.raise_on_failed_server_detection
8
- raise Net::FTP::List::ParseError, "Could not parse #{raw} since none of the parsers was up to the task"
9
- end
6
+ def self.parse(raw, **)
7
+ raise Net::FTP::List::ParseError, "Could not parse #{raw} since none of the parsers was up to the task" if Net::FTP::List.raise_on_failed_server_detection
10
8
 
11
- emit_entry(raw, {})
9
+ emit_entry(raw)
12
10
  end
13
11
  end
14
-
data/net-ftp-list.gemspec CHANGED
@@ -1,61 +1,19 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
- # stub: net-ftp-list 3.2.11 ruby lib
6
-
7
1
  Gem::Specification.new do |s|
8
- s.name = "net-ftp-list".freeze
9
- s.version = "3.2.11"
10
-
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib".freeze]
13
- s.authors = ["Stateless Systems".freeze]
14
- s.date = "2018-04-02"
15
- s.email = "enquiries@statelesssystems.com".freeze
16
- s.extra_rdoc_files = [
17
- "README.txt"
18
- ]
19
- s.files = [
20
- ".ruby-version",
21
- "Gemfile",
22
- "Gemfile.lock",
23
- "README.txt",
24
- "Rakefile",
25
- "VERSION.yml",
26
- "lib/net/ftp/list.rb",
27
- "lib/net/ftp/list/entry.rb",
28
- "lib/net/ftp/list/microsoft.rb",
29
- "lib/net/ftp/list/netware.rb",
30
- "lib/net/ftp/list/parser.rb",
31
- "lib/net/ftp/list/rumpus.rb",
32
- "lib/net/ftp/list/unix.rb",
33
- "lib/net/ftp/list/unknown.rb",
34
- "net-ftp-list.gemspec",
35
- "test/test_net_ftp_list.rb",
36
- "test/test_net_ftp_list_entry.rb",
37
- "test/test_net_ftp_list_microsoft.rb",
38
- "test/test_net_ftp_list_netware.rb",
39
- "test/test_net_ftp_list_rumpus.rb",
40
- "test/test_net_ftp_list_unix.rb"
41
- ]
42
- s.homepage = "http://github.com/stateless-systems/net-ftp-list".freeze
43
- s.rubygems_version = "2.7.6".freeze
44
- s.summary = "Parse FTP LIST command output.".freeze
2
+ s.name = 'net-ftp-list'
3
+ s.version = '3.3.0'
4
+ s.authors = ['Stateless Systems']
5
+ s.email = 'enquiries@statelesssystems.com'
6
+ s.summary = 'Parse FTP LIST command output.'
7
+ s.homepage = 'http://github.com/stateless-systems/net-ftp-list'
8
+ s.license = 'MIT'
45
9
 
46
- if s.respond_to? :specification_version then
47
- s.specification_version = 4
10
+ s.files = `git ls-files -z`.split("\x0").reject {|f| f.start_with?('test/') }
11
+ s.test_files = `git ls-files -z -- test/*`.split("\x0")
12
+ s.require_paths = ['lib']
13
+ s.required_ruby_version = '>= 2.5'
48
14
 
49
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
- s.add_development_dependency(%q<jeweler>.freeze, [">= 0"])
51
- s.add_development_dependency(%q<test-unit>.freeze, [">= 0"])
52
- else
53
- s.add_dependency(%q<jeweler>.freeze, [">= 0"])
54
- s.add_dependency(%q<test-unit>.freeze, [">= 0"])
55
- end
56
- else
57
- s.add_dependency(%q<jeweler>.freeze, [">= 0"])
58
- s.add_dependency(%q<test-unit>.freeze, [">= 0"])
59
- end
15
+ s.add_development_dependency 'rake'
16
+ s.add_development_dependency 'rubocop-bsm'
17
+ s.add_development_dependency 'test-unit'
18
+ s.add_development_dependency 'timecop'
60
19
  end
61
-
@@ -2,14 +2,13 @@ require 'test/unit'
2
2
  require 'net/ftp/list'
3
3
 
4
4
  class TestNetFTPList < Test::Unit::TestCase
5
-
6
5
  def test_all
7
6
  one = Net::FTP::List.parse('drwxr-xr-x 4 user group 4096 Dec 10 20:23 etc')
8
7
  assert_kind_of Net::FTP::List::Entry, one
9
8
  assert one.dir?
10
9
  assert !one.file?
11
10
 
12
- two = Net::FTP::List.parse("++ unknown garbage +++")
11
+ two = Net::FTP::List.parse('++ unknown garbage +++')
13
12
  assert_kind_of Net::FTP::List::Entry, two
14
13
  assert two.unknown?
15
14
  assert_equal '', two.basename
@@ -18,7 +17,7 @@ class TestNetFTPList < Test::Unit::TestCase
18
17
  def test_raise_when_flag_set
19
18
  Net::FTP::List.raise_on_failed_server_detection = true
20
19
  assert_raise(Net::FTP::List::ParseError) do
21
- Net::FTP::List.parse("++ unknown garbage +++")
20
+ Net::FTP::List.parse('++ unknown garbage +++')
22
21
  end
23
22
  ensure
24
23
  Net::FTP::List.raise_on_failed_server_detection = false
@@ -1,18 +1,18 @@
1
1
  require 'test/unit'
2
2
  require 'net/ftp/list'
3
+ require 'bigdecimal'
3
4
 
4
5
  class TestNetFTPEntry < Test::Unit::TestCase
5
-
6
6
  def test_equality
7
- a = Net::FTP::List::Entry.new('foo1', {:basename => 'foo1'})
8
- b = Net::FTP::List::Entry.new('foo2', {:basename => 'foo2'})
9
- c = Net::FTP::List::Entry.new('foo1', {:basename => 'foo1'})
7
+ a = Net::FTP::List::Entry.new('foo1', basename: 'foo1')
8
+ b = Net::FTP::List::Entry.new('foo2', basename: 'foo2')
9
+ c = Net::FTP::List::Entry.new('foo1', basename: 'foo1')
10
10
 
11
- assert a.eql? c
11
+ assert a.eql?(c)
12
12
  assert a.eql? a
13
13
  assert !a.eql?(b)
14
14
  assert !a.eql?(1)
15
- assert !a.eql?(1.0)
15
+ assert !a.eql?(BigDecimal('1.0'))
16
16
  end
17
17
 
18
18
  def test_comparison
@@ -26,24 +26,24 @@ class TestNetFTPEntry < Test::Unit::TestCase
26
26
 
27
27
  assert file2 > file1
28
28
  assert file2 >= file1
29
- assert file2 == file2
29
+ assert file2 == Net::FTP::List.parse(raw2)
30
+ assert file2 <= file3
31
+ assert file2 < file3
32
+ assert file2 >= file1
33
+ assert file2 > file1
34
+ assert file2 != file1
35
+ assert file2 != file3
30
36
  assert file2 <= file3
31
37
  assert file2 < file3
32
- assert !(file2 < file1)
33
- assert !(file2 <= file1)
34
- assert !(file2 == file1)
35
- assert !(file2 == file3)
36
- assert !(file2 > file3)
37
- assert !(file2 >= file3)
38
38
 
39
- assert !(file2 < 2)
40
- assert !(file2 < 2.0)
39
+ assert file2 >= 2
40
+ assert file2 >= 2.0
41
41
  assert file2 <= 2
42
42
  assert file2 <= 2.0
43
43
  assert file2 == 2
44
- assert file2 == 2.0
45
- assert !(file2 > 2)
46
- assert !(file2 > 2.0)
44
+ assert file2 == BigDecimal('2.0')
45
+ assert file2 <= 2
46
+ assert file2 <= 2.0
47
47
  assert file2 >= 2
48
48
  assert file2 >= 2.0
49
49
 
@@ -52,7 +52,7 @@ class TestNetFTPEntry < Test::Unit::TestCase
52
52
  end
53
53
 
54
54
  def test_raise_on_unknown_options
55
- assert_raise(ArgumentError) { Net::FTP::List::Entry.new("foo", {:bar => "baz"}) }
55
+ assert_raise(ArgumentError) { Net::FTP::List::Entry.new('foo', bar: 'baz') }
56
56
  end
57
57
 
58
58
  def test_default_values
@@ -67,7 +67,6 @@ class TestNetFTPEntry < Test::Unit::TestCase
67
67
  assert !e.file?
68
68
  assert !e.symlink?
69
69
  assert_kind_of Time, e.mtime
70
- assert_equal "Unknown", e.server_type
70
+ assert_equal 'Unknown', e.server_type
71
71
  end
72
-
73
72
  end