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.

Sunday, December 12, 2010

Devise conflicts with session.rb model

Sessions vs "sessions"

Wikidiscography.com is a session-based discography. As such, it has a model called session.rb along with the corresponding controllers and views. These sessions are not internet/computer/user/browsing sessions, but recording sessions. So I already have a model/controller/view set called "sessions."

When I installed the Devise authentication system for the first time, I got an error when I tried to sign in after creating a new user.

Sign in error

To sign in as a user, one navigates to /users/sign_in. This is a route supplied by Devise to sign in using the credentials already existing in the database. When we click okay, the following error appears: 'Routing Error No route matches "/sessions/user"'.

After signing in with valid credentials

Notice the URL that it redirected to after the attempted sign-in: /session/user. I already have a session model and a sessions_controller, so as I have the application currently set up, this will cause Rails to look for a "user" action in the file sessions_controller.rb. Devise is hitting a resource conflict.

Solution

One way to figure this out is to rename MY session resources to "music_session." I go over how to do this in a separate post. With "session" renamed to "music_session" and "session_type" renamed to "music_session_type" everywhere in the application, Devise is now able to sign in an existing user successfully.

This is not necessarily the best or only way to solve this problem. A more advanced Rails user (not me) might be able reconfigure the routes that Devise uses so that it avoids the "session" part of the url. But by doing it this way I was able to let Devise have its default settings. Now everything "just works." Thanks Rails. Thanks Plataformatec (makers of Devise).

Saturday, December 11, 2010

Adding Authentication with Devise 1.2.rc to a Rails 3.0.3 Application

NB: This post describes my experience following Ryan Bates' Railscast on installing Devise. He uses Rails 3.0.0 with Devise 1.1.rc0; I use Rails 3.0.3 with Devise 1.2.rc. As such my experience differs slightly from the screencast: I change one script command and experience two fewer problems.

Introduction: Choosing Devise

I want wikidiscography.com data to be available to anyone without requiring a log-in, but I only want registered users to be able to add, remove, and edit data. I'm not that this is the best idea; after all, wikipedia doesn't require registration. One good reason may be that correctly entering discographical data may be difficult, so I might want to have some control over who gets editing privileges. For no rigorous reason besides reading a few blog posts and checking out ruby-toolbox.com, I chose Devise. In this blog post, I'll go over how I install it to wikidiscography.com. The first thing I did was check out the ASCIIcasts on Devise and on customizing Devise. Both of these are based on Ryan Bates' railscasts.com screencasts. Thank you Ryan.

Create a new git branch

Before I change any application code, I want to create a new branch on git:
$ git branch devise
$ git checkout devise

Check that you're in the correct branch with
$ git branch
and see that the asterisk is next to the "devise" branch. Now we can safely delete files and destroy the application, and if we make irrecoverable mistakes, we can just delete the branch and be back to where we were.

Remove previous authentication code

Since I've been going through Michael Hartl's Ruby on Rails tutorial which includes a section on creating a custom authentication solution from scratch, the first thing I have to do is go through the application and remove all that code. This includes the following places:

  • user.rb
  • users_controller.rb
  • specs
  • helpers
  • Gemfile (remove unnecessary gems)
  • routes
  • layouts (remove signup link from _footer)
  • database tables (probably a user table and the associated routes; remove them by migrating down and the deleting the migrations)
Once that is done, restart the server and poke around the application to make sure all the code from the previous authentication solution has been removed. From the command line, run $ Rspec spec for one more check. If those tests pass, it's time to move on to installing Devise.

Installing Devise

From here on, I'll be following the Devise ASCIIcast, but making it specific to wikidiscography.com.

Which version?

First, which version should we install? Since this is a Rails 3 application, we should probably go with the latest version currently available. If it were a Rails 2.3 app, you should use Devise 1.0.6, according to Ryan Bates. Since a few months have passed since the Railscast was released, we should look at the Devise changelog to see what the latest version offers. The latest release as of this writing is 1.2.rc. We'll go with that for now, and if it breaks something we may need to downgrade later. (Update: It didn't break anything.)

Add it to the Gemfile

