net-ftp-list 2.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt CHANGED
@@ -17,7 +17,6 @@ See the RFC for more guff on LIST and NLST: http://www.ietf.org/rfc/rfc0959.txt
17
17
 
18
18
  == TODO & PROBLEMS
19
19
 
20
- * I'm new to Ruby, I'm sure some exist :)
21
20
  * The factory and abstract base class for parsers are one and the same. OO geeks will cry.
22
21
  * More OS's and server types. Only servers that return Unix like LIST responses will work at the moment.
23
22
  * Calling <tt>if entry.file? or entry.dir?</tt> is hard work when you really mean <tt>unless entry.unknown?</tt>
@@ -34,7 +33,7 @@ See the RFC for more guff on LIST and NLST: http://www.ietf.org/rfc/rfc0959.txt
34
33
  # Ignore everything that's not a file (so symlinks, directories and devices etc.)
35
34
  next unless entry.file?
36
35
 
37
- # If entry isn't a kind_of Net::FTP::List::Unknown then there is a bug in Net::FTP::List if this isn't the
36
+ # If entry isn't entry.unknown? then there is a bug in Net::FTP::List if this isn't the
38
37
  # same name as ftp.nlist('/some/path') would have returned.
39
38
  # Format the entry showing its file size and modification time
40
39
  puts "#{entry.basename}, #{entry.filesize}, #{entry.mtime}"
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
+ require 'yaml'
3
4
 
4
5
  begin
5
6
  require 'jeweler'
@@ -17,7 +18,7 @@ end
17
18
  require 'rake/testtask'
18
19
  Rake::TestTask.new(:test) do |test|
19
20
  test.libs << 'lib' << 'test'
20
- test.pattern = 'test/**/*_test.rb'
21
+ test.pattern = 'test/**/test_*.rb'
21
22
  test.verbose = true
22
23
  end
23
24
 
@@ -25,7 +26,7 @@ begin
25
26
  require 'rcov/rcovtask'
26
27
  Rcov::RcovTask.new do |test|
27
28
  test.libs << 'test'
28
- test.pattern = 'test/**/*_test.rb'
29
+ test.pattern = 'test/**/test_*.rb'
29
30
  test.verbose = true
30
31
  end
31
32
  rescue LoadError
@@ -1,5 +1,5 @@
1
1
  ---
2
- :major: 2
3
- :minor: 1
4
- :patch: 1
2
+ :major: 3
3
+ :minor: 2
4
+ :patch: 0
5
5
  :build:
@@ -1,43 +1,35 @@
1
1
  require 'net/ftp'
2
- require 'net/ftp/list/parser'
3
2
 
4
- # The order here is important for the time being. Corse grained parsers should appear before specializations because
5
- # the whole thing is searched in reverse order.
6
- require 'net/ftp/list/unix'
7
- require 'net/ftp/list/microsoft'
8
- require 'net/ftp/list/netware'
3
+ module Net::FTP::List
4
+ require 'net/ftp/list/parser'
5
+ require 'net/ftp/list/entry'
9
6
 
10
- module Net #:nodoc:
11
- class FTP #:nodoc:
7
+ # Parser classes should be listed top to bottom, the most specific
8
+ # (and rare!) server variations coming last
9
+ require 'net/ftp/list/unix'
10
+ require 'net/ftp/list/microsoft'
11
+ require 'net/ftp/list/netware'
12
+ require 'net/ftp/list/rumpus'
13
+ require 'net/ftp/list/unknown'
12
14
 
13
- # Parse FTP LIST responses.
14
- #
15
- # == Creation
16
- #
17
- # require 'net/ftp' # Not really required but I like to list dependencies sometimes.
18
- # require 'net/ftp/list'
19
- #
20
- # ftp = Net::FTP.open('somehost.com', 'user', 'pass')
21
- # ftp.list('/some/path') do |e|
22
- # entry = Net::FTP::List.parse(e)
23
- #
24
- # # Ignore everything that's not a file (so symlinks, directories and devices etc.)
25
- # next unless entry.file?
26
- #
27
- # # If entry isn't a kind_of Net::FTP::List::Unknown then there is a bug in Net::FTP::List if this isn't the
28
- # # same name as ftp.nlist('/some/path') would have returned.
29
- # puts entry.basename
30
- # end
31
- #
32
- # == Exceptions
33
- #
34
- # None at this time. At worst you'll end up with an Net::FTP::List::Unknown instance which won't have any extra
35
- # useful information. Methods like <tt>dir?</tt>, <tt>file?</tt> and <tt>symlink?</tt> will all return +false+.
36
- module List
37
- def self.parse(*args)
38
- Parser.parse(*args)
39
- end
15
+ def self.raise_on_failed_server_detection=(new_flag)
16
+ Thread.current[:net_ftp_list_raise_on_failed_server_detection] = !!new_flag
17
+ end
18
+
19
+ def self.raise_on_failed_server_detection
20
+ Thread.current[:net_ftp_list_raise_on_failed_server_detection]
21
+ end
22
+
23
+ # Parse a line from FTP LIST responsesa and return a Net::FTP::List::Entry
24
+ def self.parse(*args)
25
+ Parser.with_each_parser do | p |
26
+ entry = p.parse(*args)
27
+ return entry if entry
40
28
  end
