Espresso Framework

Scalable Web Framework aimed at Speed and Simplicity

Install

$ [sudo] gem install e

or update Gemfile by adding:

gem 'e'

Quick Start

One-file App:

require 'e' # or Bundler.require

class App < E
  map '/'

  def index
    "Hello Espresso World!"
  end
end

App.run

Full-fledged application using Enginery

$ enginery g
$ ruby app.rb # or rackup

Tutorial

Intro

Actions | Controllers | Slices | MVC? | Models?

Routing

Base URL | Canonicals | Actions | Action Mapping | Action Aliases | Shared Actions
Parametrization | Format | RESTful Actions | Hosts | Rewriter

Setup

Global Setup | Setup by Name | Setup by Format | Remote Setup

Workflow

Route | Params | Passing Control | Fetching Body | Halt | Redirect | Reload | Error Handlers | Hooks | Authorization | Sessions | Flash | Cookies | Content Type | Charset | Cache Control | Expires | Last Modified | Accepted Content Type | Send File | Send Files | Attachment | Headers | Helpers |

View API

Engine | Extension | Templates Path | Layouts Path | Layout
Rendering Templates | Rendering Layouts | Ad hoc Rendering | Path Resolver | Templates Compilation

Streaming

Server-Sent Events | WebSockets | Chunked Responses

Espresso Lungo

Extending Espresso functionality via Espresso Lungo gem:

Install:

$ gem install el

Load:

require 'el'

Or simply add gem 'el' to Gemfile.

Functionality added by Espresso Lungo:

CRUD | Assets | Content Helpers | Tag Factory | Cache Manager

Deploy

Controllers | Slices | Roots | Arbitrary Applications | Run | config.ru


Highlights / Motivation

Performance

In terms of performance, the only really important thing for any framework it is to add as low overhead as possible.

The overhead are the time consumed by framework to accept the request then prepare and send response.

The tests that follows will allow to disclose the overhead added by various frameworks.

The overhead are calculated by dividing 1000 milliseconds to framework’s standard speed.

The framework’s standard speed are the speed of a "HelloWorld" app running on top of given framework.

The framework’s standard speed means nothing by itself. It is only used to calculate the framework’s overhead.

Tested apps will run on Thin web server and will return a trivial "Hello World!" response.

Hardware used:

Processor Name: Intel Core i5
Processor Speed: 3.31 GHz
Number of Processors: 1
Total Number of Cores: 4
Memory: 8 GB

To run tests on your hardware, clone Espresso Framework repository and execute rake overhead inside it.

Test results:

---
         Speed    Overhead   1ms-app  5ms-app   10ms-app  20ms-app  50ms-app  100ms-app
espresso  5518      0.18ms   847      193       98        49        19        9
 sinatra  3629      0.28ms   783      189       97        49        19        9
   rails   792      1.26ms   442      159       88        47        19        9
---

1ms-app shows your app speed when your actions takes 1ms to run.
10ms-app shows your app speed when your actions takes 10ms to run.
etc.

The app speed are calculated as follow:

1000 / (time taken by action + time taken by framework)

So, if your actions takes about 1ms and you use a framework with overhead of 0.18ms, the app speed will be:

1000 / ( 1 + 0.18 ) = 847 requests per second

However, if framework's overhead is of 1ms or more, the app speed will decrease dramatically:

1000 / ( 1 + 1.26 ) = 442 requests per second

Conclusions?

The framework speed matter only if your code matter.

If you develop a site aimed to serve a serious amount of requests, you should write actions that takes insignificant amount of time.

Only after that it make sense to think about framework speed.

Worth to Note - Espresso has built-in cache manager as well as views compiler.

These tools may help you to dramatically reduce the time consumed by your actions.

Natural Action/Routes/Params

I never understood why should i create actions in some file, then open another file and directing requests to created action.
Even worse! To use params inside action, i have to remember how i named them in another file. And when i want to change a param name i have to change it in both files?

What about consistency?

A good tradeoff would be to use some DSL.

get '/book/:id' do
  params[:id]
end

Looks much better.

But! Strings/Regexps as action names? No, thanks.

What if i need to remount a bunch of actions to a new root? Say from /news to /headlines? Refactoring? Using vars/constants in names? No, Thanks.

How do i setup multiple actions?
How do i find out the currently running action?

What if i do a request like "/book/100/?id=200"? What? Should i use unique param names? No, thanks.

etc. etc.

And why should i remember so many non-natural stuff?

Is not Ruby powerful enough? I guess it is:

def book id

end

That's a regular Ruby method and it's regular Espresso action.
That's also an Espresso route. Yes, the app will respond to "/book/100"
And of course action params are used naturally, through method arguments(id rather than params[:id]).

All this offered by Ruby for free! Why to reinvent the wheel?

Expressive Setup

Usually you do not want to instruct each action about how it should behave.

It would take eras to define inside each action what content type should it return or what layout should it render.

Instead, you will use few lines of code at class level to write instructions that will be followed by all actions.

Example: Instruct all actions under App controller to return JSON Content-Type

class App < E
  content_type '.json'
  # ...
end

But what if you need to setup only specific actions?

Simple! Put your setup, well, inside setup block and pass action names as parameters.

Example: Instruct only rss and feed actions to return XML Content-Type

class App < E

  setup :rss, :feed do
    content_type :xml
  end
  # ...
end

Well, what if i need to setup for some 10 actions and another setup for another 20 actions? Should i pass 30 arguments to setup? I do not want to buy a new keyboard every month...

That's simple too. Use regular expressions.

Ex: setup only news related actions:

class App < E

  setup /news/ do
    # some setup
  end
  # ...
end

Slices

Portability and DRY done right and easy.

With Espresso, any controller can be mounted under any app.

Even more, any set of controllers - a.k.a. slices - can be mounted under any app.

To create a slice simply put your controllers under some module.

Then you can mount that module under any Espresso app.

Even more, when mounting you can easily setup all controllers(or some) at once.

And of course when mounting, you can give a mount point.

require 'e'
require 'e-ext' # needed for `Cms.run` and `Cms.mount` to work

module Cms

  class Articles < E
    # ...
  end

  class News < E
    # ...
  end

  class Pages < E
    # ...
  end
end

app = Cms.mount do
  # some setup that will run inside each controller
end

# or
app = Cms.mount do |ctrl|
  # some setup that will run inside controllers that match `ctrl` param
end

app.run

RESTful Actions

By default, verbless actions will respond to any request type.

To make some action to respond only to some request type, simply prepend the corresponding verb to the action name.

# will respond to any request type
def book
  # ...
end

# GET
def get_book
  # ...
end

# POST
def post_book
  # ...
end

# PUT
def put_book
  # ...
end

# etc.

Flexible Rewriter

With Espresso built-in rewriter you can redirect any requests to new URL.

Beside trivial redirects rewriter can also pass the control to an arbitrary controller#action or simply halt the request and send the response.

Views Compiler

For most web sites, most time are spent at templates rendering. When rendering templates, most time are spent at reading and compiling.

Espresso allow to easily skip these expensive operations by keeping compiled templates in memory and just render them on consequent requests.

Contributing

  • Fork Espresso repository
  • make your changes
  • submit a pull request


Issues/Bugs: github.com/espresso/espresso/issues

Mailing List: groups.google.com/.../espresso-framework

IRC channel: #espressorb on irc.freenode.net

Author - Silviu Rusu. License - MIT.