Serve files using Rack TryStatic directly? - ruby

I'm using Middleman to create a static site.
Middleman generates static html files into ./build directory.
Here's the config I'm currently using:
require 'rubygems'
require 'middleman'
require 'rack/contrib/try_static'
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
run Middleman::Application.server
So Middleman is serving the static files right now. How can I make Rack::TryStatic handle the requests directly?
I tried to make it something like
run Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
But it doesn't work is run only accepts 1 argument. And Rack::TryStatic requires 2 arguments, app and options to initialize, and I don't have any app.
How can I do this?
(And if it matters, I'm deploying to Heroku)

As you’ve noticed, a Rack middleware component such as Rack::TryStatic needs another app to pass requests onto. You could create a simple one to use that for example just returned a 404 response, such as:
app = lambda {|env| [404, {'Content-type' => 'text/plain'}, ['Not found']
run Rack::TryStatic.new app, :root => "build", :urls => %w[/], :try => ['.html']
or equivalently:
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
run lambda {|env| [404, {'Content-type' => 'text/plain'}, ['Not found']]}
If you have your own 404 file, you could use rack-contrib’s Rack::NotFound instead of a custom end point of your own:
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
run Rack::NotFound.new('path/to/your/404.html')
If you weren’t using the :try array of file extensions to try and serve, you could use Rack::File directly. Internally, Rack::TryStatic uses Rack::Static, which in turn uses Rack::File. Unlike TryStatic and Static, Rack::File is a Rack application in its own right, and so doesn’t need a separate app to pass requests to. Your config.ru would then simply be:
run Rack::File.new './build'
although this wouldn’t allow for “bare” requests to be served with the corresponding .html file — all requests would need to specify the whole file name.

Related

Rack Middleware in Rack Middleware?

I am trying to build a rack middleware GEM that uses rack middleware itself (RackWired).
I have an existing application, whose config.ru uses Rack::Builder. In that block (Rack::Builder), I would like to specify my middleware, and when it is called to use a third party middleware (rack-cors) inside my own to do some stuff. Confusing I know.
The problem is that Rack::Builder's context is in config.ru and my middleware (RackWired) is thus unable to access it to "use" the third party middleware (rack-cors).
The intent of my effort is here
Is there a way to use middleware within middleware?
Thank you
Right, I'm not entirely sure what you're trying to do. But you can do this
class CorsWired
def initialize(app)
#app = app
end
def call(env)
cors = Rack::Cors.new(#app, {}) do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :put, :options, :delete], :credentials => false
end
end
cors.call(env)
end
end
Your config.ru should have use CorsWired though, not use CorsWired.new
This is I think what you were asking but I think you're missing the point of middleware. You should just change your config.ru to use rack-cors before/after your middleware depending on what you want to do.
require 'rack'
require 'rack/cors'
require './cors_wired'
app = Rack::Builder.new do
use Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :put, :options, :delete], :credentials => false
end
end
use CorsWired
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end
run app

Rack::Builder and Rack::TryStatic doesn't work

I have a Middleman app which I am serving using Rack::TryStatic.
Here is the config.ru.
use Rack::TryStatic,
root: 'build',
urls: %w[/],
try: ['.html', 'index.html', '/index.html']
run lambda{ |env|
four_oh_four_page = File.expand_path("../build/404.html", __FILE__)
[ 404, { 'Content-Type' => 'text/html'}, [ File.read(four_oh_four_page) ]]
}
My understanding is that when you use a config.ru with run, map or use
methods, they are converted to a Rack::Builder object.
I've tried wrapping this config in a Rack::Builder object like this:
app = Rack::Builder.new do
use Rack::TryStatic,
root: 'build',
urls: %w[/],
try: ['.html', 'index.html', '/index.html']
run lambda{ |env|
four_oh_four_page = File.expand_path("../build/404.html", __FILE__)
[ 404, { 'Content-Type' => 'text/html'}, [ File.read(four_oh_four_page) ]]
}
end
run app
When I do this, I get the 404 page for all requests.
Why doesn't this work?
It looks like there is a bug in the current released version of TryStatic that has been fixed in master, where the try array is being lost when used in way that causes the middleware to be reinitialized.
You can avoid this by making sure the app only get s initialized once by using to_app:
run app.to_app
or equivalently use app instead of new:
app = Rack::Builder.app do
# ...

Ruby Rack Heroku : Unable to access multiple URLs

I have followed the Heroku guide on deploying static files using Ruby Rack (https://devcenter.heroku.com/articles/static-sites-ruby),
However I am unable to access any HTML file in \public apart from index.html (every URL resolves to the home page)
The config.ru file :
use Rack::Static,
:urls => ["/bootstrap", "/css", "/fonts", "/images", "/js", "/font-awesome"],
:root => "public"
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/index.html', File::RDONLY)
]
}
I have checked the following posts :
Ruby Rack Heroku: Serving Static Files
How to setup URLs for static site with Ruby Rack on Heroku
Still I am unable to go beyond the index.html page.
While I change the config.ru as shown below ... I get application error
use Rack::Static,
:urls => ["/bootstrap", "/css", "/fonts", "/images", "/js", "/font-awesome"],
:root => "public"
map "/" do
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/index.html', File::RDONLY)
]
}
end
map "/about" do
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/about/index.html', File::RDONLY)
]
}
end
Is there a way to map multiple URLs? I have 5-6 pages in my index.html, but unable to access.
Thanks for your help.
You could create a simple controller that just renders these pages. May be easier than trying to bypass the MVC. Let me know if you need help with this :)
Update -----
Example
Controller:
class PublicController < ApplicationController
def index
render "public/index"
end
def about
render "public/about/index"
end
end
Then place your html files in the views folder in the sub-directories specified in the controller.
Also don't forget to add these methods to your routes.rb file in the config directory.

