Robert Carpenter 25 Jan 2018

Amber - Crystalizing Rails and Phoenix

This guest post by Robert Carpenter will introduce you to Amber, a Rails/Phoenix-like web framework for Crystal. Don’t forget to reach out to us if you want to share some experience of your own.

The Amber Framework is a young and growing web framework written in our favorite language, Crystal. It shares many similarities with Rails, but - like Crystal itself - compatibility with Rails is not a design goal.

Here are a few reasons why I <3 Amber:

  • Familiar framework design
  • Compile time checks that save your sanity and maybe even your soul
  • Open and welcoming framework development team
  • Raw Speed

Familiar Framework Design

Amber projects follow similar design to Rails and other MVC focused web frameworks. It borrows organization and concepts where they’re successful and builds on that foundation where developer efficiency or simplicity can be improved.

Rails veterans will recognize many of these files and folders immediately:

.. (up a dir)
</repositories/offline_pink/
▸ db/migrations/
▾ src/
  ▾ controllers/
  	application_controller.cr
  	user_controller.cr
  ▸ mailers/
  ▾ models/
  	user.cr
  ▾ views/
	▾ user/
    	  new.slang
    	  show.slang

Models, views, controllers, and migrations are all right where you’d expect them. You’ll feel right at home. Environment config files follow a familiar style and a working asset pipeline is available right out of the box, if you want it (thanks to webpack).

Compile Time checks

Thanks to Crystal, a large portion of an Amber project is evaluated at compile time. Never again get an email from production complaining about something so mundane as a Missing Template, controller#action missing, or Undefined method .downcase for nil:NilClass.

Amber even parses and compiles templates and layout files which not only verifies that the code is calling methods and getters safely, it saves a lot of time during a request.

Crystal does a great job of complaining when a method call on a variable isn’t valid for all possible types of that variable. Amber uses that power to ensure the application isn’t going to break at runtime wherever possible.

Observe what happens when compiling an Amber application with this broken template:

- if domain.nam.blank?
  p No domain name

The compiler complains, notifies you of the problem, and even suggests a correction:

in macro 'macro_4598546880' expanded macro: embed:1, line 1:

>  1. if domain.nam.blank?
   2. __kilt_io__ << "
   3. "
   4. __kilt_io__ << "<p"
   5. __kilt_io__ << ">"
   6. __kilt_io__ << HTML.escape(("No domain name").to_s).to_s(__kilt_io__)
   7. __kilt_io__ << "</p>"
   8. end

undefined method 'nam' for Domain (did you mean 'name'?)

These compile time checks can save your sanity when an accidental typo slips its way into your routes file, or accidentally forgetting to commit a view template.

Open and welcoming dev team

The Amber project is active. Development on the tools, framework, libraries, and documentation is constant. Yet I’ve felt welcomed into the fold as an Amber contributor as the core team readily reviews and merges my pull requests, discusses framework direction and goals, and openly accepts suggestions and contributions from casual contributors as well as frequent benefactors. This picture is from the Github Pulse and shows how active the project has been this month:

Raw Speed

Last but certainly not least, thanks to Crystal, Amber is fast. The compiled web is real, and it’s far more friendly now than ever before.

Check out this log excerpt from a project I’ve been working on:

10:49:15 Request	| Started 2018-01-11 10:49:15 -07:00
10:49:15 Request	| Status: 200  Method: GET  Pipeline: web Format: html
10:49:15 Request	| Requested Url: /domain/30
10:49:15 Request	| Time Elapsed: 17.52ms

This particular request is slow by Amber standards, but it checks all the boxes:

  • SELECT queries across several tables1
  • validating logged-in user session, and authenticating access for the request2
  • rendering html views (not just JSON)3

Compare to a page with similar database and rendering overhead in a Rails 5 application:

processing by BookmarksController#index as HTML
Completed 200 OK in 251ms (Views: 217.9ms | ActiveRecord: 15.9ms)

That’s right. Thanks to the speed of Crystal, Amber can complete an entire request in about the same amount of time it takes Rails to query the database. That means each server can handle more traffic, is more resilient to denial of service, and every page is more responsive4.

