Thursday, June 9, 2011

Adding BlueprintCSS to Rails 3.1 and the Asset Pipeline

I have a new Rails 3.1 (release candidate) application to which I want to add BlueprintCSS. But Rails 3.1 comes with the new "asset pipeline." To install BlueprintCSS, one simply copies over the blueprint folder and references it with a stylesheet_include tag. But in the asset pipeline, where should it go? You have three options:
  1. /app/assets/stylesheets
  2. /lib/assets/stylesheets
  3. /vendor/assets/stylesheets
As far as I know, the directory /app/assets is for assets you create specifically for your application, the directory /vendor/assets is for assets from outside "vendors", and /lib/assets is for I don't know what. So we'll put BlueprintCSS in the /vendor/assets folder, since that makes the most sense to me.

Step one: Download BlueprintCSS

Download the latest version of Blueprint, either from the GitHub page or from BlueprintCSS.com. I let it download to my default location, which is ~/Downloads/.

Unpack the tarball

Now we need to unpack the downloaded tarball. I'm going to unpack it to my home directory (Linux represents this by the tilde symbol, "~"). Go to the Downloads directory containing the tar file, use the tar command, along with -x (extract), f (the file to extract), and C (the directory to which the extracted files should go).
$ cd ~/Downloads/
$ tar -xf joshuaclayton-blueprint-css-v1.0.1-8-g9bf9513.tar.gz -C ~

Copy the "blueprint" folder

Now the extracted folder is in the ~ directory (same as /home/andy/). Next, we want to copy (cp) just the stylesheet folder (called "blueprint") to our /vendor/assets/stylesheets/ directory:
$ cd ~
$ cp -r joshuaclayton-blueprint-css-9bf9513/blueprint/ wikidiscography/vendor/assets/stylesheets/
Note that we use the -r flag for "recursive," since it's a directory.

Add the stylesheets to the Rails app

Finally, we need to tell Rails about the new stylesheets. Add the following three lines to the file /app/views/layouts/application.html.erb:
<%= stylesheet_link_tag 'blueprint/screen', :media => "screen, projection" %>
<%= stylesheet_link_tag 'blueprint/print', :media => "print" %>
<!--[if lt IE8]><%= stylesheet_link_tag 'blueprint/ie' %><![endif] -->
Make sure to add them before the "application" css file. This will ensure that any changes to that file will override Blueprint (which is what we want). My application.html.rb now looks like this:
<!DOCTYPE html>
<html>
<head>
  <title>Wikidiscography</title>
  <%= stylesheet_link_tag 'blueprint/screen', :media => "screen, projection" %>
  <%= stylesheet_link_tag 'blueprint/print', :media => "print" %>
  <!--[if lt IE8]><%= stylesheet_link_tag 'blueprint/ie' %><![endif] --> 
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>
Now reload your application page, and view the source code (Ctrl-U on PCs) in the browser. At the top of the page source you should see links to the blueprint stylesheets.

Asset Pipeline did the work

This is neat because without our having to do any extra coding, Rails found the stylesheets from /app/assets/stylesheets/ and from /vendor/assets/stylesheets. This keeps our outside "vendor" stylesheets in a separate folder from the CSS we'll eventually write ourselves, and allows us to do a simple drop-in replacement of the Blueprint folder when the next version of BlueprintCSS is released.

What about Sass?

Rails 3.1 ships with Sass as the default CSS framework. But Blueprint is written in pure CSS, not Sass. However, that's not a problem, because the latest Sass syntax (files with a .scss) extension is a superset of CSS, so any .css file is also a valid .scss file. That means we don't have to do anything to Blueprint. It Just Works, and we can still use Sass for our own stylesheet code.

Thanks goes to Joshua Clayton for a great product.

Sunday, June 5, 2011

Installing Rails 3.1 in a new RVM environment on Ubuntu 10.10

I have RVM installed, and I'm running Ubuntu 10.10. I want to start a new Rails 3.1 (release candidate) project, and I want to do it within the protective confines of RVM.

Start from outside of RVM

To show how to do this from the very beginning, we can leave the RVM environment completely and go back to the system Ruby installation:
$ rvm system
$ ruby -v
ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]

