Sinatra: A Real Site

Posted by Joe Yates Mon, 31 May 2010 13:20:00 GMT

The Site

I have a young relative who draws a lot of comic strips, and thinking it would be nice if he could publish them, I looked around for an Open Source system.
I wanted something that was as near to XKCD as possible - latest comic, next/previous and random.
Not finding anything that was both pretty and ultra-simple, I decided to write my own.
The version for my relative is here - the system will be up on GitHub as soon as I finish making it configurable.

Technologies

Web Framework

If you've read the title of this post, you won't be surprised to read that I chose Sinatra.

Database

I decided to use DataMapper as the ORM.
I'm also using SQLite for both development and production.

HTML and CSS

As will be clear from the example site, I didn't work over hard on making a fresh graphic look - all I did was strip out a lot of HTML that was there to make rounded corners, and replaced it with Mozilla and WebKit specific rounding.
I then translated the HTML into HAML and the CSS into SASS (SCSS).

Development

Documentation for Sinatra, DataMapper, HAML and SASS is quite good, so things went quite smoothly. What follows is a list of the phases I went through while developing (on Ubuntu), and especially the bits I had trouble with.

DataMapper

Installation

  $ sudo gem install dm-core

  $ sudo gem install data_objects
  $ sudo gem install data_mapper
  $ sudo gem install do_sqlite3

Using DataMapper in Code

Here is minimal code for one DataMapper model:

  require 'dm-core'
  require 'data_mapper'

  DataMapper.setup({
    :adapter => 'sqlite3',
    :host => 'localhost',
    :username => '',
    :password => '',
    :database => 'db/sidcom_development'
  })

  class Comic
    include DataMapper::Resource
  
    property :title, :text
    property :permalink, :text
    property :created_at, :datetime
    property :updated_at, :datetime
  end
  Comic.auto_upgrade!

The 'Comic.auto_upgrade!' bit handles migrations - at startup, table columns are adjusted to match the declaration in the equivalent class.

Rack

Authentication

This application has a 'public' and an 'admin' area, so I actually created two Sinatra Apps, wrapping one in Rack::Auth::Digest::MD5. A local settings file holds user names and hashed (kinda salted) passwords.

Sinatra

Sinatra apps have access to a number of globals, I used:

Helpers

This app being a little more complex than my first, I decided to implement some Rails-like helpers.
The most important helper is url_for. Sinatra - by design - includes routing in the structure of an app, so it cannot provide pre-cooked methods of the sort.

HAML

I found HAML very easy to start using, what I didn't immediately understand was:

  • how to interpolate variables,
  • how to call functions,
  • and how to include partials.

These are actually all the same problem, but it took me a while to realize that!
In HAML, you can interpolate the Ruby way (with '#{...}') or the HAML way (with '=').

Variable Interpolation

Just use Ruby String interpolation:

  %h1 Hello #{ @name }!

Calling Functions

Use HAML evaluation syntax:

  %h1
    = @name
    !

HAML partials

Again, these can be evaluated as above:

  = haml(:_my_partial, :layout => false)

SASS

Generating CSS

I chose to keep my SCSS files in a subdirectory under my views directory, and generate CSS from them under the static path.

During development, you need to keep the static versions up to date, and to do so, you just need to keep a process running as follows:

  $ sass --watch views/stylesheets:static/stylesheets

Conclusion

Sinatra handles small sites very well. The code can be kept DRY, and if you decide to, Sinatra projects can be quite easily transformed into Rails projects.
Actually, I have doubts whether it was worth doing this project in Sinatra - a stripped-down Rails 3 project would have been just as lightweight and easy to develop. But, then again, it's useful to run a sytem through its paces, to have an idea what areas it can usefully be applied to.

googletest Hello World

Posted by Joe Yates Wed, 26 May 2010 13:17:00 GMT

This is a quick run down of how to get started with using googletest on Ubuntu.

Preparation

Assuming you have a working GCC build environment, all you have to do is install the googletest packages:

  $ sudo apt-get install libgtest0 libgtest-dev

Makefile

The only think of note about the Makefile is that it includes 'libgtest_main' - which implements main() and calls RUN_ALL_TESTS()

NAME = hello-world

LIBS = -lgtest_main

debug: all
run-debug:
	./${NAME}

all: $(NAME).o
	c++ -lstdc++ $(LIBS) -o $(NAME) $(NAME).o

compile: $(NAME).o

clean:
	find . -name '*.o' -exec rm -f {} ';'
	find . -name $(NAME) -exec rm -f {} ';'

$(NAME).o: $(NAME).c++
	gcc -c -I. -o $(NAME).o $(NAME).c++

.c++.o:
	gcc -c -I. -o $@ $<

Source

I've put everything into a single source file 'hello-world.c++' to keep things minimal:

/////////////////////////////
// In the header file

#include <sstream>
using namespace std;

class Salutation
{
public:
  static string greet(const string& name);
};

///////////////////////////////////////
// In the class implementation file

string Salutation::greet(const string& name) {
  ostringstream s;
  s << "Hello " << name << "!";
  return s.str();
}

///////////////////////////////////////////
// In the test file
#include <gtest/gtest.h>

TEST(SalutationTest, Static) {
  EXPECT_EQ(string("Hello World!"), Salutation::greet("World"));
}

Compilation

Just run:

  $ make

Output

This test produces the following:

$ ./hello-world
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from SalutationTest
[ RUN      ] SalutationTest.Static
[       OK ] SalutationTest.Static
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran.
[  PASSED  ] 1 test.

Conclusion

It couldn't really be much simpler!

Object Representation in C++

Posted by Joe Yates Wed, 12 May 2010 13:42:00 GMT

In this post I investigate the way that C++ objects' data and functions are laid out in memory by the two best known compilers: GCC and Microsoft's Visual C++.

Cuil Fail

Posted by Joe Yates Sat, 08 May 2010 22:46:00 GMT

Cuil was launched as a search engine in July 2008. A month earlier, East Sussex county council had tried to warn them of the inevitable outcome if they were to proceed: Cuilfail Tunnel in Lewes, United Kingdom The warning was not heeded. A spokesman said that traffic is now at record levels (for the tunnel, that is).

A Beginner's Sinatra Tutorial

Posted by Joe Yates Wed, 07 Apr 2010 17:54:00 GMT

Today, beginner programmers can and should start with Web applications. I think Sinatra is simple enough for anybody to try out, but I think there aren't enough tutorials about it.

A Rails Programmer's First Django Application 1

Posted by Joe Yates Sun, 04 Apr 2010 15:35:00 GMT

I wrote my first mini Django application - a local history timeline - to get an idea of the differences between Django and Rails.

Editing Javascript in Emacs

Posted by Joe Yates Fri, 02 Apr 2010 10:06:00 GMT

Steve Yegge of Google has open-sourced his Javascript mode for Emacs. The main features are: true program parsing, code highlighting and error highlighting.

A Sunrise and Sunset Time Calculator

Posted by Joe Yates Tue, 09 Mar 2010 09:14:00 GMT

I've created a new Ruby library that calculates sunrise and sunset times.

Show your Git Branch Name in your GNU Screen Status Line

Posted by Joe Yates Tue, 09 Mar 2010 07:57:00 GMT

I think my shell prompt is already cluttered enough, so I decided to show my current git branch in my GNU Screen status line instead.

i18n Internationalization without Rails

Posted by Joe Yates Thu, 04 Mar 2010 23:30:00 GMT

I was unable to find examples of using the Ruby internationalization gem (i18n) outside of Rails apps. In this post I give a minimal "Ciao Mondo!" example.