Lightweight Ruby Web Applications
Fast development
Inspired by posts on Western blogs like “ Clone TinyURL with 40 lines of Ruby ” or “ Clone Pastie in 15 Minutes with Sinatra & DataMapper ”, I decided to try to go through and at the same time describe the whole process of implementing a lightweight ruby web application, from design to deployment.
Instruments
For Ruby, there are a huge number of different tools for quick development. I settled on the following:
Sinatra - DSL for the web. A lightweight convention over configuration framework. Allows you to quickly and easily develop web applications, and is easily complemented by everything that you might need. The basis of our application.
DataMapper - ORM, the main competitor of ActiveRecord. It is inferior in something, surpasses the aforementioned in something, works perfectly with different databases, is easy to configure and integrate.
HAML - HTML for programmers. A markup language that is slightly more beautiful than traditional erb generates clean and valid xhtml. It contains the equivalent for CSS - SASS .
Heroku - Allows you to conveniently and even free (of course, with restrictions) place the resulting application. An optional tool, you can deploy anywhere.
What will we write?
Having chosen the tools, I thought, but what, in fact, to write? And I decided that this would be a tool for organizing my comic strip. In such an application, there is a client functionality, an admin panel, and the generation of an rss feed for subscription, which will allow us to touch on various aspects of development and bring it closer to real tasks. Well, I also love web comics :)
Code parsing
So we come to the most interesting. Almost all of the resulting code is easy to understand and you can find it on github . I recommend opening it to present the big picture, and I would like to dwell on the most important code fragments, and those fragments that caused me some difficulties in implementation.
To begin with, we will analyze the structure of the project, it is very simple:
comics.rb
config.ru
models.rb
public
views
The models.rb file contains models, database configuration, and everything related to working with it. comics.rb contains all the code for the sinatra. Also, by default, Sinatra picks up the views folder containing the views in haml and public with files accessible from the web (javascript, images).
Let's start with the models.
models.rb
- DataMapper.setup(:default, ENV['DATABASE_URL']|| "sqlite3:///#{Dir.pwd}/comics.db")
The database parameters on heroku are contained in ENV ['DATABASE_URL'], if there is no such variable, then create a sqlite database in the directory with the project. You don’t have to edit anything in the source.
models.rb
- classDateTime
- def rfc822
- self.strftime "%a, %d %b %Y %H:%M:%S %z"
- end
- end
The RSS 2.0 specification requires a date in RFC # 822 format. To do this, we add the rfc822 method to the objects of the DateTime class, which will format the timestamp as needed, and we will use it in future in the presentations.
comics.rb
- def protected!
- response['WWW-Authenticate'] = %(Basic)and \
- throw(:halt, [401, "Not authorized])and \
- returnunless authorized?
- end
- def authorized?
- comics = Comics.first
- @auth||= Rack::Auth::Basic::Request.new(request.env)
- @auth.provided? &&@auth.basic? &&@auth.credentials &&@auth.credentials == [comics.login, comics.password]
- end
Simple authentication implementation for Sinatra. Almost entirely taken from the FAQ , the difference is that the username and password are taken from the database, instead of being wired into the source. It is extremely simple to use: it is enough to enter protected in an action requiring authentication !
comics.rb
- get '/rss.xml' do
- content_type 'application/rss+xml', :charset=> 'utf-8'
- @comics = Comics.first
- @strips = Strip.all :limit=> 10
- haml(:rss, :layout=>false)
- end
Return of the rss feed. Change the Content-Type, and add: layout => false so that the feed does not render in layout.
Now a few hints in the views.
layout.haml
- %title= "#{@comics.title} — #{@strip.title}" rescue@comics.title
If you do not use an exception mechanism, when an empty variable @strip we stop error NoMethodError , since the class nil no method title . In rub, such things must always be kept in mind.
models.rb
- class Strip
- # объявляем property
- defnext
- Strip.first(:created_at.gt =>self.created_at, :order=>[:created_at.asc])
- end
- def previous
- Strip.first(:created_at.lt =>self.created_at)
- end
- def get_id
- self.id
- end
- default_scope(:default).update(:order=>[:created_at.desc])
- end
layout.haml
- - tonext = "/#{@strip.next.get_id}" rescue "#"
- - toprevious = "/#{@strip.previous.get_id}" rescue "#"
The mechanism is understandable - search for the next and previous strip for the one that we are looking at now, and displaying links to them in the view. Why did you have to make a separate get_id method instead of directly using an existing id ? The fact is that if we look at the strip that is currently last, the next method will return nil . And nil, in turn, has an id method , which I think for a long time will return "4". You can verify this yourself by experimenting with irb.
You can end this with a parsing, I’ll be happy to answer any questions about the code, and I will respond to criticism in the comments.
Deployment
The written application will easily start like any other application on the Sinatra with the ruby comics.rb team . But we want to show it to the world, and Heroku will help us with this. We register on heroku, and install gem heroku on our local machine. Now we write a config for Rack:
config.ru
- require 'comics'
- run Sinatra::Application
The next step is to create an application on heroku, and push the code there. Let's agree that the application is already in your git repository:
heroku create comics
git push heroku master
It remains only to fill the database with initial data, for this in models.rb there is an install method :
heroku console
install
That's all, you can go to the address that gem issued when creating the application, and if everything is done correctly, then enjoy the result.
References
Comics on GitHub
Demo on Heroku ( Admin , password on request in Habrahta).