41
29
  end
30
+
31
+ # Gets raised with raise_on_failed_server_detection set
32
+ class ParseError < RuntimeError
33
+ end
42
34
  end
43
35
 
@@ -0,0 +1,69 @@
1
+ # Represents an entry of the FTP list. Gets returned when you parse a list.
2
+ class Net::FTP::List::Entry
3
+
4
+ ALLOWED_ATTRIBUTES = [:raw, :basename, :dir, :file, :symlink, :mtime, :filesize, :device, :server_type] #:nodoc:
5
+
6
+ # Create a new entry object. The additional argument is the list of metadata keys
7
+ # that can be used on the object. By default just takes and set the raw list entry.
8
+ # Net::FTP::List.parse(raw_list_string) # => Net::FTP::List::Parser instance.
9
+ def initialize(raw_ls_line, optional_attributes = {}) #:nodoc:
10
+ @raw = raw_ls_line
11
+ optional_attributes.each_pair do |key, value|
12
+ raise ArgumentError, "#{key} is not supported" unless ALLOWED_ATTRIBUTES.include?(key)
13
+ instance_variable_set("@#{key}", value)
14
+ end
15
+ end
16
+
17
+ # The raw list entry string.
18
+ def raw
19
+ @raw ||= ''
20
+ end
21
+ alias_method :to_s, :raw
22
+
23
+ # The items basename (filename).
24
+ def basename
25
+ @basename ||= ''
26
+ end
27
+ alias name basename
28
+
29
+ # Looks like a directory, try CWD.
30
+ def dir?
31
+ !!(@dir ||= false)
32
+ end
33
+ alias directory? dir?
34
+
35
+ # Looks like a file, try RETR.
36
+ def file?
37
+ !!(@file ||= false)
38
+ end
39
+
40
+ # Looks like a symbolic link.
41
+ def symlink?
42
+ !!(@symlink ||= false)
43
+ end
44
+
45
+ # Looks like a device.
46
+ def device?
47
+ !!(@device ||= false)
48
+ end
49
+
50
+ # Returns the modification time of the file/directory or the current time if unknown
51
+ def mtime
52
+ @mtime || Time.now
53
+ end
54
+
55
+ # Returns the filesize of the entry or 0 for directorties
56
+ def filesize
57
+ @filesize || 0
58
+ end
59
+ alias size filesize
60
+
61
+ # Returns the detected server type if this entry
62
+ def server_type
63
+ @server_type || "Unknown"
64
+ end
65
+
66
+ def unknown?
67
+ @dir.nil? && @file.nil? && @symlink.nil? && @device.nil?
68
+ end
69
+ end
@@ -1,54 +1,38 @@
1
1
  require 'net/ftp/list/parser'
2
2
  require 'date'
3
3
 
4
- module Net
5
- class FTP
6
- module List
7
-
8
- # Parse Microsoft(NT) like FTP LIST entries.
9
- #
10
- # == MATCHES
11
- #
12
- # 06-25-07 01:08PM <DIR> etc
13
- # 11-27-07 08:45PM 23437 README.TXT
14
- #
15
- # == SYNOPSIS
16
- #
17
- # entry = Net::FTP::List::Microsoft.new('06-25-07 01:08PM <DIR> etc')
18
- # entry.dir? # => true
19
- # entry.basename # => 'etc'
20
- class Microsoft < Parser
21
-
22
- # Stolen straight from the ASF's commons Java FTP LIST parser library.
23
- # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
24
- REGEXP = %r!
25
- ^\s*
26
- ([0-9\-:\/]{5,})\s+([0-9\-:]{3,}(?:[aApP][mM])?)\s+
27
- (?:(<DIR>)|([0-9]+))\s+
28
- (\S.*)
29
- \s*$
30
- !x
31
-
32
- # Parse a Microsoft(NT) like FTP LIST entries.
33
- def initialize(raw)
34
- super(raw)
35
- match = REGEXP.match(raw.strip) or raise ParserError
36
-
37
- @mtime = DateTime.strptime("#{match[1]} #{match[2]}", "%m-%d-%y %I:%M%p")
38
-
39
- if match[3] == '<DIR>'
40
- @dir = true
41
- else
42
- @filesize = match[4].to_i
43
- @file = true
44
- end
45
-
46
- # TODO: Permissions, users, groups, date/time.
47
-
48
- @basename = match[5]
49
- end
50
- end
51
-
52
- end
4
+ # Parse Microsoft(NT) like FTP LIST entries.
5
+ #
6
+ # == MATCHES
7
+ #
8
+ # 06-25-07 01:08PM <DIR> etc
9
+ # 11-27-07 08:45PM 23437 README.TXT
10
+ class Net::FTP::List::Microsoft < Net::FTP::List::Parser
11
+ # Stolen straight from the ASF's commons Java FTP LIST parser library.
12
+ # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
13
+ REGEXP = %r!
14
+ ^\s*
15
+ ([0-9\-:\/]{5,})\s+([0-9\-:]{3,}(?:[aApP][mM])?)\s+
16
+ (?:(<DIR>)|([0-9]+))\s+
17
+ (\S.*)
18
+ \s*$
19
+ !x
20
+
21
+ # Parse a Microsoft(NT) like FTP LIST entries.
22
+ def self.parse(raw)
23
+ match = REGEXP.match(raw.strip) or return false
24
+
25
+ mtime = DateTime.strptime("#{match[1]} #{match[2]}", "%m-%d-%y %H:%M%p")
26
+ is_dir = match[3] == '<DIR>'
27
+ filesize = is_dir ? 0 : match[4].to_i
28
+
29
+ emit_entry(
30
+ raw,
31
+ :dir => is_dir,
32
+ :file => !is_dir,
33
+ :filesize => filesize,
34
+ :basename => match[5],
35
+ :mtime => mtime
36
+ )
53
37
  end