For a real sample of speed, this excerpt is from a basic Read route such as this:

  def profile
    user = current_user
    render "show.slang"
  end
01:19:11 Request    | Started 2018-01-11 13:19:11 -07:00
01:19:11 Request    | Status: 200  Method: GET  Pipeline: web Format: html
01:19:11 Request    | Requested Url: /my/profile
01:19:11 Request    | Time Elapsed: 1.75ms

Or even faster, serving a static file:

01:19:11 Request    | Started 2018-01-11 13:19:11 -07:00
01:19:11 Request    | Status: 200  Method: GET  Pipeline: static Format:
01:19:11 Request    | Requested Url: /dist/main.bundle.js
01:19:11 Request    | Time Elapsed: 329.0µs

Amazingly, because Amber views are compiled in, rendering a template and layout can be significantly faster than serving static files when the application is configured for it5:

01:45:15 Request	| Started 2018-01-17 13:45:15 -07:00
01:45:15 Request	| Status: 200  Method: GET  Pipeline: web Format: html
01:45:15 Request	| Requested Url: /
01:45:15 Request	| Time Elapsed: 371.0µs
01:45:16 Request	| Started 2018-01-17 13:45:15 -07:00
01:45:16 Request	| Status: 200  Method: GET  Pipeline: static Format: js
01:45:16 Request	| Requested Url: /dist/main.bundle.js
01:45:16 Request	| Time Elapsed: 80.01ms

No matter what your application is doing, Amber can do it faster.

Summary

A lot of software is written for the web today. Ruby and Rails showed the world that development doesn’t need to be painful for developers. Crystal is a language where development ease meets speed, and Amber is a framework built on that tradition. Developer friendliness meets raw speed and power, for the web. Or to borrow from Amber’s tag-line: Productivity. Performance. Happiness.

  1. Relevant log 

  2. Relevant log 

  3. Relevant log 

  4. Unedited logs and code samples comparing Rails and Amber here 

  5. In order to render templates and views as fast as possible, the route pipeline must be slimmed down to almost nothing, rendering many features inoperable. 

This guest post by Robert Carpenter will introduce you to Amber, a Rails/Phoenix-like web framework for Crystal. Don’t forget to reach out to us if you want to share some experience of your own.

The Amber Framework is a young and growing web framework written in our favorite language, Crystal. It shares many similarities with Rails, but - like Crystal itself - compatibility with Rails is not a design goal.

Here are a few reasons why I <3 Amber:

  • Familiar framework design
  • Compile time checks that save your sanity and maybe even your soul
  • Open and welcoming framework development team
  • Raw Speed

Familiar Framework Design

Amber projects follow similar design to Rails and other MVC focused web frameworks. It borrows organization and concepts where they’re successful and builds on that foundation where developer efficiency or simplicity can be improved.

Rails veterans will recognize many of these files and folders immediately:

.. (up a dir)
</repositories/offline_pink/
▸ db/migrations/
▾ src/
  ▾ controllers/
  	application_controller.cr
  	user_controller.cr
  ▸ mailers/
  ▾ models/
  	user.cr
  ▾ views/
	▾ user/
    	  new.slang
    	  show.slang

Models, views, controllers, and migrations are all right where you’d expect them. You’ll feel right at home. Environment config files follow a familiar style and a working asset pipeline is available right out of the box, if you want it (thanks to webpack).

Compile Time checks

Thanks to Crystal, a large portion of an Amber project is evaluated at compile time. Never again get an email from production complaining about something so mundane as a Missing Template, controller#action missing, or Undefined method .downcase for nil:NilClass.

Amber even parses and compiles templates and layout files which not only verifies that the code is calling methods and getters safely, it saves a lot of time during a request.

Crystal does a great job of complaining when a method call on a variable isn’t valid for all possible types of that variable. Amber uses that power to ensure the application isn’t going to break at runtime wherever possible.

Observe what happens when compiling an Amber application with this broken template:

- if domain.nam.blank?
  p No domain name

The compiler complains, notifies you of the problem, and even suggests a correction:

