Class: Rack::Static

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/static.rb

Overview

The Rack::Static middleware intercepts requests for static files (javascript files, images, stylesheets, etc) based on the url prefixes or route mappings passed in the options, and serves them using a Rack::File object. This allows a Rack stack to serve both static and dynamic content.

Examples:

Serve all requests beginning with /media from the “media” folder located in the current directory (ie media/*):

use Rack::Static, :urls => ["/media"]

Serve all requests beginning with /css or /images from the folder “public” in the current directory (ie public/css/* and public/images/*):

use Rack::Static, :urls => ["/css", "/images"], :root => "public"

Serve all requests to / with “index.html” from the folder “public” in the current directory (ie public/index.html):

use Rack::Static, :urls => {"/" => 'index.html'}, :root => 'public'

Serve all requests normally from the folder “public” in the current directory but uses index.html as default route for “/”

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

Set custom HTTP Headers for based on rules:

   use Rack::Static, :root => 'public',
       :header_rules => [
         [rule, {header_field => content, header_field => content}],
         [rule, {header_field => content}]
       ]

Rules for selecting files:

1) All files
   Provide the :all symbol
   :all => Matches every file

2) Folders
   Provide the folder path as a string
   '/folder' or '/folder/subfolder' => Matches files in a certain folder

3) File Extensions
   Provide the file extensions as an array
   ['css', 'js'] or %w(css js) => Matches files ending in .css or .js

4) Regular Expressions / Regexp
   Provide a regular expression
   %r{\.(?:css|js)\z} => Matches files ending in .css or .js
   /\.(?:eot|ttf|otf|woff2|woff|svg)\z/ => Matches files ending in
     the most common web font formats (.eot, .ttf, .otf, .woff2, .woff, .svg)
     Note: This Regexp is available as a shortcut, using the :fonts rule

5) Font Shortcut
   Provide the :fonts symbol
   :fonts => Uses the Regexp rule stated right above to match all common web font endings

Rule Ordering:
  Rules are applied in the order that they are provided.
  List rather general rules above special ones.

Complete example use case including HTTP header rules:

   use Rack::Static, :root => 'public',
       :header_rules => [
         # Cache all static files in public caches (e.g. Rack::Cache)
         #  as well as in the browser
         [:all, {'Cache-Control' => 'public, max-age=31536000'}],

         # Provide web fonts with cross-origin access-control-headers
         #  Firefox requires this when serving assets using a Content Delivery Network
         [:fonts, {'Access-Control-Allow-Origin' => '*'}]
       ]

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Static


88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rack/static.rb', line 88

def initialize(app, options = {})
  @app = app
  @urls = options[:urls] || ["/favicon.ico"]
  @index = options[:index]
  @gzip = options[:gzip]
  root = options[:root] || Dir.pwd

  # HTTP Headers
  @header_rules = options[:header_rules] || []
  # Allow for legacy :cache_control option while prioritizing global header_rules setting
  @header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]

  @file_server = Rack::File.new(root)
end

Instance Method Details

#add_index_root?(path) ⇒ Boolean


103
104
105
# File 'lib/rack/static.rb', line 103

def add_index_root?(path)
  @index && route_file(path) && path =~ /\/$/
end

#applicable_rules(path) ⇒ Object

Convert HTTP header rules to HTTP headers


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/rack/static.rb', line 156

def applicable_rules(path)
  @header_rules.find_all do |rule, new_headers|
    case rule
    when :all
      true
    when :fonts
      path =~ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/
    when String
      path = ::Rack::Utils.unescape(path)
      path.start_with?(rule) || path.start_with?('/' + rule)
    when Array
      path =~ /\.(#{rule.join('|')})\z/
    when Regexp
      path =~ rule
    else
      false
    end
  end
end

#call(env) ⇒ Object


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/rack/static.rb', line 119

def call(env)
  path = env[PATH_INFO]

  if can_serve(path)
    if overwrite_file_path(path)
      env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
    elsif @gzip && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
      path = env[PATH_INFO]
      env[PATH_INFO] += '.gz'
      response = @file_server.call(env)
      env[PATH_INFO] = path

      if response[0] == 404
        response = nil
      else
        if mime_type = Mime.mime_type(::File.extname(path), 'text/plain')
          response[1][CONTENT_TYPE] = mime_type
        end
        response[1]['Content-Encoding'] = 'gzip'
      end
    end

    path = env[PATH_INFO]
    response ||= @file_server.call(env)

    headers = response[1]
    applicable_rules(path).each do |rule, new_headers|
      new_headers.each { |field, content| headers[field] = content }
    end

    response
  else
    @app.call(env)
  end
end

#can_serve(path) ⇒ Object


115
116
117
# File 'lib/rack/static.rb', line 115

def can_serve(path)
  route_file(path) || overwrite_file_path(path)
end

#overwrite_file_path(path) ⇒ Object


107
108
109
# File 'lib/rack/static.rb', line 107

def overwrite_file_path(path)
  @urls.kind_of?(Hash) && @urls.key?(path) || add_index_root?(path)
end

#route_file(path) ⇒ Object


111
112
113
# File 'lib/rack/static.rb', line 111

def route_file(path)
  @urls.kind_of?(Array) && @urls.any? { |url| path.index(url) == 0 }
end