Rack provides a minimal interface between webservers that support Ruby and Ruby frameworks. Rack is a layer between the framework & the application server — RubyRack
Rack is a ruby web server interface that allows your application to communicates with the webserver. Thus, you can think of it as a translator for applications and web servers.
A simple Rack Application
Let’s create a straightforward rack application by running therackup
command and get a feeling of using Rack:
$ gem install rack
$ mkdir rack_demo
$ cd rack_demo
$ rackupconfiguration /Users/cdragon/Project/config.ru not found
Rackup
is a useful tool provided by rack, which can launch applications that meet the Rack standard. By default, using rackup
requires a configuration file usually ends with.ru
.
$ touch config.ru
With a speedy setup:
# config.ruapp = Proc.new {
[
200,
{ "Content-Type" => "text/html" },
["Hello, Rack"]
]
}run app
And….it’s up!
You can view your webpage at http://localhost:9292.
What does Rack do when running rackup?
The default specifications for Rack are:
- Provide an object that can respond to the
call
method and receive a Hash type parameter (env) - Return an array containing the following three elements:
status code(Integer)
,HTTP Headers(Hash)
,HTTP Body(Array)
In Ruby world, objects that can respond to the call method are Proc
or Lumada
. You can also create a class and define the call method by yourself, which will have the same result:
#config.ruclass App
def call(env)
[
200,
{"content-Type" => "text/html"},
["Hello, Rack #{env}"]
]
end
endapp = App.new
run app
The elements of the array accepted by Rack are actually the specifications of the HTTP message. As long as the App sends an HTTP message that meets the specifications of HTTP, it can be sent back to the UserAgent, and Rack acts as a bridge in the middle.
Why Rack? Can we talk directly to the Web server?
Imagine our Rails Application built to talk to WEBrick
as its web server. What if one day we want to use Puma
since it’s faster?
The bad thing is, your Rails is only able to speak with WEBrick
since it was built so. Without having Rack as a bridge, it might take a month for the developer to get Rails to talk to Puma
.
Having Rack as a translator between the web server and Ruby Apps gives us the flexibility to switch Web Server (Puma, WEBrick…) and Ruby Frameworks (Rails, Sinatra…)easily.
See more supported web servers and frameworks in Rack Document.
Bonus - Rack Middleware
During the execution of the rack between the server and framework, Rack allows us to customize your app needs using middleware.
The rack middleware is actually a class. When it is initialized, it will receive the entire Rack program calling the response method, which meets and return the specified rack specifications.
class App
def call(env)
[
200,
{"content-Type" => "text/html"},
["Hello, Rack"]
]
end
endclass MyMiddleware
def initialize(app, who = "no one")
@app = app
@who = who
end def call(env)
status, headers, body = @app.call(env)
body << "<br />Powered by #{@who}!"
[status, headers, body]
end
endapp = App.new
use MyMiddleware, "It's me"
run app
The fun thing is, You can add as many middlewares as you like:
use MyMiddleware1
use MyMiddleware2
use MyMiddleware3
run App
Furthermore, Rack itself ships with tons of built-in middleware. For example, Rack::Attack
is a middleware that is very useful for blocking and throttling abusive requests.
Connect the dots
Now we know Rails is one of the supported Rack frameworks. Try to run rackup
it in your existing Rails project, and you’ll see your app up and running!
$ cd my_rails_app
$ rackup
No magic. You are able to find config.ru
in your project’s directory. It was auto-generated when to initialize your Rails project.
Wrap up
Knowing Rack helps you understand how your application communicates with the webserver.
Hopefully, this post helps you understand more about behind the scene magic!
References
All images were created by the author except the featured image.