19

I'm trying to get some information from this tutorial: http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder

basically I want to have a file config.ru that tell rack to read the current directory so I can access all the files just like a simple apache server and also read the default root with the index.html file...is there any way to do it?

my current config.ru looks like this:

run Rack::Directory.new('')
#this would read the directory but it doesn't set the root to index.html


map '/' do
  file = File.read('index.html')
  run Proc.new {|env| [200, {'Content-Type' => 'text/html'}, file] }
end
#using this reads the index.html mapped as the root but ignores the other files in the directory

So I don't know how to proceed from here...

I've also tried this following the tutorials example but thin doesn't starts properly.

builder = Rack::Builder.new do

  run Rack::Directory.new('')

  map '/' do
    file = File.read('index.html')
    run Proc.new {|env| [200, {'Content-Type' => 'text/html'}, file] }
  end

end

Rack::Handler::Thin.run builder, :port => 3000

Thanks in advance

zanona
  • 12,345
  • 25
  • 86
  • 141

5 Answers5

38

I think that you are missing the the rackup command. Here is how it is used:

rackup config.ru

This is going to run your rack app on port 9292 using webrick. You can read "rackup --help" for more info how you can change these defaults.

About the app that you want to create. Here is how I think it should look like:

# This is the root of our app
@root = File.expand_path(File.dirname(__FILE__))

run Proc.new { |env|
  # Extract the requested path from the request
  path = Rack::Utils.unescape(env['PATH_INFO'])
  index_file = @root + "#{path}/index.html"

  if File.exists?(index_file)
    # Return the index
    [200, {'Content-Type' => 'text/html'}, File.read(index_file)]
    # NOTE: using Ruby >= 1.9, third argument needs to respond to :each
    # [200, {'Content-Type' => 'text/html'}, [File.read(index_file)]]
  else
    # Pass the request to the directory app
    Rack::Directory.new(@root).call(env)
  end
}
Dan Williams
  • 3,769
  • 1
  • 18
  • 26
valo
  • 1,712
  • 17
  • 12
  • very good, that's exactly what I wanted to achieve, I think I can obtain more information from here and learn more about it. Thanks a lot valo – zanona Oct 14 '10 at 15:02
  • 1
    Found this by chance and just thought I'd add this in: [REQUEST_PATH is a legacy variable that is completely undefined in Rack.](http://groups.google.com/group/rack-devel/browse_thread/thread/0a41ecc3ce2db50d) – Teddy Zetterlund Feb 12 '11 at 09:17
  • Thanks! To build on what @Teddy said, use `PATH_INFO` and not `REQUEST_PATH`. Submitted an edit request with that change. – Henrik N Sep 11 '11 at 15:49
  • 8
    WARNING: dont use this in a production application! You can simply read any file on the whole server, since the code does NOT check if the resulting file is contained within @root. Simply do GET /../../../../etc/passwd and you get /etc/passwd (well, you'll have to try on the number of ".." entries) – Michael Siebert Jan 17 '12 at 15:48
  • 4
    The rack spec requires that the body response be an Array. This works like a charm just make sure to do : `[File.read(index_file)]` as the response's third element. – superjadex12 Feb 23 '12 at 08:31
  • 1
    @MichaelSiebert: Using `..` in a path segment causes both Thin and WEBrick to return `400 Bad Request` (if the `..` is at the beginning of the path) or `403 Forbidden` (if it's not at the beginning). – Kelvin Jun 25 '12 at 17:15
  • 1
    @superjadex12 - The body response should respond to `each`, it needn't be an Array. – Swanand Jan 26 '13 at 06:52
  • @Swanand ah thanks! Didn't know that =) I'm learning the essence of 'duck typing' in all sorts of ways =p – superjadex12 Jan 30 '13 at 00:49
  • You could use a lambda too like a lot of code examples in the Rack lib show. run lambda {|env| Rack::Directory.new(@root).call(env)} – Douglas G. Allen Jan 27 '15 at 04:40
10

I ended up on this page looking for a one liner...

If all you want is to serve the current directory for a few one-off tasks, this is all you need:

ruby -run -e httpd . -p 5000

Details on how it works: http://www.benjaminoakes.com/2013/09/13/ruby-simple-http-server-minimalist-rake/

Benjamin Oakes
  • 12,262
  • 12
  • 65
  • 83
8

You can do this using Rack::Static

map "/foo" do
  use Rack::Static, 
    :urls => [""], :root => File.expand_path('bar'), :index => 'index.html'
  run lambda {|*|}
end
sinm
  • 1,357
  • 12
  • 16
  • this has solved a massive issue I was experiencing with static files under Sinatra. Thanks a lot! – Rots Sep 18 '13 at 11:02
2

For me, using Ruby 2.0 and Rack 1.5.2, sinm solution worked for serving the index page (both as default page for root and loaded explicitly), but for other files I obtained errors similar to the following:

Rack::Lint::LintError: Status must be >=100 seen as integer

I combined sinm solution with this SO answer and the snippet found on Heroku documentation to obtain the desired behavior (assuming that the entire site is contained in a folder called public):

use Rack::Static, 
  :urls => ["/images", "/js", "/css"],
  :root => "public",
  :index => 'index.html'

run Rack::File.new("public")
Community
  • 1
  • 1
edymtt
  • 1,778
  • 1
  • 21
  • 37
2

My example for doing the exact same below:

module Rack
  class DirectoryIndex
    def initialize(app)
      @app = app
    end
    def call(env)
      index_path = ::File.join($documentRoot, Rack::Request.new(env).path.split('/'), 'index.html')
      if ::File.exists?(index_path)
        return [200, {"Content-Type" => "text/html"}, [::File.read(index_path)]]
      else
        @app.call(env)
      end
    end
  end
end

require 'rack_directory_index.rb'

$documentRoot = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'build'))

Capybara.app = Rack::Builder.new do |builder|
puts "Creating static rack server serving #{$documentRoot}"
use Rack::DirectoryIndex
    run Rack::Directory.new($documentRoot)
end

Capybara.configure do |config|
    config.run_server = true
end

The solution is mostly a copy and paste from different answers but it works fine. You can find it as a gist here aswell, good luck

Martin K
  • 782
  • 7
  • 13