in macro 'macro_4598546880' expanded macro: embed:1, line 1:

>  1. if domain.nam.blank?
   2. __kilt_io__ << "
   3. "
   4. __kilt_io__ << "<p"
   5. __kilt_io__ << ">"
   6. __kilt_io__ << HTML.escape(("No domain name").to_s).to_s(__kilt_io__)
   7. __kilt_io__ << "</p>"
   8. end

undefined method 'nam' for Domain (did you mean 'name'?)

These compile time checks can save your sanity when an accidental typo slips its way into your routes file, or accidentally forgetting to commit a view template.

Open and welcoming dev team

The Amber project is active. Development on the tools, framework, libraries, and documentation is constant. Yet I’ve felt welcomed into the fold as an Amber contributor as the core team readily reviews and merges my pull requests, discusses framework direction and goals, and openly accepts suggestions and contributions from casual contributors as well as frequent benefactors. This picture is from the Github Pulse and shows how active the project has been this month:

Raw Speed

Last but certainly not least, thanks to Crystal, Amber is fast. The compiled web is real, and it’s far more friendly now than ever before.

Check out this log excerpt from a project I’ve been working on:

10:49:15 Request	| Started 2018-01-11 10:49:15 -07:00
10:49:15 Request	| Status: 200  Method: GET  Pipeline: web Format: html
10:49:15 Request	| Requested Url: /domain/30
10:49:15 Request	| Time Elapsed: 17.52ms

This particular request is slow by Amber standards, but it checks all the boxes:

  • SELECT queries across several tables1
  • validating logged-in user session, and authenticating access for the request2
  • rendering html views (not just JSON)3

Compare to a page with similar database and rendering overhead in a Rails 5 application:

processing by BookmarksController#index as HTML
Completed 200 OK in 251ms (Views: 217.9ms | ActiveRecord: 15.9ms)

That’s right. Thanks to the speed of Crystal, Amber can complete an entire request in about the same amount of time it takes Rails to query the database. That means each server can handle more traffic, is more resilient to denial of service, and every page is more responsive4.

For a real sample of speed, this excerpt is from a basic Read route such as this:

  def profile
    user = current_user
    render "show.slang"
  end
01:19:11 Request    | Started 2018-01-11 13:19:11 -07:00
01:19:11 Request    | Status: 200  Method: GET  Pipeline: web Format: html
01:19:11 Request    | Requested Url: /my/profile
01:19:11 Request    | Time Elapsed: 1.75ms

Or even faster, serving a static file:

01:19:11 Request    | Started 2018-01-11 13:19:11 -07:00
01:19:11 Request    | Status: 200  Method: GET  Pipeline: static Format:
01:19:11 Request    | Requested Url: /dist/main.bundle.js
01:19:11 Request    | Time Elapsed: 329.0µs

Amazingly, because Amber views are compiled in, rendering a template and layout can be significantly faster than serving static files when the application is configured for it5:

01:45:15 Request	| Started 2018-01-17 13:45:15 -07:00
01:45:15 Request	| Status: 200  Method: GET  Pipeline: web Format: html
01:45:15 Request	| Requested Url: /
01:45:15 Request	| Time Elapsed: 371.0µs
01:45:16 Request	| Started 2018-01-17 13:45:15 -07:00
01:45:16 Request	| Status: 200  Method: GET  Pipeline: static Format: js
01:45:16 Request	| Requested Url: /dist/main.bundle.js
01:45:16 Request	| Time Elapsed: 80.01ms

No matter what your application is doing, Amber can do it faster.

Summary

A lot of software is written for the web today. Ruby and Rails showed the world that development doesn’t need to be painful for developers. Crystal is a language where development ease meets speed, and Amber is a framework built on that tradition. Developer friendliness meets raw speed and power, for the web. Or to borrow from Amber’s tag-line: Productivity. Performance. Happiness.

  1. Relevant log 

  2. Relevant log 

  3. Relevant log 

  4. Unedited logs and code samples comparing Rails and Amber here 

  5. In order to render templates and views as fast as possible, the route pipeline must be slimmed down to almost nothing, rendering many features inoperable.