Class Rack::Directory
In: lib/rack/directory.rb
Parent: Object

Rack::Directory serves entries below the root given, according to the path info of the Rack request. If a directory is found, the file‘s contents will be presented in an html based index. If a file is found, the env will be passed to the specified app.

If app is not specified, a Rack::File of the same root will be used.

Methods

Constants

DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"
DIR_PAGE = <<-PAGE <html><head> <title>%s</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <style type='text/css'> table { width:100%%; } .name { text-align:left; } .size, .mtime { text-align:right; } .type { width:11em; } .mtime { width:15em; } </style> </head><body> <h1>%s</h1> <hr /> <table> <tr> <th class='name'>Name</th> <th class='size'>Size</th> <th class='type'>Type</th> <th class='mtime'>Last Modified</th> </tr> %s </table> <hr /> </body></html> PAGE
F = ::File
FILESIZE_FORMAT = [ ['%.1fT', 1 << 40], ['%.1fG', 1 << 30], ['%.1fM', 1 << 20], ['%.1fK', 1 << 10], ]   Stolen from Ramaze

Attributes

files  [R] 
path  [RW] 
root  [RW] 

Public Class methods

[Source]

    # File lib/rack/directory.rb, line 45
45:     def initialize(root, app=nil)
46:       @root = F.expand_path(root)
47:       @app = app || Rack::File.new(@root)
48:     end

Public Instance methods

[Source]

    # File lib/rack/directory.rb, line 56
56:     def _call(env)
57:       @env = env
58:       @script_name = env['SCRIPT_NAME']
59:       @path_info = Utils.unescape(env['PATH_INFO'])
60: 
61:       if forbidden = check_forbidden
62:         forbidden
63:       else
64:         @path = F.join(@root, @path_info)
65:         list_path
66:       end
67:     end

[Source]

    # File lib/rack/directory.rb, line 50
50:     def call(env)
51:       dup._call(env)
52:     end

[Source]

    # File lib/rack/directory.rb, line 69
69:     def check_forbidden
70:       return unless @path_info.include? ".."
71: 
72:       body = "Forbidden\n"
73:       size = body.respond_to?(:bytesize) ? body.bytesize : body.size
74:       return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
75:     end

[Source]

     # File lib/rack/directory.rb, line 127
127:     def each
128:       show_path = @path.sub(/^#{@root}/,'')
129:       files = @files.map{|f| DIR_FILE % f }*"\n"
130:       page  = DIR_PAGE % [ show_path, show_path , files ]
131:       page.each_line{|l| yield l }
132:     end

[Source]

     # File lib/rack/directory.rb, line 121
121:     def entity_not_found
122:       body = "Entity not found: #{@path_info}\n"
123:       size = body.respond_to?(:bytesize) ? body.bytesize : body.size
124:       return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
125:     end

[Source]

     # File lib/rack/directory.rb, line 143
143:     def filesize_format(int)
144:       FILESIZE_FORMAT.each do |format, size|
145:         return format % (int.to_f / size) if int >= size
146:       end
147: 
148:       int.to_s + 'B'
149:     end

[Source]

    # File lib/rack/directory.rb, line 77
77:     def list_directory
78:       @files = [['../','Parent Directory','','','']]
79:       glob = F.join(@path, '*')
80: 
81:       Dir[glob].sort.each do |node|
82:         stat = stat(node)
83:         next  unless stat
84:         basename = F.basename(node)
85:         ext = F.extname(node)
86: 
87:         url = F.join(@script_name, @path_info, basename)
88:         size = stat.size
89:         type = stat.directory? ? 'directory' : Mime.mime_type(ext)
90:         size = stat.directory? ? '-' : filesize_format(size)
91:         mtime = stat.mtime.httpdate
92: 
93:         @files << [ url, basename, size, type, mtime ]
94:       end
95: 
96:       return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ]
97:     end

TODO: add correct response if not readable, not sure if 404 is the best

      option

[Source]

     # File lib/rack/directory.rb, line 107
107:     def list_path
108:       @stat = F.stat(@path)
109: 
110:       if @stat.readable?
111:         return @app.call(@env) if @stat.file?
112:         return list_directory if @stat.directory?
113:       else
114:         raise Errno::ENOENT, 'No such file or directory'
115:       end
116: 
117:     rescue Errno::ENOENT, Errno::ELOOP
118:       return entity_not_found
119:     end

[Source]

     # File lib/rack/directory.rb, line 99
 99:     def stat(node, max = 10)
100:       F.stat(node)
101:     rescue Errno::ENOENT, Errno::ELOOP
102:       return nil
103:     end

[Validate]