#rails,

Rails as a Rack application

Awin Awin Nov 28, 2018 · 2 mins read
Rails as a Rack application
Share this

Rack provides a modular interface for developing web apps in Ruby. A Rack::Handler connects webservers with Rack. By default it ships with handlers for Thin, WEBrick, FastCGI, CGI, SCGI and LiteSpeed. It also provides a handy rackup commandline tool, which internally starts a Rack::Server.

Any object that responds to a call function and takes an env hash as parameter, returning an Array with the http response code, hash of headers, and a response body that can respond to each. [1]

For example the below instance of the class RunMe. You can run this with a Rack Handler.

require 'rack'
class RunMe
def call(env)
code = 200
headers = { 'Content-Type' => 'text/html' }
body = ['<h1>Hello Rack!</h1>']
return [code, headers, body]
end
end
app = RunMe.new
Rack::Handler::WEBrick.run app
view raw server.rb hosted with ❤ by GitHub

Save all of this in a server.rb and run it with ruby server.rb.

The rackup command line tool can be used, in which case you can forego the explicit use of the WEBrick handler. rackup internally calls Rack::Server.start

class RunMe
def call(env)
code = 200
headers = { 'Content-Type' => 'text/html' }
body = ['<h1>Hello Rack!</h1>']
return [code, headers, body]
end
end
run RunMe.new
## Running ##
# Run this by calling the following from console.
#
# rackup config.ru
#
##
view raw config.ru hosted with ❤ by GitHub

Running the rack server in Rails

When rails server is called the start method of Rails::Server is called.
Rails::Server inherits from Rack::Server.

Rails::Application is an class with a call function and has all the properties of [1]. So if we define a class that inherits from Rails::Application we can serve it with rackup.

So lets create a simple Rails::Application. Create a file named config.ru as follows:

require 'rails'
require "action_controller/railtie"
class SingleFile < Rails::Application
config.session_store :cookie_store, :key => '_session'
config.secret_key_base = '7893aeb3427daf48502ba09ff695da9ceb3c27daf48b0bba09df'
Rails.logger = Logger.new($stdout)
routes.draw do
root to: proc {|env| [200, {}, ["Hello world"]] }
end
end
run SingleFile
view raw config.ru hosted with ❤ by GitHub

This just prints ‘Hello World’. For a more useful experiment, you can define a controller and render a template.

Lets create a PagesController that renders some html inline.

require 'rails'
require 'action_controller/railtie'
class SingleFile < Rails::Application
config.session_store :cookie_store, :key => '_session'
config.secret_key_base = '7893aeb3427daf48502ba09ff695da9ceb3c27daf48b0bba09df'
Rails.logger = Logger.new($stdout)
end
class PagesController < ActionController::Base
def index
render inline: "<h1>Hello World!</h1> <p>I'm just a single file Rails application</p>"
end
end
SingleFile.routes.draw do
root to: "pages#index"
end
run SingleFile
view raw config.ru hosted with ❤ by GitHub

Tada! This right here, is a Rails application in a single file, with just 20 lines of Ruby.

For a more complex example, let split out the files. Its not single file anymore! Lets split out the config.ru file into application.rb (which handles the Rails part) and config.ru (which handles the run). And connect to a database(sqlite3) and render a template view file. So now we have a views folder with users/index.html.erb in it.

The code is as follows:

require 'rails'
require 'active_support/railtie'
require "action_controller/railtie"
require 'active_record'
class SingleFile < Rails::Application
config.session_store :cookie_store, :key => '_session'
config.secret_key_base = '7893aeb3427daf48502ba09ff695da9ceb3c27daf48b0bba09df'
Rails.logger = Logger.new($stdout)
end
# Define the Model
class User < ActiveRecord::Base
end
# Define the Controller
class UsersController < ActionController::Base
prepend_view_path 'views'
def index
Rails.logger.info view_paths
@users = User.all
end
end
# Define routes
SingleFile.routes.draw do
root to: 'users#index'
end
###################
# Some setup to connect to Sqlite3 DB
###################
def setup_db
require 'sqlite3'
sqlite_db = File.join(File.dirname(__FILE__), 'test.sqlite3')
db = SQLite3::Database.new( sqlite_db )
db.execute( "DROP TABLE users;" ) rescue nil
# Create the table
db.execute( "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email VARCHAR(100), name VARCHAR(100));" )
# Lets populate some seed data
db.execute( "INSERT INTO users VALUES(1, 'john@example.com', 'John Doe');" )
db.execute( "INSERT INTO users VALUES(2, 'jane@example.com', 'Jane Smith');" )
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => sqlite_db)
end
# Run DB setup
setup_db
view raw application.rb hosted with ❤ by GitHub
require_relative './application'
run SingleFile
view raw config.ru hosted with ❤ by GitHub
You can use this to test out features, or to log a bug report etc.

All the source code for this post is available at this Github Repo.

Although this is just a few files, it loads a lot of stuff internally. It’s definitely possible to remove some things. I’ll explore on how to render a json API and trim down the stack, but that’s for another post.

🤟

References

  1. Rails on Rack
  2. Rack Homepage
  3. Rack Source Code
Awin
Written by Awin Follow
Hi, I am Awin, founder at Neumeral. Please reachout to me for any feedback / questions.