Add the appropriate line to the Gemfile, and then let Bundler do the work of finding and installing it: Gemfile:
gem 'devise', '1.2.rc'

Command line from within wikidiscography folder:
$ sudo bundle install
(This should take about 30 seconds.) Among all the rest of the output that this generates, I get the following lines:

Installing bcrypt-ruby (2.1.2) with native extensions  Using bundler (1.0.0)  Installing orm_adapter (0.0.3)  Installing warden (1.0.2)  Installing devise (1.2.rc) 

This tells me that Bundler has installed not only Devise, but its dependencies, which are Warden, orm_adapter, and bcrypt-ruby. I don't know what these are, nor do I care. According to the ASCIIcast, the next step is to run the installation generator.

$ rails generate devise_install
For me, this returned the following error:

Could not find generator devise_install
This is why it's a good idea to use the same version of software that the tutorial uses. It turns out that the newer version of Devise that I installed (1.2.rc) has a slightly different installation script, as noted in the installation notes on Devise's GitHub page. The new install command is:

$ rails generate devise:install
This creates the files config/initializers/devise.rb and config/locales/devise.en.yml. It also presents the user with the following instructions:

===============================================================================

Some setup you must do manually if you haven't yet:

  1. Setup default url options for your specific environment. Here is an
     example of development environment:

       config.action_mailer.default_url_options = { :host => 'localhost:3000' }

     This is a required Rails configuration. In production it must be the
     actual host of your application

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root :to => "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

===============================================================================
Those are pretty good instructions, so before moving on, we follow them.

Follow the 3 installation steps

1. Copy the line in the instructions into the file config/environments/development.rb:

config.action_mailer.default_url_options = { :host => 'localhost:3000' }

2. Check the file config/routes.rb to see if there is a
root :to => ... root :to => "pages#home"
, so step 2 is already complete.
3. Check the file app/views/layouts/application.html.erb to see if we have flash messages there. Wikidiscography has the following line:

<%= flash[:notice] %>

but it doesn't have an "alert" line. I don't know if alert is redundant or new to Rails 3 or what, but I'm going to add the line to my application layout anyway because I want Devise to work. I'll remove code later if I can. So now my application layout has the following two lines:

<%= flash[:notice] %>
<%= alert %> 

As far as I can tell, we have satisfied the 3 requirements presented by the Devise installation script.

Let Devise Create a User Model