Switch to Ruby 1.9.2

Switch to Ruby 1.9.2 and then confirm by asking for the Ruby version again:
$ rvm 1.9.2
$ ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [i686-linux]

Now list all the currently available gemsets:
$ rvm list gemsets

rvm gemsets

   ruby-1.9.2-p180@global [ i386 ]
=> ruby-1.9.2-p180 [ i386 ]
As you can see, we have two existing gemsets, and the arrow indicates the one in which we're currently operating.

Create a new gemset

We need to create a new gemset in the RVM ruby 1.9.2 environment:
$ rvm gemset create 'another_project'
'another_project' gemset created (/home/andy/.rvm/gems/ruby-1.9.2-p180@another_project).
$ rvm list gemsets

rvm gemsets

   ruby-1.9.2-p180@another_project [ i386 ]
   ruby-1.9.2-p180@global [ i386 ]
=> ruby-1.9.2-p180 [ i386 ]
As you can see, we created the new gemset with the command rvm gemset create. It automatically created it within the Ruby 1.9.2 environment, because we had already asked RVM to use Ruby 1.9.2 before (with the command rvm 1.9.2). However, we're still not in that gemset; to use it, we have to ask RVM to make it the current gemset:
$ rvm 1.9.2@another_project
$ rvm list gemsets

rvm gemsets

=> ruby-1.9.2-p180@another_project [ i386 ]
   ruby-1.9.2-p180@global [ i386 ]
   ruby-1.9.2-p180 [ i386 ]
This shows that we switched gemsets by passing RVM the argument ruby_version@name_of_gemset. Now that we're in the new gemset, we can confirm that it's a clean environment by asking for a list of installed gems:

$ gem list

*** LOCAL GEMS ***

In this clean environment we can create our new Rails project, and it will be insulated from the rest of the system. This is useful because we'll always know what Ruby version we're using, and we'll always know what Gems we're using. In the old way, all the Gems were installed together, and each Rails project picked out the gems it needed from the pile. In the RVM way, each Rails project gets its own silo of gems. If we don't need a particular gem that appears, we can just delete it without worrying that it might be in use by some other Rails project.

Update RubyGems

Next step is up update RubyGems, which recently underwent some major revisions.
$ gem --version
1.6.2
$ gem update --system
Updating rubygems-update
Fetching: rubygems-update-1.8.5.gem (100%)
Successfully installed rubygems-update-1.8.5
Installing RubyGems 1.8.5
RubyGems 1.8.5 installed

=== 1.8.5 / 2011-05-31

* 2 minor enhancement:

  * The -u option to 'update local source cache' is official deprecated.
  * Remove has_rdoc deprecations from Specification.

* 2 bug fixes:

  * Handle bad specs more gracefully.
  * Reset any Gem paths changed in the installer.


------------------------------------------------------------------------------

RubyGems installed the following executables:
 /home/andy/.rvm/rubies/ruby-1.9.2-p180/bin/gem

RubyGems system software updated
$ gem --version
1.8.5

Install Rails


Now I can install gems as I normally would (including via bundler), and RVM will keep them in this particular project's gemset, isolated from the rest of the system.

However, when I try installing rails as my first gem, I get the following error:


$ gem install rails -v ">=3.1.0rc"
ERROR:  While executing gem ... (Errno::EACCES)
    Permission denied - /home/andy/.gem/specs


RVM is trying to create the directory "specs" inside the .gem folder, but doesn't have the necessary permission. This may be because of an incorrect way I installed RVM. But to fix it, I will just create the folder manually and give it liberal permissions:
sudo mkdir ~/.gem/specs
sudo chmod 777 ~/.gem/specs
This is hack, but it works. Now I can install gems without issue.

Again try installing Rails

In this clean and updated environment, we can now install Rails:
$ gem install rails -v ">=3.1.0rc"
Fetching: multi_json-1.0.3.gem (100%)
Fetching: activesupport-3.1.0.rc1.gem (100%)

[...snipped...]

Fetching: bundler-1.0.14.gem (100%)
Fetching: rails-3.1.0.rc1.gem (100%)
Successfully installed multi_json-1.0.3
Successfully installed activesupport-3.1.0.rc1