How to enable gzip compression for static Rack sites on Heroku Cedar?

Following the Creating Static Sites in Ruby with Rack article, I get a static site on Heroku with a config.ru that looks like this:
use Rack::Static,
:urls => ["/images", "/js", "/css"],
:root => "public"
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/index.html', File::RDONLY)
]
}
When I run YSlow over the result, it reported none of the files were gzipped. What do I do to compress both the assets and the public/index.html?
From my previous experience with Sprockets, Sinatra, and the Rack::Deflater, I was pretty sure I was just another use Rack::Deflater line away from what I wanted.
I changed the config.ru to this:
use Rack::Static,
:urls => ["/images", "/js", "/css"],
:root => "public"
use Rack::Deflater
run lambda # ...same as in the question
and I was able to verify that responses were sent gzipped:
$ curl -H 'Accept-Encoding: gzip' http://localhost:9292 | file -
/dev/stdin: gzip compressed data
but not for static assets under /css, /js, or /images:
$ curl -H 'Accept-Encoding: gzip' http://localhost:9292/css/bootstrap.min.css | file -
/dev/stdin: ASCII English text, with very long lines
And that's when I realized this was a standard middleware stack—Rack::Static intercepts the call to static files and thus skips the following stack! That's why it worked for public/index.html but not for assets.
The following config.ru worked (note that use Rack::Deflater now precedes use Rack::Static):
use Rack::Deflater
use Rack::Static,
:urls => ["/images", "/js", "/css"],
:root => "public"
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/index.html', File::RDONLY)
]
}
Verified with:
$ curl -H 'Accept-Encoding: gzip' http://localhost:9292/css/bootstrap.min.css | file -
/dev/stdin: gzip compressed data, from Unix

Rack throwing an error when trying to serve a static file

use Rack::Static, :urls => ['/stylesheets', '/images'], :root => 'public'
run proc { |env| [200, { 'Content-Type' => 'text/html', 'Cache-Control' => 'public, max-age=86400' }, File.open('public/index.html')] }
I get private method `open' called for Rack::File:Class when I rackup. Really can't see where the problem is. Running rack 1.1. Help please...
There is a Rack::File class, which has precedence in your rackup file because of the way Ruby looks up names. This is not the class you are looking for, you want Ruby's own File class. That class can be referenced directly without lookup ambiguity by using the prefix ::
::File.open('public/index.html')

Resources