54
38
  end
@@ -1,54 +1,35 @@
1
1
  require 'net/ftp/list/parser'
2
2
  require 'time'
3
3
 
4
- module Net
5
- class FTP
6
- module List
7
-
8
- # Parse Netware like FTP LIST entries.
9
- #
10
- # == MATCHES
11
- #
12
- # d [RWCEAFMS] dpearce 512 Jun 27 23:46 public.www
13
- #
14
- # == SYNOPSIS
15
- #
16
- # entry = Net::FTP::List::Netware.new('d [RWCEAFMS] dpearce 512 Jun 27 23:46 public.www')
17
- # entry.dir? # => true
18
- # entry.basename # => 'public.www'
19
- class Netware < Parser
20
-
21
- # Stolen straight from the ASF's commons Java FTP LIST parser library.
22
- # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
23
-
24
- REGEXP = %r!^
25
- (d|-){1}\s+
26
- \[(.*?)\]\s+
27
- (\S+)\s+(\d+)\s+
28
- (\S+\s+\S+\s+((\d+:\d+)|(\d{4})))
29
- \s+(.*)
30
- $!x
31
-
32
- # Parse a Netware like FTP LIST entries.
33
- def initialize(raw)
34
- super(raw)
35
- match = REGEXP.match(raw.strip) or raise ParserError
36
-
37
- @mtime = Time.parse(match[5])
38
- @filesize = match[4].to_i
39
-
40
- if match[1] == 'd'
41
- @dir = true
42
- else
43
- @file = true
44
- end
45
-
46
- # TODO: Permissions, users, groups, date/time.
47
-
48
- @basename = match[9]
49
- end
50
- end
51
-
52
- end
4
+ # Parse Netware like FTP LIST entries.
5
+ #
6
+ # == MATCHES
7
+ #
8
+ # d [RWCEAFMS] dpearce 512 Jun 27 23:46 public.www
9
+ class Net::FTP::List::Netware < Net::FTP::List::Parser
10
+ # Stolen straight from the ASF's commons Java FTP LIST parser library.
11
+ # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
12
+ REGEXP = %r!^
13
+ (d|-){1}\s+
14
+ \[(.*?)\]\s+
15
+ (\S+)\s+(\d+)\s+
16
+ (\S+\s+\S+\s+((\d+:\d+)|(\d{4})))
17
+ \s+(.*)
18
+ $!x
19
+
20
+ # Parse a Netware like FTP LIST entries.
21
+ def self.parse(raw)
22
+ match = REGEXP.match(raw.strip) or return false
23
+
24
+ is_dir = match[1] == 'd'
25
+
26
+ emit_entry(
27
+ 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]
33
+ )
53
34
  end
54
35
  end
@@ -1,101 +1,28 @@
1
- module Net
2
- class FTP
3
- module List
1
+ # Abstract FTP LIST parser. It really just defines and documents the interface.
2
+ class Net::FTP::List::Parser
4
3
 
5
- # ParserError
6
- #
7
- # Raw entry couldn't be parsed for some reason.
8
- #
9
- # == TODO
10
- #
11
- # Get more specific with error messages.
12
- class ParserError < RuntimeError; end
4
+ @@parsers = []
13
5
 
14
- # Abstract FTP LIST parser.
15
- #
16
- # It really just defines and documents the interface.
17
- #
18
- # == Exceptions
19
- #
20
- # +ParserError+ -- Raw entry could not be parsed.
21
- class Parser
22
- @@parsers = []
23
-
24
- # Parse a raw FTP LIST line.
25
- #
26
- # By default just takes and set the raw list entry.
27
- #
28
- # Net::FTP::List.parse(raw_list_string) # => Net::FTP::List::Parser instance.
29
- def initialize(raw)
30
- @raw = raw
31
- end
32
-
33
- # The raw list entry string.
34
- def raw
35
- @raw ||= ''
36
- end
37
- alias_method :to_s, :raw
38
-
39
- # The items basename (filename).
40
- def basename
41
- @basename ||= ''
42
- end
43
-
44
- # Looks like a directory, try CWD.
45
- def dir?
46
- !!(@dir ||= false)
47
- end
48
-
49
- # Looks like a file, try RETR.
50
- def file?
51
- !!(@file ||= false)
52
- end
53
-
54
- # Looks like a symbolic link.
55
- def symlink?
56
- !!(@symlink ||= false)
57
- end
58
-
59
- def mtime
60
- @mtime
61
- end
62
-
63
- def filesize
64
- @filesize
65
- end
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
66
11
 