[...snipped...]

Successfully installed bundler-1.0.14
Successfully installed rails-3.1.0.rc1
30 gems installed
Installing ri documentation for multi_json-1.0.3...
Installing ri documentation for activesupport-3.1.0.rc1...

[...snipped...]

Installing ri documentation for bundler-1.0.14...
Installing ri documentation for rails-3.1.0.rc1...
Installing RDoc documentation for multi_json-1.0.3...
Installing RDoc documentation for activesupport-3.1.0.rc1...
ERROR:  While generating documentation for activesupport-3.1.0.rc1
... MESSAGE:   incompatible character encodings: UTF-8 and ASCII-8BIT
... RDOC args: --op /home/andy/.rvm/gems/ruby-1.9.2-p180@another_project/doc/activesupport-3.1.0.rc1/rdoc lib --title activesupport-3.1.0.rc1 Documentation --quiet

It crashed with a big error at the end: "incompatible character encodings: UTF-8 and ASCII-8BIT".

I have no idea what that is or why it's happening. First let's try running the installer again, and if that doesn't work, we'll pass it the argument --no-rdocs to skip the RDocs, since this is an RDocs problem and not a Rails issue.

$ gem install rails -v ">=3.1.0rc"
Successfully installed rails-3.1.0.rc1
1 gem installed
Installing ri documentation for rails-3.1.0.rc1...
Installing RDoc documentation for rails-3.1.0.rc1...

That time it worked. I'll leave it to more advanced users to deal with the underpinnings of the problem.

Create a new Rails project

Now that we have Rails installed, we can create a new Rails project:

$ rails new another_project
      create  
      create  README
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile

[...snipped...]

      create  tmp/cache/.gitkeep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.gitkeep
      create  vendor/plugins
      create  vendor/plugins/.gitkeep
         run  bundle install
Fetching source index for http://rubygems.org/
Using rake (0.9.2) 
Using multi_json (1.0.3) 
Using activesupport (3.1.0.rc1) 

[...snipped...]

Installing ansi (1.2.5) 
Using bundler (1.0.14) 
Installing coffee-script-source (1.1.1) 
Installing execjs (1.1.0) 
Installing coffee-script (2.2.0) 
Using rack-ssl (1.3.2) 
Using thor (0.14.6) 
Using railties (3.1.0.rc1) 
Installing jquery-rails (1.0.9) 
Using rails (3.1.0.rc1) 
Installing sass (3.1.2) 
Installing sqlite3 (1.3.3) with native extensions 
Installing turn (0.8.2) 
Installing uglifier (0.5.4) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

Note that it created all the files it needs, then at the end ran the command bundle install, which then downloaded and installed more gems. Of note are the coffee-script, sass, and jquery gems, which are controversial new defaults to Rails 3.1.

Start the server

Since Rails downloaded and installed correctly and we've created a new project with it, let's see if it actually works.

