net-ftp-list 3.2.11 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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