67
- class << self
68
- # Acts as a factory.
69
- #
70
- # TODO: Having a class be both factory and abstract implementation seems a little nutty to me. If it ends up
71
- # too confusing or gives anyone the shits I'll move it.
72
- def inherited(klass) #:nodoc:
73
- @@parsers << klass
74
- end
12
+ # Automatically add an inheriting parser to the list of known parsers.
13
+ def self.inherited(klass) #:nodoc:
14
+ @@parsers.push(klass)
15
+ end
75
16
 
76
- # Factory method.
77
- #
78
- # Attempt to find a parser and parse a list item. At worst the item will return an Net::FTP::List::Unknown
79
- # instance. This may change in the future so that only parsable entries are kept.
80
- def parse(raw)
81
- @@parsers.reverse.each do |parser|
82
- begin
83
- return parser.new(raw)
84
- rescue ParserError
85
- next
86
- end
87
- end
88
- end
89
- end
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
90
21
 
91
- end
22
+ private
92
23
 
93
- # Unknown parser.
94
- #
95
- # If all other attempts to parse the entry fail this class will be returned. Only the +raw+ and +to_s+
96
- # methods will return anything useful.
97
- class Unknown < Parser
98
- end
99
- end
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)
100
27
  end
101
28
  end
@@ -0,0 +1,33 @@
1
+ require 'net/ftp/list/parser'
2
+
3
+ # Parse Rumpus FTP LIST entries.
4
+ #
5
+ # == MATCHES
6
+ # drwxr-xr-x folder 0 Nov 30 10:03 houdini
7
+ # -rw-r--r-- 0 101426 101426 Jun 7 2008 imap with spaces.rb
8
+ class Net::FTP::List::Rumpus < Net::FTP::List::Parser
9
+
10
+ REGEXP = %r!^
11
+ ([drwxr-]{10})\s+
12
+ (folder|0\s+\d+)\s+
13
+ (\d+)\s+
14
+ (\w+)\s+
15
+ (\d{1,2})\s+
16
+ (\d{2}:\d{2}|\d{4})\s+
17
+ (.+)
18
+ $!x
19
+
20
+ # Parse a Rumpus FTP LIST entry.
21
+ def self.parse(raw)
22
+ match = REGEXP.match(raw.strip) or return false
23
+
24
+ emit_entry(
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
31
+ )
32
+ end
33
+ end
@@ -1,65 +1,66 @@
1
1
  require 'time'
2
2
  require 'net/ftp/list/parser'
3
3
 
4
- module Net
5
- class FTP
6
- module List
4
+ # Parse Unix like FTP LIST entries.
5
+ #
6
+ # == MATCHES
7
+ #
8
+ # drwxr-xr-x 4 steve group 4096 Dec 10 20:23 etc
9
+ # -rw-r--r-- 1 root other 531 Jan 29 03:26 README.txt
10
+ class Net::FTP::List::Unix < Net::FTP::List::Parser
7
11
 