$ cd another_project/
$ rails server
/home/andy/.rvm/gems/ruby-1.9.2-p180@another_project/gems/execjs-1.1.0/lib/execjs/runtimes.rb:43:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
 from /home/andy/.rvm/gems/ruby-1.9.2-p180@another_project/gems/execjs-1.1.0/lib/execjs.rb:5:in `'
 from /home/andy/.rvm/gems/ruby-1.9.2-p180@another_project/gems/execjs-1.1.0/lib/execjs.rb:4:in `'
 from /home/andy/.rvm/gems/ruby-1.9.2-p180@another_project/gems/coffee-script-2.2.0/lib/coffee_script.rb:1:in `require'

[...snipped...]

 from script/rails:6:in `require'
 from script/rails:6:in `
'

Big no. This threw errors from a whole bunch of gems, but there is a message at the top: "Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes."

That's clear enough, so let's visit that URL and see what's there. If we go there, we see in the README file Sam Stephenson explain that ExecJS (the gem that initially threw the error) "automatically picks the best runtime available to evaluate your JavaScript program" then does something with it. But it can't "pick the best available runtime" if there aren't any installed. So he lists a few, and we'll pick the top one, just because it's on the top: "therubyracer - Google V8 embedded within MRI Ruby." Click on the link, and we see instructions for how to install it: gem install therubyracer.

That's easy enough to do at the command line, but don't do it. Since this is a requirement of a Rails gem, we should ask Bundler to deal with it, so that any future upgrades or issues will be dealt with from within Rails, and so that someone forking this project on Github will have it automatically installed.

Add the RubyRacer gem to the Gemfile

In the root directory of the new application, open the file "Gemfile" and add the following line near the top:
gem "therubyracer", :require => 'v8'
This is what the top four lines of my Gemfile now look like:
source 'http://rubygems.org'

gem 'rails', '3.1.0.rc1'
gem "therubyracer", :require => 'v8'

Run bundler

After saving and closing the Gemfile, we need to ask Bundler to actually do something with it:
$ bundle install
Fetching source index for http://rubygems.org/
Using rake (0.9.2) 
Using multi_json (1.0.3) 
Using activesupport (3.1.0.rc1) 

[...snipped...]

Using rails (3.1.0.rc1) 
Using sass (3.1.2) 
Using sqlite3 (1.3.3) 
Installing therubyracer (0.8.2) with native extensions 
Using turn (0.8.2) 
Using uglifier (0.5.4) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
This checked all the gem dependencies in this Rails application, found a new one in TheRubyRacer, and installed it. That's all we have to do. Now try starting up the server again.

Try the server again

$ rails server
=> Booting WEBrick
=> Rails 3.1.0.rc1 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-06-06 11:43:39] INFO  WEBrick 1.3.1
[2011-06-06 11:43:39] INFO  ruby 1.9.2 (2011-02-18) [i686-linux]
[2011-06-06 11:43:39] INFO  WEBrick::HTTPServer#start: pid=27025 port=3000
This time it worked, so in a web browser we can navigate to http://localhost:3000/ to see the Rails default (the "smoke test") and see that the application has been properly set up.

Create the .rvmrc file

With everything up and running, we're ready to commit this code and push it to GitHub. But first, we should create a .rvmrc file to ensure that RVM knows how to properly set up this project's environment each time we want to work on it.

To do so, create a new file in the project's root directory called .rvmrc, and add the following line to it:
rvm 1.9.2@another_project
Save and close the file, leave the project directory, and cd back into it. This will cause RVM to see this file, execute the command, and change the gemset and the Ruby version to "another_project" and "1.9.2", respectively. When I do this on my machine, I get the following prompts from RVM:

$ cd ..
$ cd another_project

  ===============================================================
  = NOTICE:                                                     =
  ===============================================================
  = RVM has encountered a new or modified .rvmrc file in the    =
  = current working directory. Resource files may execute       =
  = arbitrary instructions, so RVM will not use an .rvmrc file  =
  = that has not been explicitly marked as 'trusted.'           =
  =                                                             =
  = Examine the contents of this file carefully to be sure the  =
  = contents are good before trusting it!                       =
  =                                                             =
  = You will now be given a chance to read the .rvmrc file      =
  = before deciding whether or not its contents are safe. After =
  = reading the file, you will be prompted 'yes or no' to set   =
  = the trust level for this particular version of the file.    =
  =                                                             =
  = Note: You will be re-prompted each time the .rvmrc file     =
  = changes, and may change the trust setting manually at any   =
  = time.                                                       =
  =                                                             =
  = Press 'q' to exit the reader when finished reading the file =
  ===============================================================

  (press enter to review the .rvmrc file)


rvm 1.9.2@another_project


  Examination of  /home/andy/another_project/.rvmrc is now complete.

  ================================================================
  = Trusting an .rvmrc file means that whenever you cd into this =
  = directory, RVM will run this .rvmrc script in your shell.    =
  =                                                              =
  = If the contents of the file change, you will be re-prompted  =
  = to review the file and adjust its trust settings. You may    =
  = also change the trust settings manually at any time with     =
  = the 'rvm rvmrc' command.                                     =
  =                                                              =
  = Now that you have examined the contents of the file, do you  =
  = wish to trust this particular .rvmrc?                        =
  ================================================================

  (yes or no) > yes
Now if I leave the project directory and change to a new gemset and a different version of Ruby, RVM will switch me back to the correct Ruby version and gemset whenever I return to the project directory.

$ cd ..
$ ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [i686-linux]
$ rvm system
$ ruby -v
ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]
$ cd another_project
$ ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [i686-linux]

That's pretty neat. Now if I check this file into my GitHub repository, someone with RVM installed could fork it or clone it, and they would have the exact same environment I do by just running bundle install.

So check it into the code repository:
$ git add .rvmrc
$ git commit -m "added project .rvmrc file"
$ git push

Summary

With RVM already installed on Ubuntu 10.10, we switched to Ruby 1.9.2, created a new gemset, installed Rails after manually creating a /specs/ folder, created a new Rails 3.1 project, started the server after adding a JavaScript runtime to our Gemfile, and created a .rvmrc file.

Summary of commands for power users

Switch to Ruby 1.9.2:
$ rvm 1.9.2
$ ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [i686-linux]
Create a new gemset:
$ rvm gemset create 'another_project'
'another_project' gemset created (/home/andy/.rvm/gems/ruby-1.9.2-p180@another_project).
$ rvm list gemsets

rvm gemsets

   ruby-1.9.2-p180@another_project [ i386 ]
   ruby-1.9.2-p180@global [ i386 ]
=> ruby-1.9.2-p180 [ i386 ]
Switch to the new gemset:
$ rvm 1.9.2@another_project
Show that it has no gems:
$ gem list

*** LOCAL GEMS ***

Update RubyGems:
$ gem --version
1.6.2
$ gem update --system
[...snipped...]

RubyGems system software updated
$ gem --version
1.8.5
Create /specs directory and give it liberal permissions:
$ sudo mkdir ~/.gem/specs
$ sudo chmod 777 ~/.gem/specs
Install Rails, watch it fail on RDocs, then install again:
$ gem install rails -v ">=3.1.0rc" #this fails on an RDocs error, so do it again:
$ gem install rails -v ">=3.1.0rc"
Create a new Rails 3.1 project and try to start its server:
$ rails new another_project
$ cd another_project/
$ rails server
Server failed due to lack of JavaScript runtime, so add it to Gemfile and run bundler:
echo "gem 'therubyracer', :require => 'v8'" >> Gemfile
$ bundle install
$ rails server
Create .rvmrc file:
$ echo "rvm 1.9.2@another_project" >> .rvmrc

~fin~

Starting over with RVM and Rails 3.1

I have done no work on this site for a long period now. In the meantime, Rails is at version 3.1, with new defaults (AssetPipeline, jQuery, Sass, CoffeeScript). I started the site in Rails 2.3.5, and while it's been upgraded to Rails 3.0.3, a lot of scaffold-generated code remains, and I think a new project generated from Rails 3.1 would have significantly different code. So since I haven't done much work on the site besides let Rails build scaffolds, I'm going to scrap the project and start over.

However, I already have a much more clear idea of what I need for the site, so starting over won't set me back far. What I know:
  • It will be hosted on Heroku (free)
  • It will have the domain name www.wikidiscography.com (which I already own and have set up to point to wikidiscography.heroku.com)
  • It will use jQuery and Sass
  • It will use BlueprintCSS
  • It will use RVM
  • Authentication:
    • Will use Devise and CanCan, but:
    • Will investigate using Rails built-in authentication to roll my own authentication system
    • Will investigate using OmniAuth to authenticate only with Facebook/Twitter/LinkedIn (why store passwords and user info if I can let Facebook do it for me?)
  • It will use SQLite on my end (although Heroku ports everything to PostGresSQL on its end)
  • It will use Rails defaults whenever possible, and it will use plugins whenever possible to keep the business logic to a minimum
  • It will stay up to date. I don't want to be left in the dust anymore, so I will try to keep it pegged to the latest stable release of Rails.
  • I'm going to use RSpec and build tests before I build features
  • It will show a clearer roadmap of what it wants to be and have instructions for forking and contributing. 
This may be an ambitious list, but I think it will help me stay on track.