Fiddling Around With Sinatra
JSFiddle is a site that’s been around for a while now. It allows you to create a ‘fiddle’ which is basically a self contained page of HTML, CSS and JavaScript. These can be used to show off a particular technique or bug or just show off your smooth markup skills.
In the last year, a couple more sites have appeared that do a similar thing. First there was Dabblet and just recently Code Pen was launched. I thought it might be fun to have a go at cloning this type of app in Sinatra. It should be fairly straightforward as Sinatra uses Tilt to render views. This gives you access to loads of different rendering engines, as well as Sass and Less for CSS preprocessing.
My idea is to create a simple app called Riddle (A Ruby flavored Fiddle) that allows users to create a ‘riddle’ which is basically a self-contained piece of HTML, CSS and JavaScript. To start with, I am going to limit users to using either markdown or plain old HTML for markup and SCSS or vanilla CSS for styling. Hopefully, this will help to show how Sinatra’s view helpers work and how to serve CSS and JavaScript using Sinatra.
Mock Up
I wanted to get the site architecture of the app right, so I concentrated on the views first to show how a user would navigate around the app. We want a start page that lists all the riddles that have been created and lets the user click to view one. There should also be a button at the top to create a new riddle. This should display a form where you can add the the HTML, CSS and JavaSript. Once the user clicks on save, they will be taken to the newly created view.
So, let’s get coding! We need to start off by creating an empty text file called main.rb and add the following lines of code:
[gist id=’3359802′]
What’s going on here? This is a full Sinatra app, all in one file. First of all, we require the sinatra and slim gems. Slim is my preferred view engine, but feel free to use another (such as ERB or Haml). Next, we have the three routes that we need – index
, new
and show
– all of which have their own view, rendered with slim. Then, at the bottom of the page I’ve incldued those views inline (cool Sinatra feature!). The first is a standard HTML5 layout. Following the layout, there are three holding pages that just describe what that page will eventually do.
Give it a try – start a server up by typing ruby main.rb
into a terminal and then navigate to ‘http://localhost:4567’ to see it working.
I love this about Sinatra – you can mock up the architechture of a site quickly to demonstrate how a user will navigate around the app. Using this feature, it’s simple to make sure it fits with your expectations before you start doing any of the heavy lifting.
Everything is working fine, now it’s time to start create some riddles – to that, we’re going to need a database …
Creating Some Riddles
Now we have the app’s structure in place, it’s time to create the database that will hold the information about the Riddles. To do this, we first need to configure the database. I’m going to use DataMapper as the ORM, which is really easy to configure in Sinatra. Just add the following lines to the top of main.rb:
[gist id=’3331618′]
We need to require quite a few extra gems to get DataMapper working. This is mainly because it is quite modular by design – a good thing – as it means you can pick and choose which modules you want to include on a per project basis. We’re going to make use of the migrations module later to create the database. We also need to configure the database. This is done inside the configure
block where the database used is set as an environment variable, or simply a SQLite file called ‘development.db’ in the root directory of the app. This sets us up for using Heroku for deployment later, as their databases are stored in an environment variable called ENV[‘DATABASE_URL’]. If this isn’t present, then we must be working locally, so the SQLite file will be used instead.
Our next job is to create the Riddle class.
[gist id=’3331622′]
This is all fairly standard DataMapper stuff. You need to include the DataMapper::Resource
module and then set the id
property. Next, we have the properties of each Riddle – they will have a title, some HTML, CSS and JavaScript. After this, I’ve added an extra method that sets the title to “Yet another untitled Riddle” if an empty string is entered. DataMapper has a default option, but this is no good when dealing with forms on the web because any empty fields go into the database as empty strings, rather than nil. This is one way of dealing with it.
It’s really important to remember to use the DataMapper.finalize
declaration at the end of all your classes. Things go badly if you don’t do this (can’t tell you how many times I’ve forgotten this!)
It’s time to migrate the database. This is a simple job, all we need to do is go into a terminal and navigate to the folder where our riddle app resides and then launch an irb session:
$ irb ruby-1.9.2-p180 :001 > require './main' ruby-1.9.2-p180 :001 > DataMapper.auto_migrate!
Now the database has been created, we need to update our routes so that we can actually create a riddle.
[gist id=’3331626′]
We also need to update our show
view. We’re only going to display what has been saved, rather than displaying it as actual HTML, CSS and JavaScript for now, just to check that everything is working:
[gist id=’3331629′]
The change to the form adds a name to the form elements. These are picked up by our Sinatra application and used in the following line to create the riddle:
riddle = Riddle.create(params[:riddle])
Go ahead and give this a test and you’ll see that it’s working – everything is being saved to the database and then being displayed on the show
page!
Before we go on, let’s just update the index
page so that it lists all of the riddles that have been created in reverse order. Simply add a line to the handler to get all the riddles in reverse order:
[gist id=’3331631′]
Then we just need to update the index view so that it shows all the riddles (assuming there are any):
[gist id=’3331633′]
Brilliant! Restart the server (press control+c in the terminal then retype ruby main.rb
), then have a quick look at ‘http://localhost:4567’ to see a list of all your creations so far. Now we just need to get them working properly….
Putting It All Together
Now that we know that the riddles are being saved, it’s time to think about how they are displayed. Let’s update the ‘@@show’ view so that it shows the title of the riddle and displays the HTML underneath:
[gist id=”3331634″]
Give this a test and it should display the HTML correctly. We can do a little better than this by utilizing Tilt to use Markdown to display the markup instead. This will allow people to use the easier markdown language. The good thing about this is thsat, if you enter just vanilla HTML, markdown will still display it. This allows the user to use choose to use either markdown or HTML. To use markdown, you need to include a library that supports it, I’m going for Red Carpet:
require 'redcarpet'
Then all you need to do is update the show view to use the Sinatra helper to display the markup using markdown instead:
[gist id=’3331636′]
Give this a try – here’s some markdown you can enter as a quick test:
# Hello ## world * one * two * three
Now that we can display the markup, it’s time to get the styles working …
We’re going to insert the riddle’s styles as if it’s an actual stylesheet in the head of the document. We need to add the following line to the @@layout
:
- if @riddle link rel="stylesheet" href="/css/riddle/#{@riddle.id}/styles.css"
This checks to see if there is a @riddle object, then links to a stylesheet that includes the id of the riddle. This stylesheet doesn’t actually exist as an actual file – it’s in the database – but we can fetch it as if it is by using Sinatra’s cool and simple routing mechanism:
[gist id=’3331640′]
When the link gets called, Sinatra will serve the styles held in the database using the SCSS helper. The reason I’ve used SCSS is that I’ve added the id ‘#riddle’ in front of the styles. The SCSS engine will now namespace all of the styles and place the ‘#riddle’ id in front of them all, meaning that these styles will only affect anything inside the #riddle div (notice that the HTMl markup for the riddle was placed inside a div with an id of ‘riddle’ in the show view. In other words, these styles will only affect the riddle!)
Give this a quick test and you should find it working as expected. What’s cool is this looks like it’s a link to an external stylesheet – you can even view the styles by going to ‘http://localhost:4567/css/riddle/9/styles.css’. It also means that the user can choose to use plain CSS or SCSS.
Last of all, let’s get the JavaScript working. In a similar way to the CSS, we’re going to make this look like it’s coming from an external file, so we need an extra line in the @@layout:
- if @riddle link rel="stylesheet" href="/css/riddle/#{@riddle.id}/styles.css" script src="/js/riddle/#{@riddle.id}/script.js"
This will load a script that refers to the riddle’s id if a @riddle object exists. Now we just need the handler to deal with this:
[gist id=’3331642′]
We need to do a bit more here. Earlier, when we were displaying the styles, Sinatra knew that the content type was a stylesheet because we were using the SCSS helper. This time we need to declare explicitly that the file is JavaScript. We also need to make sure that the layout isn’t used again.
Give this a test and it should be working great. In no time at all, we’ve managed to build a way of creating pages using HTML, CSS and JavaScript. As an added bonus, it supports markdown and SCSS out of the box! And all of this in just over 100 lines of Ruby code!
But we can do more! In the next part I’m going to ramp things up by unleashing the full force of Tilt and letting the user choose any of the view engines … including CoffeeScript! I’ll also include the ability to edit Riddles after they’ve been created. Hope to see you soon in part 2!