In this post I’ll continue what I started in the previous post: using Sass. We’ll explore a different option around how to use this engine. Also, while I’ve focused on changing the CSS and HTML elements of our application, I’ve done very little with the JavaScript portion. So here we’ll explore that a little bit as well.
In the last post you converted to using Sass (specifically the SCSS portion of it). As I mentioned in that post, SCSS was simply introduced as a syntax for Sass to use, similar to CSS but with some additions. This time, let’s use Sass more directly, as a compilation aspect.
We’re going to change a lot here so you might want to make a branch in Git in case you easily want to go back to what you were doing before.
Using Sass, Not Just SCSS
I’m going to be using the Sass gem’s Sass::Plugin::Rack functionality. Sass was given native support for all Rack-based frameworks and this plugin is what you use to take advantage of that. For every request, the plugin looks for Sass files in the public/stylesheets/sass directory and compiles them to CSS files in the public/stylesheets directory, if necessary. If the file has already been compiled, then nothing happens.
To get this working, change your config.ru file to look like this:
1 2 3 4 5 6 7 8 |
require './app' require 'sass/plugin/rack' Sass::Plugin.options[:style] = :expanded use Sass::Plugin::Rack run Project::TestApp |
With this in place, you can (and must) put your stylesheets into the public/stylesheets/sass/ directory, and they will be automatically compiled to CSS when a call is made to serve up CSS. So first make a directory called public/stylesheets/sass.
If you still have the public/css directory hanging around (perhaps with a style.css file in it), delete that directory. Now use Git to move your scss file (from the views directory) to the sass directory you just created, giving it the extension .sass in the process:
git mv views/style.scss public/stylesheets/sass/style.sass
Incidentally, I will note that you can configure a different path for where Sass will look for the stylesheets. Specifically, you could include this in config.ru:
1 |
Sass::Plugin.options[:css_location] = "./css" |
While that would have allowed me to be consistent with my previous location in the public folder, I opted to not go this route for a reason I’ll discuss a bit later. That said, the reason I did this is not a technical one so feel free to choose your own path.
Now we come to an implementation detail: the structure of Sass is different than SCSS. So open your style.sass file. Put the following in it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$success: #02d330 header background: #cccccc border: 1px solid text-align: center letter-spacing: 0.3em h1 font: 42px normal Georgia, "Times New Roman", Times, serif .notice color: $success text-align: center border: 1px solid .error color: red |
Assuming you followed the last post in this series, you might want to compare this sass version with your previous scss version. What you will probably notice is that you are just further tightening up how rules are expressed.
Here things can get confusing if you don’t know what’s going to happen next. Given this default structure, when style.sass is compiled, it will be compiled to style.css and that generated file will be put in your public/stylesheets directory. What this means is that you should change your layout.slim file so that the call to the css file in the head section looks like this:
1 2 3 |
... link href="/stylesheets/style.css" media="screen" rel="stylesheet" ... |
This will now serve up your css file correctly. Any css files will be generated so you will want to add this to your .gitignore file:
public/stylesheets/*.css
The reason for that is to prevent checking generated CSS files into git during development.
Test it out. Is it working? If so, you can also get rid of this now-superfluous route in app.rb:
1 2 3 |
get '/css/style.css' do scss :style end |
As you get rid of that, keep in mind that what that route was doing was very simple: it was telling Sinatra that if a style.css file was referenced, instead of simply sending it to the browser, that file should be processed by the Scss engine (which is really the Sass engine using SCSS as a syntax).
Okay, so now why did I go this stylesheets directory path and why not just keep that route above in place? It seems easier, right? The reason is that what I’m doing here takes you a step closer to how Rails does things with its asset pipeline, including the directories that are used and the way that elements, like stylesheets, are generated from a compilation mechanism. I won’t go into details here but suffice it to say that our Sinatra application — at least for the web-specific components — is starting to look at least a little more like how a Rails app would.
At this point: try to deploy to Heroku. Everything I’ve done here with you should work just fine. I say that cautiously because hosting Sass (or any dynamically generated asset) on Heroku used to be a challenge, as most of the libraries that generate browser-ready versions of these assets expect to have write access to the filesystem. I have tested this out with my own application and everything works fine but there was a time when the public folder on Heroku was only readable and thus you could not compile resources into it.
Coffee With Your JavaScript?
Now let’s look at a way to replace our JavaScript. A lot of people are talking about CoffeeScript. I’m not one of the them and I’m not even sure I like CoffeeScript all that much. I don’t mind Slim and Sass because they are basically slight variations on the existing technologies of HTML and CSS and thus, to me, don’t hide too much. CoffeeScript covers up a lot of what JavaScript is actually doing. That may be great if you know JavaScript really well. If you don’t, that can be a problem.
That said, jQuery is a way to abstract complex JavaScript functionality and I’ve never had a problem with that. So I don’t know. What I can say is you are getting into a slightly different world if you go the CoffeeScript route. Nevertheless, I think it’s good to know about it and good to try it out and make sure you understand how to use it with Sinatra in case you do want to use it as your main scripting language.
As with HTML to Slim and CSS to SCSS, there are coverters out there to help you along, such as js2coffee. The usual caveats apply with converters — but even moreso with something like this because you are converting a scripting and/or programming language, which tends to be fraught with peril.
With what we’re going to do here, you can put either CoffeeScript (.coffee) or regular Javascript (.js) files in a public/javascripts/ directory and those scripts will be available in your application. So change your public/js directory to public/javascripts. Again, this is also to start matching up what Rails does with its asset pipeline. Now change your application.js file to application.coffee.
git mv public/javascripts/application.js public/javascripts/application.coffee
Here is the startdate calculator in CoffeeScript:
1 2 3 4 5 6 7 8 9 10 |
@convertToCalendar = (form) -> origin = new Date("July 5, 2318 12:00:00") stardate = eval(document.forms.computeStardate.stardateValue.value) stardatesPerYear = stardate * 34367056.4 milliseconds = origin.getTime() + stardatesPerYear result = new Date() result.setTime milliseconds form.calendarValue.value = result true |
A few things to note. If you do a conversion from the original JavaScript, you might find that the “eval” function is rendered as “eval_”. I have no idea why but just change it back to “eval”. It certainly won’t work otherwise. Also note the @ in front of convertToCalendar function. A lot of converters won’t put that in place for you. What that does is make the function available as part of the global scope so that it can be called from the event methods in your markup page. If you don’t do that, your JavaScript will never be called.
So that’s the basic details in place. Now in order to actually get this to work, we’re going to need the rack-coffee gem. Add that to your Gemfile:
1 2 3 4 5 6 7 8 9 10 11 |
source :rubygems ruby '1.9.3' gem 'sinatra', '1.3.3' gem 'sinatra-reloader' gem 'sinatra-flash' gem 'thin' gem 'slim' gem 'sass' gem 'rack-coffee' |
And, as before, make sure to update your Gemfile.lock:
bundle update
In app.rb, make sure you add a require for your new gem:
1 2 3 4 5 6 7 |
require 'sinatra' require 'sinatra/reloader' require 'sinatra/flash' require 'slim' require 'sass' require 'rack/coffee' ... |
In your config.ru file, add just one line:
1 2 3 4 5 6 7 8 9 10 |
require './app' require 'sass/plugin/rack' Sass::Plugin.options[:style] = :expanded use Sass::Plugin::Rack use Rack::Coffee, root: 'public', urls: '/javascripts' run Symbiont::TestApp |
As with the stylesheet reference change in layout.slim, we have to change the javascript reference as well since we are now using “javascripts” rather than “js”. So make this change in layout.slim:
1 2 3 |
... script src="/javascripts/application.js" ... |
With that, go to http://localhost:9292/stardate and you should see that the stardate calculator works just as it did before.
You should most definitely redeploy to Heroku at this point and make sure the JavaScript is working.
Incidentally, all of this may seem more confusing than it would otherwise be because I essentially started you down one path: HTML/ERB, CSS, and JavaScript. Along the way we want to Slim, SCSS then Sass, and finally CoffeeScript. That meant we had to periodically add gems, get our execution environment in sync, change files and directories and so on. Obviously had I made decisions about technologies I wanted to use at the start, there would have been less churn.
And that’s at least part of what I hope I did here: give you a glimpse into the possibilities that are available to you and a way to structure them.
This may be a good place to end this series. You have a way to use a simplified web development solution (Sinatra) and you know how to deploy it to the cloud (Heroku). You know how to use the common elements of web development (HTML, CSS, JavaScript) with this solution. You saw how to make pages dynamic by sending parameters to them or having embedded logic within them. You saw how to use alternative technologies for all of the common elements (Slim, Sass/SCSS, CoffeeScript).
For me, my next steps will be to take what I’ve learned here and deploy to a different platform, most likely OpenShift. I also want to get into a database-backed web application. This can prove a little challenging because how you use a database on your local environment can differ quite a bit when you attempt to deploy to a remote solution like Heroku. Anyway, this series may pick up again in a bit after I have a solution worked out.
If I’d written a wish list of things I’d like to learn and play around with then it would pretty much have been the contents of this series.
Completed the lesson, deployed to Heroku with no problems and all set to start diving in and learning more about what I’ve actually been doing
Many many thanks for this, I’ve done a blog post on it so hopefully more people will come along and use this great resource
http://expectedresults.blogspot.com/2013/01/stretching-ol-blue-eyes-on-rack-to-get.html
Hi Jeff,
congratulations on an excellent series of articles. This is THE BEST Sinatra tutorial on the internet,
both in terms of content and presentation, and it works!! I’ve successfully deployed the example app on Heroku
which is very gratifying. Thank you so much.
I look forward to future articles and would be interested in your reasons for considering different deployment
options. I hope you consider DataMapper for your database, it seems quite easy to use.
Best wishes