8
- # Parse Unix like FTP LIST entries.
9
- #
10
- # == MATCHES
11
- #
12
- # drwxr-xr-x 4 steve group 4096 Dec 10 20:23 etc
13
- # -rw-r--r-- 1 root other 531 Jan 29 03:26 README.txt
14
- #
15
- # == SYNOPSIS
16
- #
17
- # entry = Net::FTP::List::Unix.new('drwxr-xr-x 4 steve group 4096 Dec 10 20:23 etc')
18
- # entry.dir? # => true
19
- # entry.basename # => 'etc'
20
- class Unix < Parser
12
+ # Stolen straight from the ASF's commons Java FTP LIST parser library.
13
+ # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
14
+ REGEXP = %r{
15
+ ([pbcdlfmpSs-])
16
+ (((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\+?\s+
17
+ (\d+)\s+
18
+ (\S+)\s+
19
+ (?:(\S+(?:\s\S+)*)\s+)?
20
+ (?:\d+,\s+)?
21
+ (\d+)\s+
22
+ ((?:\d+[-/]\d+[-/]\d+)|(?:\S+\s+\S+))\s+
23
+ (\d+(?::\d+)?)\s+
24
+ (\S*)(\s*.*)
25
+ }x
21
26
 
22
- # Stolen straight from the ASF's commons Java FTP LIST parser library.
23
- # http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
24
- REGEXP = %r{
25
- ([bcdlfmpSs-])
26
- (((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\+?\s+
27
- (\d+)\s+
28
- (\S+)\s+
29
- (?:(\S+(?:\s\S+)*)\s+)?
30
- (\d+)\s+
31
- ((?:\d+[-/]\d+[-/]\d+)|(?:\S+\s+\S+))\s+
32
- (\d+(?::\d+)?)\s+
33
- (\S*)(\s*.*)
34
- }x
27
+ ONE_YEAR = (60 * 60 * 24 * 365)
35
28
 
36
- # Parse a Unix like FTP LIST entries.
37
- def initialize(raw)
38
- super(raw)
39
- match = REGEXP.match(raw.strip) or raise ParserError
29
+ # Parse a Unix like FTP LIST entries.
30
+ def self.parse(raw)
31
+ match = REGEXP.match(raw.strip) or return false
40
32
 
41
- case match[1]
42
- when /d/ then @dir = true
43
- when /l/ then @symlink = true
44
- when /[f-]/ then @file = true
45
- when /[bc]/ then # Do nothing with devices for now.
46
- else ParserError 'Unknown LIST entry type.'
47
- end
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
48
41
 
49
- # TODO: Permissions, users, groups, date/time.
50
- @filesize = match[18].to_i
51
- @mtime = Time.parse("#{match[19]} #{match[20]}")
42
+ # TODO: Permissions, users, groups, date/time.
43
+ filesize = match[18].to_i
44
+ mtime = Time.parse("#{match[19]} #{match[20]}")
45
+ mtime -= ONE_YEAR if mtime > Time.now
52
46
 
53
- @basename = match[21].strip
47
+ basename = match[21].strip
54
48
 
55
- # filenames with spaces will end up in the last match
56
- @basename += match[22] unless match[22].nil?
49
+ # filenames with spaces will end up in the last match
50
+ basename += match[22] unless match[22].nil?
57
51
 
58
- # strip the symlink stuff we don't care about
59
- @basename.sub!(/\s+\->.+$/, '') if @symlink
60
- end
61
- end
52
+ # strip the symlink stuff we don't care about
53
+ basename.sub!(/\s+\->.+$/, '') if symlink
62
54
 
63
- end
55
+ emit_entry(
56
+ raw,
57
+ :dir => dir,
58
+ :file => file,
59
+ :device => device,
60
+ :symlink => symlink,
61
+ :filesize => filesize,
62
+ :basename => basename,
63
+ :mtime => mtime
64
+ )
64
65
  end
65
66
  end
@@ -0,0 +1,14 @@
1
+ require 'net/ftp/list/parser'
2
+
3
+ # If all other attempts to parse the entry fail this is the parser that is going to be used.
4
+ # It might be a good idea to fail loudly.
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
10
+
11
+ emit_entry(raw, {})
12
+ end
13
+ end
14
+
@@ -1,50 +1,57 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{net-ftp-list}
8
- s.version = "2.1.1"
8
+ s.version = "3.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Stateless Systems"]
12
- s.date = %q{2010-07-30}
12
+ s.date = %q{2011-01-09}
13
13
  s.email = %q{enquiries@statelesssystems.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.txt"
16
16
  ]
17
17
  s.files = [
18
- ".gitignore",
19
- "README.txt",
20
- "Rakefile",
21
- "VERSION.yml",
22
- "lib/net/ftp/list.rb",
23
- "lib/net/ftp/list/microsoft.rb",
24
- "lib/net/ftp/list/netware.rb",
25
- "lib/net/ftp/list/parser.rb",
26
- "lib/net/ftp/list/unix.rb",
27
- "net-ftp-list.gemspec",
28
- "test/test_net_ftp_list_microsoft.rb",
29
- "test/test_net_ftp_list_netware.rb",
30
- "test/test_net_ftp_list_unix.rb"
18
+ "README.txt",
19
+ "Rakefile",
20
+ "VERSION.yml",
21
+ "lib/net/ftp/list.rb",
22
+ "lib/net/ftp/list/entry.rb",
23
+ "lib/net/ftp/list/microsoft.rb",
24
+ "lib/net/ftp/list/netware.rb",
25
+ "lib/net/ftp/list/parser.rb",
26
+ "lib/net/ftp/list/rumpus.rb",
27
+ "lib/net/ftp/list/unix.rb",
28
+ "lib/net/ftp/list/unknown.rb",
29
+ "net-ftp-list.gemspec",
30
+ "test/test_net_ftp_list.rb",
31
+ "test/test_net_ftp_list_entry.rb",
32
+ "test/test_net_ftp_list_microsoft.rb",
33
+ "test/test_net_ftp_list_netware.rb",
34
+ "test/test_net_ftp_list_rumpus.rb",
35
+ "test/test_net_ftp_list_unix.rb"
31
36
  ]
32
37
  s.homepage = %q{http://github.com/stateless-systems/net-ftp-list}
33
- s.rdoc_options = ["--charset=UTF-8"]
34
38
  s.require_paths = ["lib"]
35
- s.rubygems_version = %q{1.3.5}
39
+ s.rubygems_version = %q{1.3.7}
36
40
  s.summary = %q{Parse FTP LIST command output.}
37
41
  s.test_files = [
42
+ "test/test_net_ftp_list.rb",
43
+ "test/test_net_ftp_list_entry.rb",
38
44
  "test/test_net_ftp_list_microsoft.rb",
39
- "test/test_net_ftp_list_unix.rb",
40
- "test/test_net_ftp_list_netware.rb"
45
+ "test/test_net_ftp_list_netware.rb",
46
+ "test/test_net_ftp_list_rumpus.rb",
47
+ "test/test_net_ftp_list_unix.rb"
41
48
  ]
42
49
 
43
50
  if s.respond_to? :specification_version then
44
51
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
52
  s.specification_version = 3
46
53
 
47
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
54
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
55
  else
49
56
  end
50
57
  else
@@ -0,0 +1,26 @@
1
+ require 'test/unit'
2
+ require 'net/ftp/list'
3
+
4
+ class TestNetFTPList < Test::Unit::TestCase
5
+
6
+ def test_all
7
+ one = Net::FTP::List.parse('drwxr-xr-x 4 user group 4096 Dec 10 20:23 etc')
8
+ assert_kind_of Net::FTP::List::Entry, one
9
+ assert one.dir?
10
+ assert !one.file?
11
+
12
+ two = Net::FTP::List.parse("++ unknown garbage +++")
13
+ assert_kind_of Net::FTP::List::Entry, two
14
+ assert two.unknown?
15
+ assert_equal '', two.basename
16
+ end
17
+
18
+ def test_raise_when_flag_set
19
+ Net::FTP::List.raise_on_failed_server_detection = true
20
+ assert_raise(Net::FTP::List::ParseError) do
21
+ Net::FTP::List.parse("++ unknown garbage +++")
22
+ end
23
+ ensure
24
+ Net::FTP::List.raise_on_failed_server_detection = false
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'test/unit'
2
+ require 'net/ftp/list'
3
+
4
+ class TestNetFTPEntry < Test::Unit::TestCase
5
+
6
+ def test_raise_on_unknown_options
7
+ assert_raise(ArgumentError) { Net::FTP::List::Entry.new("foo", {:bar => "baz"}) }
8
+ end
9
+
10
+ def test_default_values
11
+ e = Net::FTP::List::Entry.new('')
12
+ assert_equal 0, e.filesize
13
+ assert_equal 0, e.size
14
+ assert_equal '', e.basename
15
+ assert_equal '', e.name
16
+ assert e.unknown?
17
+ assert !e.dir?
18
+ assert !e.directory?
19
+ assert !e.file?
20
+ assert !e.symlink?
21
+ assert_kind_of Time, e.mtime
22
+ assert_equal "Unknown", e.server_type
23
+ end
24
+
25
+ end
@@ -4,18 +4,17 @@ require 'net/ftp/list'
4
4
  class TestNetFTPListMicrosoft < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
- # DATE TIME DIR SIZE NAME
8
7
  @dir = Net::FTP::List.parse('06-25-07 01:08PM <DIR> etc')
9
8
  @file = Net::FTP::List.parse('11-27-07 08:45PM 23437 README.TXT')
10
9
  end
11
10
 
12
11
  def test_parse_new
13
- assert_instance_of Net::FTP::List::Microsoft, @dir, 'LIST M$ directory'
14
- assert_instance_of Net::FTP::List::Microsoft, @file, 'LIST M$ file'
12
+ assert_equal "Microsoft", @dir.server_type, 'LIST M$ directory'
13
+ assert_equal "Microsoft", @file.server_type, 'LIST M$ directory'
15
14
  end
16
15
 
17
16
  def test_rubbish_lines
18
- assert_instance_of Net::FTP::List::Unknown, Net::FTP::List.parse("++ bah! ++")
17
+ assert_instance_of Net::FTP::List::Entry, Net::FTP::List.parse("++ bah! ++")
19
18
  end
20
19
 
21
20
  def test_ruby_microsoft_mtime
@@ -36,7 +35,12 @@ class TestNetFTPListMicrosoft < Test::Unit::TestCase
36
35
  end
37
36
 
38
37
  def test_filesize
39
- assert @dir.filesize.nil?
38
+ assert_equal 0, @dir.filesize
40
39
  assert_equal 23437, @file.filesize
41
40
  end
41
+
42
+ def test_zero_hour
43
+ file = Net::FTP::List.parse('10-15-09 00:34AM <DIR> aspnet_client')
44
+ assert_equal 1255566840.to_s, file.mtime.strftime('%s')
45
+ end
42
46
  end
@@ -9,12 +9,8 @@ class TestNetFTPListNetware < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  def test_parse_new
12
- assert_instance_of Net::FTP::List::Netware, @dir, 'LIST Netware directory'
13
- assert_instance_of Net::FTP::List::Netware, @file, 'LIST Netware file'
14
- end
15
-
16
- def test_rubbish_lines
17
- assert_instance_of Net::FTP::List::Unknown, Net::FTP::List.parse("++ bah! ++")
12
+ assert_equal "Netware", @dir.server_type, 'LIST Netware directory'
13
+ assert_equal "Netware", @file.server_type, 'LIST Netware file'
18
14
  end
19
15
 
20
16
  def test_ruby_netware_mtime
@@ -0,0 +1,40 @@
1
+ require 'test/unit'
2
+ require 'net/ftp/list'
3
+
4
+ class TestNetFTPListRumpus < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @dir = Net::FTP::List.parse 'drwxrwxrwx folder 0 Nov 16 22:12 Alias'
8
+ @file = Net::FTP::List.parse '-rw-r--r-- 0 101426 101426 Jun 7 2008 imap with spaces.rb'
9
+ end
10
+
11
+ def test_parsed
12
+ assert_equal "Rumpus", @dir.server_type, 'LIST Rumpus directory'
13
+
14
+ ## TODO: this rumpus file is getting picked up as unix, which if you check
15
+ ## how it looks above, it looks like unix to me, i dunno how to fix it.
16
+ #assert_equal "Rumpus", @file.server_type, 'LIST Rumpus file with spaces'
17
+ end
18
+
19
+ def test_ruby_unix_like_date
20
+ assert_equal Time.parse("Nov 16 22:12"), @dir.mtime
21
+ assert_equal Time.parse("Jun 7 2008"), @file.mtime
22
+ end
23
+
24
+ def test_dir
25
+ assert_equal 'Alias', @dir.basename
26
+ assert !@dir.file?
27
+ assert @dir.dir?
28
+ end
29
+
30
+ def test_file
31
+ assert_equal 'imap with spaces.rb', @file.basename
32
+ assert @file.file?
33
+ assert !@file.dir?
34
+ end
35
+
36
+ def test_filesize
37
+ assert_equal 0, @dir.filesize
38
+ assert_equal 101426, @file.filesize
39
+ end
40
+ end
@@ -3,31 +3,49 @@ require 'net/ftp/list'
3
3
 
4
4
  class TestNetFTPListUnix < Test::Unit::TestCase
5
5
 
6
+ ONE_YEAR = (60 * 60 * 24 * 365)
7
+
8
+ FIRST_MINUTE_OF_YEAR = "Jan 1 00:00"
9
+ LAST_MINUTE_OF_YEAR = "Dec 31 23:59"
10
+ OTHER_RELATIVE_TIME = "Mar 11 07:57"
11
+ SYMBOLIC_LINK_TIME = "Oct 30 15:26"
12
+ ABSOLUTE_DATE = "Feb 15 2008"
13
+
6
14
  def setup
7
- @dir = Net::FTP::List.parse 'drwxr-xr-x 4 user group 4096 Dec 10 20:23 etc'
8
- @file = Net::FTP::List.parse '-rw-r--r-- 1 root other 531 Jan 29 03:26 README'
9
- @other_dir = Net::FTP::List.parse 'drwxr-xr-x 8 1791 600 4096 Mar 11 07:57 forums'
10
- @spaces = Net::FTP::List.parse 'drwxrwxr-x 2 danial danial 72 May 23 12:52 spaces suck'
11
- @symlink = Net::FTP::List.parse 'lrwxrwxrwx 1 danial danial 4 Apr 30 15:26 bar -> /etc'
12
- @older_date = Net::FTP::List.parse '-rwxrwxrwx 1 owner group 154112 Feb 15 2008 participando.xls'
15
+ @dir = Net::FTP::List.parse "drwxr-xr-x 4 user group 4096 #{FIRST_MINUTE_OF_YEAR} etc" rescue nil
16
+ @file = Net::FTP::List.parse "-rw-r--r-- 1 root other 531 #{LAST_MINUTE_OF_YEAR} README" rescue nil
17
+ @other_dir = Net::FTP::List.parse "drwxr-xr-x 8 1791 600 4096 #{OTHER_RELATIVE_TIME} forums" rescue nil
18
+ @spaces = Net::FTP::List.parse 'drwxrwxr-x 2 danial danial 72 May 23 12:52 spaces suck' rescue nil
19
+ @symlink = Net::FTP::List.parse "lrwxrwxrwx 1 danial danial 4 #{SYMBOLIC_LINK_TIME} bar -> /etc" rescue nil
20
+ @older_date = Net::FTP::List.parse "-rwxrwxrwx 1 owner group 154112 #{ABSOLUTE_DATE} participando.xls" rescue nil
21
+ @block_dev = Net::FTP::List.parse 'brw-r----- 1 root disk 1, 0 Apr 13 2006 ram0' rescue nil
22
+ @char_dev = Net::FTP::List.parse 'crw-rw-rw- 1 root root 1, 3 Apr 13 2006 null' rescue nil
23
+ @socket_dev = Net::FTP::List.parse 'srw-rw-rw- 1 root root 0 Aug 20 14:15 log' rescue nil
24
+ @pipe_dev = Net::FTP::List.parse 'prw-r----- 1 root adm 0 Nov 22 10:30 xconsole' rescue nil
13
25
  end
14
26
 
15
27
  def test_parse_new
16
- assert_instance_of Net::FTP::List::Unix, @dir, 'LIST unixish directory'
17
- assert_instance_of Net::FTP::List::Unix, @file, 'LIST unixish file'
18
- assert_instance_of Net::FTP::List::Unix, @other_dir, 'LIST unixish directory'
19
- assert_instance_of Net::FTP::List::Unix, @spaces, 'LIST unixish directory with spaces'
20
- assert_instance_of Net::FTP::List::Unix, @symlink, 'LIST unixish symlink'
21
- end
22
-
23
- def test_rubbish_lines
24
- assert_instance_of Net::FTP::List::Unknown, Net::FTP::List.parse("++ bah! ++")
28
+ assert_equal "Unix", @dir.server_type, 'LIST unixish directory'
29
+ assert_equal "Unix", @file.server_type, 'LIST unixish file'
30
+ assert_equal "Unix", @other_dir.server_type, 'LIST unixish directory'
31
+ assert_equal "Unix", @spaces.server_type, 'LIST unixish directory with spaces'
32
+ assert_equal "Unix", @symlink.server_type, 'LIST unixish symlink'
33
+ assert_equal "Unix", @block_dev.server_type, 'LIST unix block device'
34
+ assert_equal "Unix", @char_dev.server_type, 'LIST unix char device'
35
+ assert_equal "Unix", @socket_dev.server_type, 'LIST unix socket device'
36
+ assert_equal "Unix", @pipe_dev.server_type, 'LIST unix socket device'
25
37
  end
26
38
 
27
39
  def test_ruby_unix_like_date
28
- assert_equal Time.parse("Mar 11 07:57"), @other_dir.mtime
29
- assert_equal Time.parse("Apr 30 15:26"), @symlink.mtime
30
- assert_equal Time.parse("Feb 15 2008"), @older_date.mtime
40
+ {
41
+ FIRST_MINUTE_OF_YEAR => @dir,
42
+ LAST_MINUTE_OF_YEAR => @file,
43
+ OTHER_RELATIVE_TIME => @other_dir,
44
+ SYMBOLIC_LINK_TIME => @symlink,
45
+ ABSOLUTE_DATE => @older_date
46
+ }.each do |date_and_time, parsed_entry|
47
+ assert_equal parse_adjust_date_and_time(date_and_time), parsed_entry.send(:mtime)
48
+ end
31
49
  end
32
50
 
33
51
  def test_ruby_unix_like_dir
@@ -67,4 +85,32 @@ class TestNetFTPListUnix < Test::Unit::TestCase
67
85
  assert_equal 4, @symlink.filesize
68
86
  assert_equal 154112, @older_date.filesize
69
87
  end
88
+
89
+ def test_unix_block_device
90
+ assert_equal 'ram0', @block_dev.basename
91
+ assert @block_dev.device?
92
+ end
93
+
94
+ def test_unix_char_device
95
+ assert_equal 'null', @char_dev.basename
96
+ assert @char_dev.device?
97
+ end
98
+
99
+ def test_unix_socket_device
100
+ assert_equal 'log', @socket_dev.basename
101
+ assert @socket_dev.device?
102
+ end
103
+
104
+ def test_unix_pipe_device
105
+ assert_equal 'xconsole', @pipe_dev.basename
106
+ assert @pipe_dev.device?
107
+ end
108
+
109
+
110
+ private
111
+ def parse_adjust_date_and_time(date_and_time)
112
+ parsed_time = Time.parse(date_and_time)
113
+ parsed_time -= ONE_YEAR if parsed_time > Time.now
114
+ parsed_time
115
+ end
70
116
  end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-ftp-list
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ prerelease: false
5
+ segments:
6
+ - 3
7
+ - 2
8
+ - 0
9
+ version: 3.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Stateless Systems
@@ -9,7 +14,7 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-07-30 00:00:00 +10:00
17
+ date: 2011-01-09 00:00:00 +11:00
13
18
  default_executable:
14
19
  dependencies: []
15
20
 
@@ -22,48 +27,60 @@ extensions: []
22
27
  extra_rdoc_files:
23
28
  - README.txt
24
29
  files:
25
- - .gitignore
26
30
  - README.txt
27
31
  - Rakefile
28
32
  - VERSION.yml
29
33
  - lib/net/ftp/list.rb
34
+ - lib/net/ftp/list/entry.rb
30
35
  - lib/net/ftp/list/microsoft.rb
31
36
  - lib/net/ftp/list/netware.rb
32
37
  - lib/net/ftp/list/parser.rb
38
+ - lib/net/ftp/list/rumpus.rb
33
39
  - lib/net/ftp/list/unix.rb
40
+ - lib/net/ftp/list/unknown.rb
34
41
  - net-ftp-list.gemspec
42
+ - test/test_net_ftp_list.rb
43
+ - test/test_net_ftp_list_entry.rb
35
44
  - test/test_net_ftp_list_microsoft.rb
36
45
  - test/test_net_ftp_list_netware.rb
46
+ - test/test_net_ftp_list_rumpus.rb
37
47
  - test/test_net_ftp_list_unix.rb
38
48
  has_rdoc: true
39
49
  homepage: http://github.com/stateless-systems/net-ftp-list
40
50
  licenses: []
41
51
 
42
52
  post_install_message:
43
- rdoc_options:
44
- - --charset=UTF-8
53
+ rdoc_options: []
54
+
45
55
  require_paths:
46
56
  - lib
47
57
  required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
48
59
  requirements:
49
60
  - - ">="
50
61
  - !ruby/object:Gem::Version
62
+ segments:
63
+ - 0
51
64
  version: "0"
52
- version:
53
65
  required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
54
67
  requirements:
55
68
  - - ">="
56
69
  - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
57
72
  version: "0"
58
- version:
59
73
  requirements: []
60
74
 
61
75
  rubyforge_project:
62
- rubygems_version: 1.3.5
76
+ rubygems_version: 1.3.7
63
77
  signing_key:
64
78
  specification_version: 3
65
79
  summary: Parse FTP LIST command output.
66
80
  test_files:
81
+ - test/test_net_ftp_list.rb
82
+ - test/test_net_ftp_list_entry.rb
67
83
  - test/test_net_ftp_list_microsoft.rb
68
- - test/test_net_ftp_list_unix.rb
69
84
  - test/test_net_ftp_list_netware.rb
85
+ - test/test_net_ftp_list_rumpus.rb
86
+ - test/test_net_ftp_list_unix.rb
data/.gitignore DELETED
@@ -1,7 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- coverage
4
- rdoc
5
- pkg
6
- .idea/*
7
- .idea/**/*