Delving into more Ruby Syntax and Understanding Rack & Sinatra
Last time I sprinted through a huge range of technologies based around or connected with ruby:
- heroku
- sinatra
- passenger
- haml
- sass
- mongodb (& MongoHQ)
- ajax
If you’re interested in those technologies, check out the last post.
I was very pleased to have got a fully working landing page live and collecting emails in a NoSQL database (check it out at Quotally.com). However, having gone through those technologies so fast I felt that I had perhaps overlooked some of the details of how these technologies were actually working and how they were slotting together.
Therefore this week I am aiming to get my head around what “Rack” is and how the Sinatra framework is working under the hood. I hope by documenting my learning that you can also learn or you can point out some parts where I am going wrong :)
Delving into more Ruby Syntax
In the first week, I had my first experiences of Ruby syntax and I was very impressed. This week I have chosen to go further into the syntax and I am using Why’s (poignant) guide to ruby.
Blocks & block arguments
Coming from PHP, blocks look somewhat familiar but seem to be written slightly differently, with the code inside the block starting right after the opening curly brace:
1 2.times { puts "Hello World!" }
In reading around, I have in fact learned that the syntax must be done this way, and the following is not allowed although it is common to see things like it in PHP:
1 # Won't work 2 2.times 3 { 4 puts "Hello World!" 5 }
However, I see that the curly braces can also be traded for do … end. Got to love the code they come up with in Why’s poignant guide:
1 loop do 2 print "Much better." 3 print "Ah. More space!" 4 print "My back was killin' me in those crab pincers." 5 end
Block arguments are a set of variables at the start of the block, surrounded by pipe characters. I was at first, expecting this to work:
1 name = "Joel" 2 2.times { |name| puts "Hello #{name}!" }
However, the output was as follows:
1 Hello 0! 2 Hello 1!
Not quite what I was expecting :) So I have just learned that the parameters put into the blocks are not explicitly named variables which already exist but are rather parameters from the method which is calling the block. Rubyists please correct me if I’m wrong here :)
Questions for experienced Rubyists
I’d love to know what is preferred between { } and do … end – is it simply do … end for longer blocks?
I’d also love to see how you would go about changing my last bit of code to make it do what I expected, which was to print “Hello Joel!” twice.
Ranges
Funnily enough, ranges are something pretty new to me and they seem like they could be handy at times. Ranges are numbers or other characters within parenthesis with two dots or three dots in between. Two dots indicates the last value in the range is included, whereas three dots indicates it is excluded. An example:
#!ruby (1..3).each do |x|
1 puts "Hello!" 2 puts "Times said Hello: #{x}!"
end
Having seen this syntax, I went looking for similar things within PHP. I came across the range() function, which can be used as follows:
1 foreach (range(0, 12) as $number) { 2 echo $number; 3 }
Not bad, but I have to say the ruby syntax wins for me once again.
That’s enough syntax learning, let’s get onto something else.
Understanding Rack
Last time, I got a simple web application which allows people to submit their email and it be saved in a NoSQL MongoDB on MongoHQ. It was hosted on heroku, and as such I found that I needed to use “Rack”. For this reason, I managed to get Rack working and use the Sinatra framework to build the application I wanted. However, I went over this rather quickly and felt that I had not fully understood the role Rack was playing in this setup. For this reason, today I’m delving further into Rack.
What is Rack?
The first thing I wanted to know is what exactly is Rack. The obvious thing to do was go back to the Rack website and find a definition:
A Rack application is an Ruby object (not a class) that responds to call. It takes exactly one argument, the environment and returns an Array of exactly three values: The status, the headers, and the body.
This makes a lot of sense. So a Rack application is simply a Ruby object which returns an array of values.
A very simple Rack application
I then found a handy tutorial which had a few examples of Rack Middleware, the first of which was a basic Rack application:
1 use Rack::ContentLength 2 3 app = lambda { |env| [200, { 'Content-Type' => 'text/html' }, 'Hello World'] } 4 run app
The above app can actually be run by saving the code to a file called “basic_rack.ru” and running the following in Terminal:
1 rackup basic_rack.ru -p 3000I found another useful article, and the following I think sums up Rack nicely:
Rack is a piece of software, but it’s small, and its main purpose is to enforce an interface specification.
Now that we know that Rack is an interface specification, it makes complete sense that Heroku uses Rack as the way to specify this specification. Let’s move on to understanding Sinatra.
Understanding Sinatra
To understand Sinatra, I went back to my code for the simple application I built last time. Looking at the config.ru file we can see how Sinatra on Rack is triggered:
1 require 'hello' 2 run Sinatra::Application
Delving into the Sinatra code from GitHub, I found the Response class which is what finally delivers the application. It can be seen that this similarly returns an array with http status code, header and body:
1 # The response object. See Rack::Response and Rack::ResponseHelpers for 2 # more info: 3 # http://rack.rubyforge.org/doc/classes/Rack/Response.html 4 # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html 5 class Response < Rack::Response 6 def finish 7 @body = block if block_given? 8 if [204, 304].include?(status.to_i) 9 header.delete "Content-Type" 10 [status.to_i, header.to_hash, []] 11 else 12 body = @body || [] 13 body = [body] if body.respond_to? :to_str 14 if body.respond_to?(:to_ary) 15 header["Content-Length"] = body.to_ary. 16 inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s 17 end 18 [status.to_i, header.to_hash, body] 19 end 20 end 21 end
I decided at this point that it was not worth looking through more of the code: what would rather be more useful is a discussion of the suitable applications of Sinatra over other Ruby frameworks. This is particularly relevant right now since the “coming soon” page to collect interested people’s emails has been built and I am now looking to start work on the full application.
The difference between Sinatra and Ruby frameworks
Sinatra is not a Model-View-Controller (MVC) based framework for creating websites. It is rather a simple yet powerful Domain Specific Language for creating RESTful applications by defining actions and how the application will resond to them.
To remind ourselves, let’s look at a very simple Sinatra application:
1 require 'rubygems' 2 require 'sinatra' 3 4 get '/' do 5 "Hello world" 6 end
As can be seen, the code is minimal, and the entire web application could easily be deployed as a single file.
What should Sinatra be used for?
I think an article I came across answered this pretty well:
API implementations, quick minimal applications, and web development that does not want or need things that are included in Rails, like ActiveRecord. Control panel mini-applications, or perhaps widgets.
Taking a look at some of the responses of Rubyists using Sinatra, here are some other insights:
In general, Sinatra is just fun to use as it provides the most direct and clean route to get an idea or a piece of code on the web.
I always reach for Sinatra when I want to prototype an idea. It’s easy to get something in place so I can iterate on the idea quickly. Sinatra is great for deploying prototypes too!
Sinatra is a great tool to accomplish small tasks as a minimal layer on top of http protocol.
Sinatra’s lean DSL encourages me to produce similarly beautiful code. Sinatra never gets in my way. Sinatra goes out of its way to make my life simple.
Sinatra lets you create REST based services with minimalistic approach which is ideal of mobile back ends.
So it seems that most people agree it’s great for small applications, prototyping or for creating an API. That gives me an idea…
Combining Sinatra and a framework such as Rails by using API-driven development
This idea is one I’ve been a fan of for a while, and I actually put it into practice at OnePage, though that is written in PHP.
The idea is to build the API for your application right from the offset, and build your web app to be the first application which makes use of your API. By creating your API and your web app alongside each other, I believe there are at least three immediate advantages:
- The methodology naturally separates code and should improve code quality
- If your own app uses your API, then it’s going to have to be solid
- This method allows high agility, since you can release your public API almost instantly at any point in time
I have spoken to some people and it is a concept which can be hard to get your head around, but having played with Sinatra and also wanting to experience a more substantial Ruby framework such as Rails or Merb, this seems like a perfect way forward which will still give me the experience of the Ruby language whilst working with Sinatra and also the magic and opportunity to discover the reason for the hype around a framework such as Rails.
What do you think of this approach? I would love your comments.
Next time
Next time I plan to reveal more about the specific application I’m building in order to learn Ruby, and I will be looking into more syntax in addition to assessing the various frameworks out there. Do you think it’s too soon to delve into a framework? Let me know your thoughts.
Please comment :)
The reason I am doing this blog is that hopefully by documenting my journey from PHP to Ruby the information can be useful to others, whether starting Ruby from scratch, coming from another language or even those experienced in Ruby. In addition to this, a side-effect is that it really accelerates my learning of the language due to experienced folks chiming in with their comments and insights. Please comment and let me know how I can improve, or simply what you thought or would like me to blog about next time :)
Just before I finish, please help me decide how to format my code snippets from now on:
Useful links
Some useful links I came across in this session:
- My last post on heroku, sinatra, passenger, haml, sass, mongodb & ajax
- Quotally, the app I’m building in this process
- My first post on getting started with Ruby syntax
- Why’s (poignant) guide to ruby
- The Rack website
- Ruby Rack Middleware Tutoriall
- Anatomy of a Ruby Web Application
- Sinatra, a Ruby web framework, and Why it Matters
- 20+ Rubyists are using Sinatra – Do you?
Feel free to keep up with my Ruby Delicious Bookmarks too.