(I'm still following the ASCIIcast.) Now that Devise has been properly installed (I hope), from the command line type:

$ rails generate devise User
For wikidiscography, this creates the following output:

invoke  active_record
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
      create    db/migrate/20101213002243_devise_create_users.rb
      insert    app/models/user.rb
       route  devise_for :users



Notice that instead of generating Test::Unit files as in the ASCIIcast, it invoked Rspec and generated Rspec specs. That's because I previously set up Rspec as my testing environment. Besides that difference, everything seems to be working the same. It also generated a user.rb file at /app/models/user.rb, and a database migration at /db/migrate/20101211053242_devise_create_users.rb. These files give us four modules by default: database_authenticatable, recoverable, rememberable, and trackable. I don't really know or care what these do at the moment, so I'm going to accept the defaults and run the migration.

Migrate the database

From the command prompt, type:

$ rake db:migrate
This produces the following output, showing a new users table, and indexes on two columns:

==  DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
   -> 0.0145s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0327s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0024s
==  DeviseCreateUsers: migrated (0.0502s) =====================================

Once you've migrated the database, restart the server.

Check out the new routes

At the command line, type in

$ rake routes

to see a list of all routes available to the application. Notice the routes associated with the controller "devise":

new_user_session GET    /users/sign_in(.:format)       {:action=>"new", :controller=>"devise/sessions"}
            user_session POST   /users/sign_in(.:format)       {:action=>"create", :controller=>"devise/sessions"}
    destroy_user_session GET    /users/sign_out(.:format)      {:action=>"destroy", :controller=>"devise/sessions"}
           user_password POST   /users/password(.:format)      {:action=>"create", :controller=>"devise/passwords"}
       new_user_password GET    /users/password/new(.:format)  {:action=>"new", :controller=>"devise/passwords"}
      edit_user_password GET    /users/password/edit(.:format) {:action=>"edit", :controller=>"devise/passwords"}
                         PUT    /users/password(.:format)      {:action=>"update", :controller=>"devise/passwords"}
cancel_user_registration GET    /users/cancel(.:format)        {:action=>"cancel", :controller=>"devise/registrations"}
       user_registration POST   /users(.:format)               {:action=>"create", :controller=>"devise/registrations"}
   new_user_registration GET    /users/sign_up(.:format)       {:action=>"new", :controller=>"devise/registrations"}
  edit_user_registration GET    /users/edit(.:format)          {:action=>"edit", :controller=>"devise/registrations"}
                         PUT    /users(.:format)               {:action=>"update", :controller=>"devise/registrations"}
                         DELETE /users(.:format)               {:action=>"destroy", :controller=>"devise/registrations"}
                    root        /(.:format)                    {:action=>"index", :controller=>"welcome"}

Where did all these routes come from? Check out the /config/routes.rb file and notice the line that the devise generator added at the top:

devise_for :users
Again, I don't recognize that syntax, I don't understand how it works, but I'll accept the routes it gives me and happily move on. If you really want to dig into it, check out the file /usr/lib/ruby/gems/1.8/gems/devise-1.2.rc/lib/devise/rails/routes.rb. It's well commented with detailed instructions on how to read and modify routes. Good luck.

Use the new sign_up route

(Still following the ASCIIcast.) At this point, Devise is ready to go. Navigate to /users/sign_up and we'll see a sign-up form. But wait a minute: The Devise generator did not generate any view files, and I certainly never made a sign-in form. So where is this coming from? To see the answer, take a look at the server console output:


Started GET "/users/sign_up" for 127.0.0.1 at Sun Dec 12 19:29:51 -0500 2010
  Processing by Devise::RegistrationsController#new as HTML
Rendered /usr/lib/ruby/gems/1.8/gems/devise-1.2.rc/app/views/devise/shared/_links.erb (1.9ms)
Rendered /usr/lib/ruby/gems/1.8/gems/devise-1.2.rc/app/views/devise/registrations/new.html.erb within layouts/application (56.0ms)
Completed 200 OK in 65ms (Views: 62.6ms | ActiveRecord: 0.0ms)

This shows us a few things: First, it says "Processing by Devise::RegistrationController#new". I never made a "registration controller", but apparently by pointing the application to /users/sign_up it called the "new" method on this phantom controller. Second, the penultimate line gives us more information: "Rendered /usr/lib/ruby/gems/1.8/gems/devise-1.2.rc/app/views/devise/registrations/new.html.erb". There's the answer: The sign-up form is in the gem subdirectory. So if I navigate to usr/lib/ruby/gems/1.8/gems/devise-1.2.rc/app/, I see all the files that Devise uses to work its magic. At this point I'm content to let it be magical without deeply understanding it.

Try signing up

I try out the sign-up form by putting in my name and email address and submitting the form. It works! And it displays a flash message that reads "Welcome! You have signed up successfully." I notice that is also displayed an empty flash[:notice] message, but that is probably something in my own code, and something to debug later. For now, since we know user registration is functioning properly, we move forward and test out other features.

Try signing out

Now that I'm registered and signed in, I click around a few pages, and then try signing out. There's no "sign out" button because I never created one, but as Ryan Bates points out via the ASCIIcast, you can sign out by pointing the browser to the sign-out route. Which route is that, exactly? Find it by referencing the routes output from

$ rake routes
The line we need is "/users/sign_out", so type that into the address bar and see what happens.

After visiting /users/sign_out
Voila, we are signed out and given a friendly flash message: "Signed out successfully."

Sign in again

Since we now have a user in the database, we go back to /users/sign_in to sign in using the credentials we supplied when signing up. At this point in the ASCIIcast, Ryan Bates describes an error caused by Rails and how to fix it. Since that episode, the bug has been fixed, and everything just works. Devise is now able to sign in an existing user successfully.

Git commit; git push

Now that everything seems to be working properly, I want to commit my changes and and merge back into the master branch.

$ git commit -am "installed and configured Devise"
$ git checkout master
$ git merge devise
$ git branch -d devise

In the code above, I first committed my changes and added a custom message; then I moved from the devise branch to the master branch using the "checkout" command; then from the master branch I called for the devise branch to merge into the master branch; then I deleted the devise branch.

Done. This got us most of the way through the ASCIIcast, but with considerably fewer problems. This is because we are using a newer version of Rails (3.0.3 instead of a 3 release candidate) and a new version of Devise (1.2.rc instead of 1.1.rc0).

Next post will deal with actually using Devise and adding real user authentication.

When and how to create and merge branches on git

The Scenario

Here's the scenario: I want to add a new and different authentication system (Devise) to wikidiscography.com. Should I just commit any outstanding changes I've made, then do the entire switchover, then do another commit, so that all my Devise additions are in one neat little commit? No, probably not. Why? Because I'm not a great coder or a great user of Rails, so my attempt to install and configure Devise will probably take several days. Sometime in the middle of the switchover I might find that I have to make other changes, so those changes would be rolled into the Devise git commit. My understanding of git best practices is that changes should be committed as logical groups. So what's the answer? I think the answer is to create a new branch, make the Devise changes there until it works properly, then merge that new branch back into the master branch. Simple enough.

NB: For a great explanation git and how to use it, see http://gitref.org/

Make a new git branch

Much of this is learned from http://gitref.org/branching/
From the command, from within the application folder, create a new git branch and call it "devise":

$ git checkout -b devise


(The -b flag creates the new branch and immediately switches over to it). Check that you're in the correct branch with $ git branch, and see that the asterisk is next to the "devise" branch. Now we can safely delete files and destroy the application, and if we make irrecoverable mistakes, we can just delete the branch and clone the original repository from Git.

But now that I've been working on the Devise configuration for a while, I find that I have to make another major change that again really deserves its own separate commit or even its own branch. I have to rename two sets of models/controllers/views from "session" and "session_type" to "msession" and "msession_type," respectively. But I'm already in the Devise branch; can I just make another branch, make the renaming changes, and then merge back? Do I merge it back to the Devise branch, or back into the master branch? Let's just move forward and see what happens.


Make another new git branch

From the terminal again, type
$ git checkout -b rename_session_to_msession

and ensure you're in the new branch with git branch. Now go through all the code and replace "session" with "music_session" everywhere. I also did this with migrations, which probably wasn't the best way to do it. I think the Rails way would have been to create another migration in which I renamed the tables and the table columns. This might cause problems later when I try to push everything to Heroku. But for now, it's enough for me to get my development environment working.

After going through the application and making sure everything still worked now that "session" has been replaced with "music_session", I commit my changes.
$ git commit -am "changed 'session' to 'music_session' everywhere".

Now I have a branch with a committed set of changes.


Merge the branch

Now I want to merge this branch back into the "devise" branch. First switch to the "devise" branch:
$ git checkout devise

Then tell Git to merge the "rename_session_to_msession" branch into the "devise" branch:

$ git merge rename_session_to_msession

Now I'm done with the "rename_session_to_msession" branch, so I can delete it:

$ git branch -d rename_session_to_msession

Now my "devise" branch shows the renamed session items, so I can continue working on the Devise configuration.

Done.

Friday, December 10, 2010

Ruby on Rails Tutorial: Learn Rails by Example

A major aid in my attempted progress in understanding Rails and Ruby programming has been Michael Hartl's excellent Ruby on Rails Tutorial. He takes the reader through literally every step of building a database-backed web application in Ruby on Rails, and he assumes virtually no prior knowledge. Three things make this tutorial especially valuables. The tutorial:
  1. is based on Rails 3, but includes a parallel course on Rails 2.3;
  2. includes steps on how to deploy on Heroku; and
  3. includes test-driven development with Rspec.
 There are many more reasons why this tutorial is great, but those three should be enough to merit a look by most of us would-be developers. Thanks Michael.

http://railstutorial.org