Now that we've got our bearings in Rails, we can move on to making some improvements and implementing the rest of the features. Along the way we'll take a look at a handful of new Gems to make our work easier.

First off, we'll be beautifying our app some.

Then we'll work on the file upload and Markdown processing for chapters.

Next we'll implement bookmarks and errata, which will serve as an excellent excuse to play with some Ajax.

After that we'll provide our users with a means of logging into our application.

And finally we'll move on to setting up a means of billing new users.

An important thing to note here is that, unlike last chapter, some of what we'll see in this chapter is not anything we'd necessarily be expected to know off the top of our heads.

We will cover a lot of things from Rails proper that we haven't had reason to look at yet, but we'll also be taking a look at some more specialized problems (e.g. parsing text as Markdown) that you won't run across in every single Rails project. Along the way, we'll be using a variety of Gems to help us solve these problems.

Personally, I had to reference the GitHub READMEs for most of these Gems as I wrote this chapter and built the app, so if you understand what's generally going on, you're good; the rest up you can look up.

Let's do this.

9.1 | Some Styling

In case I didn't make it clear in the section on CSS earlier, I'm not a designer.

I just pretend I am (read: wish I was).

Even so, I still like for a site to look presentable, so I do end up making largely futile attempts at styling mine. To this end, something that has helped me tremendously is Twitter Bootstrap, "Simple and flexible HTML, CSS, and Javascript for popular user interface components and interactions." Much like Rails itself, Twitter Bootstrap won't solve all of our problems, but it does offer us a great place to start.

But the good fortune doesn't stop with Twitter Bootstrap because there's also twitter-bootstrap-rails, a gem that can get us started using Twitter Bootstrap in our Rails application quickly.

To get started, we'll want to do is add it to our Gemfile. Open up the Gemfile in Rails root and add the line gem 'twitter-boostrap-rails' somewhere inside it (I placed mine after gem 'thin'), then $ bundle install.

Now that we have the Gem installed, we have to "install" it in our application. We do this by way of a Rails generator:

$ rails generate bootstrap:install
      insert  app/assets/javascripts/application.js
      create  app/assets/javascripts/bootstrap.js.coffee
      create  app/assets/stylesheets/bootstrap_and_overrides.css.less
        gsub  app/assets/stylesheets/application.css
        gsub  app/assets/stylesheets/application.css

We won't have to do this for every Gem, but as you can see above twitter-bootstrap-rails had some CSS and JS files to place in our application and it does so with the bootstrap:install Rails generator.

So we've got some more CSS and JS now, but we're also going to need to adjust app/views/layouts/application.html.erb to use Twitter Bootstrap. We could read the documentation and make these changes by hand, but it's much easier to just let twitter-bootstrap-rails handle that for us too:

$ rails generate bootstrap:layout application fixed
    conflict  app/views/layouts/application.html.erb
Overwrite /home/brad/rails/book/app/views/layouts/application.html.erb? (enter "h" for help) [Ynaqdh] 
       force  app/views/layouts/application.html.erb

This should run and hang up on the conflict. The problem is that twitter-bootstrap-rails is trying to replace our existing application layout view and we need to confirm that we want to overwrite it. Pressing ENTER will replace the file (like we want).

For the Overwrite line we can just hit ENTER to confirm that we want to replace the file, but if you look closely you'll see that there are a series of options inside of the [...] at the end of the line.

When you're prompted with something like [Ynaqdh] in Bash, the y indicates "yes", n indicates "no", and the h usually indicates "help". You can type in any of these and then hit ENTER to respond, but the fact that the Y is capital here means that it is the default. Because of this, we can simply hit ENTER by itself to say "yes, I want to overwrite the file".

You can check the README on GitHub for the details, but what we're doing here is asking the Gem to generate a layout for us. We are also indicating with the other arguments that we want the generated layout to be named application and that we want it to be fixed.

The choice is between fixed or fluid, and both will work nicely on mobile devices, but I prefer fixed.

Now if we fire our server back up and refresh any chapter page, we'll see the changes that twitter-bootstrap-rails has made for us.

You can actually leave your rails server running and still see most of the changes we'll make in this chapter. Certain things, like changing anything in config or lib, will require us to restart the application though.

If you aren't seeing the changes you've made, try restarting the server.

If you get an error complaining that a table can't be found, run $ rake db:migrate.

Looking at /chapters, we'll see that parts of the page have definitely changed for the better. If we resize the browser window to something much smaller, we can see the elements on the page collapse and reorder to accommodate the smaller viewport. This is the generated CSS and our new layout at work.

Still, the list of our chapters is pretty bland. Again, we could make some changes ourselves, but twitter-bootstrap-rails offers even more help in the form of another generator.

$ rails generate bootstrap:themed Chapters
    conflict  app/views/chapters/index.html.erb
Overwrite /home/brad/rails/book/app/views/chapters/index.html.erb? (enter "h" for help) [Ynaqdh] 
       force  app/views/chapters/index.html.erb
    conflict  app/views/chapters/new.html.erb
Overwrite /home/brad/rails/book/app/views/chapters/new.html.erb? (enter "h" for help) [Ynaqdh] 
       force  app/views/chapters/new.html.erb
    conflict  app/views/chapters/edit.html.erb
Overwrite /home/brad/rails/book/app/views/chapters/edit.html.erb? (enter "h" for help) [Ynaqdh] 
       force  app/views/chapters/edit.html.erb
    conflict  app/views/chapters/_form.html.erb
Overwrite /home/brad/rails/book/app/views/chapters/_form.html.erb? (enter "h" for help) [Ynaqdh] 
       force  app/views/chapters/_form.html.erb
    conflict  app/views/chapters/show.html.erb
Overwrite /home/brad/rails/book/app/views/chapters/show.html.erb? (enter "h" for help) [Ynaqdh] 
       force  app/views/chapters/show.html.erb

$ rm app/assets/stylesheets/scaffold.css.scss

The rm at the end will get rid of some scaffolded styles that mess with Twitter Bootstrap. You may have to repeat this step periodically if you end up generating any more scaffolds.

Take a look at /chapters in the browser now. Much better.

We won't be looking at these new views just yet, but feel free to.

If you do take a look, you'll notice that they are definitely different from what we've seen thus far. In addition to all the Twitter Bootsrap structure, the model and attribute names are accessed dynamically. For instance, there will be a number of lines that look something like this:

Chapter.model_name.human.pluralize

Most of these are fairly readable, but feel free to play around with these methods in rails console. When changing or adding on to these views though, it's easiest to hard-code your updates.

We'll be making plenty of changes to these views as we go, but this gives us a nice place to start.

So with that, twitter-bootstrap-rails has got us off and running as best it can. Now it's time for us to mess with things on our own.

Speaking of messing with things, there are a couple things that are now in our new application layout that don't belong: the sidebar and the top links. Really, anything that has the placeholders Link1, Link2, or Link3 needs to be changed or removed.

The sidebar could honestly work pretty well for listing chapters, but it probably won't display well on mobile devices. Instead, we'll be doing this a much cleaner way using the .navbar (the bar at the top of the page).

First, let's rip out the sidebar. Change this:

<div class="container">
  <div class="row">
    <div class="span3">
      <div class="well sidebar-nav">
        <ul class="nav nav-list">
          <li class="nav-header">Sidebar</li>
          <li><%= link_to "Link1", "/path1"  %></li>
          <li><%= link_to "Link2", "/path2"  %></li>
          <li><%= link_to "Link3", "/path3"  %></li>
        </ul>
      </div><!--/.well -->
    </div><!--/span-->
    <div class="span9">
      <%= yield %>
    </div>
  </div><!--/row-->

  <footer>
    <p>© Company 2012</p>
  </footer>

</div> <!-- /container -->

to this:

<div class="container">
  <div class="row">
    <div class="span12">
      <%= yield %>
    </div>
  </div><!--/row-->

  <footer>
    <p>© Company 2012</p>
  </footer>

</div> <!-- /container -->

So we're getting rid of <div class="span3"> and its contents, then changing <div class="span9"> to <div class="span12">.

We're not going to cover all the details of how Twitter Bootstrap works, because it's discussed at length on their own site, but basically we use div.row's (or div.row-fluid's) to create a vertical component and then control how much of it is filled horizontally with .span1 to .span12. The digit at the end of these class names will dictate how many twelfths of the width the element will occupy. For instance, a .span12 will use 12 out of 12 units of the full horizontal space, whereas a .span3 will use 3/12ths of the width.

It probably makes way more sense to see this as a diagram. You can find that and an explanation here.

Here we choose a .span12 because we don't have anything else we want to display besides our chapter body.

Next we'll want a way to put links to the chapters in the top nav. Furthermore, to make this work cleanly on smaller screens, we'll put this list of chapters in a dropdown nav element. To accomplish this, we'll change this:

<div class="container nav-collapse">
  <ul class="nav">
    <li><%= link_to "Link1", "/path1"  %></li>
    <li><%= link_to "Link2", "/path2"  %></li>
    <li><%= link_to "Link3", "/path3"  %></li>
  </ul>
</div><!--/.nav-collapse -->

to this:

<div class="container nav-collapse">
  <ul class="nav">
    <li class="dropdown">
      <a class="dropdown-toggle" data-toggle="dropdown" href="#">Chapters</a>

      <ul class="dropdown-menu">
        <li>
          <%= link_to "Table of Contents", chapters_path %>
        </li>

        <li class="divider"></li>

        <% Chapter.all.sort.each do |chapter| %>
          <li><%= link_to chapter.title, chapter_path(chapter) %></li>
        <% end %>
      </ul>

    </li>
  </ul>
</div><!--/.nav-collapse -->

Notice that we call Chapter.all.sort here. Without arguments, sort will order the chapters by id, which is exactly what we want for now.

Also, be aware that this is super inefficient because it requires that all of the chapters are loaded from the DB for every web request. We'll be ignoring this though, for the sake of pushing forward.

There's quite a bit of HTML here, but notice in the middle of it all that we're looping through all the chapters and populating a <ul> with <li>'s containing links to each chapter.

Presently, this results in the following HTML for me:

<div class="container nav-collapse">
  <ul class="nav">
    <li class="dropdown open">
      <a class="dropdown-toggle" data-toggle="dropdown" href="#">Chapters</a>

      <ul class="dropdown-menu">
        <li>
          <a href="/chapters">Table of Contents</a>
        </li>

        <li class="divider"></li>

        <li><a href="/chapters/1">1 | Chapter 1</a></li>
        <li><a href="/chapters/2">2 | Chapter 2</a></li>
        <li><a href="/chapters/3">3 | Chapter 3</a></li>
        <li><a href="/chapters/4">4 | Chapter 4</a></li>
        <li><a href="/chapters/5">5 | Chapter 5</a></li>
      </ul>

    </li>
  </ul>
</div>

You might notice that my chapter ids go 1, 2, 3, 5. I deleted my chapter with an id of 4 and won't be able to get it back.

This is the nature of ActiveRecord ids and something we could iron out later by, for example, giving Chapter a number attribute.

So if you check out /chapters now, you'll see Chapters in the top nav. When clicked, a dropdown will appear with all the chapter titles as links to their respective chapters.

Better yet, if you shrink the window this nav collapses. You can then click the expand button in the upper right to see all of the chapters listed out cleanly there too.

For more on how Twitter Bootstrap expects the markup to look for dropdowns, you can read here.

Boom. Styled.

Not that we're totally done styling our site, but we have made quite a bit of progress here. Anything else that we decide we need, we'll do as we go.

9.2 | File Upload

Now, if you'll remember back to our brainstorming from last chapter, one of the features we wanted was for the author to be able to create and update chapters by uploading Markdown documents.

Well, we don't have a way to restrict this to just the author yet. ...heck, we don't even have users. We'll be correcting these problems next.

Think of Markdown as a kind of HTML shorthand that's useful for writing content, like a chapter of a book. It's so useful in fact that I've written this entire book in Markdown.

"You threw this feature in just because it was helpful to you, didn't you?"

I... No, it... ... okay, initially yes. But it does give us a good reason to look at how to handle and process uploaded files in Rails, which is good stuff to know.

Just so we're all familiar, here's an example Markdown document whose content illustrates the syntax:

# This is an h1

This will be its own p element

And this will be another p element

## This is an h2

Here is `some code`. _This will be in italics_, while **this will be bold**. Here is a [link to google](http://google.com).

* this is an li in a ul
* this is an li in a ul
* this is an li in a ul

1. this is an li in a ol
2. this is an li in a ol
3. this is an li in a ol

For more on Markdown, you can read up where it started.

But really, the important thing here for us is not understanding Markdown, but handling it. Specifically if we upload Markdown, we want it to appear as HTML when a chapter is rendered. This means that we'll have to convert it after it's been uploaded.

So there are two parts here:

  1. file upload
  2. Markdown processing

And we'll attack them in that order.

File upload

There are a number of Gems to handle file uploads, but my preference is definitely Paperclip.

And keep in mind that Paperclip has a lot more functionality than we'll see here. For instance, it integrates closely with rmagick for easy image processing after upload.

As you might guess, we'll first have to install Paperclip to our bundle. Place gem 'paperclip' somewhere in your Gemfile and run $ bundle install.

It's really not necessary for our needs that we persist uploaded files, but we will in order to take a little deeper of a look at Paperclip.

So to get started with Paperclip, we need to migrate the database so that our Chapter model has the attributes that Paperclip requires to keep track of files. Creating a migration for this is best left to the following Paperclip generator:

$ rails generate paperclip chapter markdown
      create  db/migrate/20120823071412_add_attachment_markdown_to_chapter.rb

$ rake db:migrate
==  AddAttachmentMarkdownToChapter: migrating =================================
-- add_column(:chapters, :markdown_file_name, :string)
   -> 0.0006s
-- add_column(:chapters, :markdown_content_type, :string)
   -> 0.0004s
-- add_column(:chapters, :markdown_file_size, :integer)
   -> 0.0004s
-- add_column(:chapters, :markdown_updated_at, :datetime)
   -> 0.0006s
==  AddAttachmentMarkdownToChapter: migrated (0.0023s) ========================

Don't forget to $ rake db:migrate!

Next we'll need to adjust our Chapter model:

### app/models/chapter.rb ###

class Chapter < ActiveRecord::Base
  attr_accessible :title, :body, :markdown
  has_attached_file :markdown
end

Make sure to add :markdown to attr_accessible.

has_attached_file is a method offered by Paperclip that will use those four new columns from the migration to keep track of files and their metadata.

Now all that is left is to adjust our UI to allow for file selection and upload. To add this, we'll need to reopen the view in app/views/chapters/_form.html.erb and change it to look like this:

### app/views/chapters/_form.html.erb ###

<%= form_for @chapter, :html => { :class => 'form-horizontal', :multi_part => true } do |f| %>

  ...

  <div class="control-group">
    <%= f.label :markdown, :class => 'control-label' %>
    <div class="controls">
      <%= f.file_field :markdown %>
    </div>
  </div>

  ...

Make sure to change form_for to match what is above, because none of this works without the :multi_part => true.

Multipart forms are actually a feature of HTML, not just Rails or Paperclip.

Much like we used f.text_field :title earlier, here we're using f.file_field :markdown to get uploaded files into the controller in params[:markdown].

At this point we're able to upload files, so create a new chapter by uploading a Markdown file and fire up rails console.

$ rails console
Loading development environment (Rails 3.2.8)

> chapter = Chapter.last
Chapter Load (0.7ms)  SELECT "chapters".* FROM "chapters" ORDER BY "chapters"."id" DESC LIMIT 1
=> #<Chapter id: 7, title: "test", created_at: "2012-08-23 07:40:42", updated_at: "2012-08-23 07:40:42", body: "", markdown_file_name: "00.md", markdown_content_type: "text/x-markdown", markdown_file_size: 9050, markdown_updated_at: "2012-08-23 07:40:41">

> chapter.markdown
=> /system/markdowns/7/original/00.md?1345707641

> chapter.markdown.url
=> /system/markdowns/7/original/00.md?1345707641

> chapter.markdown_file_name
=> "00.md"

> chapter.markdown_content_type
=> "text/x-markdown"

> chapter.markdown_file_size
=> 9050

As you can see, we have several different methods available to us to help with the file, but the most useful ones are usually those used to get a path to the file.

The system directory mentioned in the path is located inside of public, so these static files can be accessed over HTTP. For example, http://localhost:3000/system/markdowns/7/original/00.md will get me to the Markdown file that I just uploaded.

And the query parameters after the paths above are timestamps using Unix time. You can find a converter here if you'd like to see when exactly I uploaded the file above (1345707641).

Also, it won't be a problem for us here, but these files wouldn't be persisted in our production environment because Heroku uses a read-only filesystem. If we did want to keep these, we'd have to use something like Amazon S3 with Heroku and configure Paperclip to use it.

Markdown Processing

Now that we can upload files, we need to convert them from Markdown to HTML. But before we worry about the conversion, we first need some kind of mechanism to process files once they've been uploaded.

There are a number of ways to do this, but the cleanest is to make our own PaperClip::Processor. Paperclip will be looking for this inside the directory lib/paperclip_processors directory, so that's exactly where we'll put it.

Here's our very own processor:

### lib/paperclip_processors/chapter_markdown_converter.rb ###

module Paperclip
  class ChapterMarkdownConverter < Processor
    def initialize(file, options={}, attachment=nil)
      @file    = file
      @options = options
      @chapter = attachment.instance
    end

    def make
      begin
        if @chapter.markdown_content_type == "text/x-markdown"
          @chapter.title = "UPLOADED CHAPTER"
          @chapter.body  = @file.read
        end
      rescue
      end
      
      @file
    end
  end
end

Now we'll just need to tell our our chapter model to use this processor when a file is uploaded/attached:

### app/models/chapter.rb ###

class Chapter < ActiveRecord::Base
  ...
  has_attached_file :markdown, :processors => [:chapter_markdown_converter], :styles => { a:'b' }
end

So first of all, let's talk about what this code aims to accomplish.

As things stand above, this processor will set the body of a chapter to the contents of the file uploaded for it and will also set the title to "UPLOADED CHAPTER". It does this if and only if the uploaded file is a Markdown file.

With that in mind, let's start addressing how it works.

In order to make a Paperclip processor we first need to create a class under the Paperclip namespace that inherits from Paperclip::Processor. Here we do this by making Paperclip::ChapterMarkdownConverter:

module Paperclip
  class ChapterMarkdownConverter < Processor
    ...
  end
end

Notice that our ChapterMarkdownConverter class falls under the Paperclip namespace here because we define it inside of module Paperclip.

Also notice that we use Processor by itself in class ChapterMarkdownConverter < Processor to have our class inherit from Paperclip::Processor. This is possible because Processor inside of module Paperclip can simply be referred to as Processor because we are already inside the Paperclip namespace.

READERS - Was this namespace explanation sufficient? This is a part of Ruby that I didn't fully grasp initially and I want to make sure that I explain it well enough here.

This processor class needs both an initialize method and a make method. The initialize method will be run first and assigns the uploaded file and its Chapter instance to instance variables. Then the make method is run and uses those instance variables to do the actual processing.

Keep in mind that initialize is just a regular old Ruby constructor and that the reason these methods are called in this order is probably because Paperclip is probably implemented in such a way that its processors are run with a line like the following:

Paperclip::ChapterMarkdownConverter.new(file, options, attachment).make

One rather curious thing about the code above is how we get a Chapter instance stored in @chapter:

@chapter = attachment.instance

To get an idea of what's going on here, let's play around in the rails console a little more.

# first, let's grab an instance of `Chapter` in a variable named `chapter`
> chapter = Chapter.first
=> #<Chapter id: 1, ..., markdown_file_name: "01.md", markdown_content_type: "text/x-markdown", ...>

# and store its `.markdown` as `attachment`
> attachment = chapter.markdown
=> /system/markdowns/1/original/01.md?1349168694

# hmm, this returns a string, but it must to be some kind of more complicated object...

# let's test its class
> attachment.class
=> Paperclip::Attachment

# a-ha!

# we can access the record that this `Paperclip::Attachment` is attached to by calling `instance` on it
> chapter === attachment.instance
=> true

# see, the instance the attachment is attached to is the chapter instance

Then in the make method, we first check that the uploaded file is Markdown with if @chapter.markdown_content_type == "text/x-markdown", markdown_content_type being a method we got for free with has_attached_file, and then set a new title and body for the chapter. In doing this, we see that we can access the contents of @file by calling the read method on it.

Something interesting about our make method is that we've wrapped most everything in it inside of a begin/rescue. This is done to catch any errors that might spring up during our processing (we were just handed some random file after all).

And yes, we really should be handling the error somehow.

And finally, we see that make returns @file. The reason for this is that after a processor is run, some mechanism in Paperclip takes this return and tries to call .close on it, expecting it to be a file.

"Whoa, how do you know all that?"

Because I made a broken version first that errored and told me that "foobar" had no method close.

I figured that it probably wanted the file returned. It did. Trial and error.

Oh, and in case you're wondering, the reason it expects back a file (per the documentation) is that: "All #make needs to return is an instance of File (Tempfile is acceptable) which contains the results of the processing."

We won't be bothering with that here, but only because we will never need a file containing the post-processing results.

One last curiosity about our processor code is that despite all the changes we make to @chapter, we never do a @chapter.save. Even without it, our changes are written to the DB.

I'm not intimately familiar with the inner workings of Paperclip, but I would imagine that this processor takes place somewhere in the middle of the greater record creation/updating lifecycle and that some kind of @chapter.save gets made somewhere later on down the road.

Whatever the case, we apparently don't have to bother with it.

Well, to be more accurate: everything blows up if we bother with a @chapter.save ourselves... so, we won't worry about it!

As for the changes to our model, the important part is the :processors => [:chapters_markdown_converter]. Paperclip takes the the symbol :chapters_markdown_converter and turns it into ChapterMarkdownConverter to get at processor class.

Also, processors apparently won't run without :styles, so I just threw it a bogus hash to sate it.

Handling Markdown

Now that we have an upload processor, let's make it do something useful: convert Markdown to HTML.

You guessed it. It's time for another Gem.

Add gem 'redcarpet' to your Gemfile and run bundle install.

Redcarpet is used by none other than GitHub to parse Markdown, so it's plenty good for us. Redcarpet offers a Markdown class that we'll be using for our parsing.

All we'll need to do is make a few changes to our make method:

### lib/paperclip_processors/chapter_markdown_converter.rb ###

require 'redcarpet/compat'

...

def make
  begin
    if @chapter.markdown_content_type == "text/x-markdown"
      file_text = @file.read.force_encoding("UTF-8")

      title     = file_text.split("\n").first[/([#]+\s+)(.+)/, 2]

      markdown  = Markdown.new(file_text)
      html      = markdown.to_html

      @chapter.title = title
      @chapter.body  = html
    end
  rescue
  end
  
  @file
end

...

Make sure to add the require 'redcarpet/compat' at the beginning of the file, otherwise the Markdown class won't be defined in this file.

Okay, so there's quite a bit of code here, but the variable names should make it pretty clear what is going on. Either way, let's break it down.

file_text = @file.read.force_encoding("UTF-8")

First, we need to get the text from the uploaded file (the Markdown). In initialize we put this file in the instance variable @file and we access its contents here with @file.read. We then call .force_encoding("UTF-8") on that resulting string to ensure that the text is encoded as UTF-8.

Whoa, why'd you think to do that?

I didn't. It complained when I tried to pass the string straightaway to be parsed as Markdown, claiming it was ASCII 8-bit and that it wanted UTF-8. So I googled around to find how exactly encode a string as UTF-8. ...and it worked!

It's not really that important to understand UTF right now, so we'll gloss over it. That said, there is a link in the resources page about UTF so you don't forget to read up about it later.

first_line = file_text.split("\n").first[/([#]+\s+)(.+)/, 2]

Next, we need to grab the title from the first line of of the file contents. It makes sense that most chapters will probably start off with an <h1>, so we'll also want to get the text after the Markdown syntax for a heading (#).

In order to get the first line of the file, we split on "\n" to get an array of all of the lines and then call first on that array to get the first line.

At this point we have a line that looks something like "# 9 | More of the Project". To trim off the leading # and whitespace and grab just the "9 | More of the Project", we use the [] regex operator on the string like this: [/([#]+\s+)(.+)/, 2]. So we're calling [] on the string here and passing two arguments: a regex and an integer.

If the regex looks completely nonsensical to you, don't worry; I know plenty of awesome programmers who hate trying to read regular expressions.

The regex /([#]+\s+)(.+)/ contains two groups (as defined with the ()'s) that read, in order, as "any number of #'s followed by any amount of whitespace" and, well, "anything (except a newline)". The 2 in [..., 2] indicates that we want the matched string for the 2nd group returned from the [] call.

markdown = Markdown.new(file_text)

Here we're simply using the Markdown class from Redcarpet to parse the text as Markdown. We can then use the resulting object to perform the conversion to HTML. In fact, that's next.

html = markdown.to_html

Here we finally convert the Markdown to HTML, specifically an HTML string.

After that, we just assign the title and body to the record and return the file like before.

Now we can expect our chapter bodies to contain HTML text. For this to display properly, we'll have to make a slight adjustment to our chapter show view.

Move the scaffolded view around to get it looking like this:

### app/views/chapters/show.html.erb ###

<% content_for :title, @chapter.title %>

<div class="form-actions">
  <%= link_to t('.back', :default => t("helpers.links.back")),
              chapters_path, :class => 'btn'  %>
  <%= link_to t('.edit', :default => t("helpers.links.edit")),
              edit_chapter_path(@chapter), :class => 'btn' %>
  <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
              chapter_path(@chapter),
              :method => 'delete',
              :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')),
              :class => 'btn btn-danger' %>
</div>

<div id="chapter-body">
  <%= raw @chapter.body %>
</div>

The two important parts here are at the very top and very bottom.

At the top we have a <% content_for :title, @chapter.title %>. Thanks to the way Twitter Bootstrap set up our application layout, this is the way we can set the page <title>, even though it's defined in the layout, not in this view. If we don't use content_for :title ourselves, it defaults to "Book" for the <title>.

"Book" because we originally did $ rails new book.

Have a look:

### app/views/layout/application.html.erb ###

<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <title><%= content_for?(:title) ? yield(:title) : "Book" %></title>
...

Then at the bottom of the view, we have a div#chapter-body container element that will hold our chapter body so that we can get at it easily with jQuery later on.

We then place our chapter body within this div#chapter-body, but we do so by first passing @chapter.body into the raw method. This is necessary because we want the chapter body treated as HTML, not text.

Giving this a try without raw will demonstrate to you immediately why it's necessary.

Boom. Feature complete.

9.3 | Marks

Before we focus on user login and restricting chapter upload to the author, let's look at our other big feature: marks.

From our brainstorming session:

"While viewing chapters, users should be able to keep track of their progress with a bookmark. They should also be able to point out errors in the text. These errata should be emailed to the author by the application."

First, let's think about this from a UI standpoint.

The user interface for creating these marks should be part of the chapter show page, created in the page with JavaScript when a user clicks on a particular element. Additionally, the requests associated with these actions will be done with Ajax.

Then, we'll need to think about the marks themselves.

Bookmarks and errata actually have something in common: they reference a specific point in the chapter. Because of this, we'll be implementing this functionality as a Mark and then creating subclasses to handle the individual functionality of bookmarks and errata. Always remember: Don't Repeat Yourself

Ironically enough, yes, that was me repeating myself.

And for simplicity sake, the reference to a given point in a chapter referenced by a mark will be the index of a top level element within div#chapter-body. We'll talk more about this later.

Also, this certainly isn't the best way to do this, but doing anything more granular would take more time, attention, and work that is just plain out of scope for us right now.

As for the subclasses, users will be allowed one bookmark and can submit as many errata as they like. Bookmarks pertain to a chapter and place in that chapter. Errata pertain to a chapter, a place in that chapter, and should also allow for a note (to be included in the email sent to the author).

However, the way we'll be subclassing will require that both classes use the same table (marks), so the table will need to support all of the following, even if bookmarks will never need a note:

  1. chapter reference
  2. element index
  3. note

Again, this is a waste, but not worth spending any time worrying about within the scope of this book.

This means of subclass that we'll be using for bookmarks and errata is a Rails technique called Single Table Inheritance (STI). As the name implies, we will be storing the records for bookmarks and errata in a single table, marks, because these two classes will inherit from Mark.

So with all of that said, let's start scaffolding marks.

Actually, we won't be needing any views for marks since we'll be baking their user interfaces into the chapter views ourselves. Because of this, we won't need a full scaffold and will use a the resource generator instead:

$ rails generate resource Mark chapter:references index:integer note:text type:string
      invoke  active_record
      create    db/migrate/20120829082355_create_marks.rb
      create    app/models/mark.rb
      invoke    test_unit
      create      test/unit/mark_test.rb
      create      test/fixtures/marks.yml
      invoke  controller
      create    app/controllers/marks_controller.rb
      invoke    erb
      create      app/views/marks
      invoke    test_unit
      create      test/functional/marks_controller_test.rb
      invoke    helper
      create      app/helpers/marks_helper.rb
      invoke      test_unit
      create        test/unit/helpers/marks_helper_test.rb
      invoke    assets
      invoke      coffee
      create        app/assets/javascripts/marks.js.coffee
      invoke      scss
      create        app/assets/stylesheets/marks.css.scss
      invoke  resource_route
      route    resources :marks

The difference here from the scaffolding we did for chapters is that we're using $ rails generate resource instead of $ rails generate scaffold.

After this is done, remember to run rake db:migrate to make the table for marks.

As we can see from the output, this generator creates a directory, app/views/marks, for our views, but doesn't put anything in it. Just what we wanted.

But looking through the arguments passed to this generator, there are two things that stand out: chapter:references and type:string

chapter:references is how we keep track of the chapter that a mark is associated with. It creates an integer column to keep track of the id of the Chapter that a given mark belongs to. And from a generator standpoint, chapter:references functions mostly the same as if we had instead passed chapter_id:integer:index as an argument (with the exception that it also threw a line in app/models/mark.rb as we'll soon see).

The chapter_id this creates is an integer foreign key that is indexed.

Because chapters will have many marks, each of those marks will need a means to reference the chapter they belong to. chapter_id accomplishes this as a foreign key whose value will be the primary key (the id) of the chapter it belongs to.

The other curious argument passed to the generator, type:string, is for use in the eventual subclassing of marks we will be doing in order to create bookmarks and errata. In ActiveRecord, type is treated specially and reserved for use in single table inheritance. type holds the string value of the subclass name for the record.

In short, bookmarks will have a type of "Bookmark" and errata will have a type of "Erratum".

Because both bookmarks and errata will be stored in the marks table, this type string will be the only way that Rails will be able to know what each record's class is.

Also, these records will also end up being associated with a user, but as we don't yet have users, we'll just ignore that for now.

Mark Functionality

So let's take a look at our newly scaffolded Mark model.

### app/models/mark.rb ###

class Mark < ActiveRecord::Base
  belongs_to :chapter
  attr_accessible :index, :note, :type
end

There's something we haven't yet seen in this model: belongs_to :chapter.

This line was added here because we specified chapter:references in our generator call. belong_to sets up the association for these models, at least from the marks' perspective. We'll be setting up what's necessary from the chapter side once we start on our subclasses. This belongs_to will set Mark instances up so that we can access their associated chapter with a chapter method as in mark_instance.chapter.

Also, it's important to note that these association methods (e.g. belong_to) expect that foreign keys follow the convention [model]_id (e.g. chapter_id) and that they are integers values representing the primary key id of the referenced model.

What this means to us here is that belongs_to :chapter expects that a given Mark to have a chapter_id that represents the id of the Chapter it belongs to.

We can certainly tell it to expect something else, but this is the default.

Before we move on, we're going to want to remove :type from attr_accessible and add :chapter_id. My model ends up looking like this:

### app/models/mark.rb ###

class Mark < ActiveRecord::Base
  attr_accessible :index, :note, :chapter_id

  belongs_to :chapter
end

We can remove type from here because we won't need to be setting this directly by web request and therefore won't need the attribute to be accessible.

You'll also notice that I moved things around. I like to have my attr_accessor up top, then associations, then class methods, then instance methods.

No real reason for this, it's just the order that makes sense to me.

Our Subclasses

With that, let's stub out our subclasses.

### app/models/bookmark.rb ###

class Bookmark < Mark
end
### app/models/erratum.rb ###

class Erratum < Mark
end

This may look like like nothing, but check out what we can do in rails console now:

# we can create a blank `Erratum`
> Erratum.create
=> #<Erratum id: 1, chapter_id: nil, index: nil, note: nil, type: "Erratum", created_at: "2012-08-30 03:36:27", updated_at: "2012-08-30 03:36:27">

# we can create a blank `Bookmark`
> Bookmark.create
=> #<Bookmark id: 2, chapter_id: nil, index: nil, note: nil, type: "Bookmark", created_at: "2012-08-30 03:36:40", updated_at: "2012-08-30 03:36:40">


# we can retrieve the `Erratum`
> Erratum.first
=> #<Erratum id: 1, chapter_id: nil, index: nil, note: nil, type: "Erratum", created_at: "2012-08-30 03:36:27", updated_at: "2012-08-30 03:36:27">

# we can retrieve the `Bookmark`
> Bookmark.first
=> #<Bookmark id: 2, chapter_id: nil, index: nil, note: nil, type: "Bookmark", created_at: "2012-08-30 03:36:40", updated_at: "2012-08-30 03:36:40">


# and we can tell that they are both `Mark`s
# notice the `type` values for each
> Mark.all
=> [#<Erratum id: 1, chapter_id: nil, index: nil, note: nil, type: "Erratum", created_at: "2012-08-30 03:36:27", updated_at: "2012-08-30 03:36:27">, 
#<Bookmark id: 2, chapter_id: nil, index: nil, note: nil, type: "Bookmark", created_at: "2012-08-30 03:36:40", updated_at: "2012-08-30 03:36:40">]


# rails even knows the plural of "Erratum"!
> Erratum.to_s.pluralize
=> "Errata"

So even though we only created empty classes, we've already got some basic functionality thanks to the type column and inheritance.

We'll stuff plenty more logic inside these classes in a bit, but first let's get the necessary CRUD features for each working.

Actions and Routes

So let's think about how our users will interact with different kinds of marks. To start from the beginning, we'll need to look at controller actions and routes. What are we going to want to be able to do with marks?

First, users should be able to create errata and the author to be able to later view them within a chapter view. This means that we just need create (submit erratum) and index (view all errata for a chapter) actions for errata.

We will need to destroy errata too (as they are corrected), but the way we'll be doing this is a little unusual, so we'll be working on it later.

Each user will also be allowed a single bookmark to keep track of where they left off in the book. This will of course require a create action, but it's debatable whether or not an update action is needed too. For simplicity's sake, our application will simply destroy the old bookmark and create a new one in order to "update the bookmark". A show action will also be needed to let the user retrieve their bookmark.

So now that we know what actions we want, let's think about routes. This time around, we have something new to consider: marks have an association to a chapter.

We could handle this a number of ways, but the most elegant would be with the use of nested routes. A nested route for marks#create to create a erratum belonging to the chapter with an id of 123 would be POST /chapters/123/erratum, for example.

So let's adjust config/routes.rb to add a create route for bookmarks and errata.

Your file should look something like this:

Book::Application.routes.draw do
  resources :marks

  resources :chapters
end

And we want to change it to this:

Book::Application.routes.draw do
  resource  :bookmark, only: [:show], controller: :marks, type: 'Bookmark'

  resources :chapters do
    resource  :bookmark, only: [:create],         controller: :marks, type: 'Bookmark'
    resources :errata,   only: [:create, :index], controller: :marks, type: 'Erratum'
  end
end

We see here with resources :chapters do that the resources method accepts a block. Any subsequent calls to resources within this particular blocked will be nested under /chapters.

We also see that with bookmarks, we are using the singular resource and the singular :bookmark. For errata, we are using the usual resources because this is a plural resource.

It's also clear here that resources and resource accept more than one argument.

First we see that an argument in the form only: [:action1, :action1] can be used to specifiy what actions we want.

Inversely, except: [:action] can be used to exclude actions.

Next, we see that the form controller: :controller_name_symbol can be used to specifiy the controller the route should use. Without controller :marks above, bookmarks would get routed to BookmarksController and errata to ErrataController. Seeing as those controllers don't and won't exist, that would be a problem.

These methods also accept the form action: :action_name as an argument.

We also see arguments of the form type: 'ClassNameString' above which are actually more literal rather than magic. What these do is make sure that the proper type is passed as a parameter for these requests. For example, 'Bookmark' will be stuffed in params[:type] for bookmark requests because of the type: 'Bookmark' above. The reason this is needed is that both of these classes will be using the marks DB table and MarksController, but we need to make sure that they are each saved as the proper type (subclass).

And finally, we can see that we have one bookmark route nested and one unnested. The reason for this is that POST /chapter/1/bookmark will be used to create a bookmark within the chapter with an id of 1 while /bookmark will be used to retrieve the bookmark for the current user.

...once we have a current user.

All right, let's look at all of our available routes now.

$ rake routes
        bookmark GET    /bookmark(.:format)                      marks#show {:type=>"Bookmark"}
chapter_bookmark POST   /chapters/:chapter_id/bookmark(.:format) marks#create {:type=>"Bookmark"}
  chapter_errata GET    /chapters/:chapter_id/errata(.:format)   marks#index {:type=>"Erratum"}
                 POST   /chapters/:chapter_id/errata(.:format)   marks#create {:type=>"Erratum"}
        chapters GET    /chapters(.:format)                      chapters#index
                 POST   /chapters(.:format)                      chapters#create
     new_chapter GET    /chapters/new(.:format)                  chapters#new
    edit_chapter GET    /chapters/:id/edit(.:format)             chapters#edit
         chapter GET    /chapters/:id(.:format)                  chapters#show
                 PUT    /chapters/:id(.:format)                  chapters#update
                 DELETE /chapters/:id(.:format)                  chapters#destroy

Take a look at the the first four routes, as those are the ones new to us.

So let's say that we make a request GET /chapters/123/errata. This will land up in marks#index and we'll want to look up all the errata for a given chapter. We can obtain the proper chapter simply with @chapter = Chapter.find(params[:chapter_id]), thanks to the chapter_id provided by the nested routes.

But once we have this @chapter, we'll want to be able to do is call chapter.errata to get all of its errata. Before this will work though, we'll have to make a change to our chapter model to inform Rails of this relationship from the chapter's point of view:

class Chapter < ActiveRecord::Base
  ...
  has_many :errata
end

has_many works something like the opposite of belongs_to: it gets Erratum from the :errata argument and searches for any Erratum that have a chapter_id that matches self.id (self being an instance of Chapter).

Now we can do even more in rails console.

# create a `Chapter` to play with
> chapter = Chapter.create title: "testing errata relationship"
=> #<Chapter id: 13, title: "testing errata relationship", created_at: "2012-08-30 08:09:36", updated_at: "2012-08-30 08:09:36", body: nil, markdown_file_name: nil, markdown_content_type: nil, markdown_file_size: nil, markdown_updated_at: nil>

# create an `Erratum`, associated with nothing
> erratum = Erratum.create note: 'test erratum', index: 1
=> #<Erratum id: 3, chapter_id: nil, index: 1, note: "test erratum", type: "Erratum", created_at: "2012-08-30 08:11:35", updated_at: "2012-08-30 08:11:35">

# check to make sure `chapter` has no errata
> chapter.errata
=> []

# push `erratum` onto `chapter`'s errata
> chapter.errata << erratum
=> [#<Erratum id: 3, chapter_id: 13, index: 1, note: "test erratum", type: "Erratum", created_at: "2012-08-30 08:11:35", updated_at: "2012-08-30 08:12:36">]

# check again to see that it was added
> chapter.errata
=> [#<Erratum id: 3, chapter_id: 13, index: 1, note: "test erratum", type: "Erratum", created_at: "2012-08-30 08:11:35", updated_at: "2012-08-30 08:12:36">]

Here we add erratum to chapter.errata with the << operator, but we also could have blown away any previous errata for the chapter and used chapter.errata = [erratum] instead.

Alternatively, we could have set the :chapter_id in the create call to associate the two, like this: Erratum.create ..., chapter_id: chapter.id.

I've seen it suggested that you not do use this last method, but it is the only option that doesn't require you to load the other resource. That is, doing something like Erratum.create chapter_id: params[:chapter_id] does not require the associated Chapter to be retrieved from the DB.

This might seem completely trivial, but depending on what you're doing and how often you're doing it, it could be a big deal.

Now that we've got our chapter model configured to have many errata, let's build a marks#index action.

Oh, and remember that we don't need an index action for bookmarks, so we'll hardcode this one for errata.

### app/controllers/marks_controller.rb ###

class MarksController < ApplicationController
  respond_to :json

  def index
    @errata = Chapter.find(params[:chapter_id]).errata
    respond_with @errata
  end
end

Here we're skipping the format.json style we saw from our earlier scaffolded controller and use respond_to in combination with respond_with instead. One important note about this is that we call respond_to :json outside of the actions to indicate that all the actions will respond to json and we use respond_with inside of actions to render resources.

This index action will just render the errata as JSON, but we assign them to an instance variable anyway.

No particular reason for this here, but I tend throw things in instance variables like this in case I come back later and expect to find them there.

Let's test this out. Your chapter id will vary, but since mine was 13, I hit /chapters/13/errata.json and got:

[
  {
    chapter_id: 13,
    created_at: "2012-08-30T08:11:35Z",
    id: 3,
    index: 1,
    note: "test erratum",
    updated_at: "2012-08-30T08:12:36Z"
  }
]

Well, that's the version I saw thanks to JSONView. Literally though, I was served the string value for [{"chapter_id":13,"created_at":"2012-08-30T08:11:35Z","id":3,"index":1,"note":"test erratum","updated_at":"2012-08-30T08:12:36Z"}].

So that's working, but we since we don't have a means of making new mark records outside of rails console, let's add a create action to our controller. This action will be different than those we've seen before, because it will need to handle requests to create both errata and bookmarks. Accomplishing this will require some fancy footwork to accommodate each subclass.

### app/controllers/marks_controller.rb ###

class MarksController < ApplicationController
  respond_to :json

  before_filter :prepare_params, only: [:create]

  def index
  ...

  def create
    if @mark = klass.create(params[:mark])
      render :json => @mark
    else
      head :unprocessable_entity
    end
  end

private
  def klass
    @klass ||= params.delete(:type).constantize
  end

  def prepare_params
    class_sym = klass.to_s.downcase.intern
    params[:mark] = params.delete(class_sym)
    params[:mark][:chapter_id] = params.delete(:chapter_id)
  end
end

Though we may not understand the exact mechanisms that allow it to work, the logic inside of the new create action is fairly straightforward: we try to create a new mark from the parameters, then return the JSON for the newly created mark if that succeeds or return an appropriate HTTP 422 status code if it fails.

The one somewhat strange thing is that we're using render :json => @mark here instead of the respond_with @mark style that we saw in index.

The reason we use this is that respond_with handles this with a redirect to either bookmark_url or erratum_url and will blow up for the latter, because we don't have it defined in our routes. To avoid this, we'll just use render.

That said, what on earth is klass? And how does any of this work?

To get some answers, let's work through things by order of execution.

First of all, the before_filter :prepare_params, only: [:create] at the top of the controller will cause the prepare_params method the be executed before execution moves into the create action

This won't happen for the other actions because of the :only.

Moving down to the prepare_params method definition, we see that it is below a private keyword, so it will be scoped as a private method.

This makes sense for non-action methods inside of a controller, since they'll never need to be accessed from outside of the controller.

Before we look at this method itself, we should first discuss its purpose.

When create receives a request it will be getting the id of the Chapter to be associated with the mark passed in by way of the nested route. Because it is passed in using a nested route, it will be stored at params[:chapter_id] and not in, say, params[:bookmark][:chapter_id] as we would like it to be. So our first goal here is to put this chapter_id with the rest of the mark data either in params[:bookmark][:chapter_id] or params[:erratum][:chapter_id].

Also, as we just mentioned, params will have either a params[:bookmark] or params[:erratum] hash, depending on the kind of request. To handle both of these and have everything in the same place, we'll be removing whichever one of these hashes we receive and place it inside of params[:mark] instead. For example, if we receive a request with params[:bookmark], we will want to move that data to params[:mark].

Looking into prepare_params itself we see that it is immediately calling our other private method in the controller, klass, in the line class_sym = klass.to_s.downcase.intern. The purpose of this line is to get a symbol representing the proper class, either :bookmark or :erratum. Assuming for now that klass does its job and class_sym contains the proper symbol, the next two lines:

  1. delete all of the mark data out of params
  2. place it back in at params[:mark]
  3. move params[:chapter_id] in with the other mark data at params[:mark]

In summary, this method will take a params hash that looks like this:

{
  chapter_id: 1,
  bookmark: {    
    index: 1
  }
}

and make it look like this:

{
  mark: {    
    chapter_id: 1,
    index: 1
  }
}

Awesome. The latter hash is something that we can pass straight into a create for a subclass of Mark.

But how do we get the right subclass?

Let's look into that.

Looking at the first line of the create action, we can see our klass method is being called yet again with klass.create.

klass returns the proper subclass of Mark that we want to use to create our new record. It will return either Bookmark or Erratum, depending on whether params[:type] contains "Bookmark" or "Erratum", which means that klass.create will end up being equivalent to either Bookmark.create or Erratum.create.

And the value inside of params[:type] is dictated by which route brought us to the action.

Why is the method named klass and not class?

class is a Ruby keyword and because we're not defining a class, we don't want to use it here. We could name this method any number of things (e.g. clazz, mark_class), but I like using klass for these situations.

There's really only one line inside the klass method, but it's pretty packed: @klass ||= params.delete(:type).constantize.

There are really two parts of this line, one is concerned with getting the class and the other is concerned with not working too hard.

First and foremost, this line is concerned with returning the right class, given the string representation of a class name. For instance, if params[:type] contains "Bookmark", it should return Bookmark. It also uses params.delete(:type) to procure the type string, because we don't need that key/value pair stored in params after we've worked with it. To convert this string representation of a class name into the class it represents, we can then use a method that is defined by Rails on all strings named constantize.

To see this in action, let's emulate our new create action step by step in the rails console.

# a regular hash to mimic `params` in our `create` action 
> params = {
*   type: 'Bookmark',
*   bookmark: {
*     index: 1
*   },
*   chapter_id: 1
* }
=> {:type=>"Bookmark", :bookmark=>{:index=>1}, :chapter_id=>1}

# remove the type from `params`
> klass_str = params.delete(:type)
=> "Bookmark"

# is gone
> params
=> {:bookmark=>{:index=>1}, :chapter_id=>1}

# turn the type string into a class
> klass = klass_str.constantize 
=> Bookmark(id: integer, chapter_id: integer, index: integer, note: text, type: string, created_at: datetime, updated_at: datetime, user_id: integer)

> klass == Bookmark
=> true

# turn that class into an all-lowercase symbol
> class_sym = klass.to_s.downcase.intern
=> :bookmark

# move `params[:bookmark]` to `params[:mark]` 
> params[:mark] = params.delete(class_sym)
=> {:index=>1}

# `chapter_id` still needs to be moved
> params
=> {:chapter_id=>1, :mark=>{:index=>1}}

# delete it and move it in with the mark data
> params[:mark][:chapter_id] = params.delete(:chapter_id)
=> 1

# everything is as it should be
> params
=> {:mark=>{:index=>1, :chapter_id=>1}}

# finally create the new record
> klass.create params[:mark]
=> #<Bookmark id: 17, chapter_id: 1, index: 1, note: nil, type: "Bookmark", created_at: "2012-10-04 07:27:19", updated_at: "2012-10-04 07:27:19", user_id: nil>

# in this case, the above is equivalent to:
# Bookmark.create {:mark=>{:index=>1, :chapter_id=>1}}

So that explains pretty much everything, but what is going on with the ||= in @klass ||= params.delete(:type).constantize?

Basically we're mixing instance variables and the ||= conditional assignment operator to perform some primitive caching.

We only want to delete params[:type] and transform its value into a class once. After that, we know it won't change for the duration of the request and really just want it to always return that value.

Let's head back to the $ rails console to take a look at how this works.

 
$ rails console
Loading development environment (Rails 3.2.8)

> @klass ||= String
=> String

> @klass
=> String

> @klass ||= Hash
=> String

> @klass
=> String

The key here is that during our second usage of ||= above, Ruby doesn't bother to assign Hash to @klass because @klass is already defined. Furthermore, that attempt returns String because that's the value already assigned to @klass.

We take advantage of this caching because we are calling klass twice, once in prepare_params to get class_sym and once in create for klass.create params[:mark].

The advantages here are marginal, but this can be a very powerful tool.

READERS - Two questions. Did you understand the explanations above? Did you think the examples were more useful than the text? I took forever to proof-read this section but I still feel it may be a bit muddied.

The "View"

With everything else in place, let's start working on our user interface for creating errata.

Keeping in mind that we'll want somthing that works nicely for mobile devices as well, a good solution for this UI would be a modal window. More specifically, we will be making use of the modal conveniently offered by Twitter Bootstrap.

It's likely that you've already used this functionality since you're reading this part of the book, but we'll be reasoning through why it is the way it is.

Here is a description of this functionality in English:

On click of any child element of div#chapter-body, a modal window should appear and offer three different buttons: "Cancel", "Bookmark", and "Report Erratum". "Cancel" will close the modal, "Bookmark" should delete the user's previous bookmark and create a new one for the proper chapter and element, and "Report Erratum" should display another UI element to create a new erratum. This erratum interface should consist of a friendly message, a <textarea> for the note, and a "Submit" button to create the erratum. Additionally, pressing the "Bookmark" button should also highlight the bookmarked element if the request for creation was successful. Failure for any Ajax request to the server should cause an alert to appear to notify the user. All actions resulting in an Ajax request should also close the modal.

Well, I suppose the friendliness of the message isn't a functional requirement but...

With that, the place to start is pretty obvious: we need to build a modal!

The modal itself is just plain HTML, but it should be display: none by default so that we don't see it on page load. A shortcut to accomplish this is to use the .hide offered by Twitter Bootstrap.

i.e. Twitter Bootstrap has already declared .hide { display: none; }.

The HTML for our modal needs to be loaded into app/views/chapters/show.html.erb, but let's place the markup for our modal in a partial to keep from further cluttering up our chapters/show view.

First, we'll create our partial:

### app/views/chapters/_modal.html.erb ###

<div class="modal hide fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
    <h3>Bookmark or Erratum</h3>
  </div>
  <div id="erratum-ui" class="modal-body">
    <p>A reference to the area you clicked will be included in the report, but feel free to add a note to help the author find the error. Comments, suggestions, and questions are also welcome.</p>
    <%= form_for Erratum.new, url: chapter_errata_path(@chapter), remote: true do |f| %>
      <%= f.text_area :note, :class => 'text_area' %>
      <%= f.hidden_field :index, :class => 'index' %>
      <%= f.submit "Submit", :class => 'btn btn-primary' %>
    <% end %>
  </div>
  <div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
    <%= form_for Bookmark.new, url: chapter_bookmark_path(@chapter), remote: true do |f| %>
      <%= f.hidden_field :index, :class => 'index' %>
      <%= f.submit "Bookmark", :class => 'btn btn-primary' %>
    <% end %>
    <button id="show-erratum-ui" class="btn btn-primary">Report Erratum</button>
  </div>
</div>

And then we'll need to make chapters/show use it:

This can go anywhere at the top level of the view.

### app/views/chapters/_modal.html.erb ###
...
<%= render partial: 'modal' %>
...

The modal markup above might look involved, but I more or less copy-pasted it from the Twitter Bootstrap documentation and adapted it to our needs by tossing in some stuff like form_for ... remote: true.

The classes for the top level <div>'s all have special purposes: modal let's Twitter Bootstrap know that this should be treated as a modal, hide is used so that it will be hidden on page load, and fade causes the modal to animate in instead of just appearing with no animation.

The id="modal" is there to help us get ahold of this element easily with jQuery.

.modal-header is pretty straightforward, just a heading and a close button.

When rendered, .modal-body#erratum-ui will contain a <form> for a new erratum for the proper chapter. This element also has the hide class, so that it will be hidden when the modal opens.

.modal-footer has three buttons, the "Cancel" button being the only really peculiar one, as it has a data-dismiss="modal" attribute. This is an easy way of assigning a button to close the modal without explicitly using JavaScript.

You'll notice that all of the <button>'s have btn classes. These impact the styling of the buttons and are explained in more detail in the Twitter Bootstrap documentation.

We're also going to want to add a little bit of CSS to app/assets/stylesheets/chapters.css.scss to make the bookmark button display properly and to size the text box more reasonably:

### app/assets/stylesheets/chapters.css.scss ###

form#new_bookmark {
  display: inline;
}

#erratum-ui textarea {
  width:  98%;
  height: 150px;
}

This file ends in .scss, but we can still write regular CSS in it. There'll be more on SCSS later on.

All in all, not particularly interesting HTML, but Twitter Bootstrap will make it display quite nicely.

Speaking of, let's work on getting the modal to actually display.

First, delete the file app/assets/javascripts/chapters.js.coffee which was created by our controller generator.

Then create a file named app/assets/javascripts/chapters.js and add add to following to it:

### app/assets/javascripts/chapters.js ###

// runs everything inside on page ready
$(function(){

  // only run for paths of the form /chapters/:chapter_id
  if(window.location.pathname.match(/^\/chapters\/\d+/)){

    // a convenience variable to get at our modal with jQuery
    $modal = $('#modal');

    // bind the click event on all `#chapter-body` children to execute `chapterElementClickHandler`
    $('#chapter-body').children().click(chapterElementClickHandler);

    function chapterElementClickHandler(){
      // the index of the element that was clicked
      index = $(this).index();
      // set all hidden fields for index appropriately
      $('input.index').val(index);

      // show the modal
      $modal.modal('show');
    }

  }

});

The regular expression /^\/chapters\/\d+/ will test the path to see if it begins (^) with /chapters/[any number of digits]. This chapters.js file will get pulled in on every page load for the entire site (per the // require_tree . in application.js), but we don't need to bother executing it unless we're on a chapters/show page (and not preface or resources).

The comments should explain this sufficiently, but there are a couple notes.

First, the $modal variable name is nothing special, but follows a convention you see some people use to indicate the contents of a variable is a jQuery object.

Of course, still others are vehemently against this. I put it in here because I think it's important to be able to read lines like these that use controversial conventions, even if you don't agree with them.

Next, we used jQuery's .click in this case because we're not planning on dynamically adding anything to #chapter-body. If we were, we'd probably want to use some kind of .live('click', ...) that would bind to all elements described, present and future.

Then in the callback, we grab the clicked element index and populate the hidden fields with its value. The reason that we are doing this here in this callback and not in the actual callback for the erratum creation is that we won't have the same $(this) for that callback and will have lost our chance at grabbing an index by then. So, to look ahead, we populate the hidden fields for both forms with the index while we have it in hand.

Also, the input in input.index is technically unnecessary, but is a nice declarative touch.

Of course, .val is only used on <inputs>'s and the like, but having input right in the line is helpful while skimming.

And can also find out more about the .index() jQuery method used above here.

And finally, if you've assidously read the Twitter Bootstrap documentation, you'll know that .modal() will actually work the same as the .modal('show') above, but, again, adding 'show' makes it more clear what is happening.

If we revisit a chapter show view in the browser, we'll now see the modal appear. Clicking "Cancel", the close "×", anywhere outside the modal, or hitting ESC will cause it to close. We'll get to the other buttons in a minute, but for now one glaring problem is that the UI to create an erratum is visible, even though we don't want it to be. We could use CSS to fix this, but what we really want is for this UI component to be hidden every time the modal opens and for it to appear only when we click "Report Erratum".

Let's solve both of these problems by adding another click handler and modifying our existing callback.

All JavaScript for the rest of this section will also live inside of the $(function(){...});.

### app/assets/javascripts/chapters.js ###
...

  // existing callback
  function chapterElementClickHandler(){
    // NEW! - hide the erratum interface before anything else
    $('#erratum-ui').hide();

    index = $(this).index();
    $('input.index').val(index);

    $modal.modal('show');
  }

  // click handling for the "Report Erratum" button
  $('#show-erratum-ui').click(function(){ $('#erratum-ui').show() });

...

The logic for showing the erratum UI is only one line, so we just pass in an anonymous function as the callback to perform the .show().

With that working as expected, we'll now want to make an HTTP POST to our app from this UI to create a new erratum. To accomplish this without an unnecessary page refresh, we'll be using Ajax. We could do this ourselves rather easily with jQuery's .post method, but there's an even easier Rails 3 way of handling this, so we'll be using that instead.

Actually, believe it or not, it already works.

How?

Because both of our form_for calls in the modal use remote: true to indicate that when they POST, they should do so using a jquery-ujs XHR. And by putting the note field in the form, hitting the nested route, and populating the hidden fields on click of an element, we're pretty much loaded up and ready to go.

We'll talk about UJS in Rails later.

Make sure rails server is running, open the modal, drop yourself a erratum note, and press "Submit".

If you look at the output of rails server you'll see that it received the POST and performs an SQL transaction containing a INSERT INTO "marks". Looking closer at the tail end of the SQL, you'll also be able to see the proper chapter_id, index, note, and type.

Score.

As I mentioned earlier, this same effect could be acheived with jQuery rather easily, like this

$.post('/chapters/'+chapter_id+'/errata', { erratum: {index: index, note: note} });

Assuming chapter_id, index, and note are defined.

Still, we're definitely not done with this feature yet, because the modal just hands around even after we've submitted the form and created our new mark. Additionally, we're assuming success and not covering an Ajax error state.

To rectify this, we need to assign some callbacks to the XHR. And to do that, we'll be binding to a couple different events for the forms.

The reason we'll be binding to the forms for this is that the Rails JS that is making the XHR for us will fire events pertaining to that XHR on the form submitted.

And remember, the reason this JS knows that we want it to send the XHR on form submit for us is because we made the form with form_for ... remote: true.

This may all seem wildly disjointed, but once it all sinks in, it's really a quite elegant solution.

We'll be using jQuery's .bind to bind to these events and the events that will be fired on a submitted form are as follows:

  • ajax:beforeSend
  • ajax:complete
  • ajax:success
  • ajax:errror

First, let's deal with form#new_erratum:

### app/assets/javascripts/chapters.js ###
...


  // the callbacks
  function hideModal(){ $modal.modal('hide'); }
  function ajaxError(){ alert('Something went wrong. Please wait a moment and try again.'); }

  // binding to the ajax events
  $('form#new_erratum')
    .bind('ajax:beforeSend', hideModal)
    .bind('ajax:error', ajaxError);

...

Notice that we can chain these .bind calls. You see this quite a bit with jQuery methods.

We could also hide the modal on ajax:complete or one of the other callbacks, but we don't know how long that could take, so it's probably a better idea to do it on ajax:beforeSend, especially now that we're covering the error case.

The modal will now hide after we click "Submit". And now if you kill rails server and try to create an erratum, you'll get an alert informing you of the failure.

This ajax:[event] functionality was actually just added in Rails 3 and is referred to as Unobtrusive JavaScript (UJS). Previous versions of Rails would use JS Ajax libraries to send the requests and send back responses containing JS as text to be evaluated, but this ended up leading to JS spread all over the place and was deemed "obtrusive".

So now, before a remote: true <form> is submitted, Rails UJS does something like form.trigger('ajax:beforeSend'). When it gets a response it fires form.trigger('ajax:complete') and so on. By binding to these events we are able to react appropriately using callbacks.

You can find more about Rails UJS on the resources page.

Again, this could be done with plain jQuery several ways. An example would be to tag on to our $.post from before:

$.post(...).success(successCallback).error(errorCallback);

We don't yet have users to attach bookmarks (or errata) to, but as that will end up being an entirely controller-side job, we can finish off our bookmark creation UI while we're here too.

As was the case with errata, the bookmark form works as-is for record creation, but it won't close the modal and won't alert an error if the XHR fails. And with bookmarks, we'll also want to handle a successful bookmark creation: we'll want to highlight the element that was bookmarked.

Often times when we create a record, the result will mean a change in how a particular view is rendered. The problem that arises when doing this with Ajax is that because the page does not reload, we don't see the change reflected in the page. It then becomes our responsibility to make the appropriate change with JavaScript instead.

Here this is quite simple; we just need to clear any previous bookmarks and then mark the new one.

First we'll need some CSS for a background-color for the .bookmark class to make it stand out:

### app/assets/stylesheets/chapters.css.scss ###

...

.bookmark {
  background-color: #88CCFF;
}

Then we'll just need to create a callback to clear all elements of that class and apply it to the element for the new bookmark, as well as bind to all the right events for form#new_bookmark:

### app/assets/javascripts/chapters.js ###
...

  function highlightBookmark(evt, bookmark, jqXHR){
    // get anything with a class of `.bookmark` and clear that class
    $('.bookmark').removeClass('bookmark');

    // find the proper element to highlight using `.eq()` 
    // and `bookmark.index` from the JSON response
    el = $('#chapter-body').children().eq(bookmark.index);
    // add the class to the element
    el.addClass('bookmark');
  }

  // assign all the callbacks, reusing our two from before
  $('form#new_bookmark')
    .bind('ajax:beforeSend', hideModal)
    .bind('ajax:error', ajaxError)
    .bind('ajax:success', highlightBookmark);

...

And since we've already written marks#create to handle both subclasses, we're done messing with marks for now.

But we'll see them again later.

9.4 | User Login

So now that we've added implemented chapter upload and marks, what our app really needs now is the ability to associate certain records with the people that created them.

Furthermore, we don't want anyone but the author editing chapters in our app. In fact, we don't even want just anyone viewing the chapters.

What this boils down to is that we need to know who a visitor is and what they're allowed to see and do. Beyond that, there are times when we won't know who the visitor is and we should respond accordingly until they identify themselves.

Functionally, what we need to add is a way for users to log in and a way to permit only specific users (e.g. the author) to update a chapter. These needs fall into the respective categories of authentication and authorization.

9.4.1 | Authentication

First we'll need to deal with authentication, the process of proving that users are who they claim to be.

There are different methods of doing this, probably the most common one being the use of an email/username and password pair, but lately OAuth 2.0 has become rather popular as an alternative so we're going to give it a try instead.

With OAuth, we still keep track of users within our app, but we will essentially outsource the authentication process to an OAuth provider. The application you're viewing this chapter on offers four such providers: Facebook, Twitter, Google, and GitHub. For now we'll just be implementing authentication with GitHub as our OAuth provider, but the process is largely the same for the others.

In short, to authenticate our users, we'll first send them to GitHub to ensure that they're logged in there and to ask them to allow access to their account by our application. Once they accept, GitHub will direct them back to our application, passing along some information about their GitHub account that we will be able to use to uniquely identify them.

Going this route will be slightly more involved, but it will give us reason to look at a few interesting things, force us to interact some with third-party services, and consider how exactly we remember that a user is logged in to our application.

And for the time being, we're also going to ignore the need for billing and just focus on creating users.

If you want to go the traditional email/password route, I highly recommend the Devise Gem. Installing Devise is incredibly straightforward and well documented, so check out the link above if you're interested.

And though Devise can be used alone, it can also be used in combination with the method we're following here.

Setup

To kick things off, we'll be adding GitHub as an OAuth provider for our application.

And, it will probably come as no surprise, there is a Gem to help us with this. This Gem is called OmniAuth and it comes in different "strategies" for different providers.

Because we're only concerning ourselves with GitHub, we'll want to add gem 'omniauth-github' to our Gemfile and $ bundle install.

Next, we'll need to register our application with GitHub, which we can do here. Just fill out the form:

Form Field Value
Application Name whatever makes sense to you
Main URL http://localhost:3000
Callback URL http://localhost:3000/auth/github/callback

Then, of course, hit Register Application.

After registering the app, we'll be presented with two hexadecimal strings, Client ID and Client Secret. Both of these will be needed when we initialize OmniAuth in our application, so there's no better place to put them than in a file in config/initializers:

### config/initializers/omniauth.rb ###

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, '[GITHUB_CLIENT_ID]', '[GITHUB_CLIENT_SECRET]'
end

Be sure to drop in your client ID and secret strings you got from GitHub as the two string arguments to provider above.

The OAuth Process

Assuming the application is already registered with a provider (as we just did with GitHub), OAuth is used by an application to bounce over to the provider service to login a user (to that provider) and have them accept the request by the referring application for user information. If the user accepts, they are then redirected back to the originating application along with a bundle of user information offered by the provider. That application then uses the information to create a record to keep track of the user.

For our purposes, the OAuth login process for a brand new user looks like this:

  1. the visitor clicks some kind of link to "Login with GitHub", a link that points to /auth/github
  2. OmniAuth redirects this path to GitHub's OAuth service and asks the user if they want to allow access to our application
    • notice that this redirect is not to somewhere else in our application, but instead to GitHub
    • our application is uniquely identified because of client ID and secret that we just put in our initializer
  3. assuming the visitor allows access to our application, GitHub redirects _back to our application, at http:localhost:3000/auth/github/callback, with a hash of information about the user
    • notice that this is the callback URL we just used when we registered our app with GitHub
  4. we use this information to create a user record
  5. now that we have a user record, we send the visitor back a session (a special HTTP cookie) that contains the user ID for that record

GitHub's OAuth service is located at https://github.com/login/oauth/authorize.

The most important thing to notice here is that the client will have to bounce from our application to another service before finally arriving back at our application with information that will allow us to register and log them in.

As for identifying a logged in user once they have a user record in our system, all we'll need is their user ID stored in the session cookie and we can use ActiveRecord to grab the record for that user.

Storing the user ID in a cookie like this would definitely be a security issue, except for the way that Rails treats the session cookie. We'll look more at this later on in the section on security.

Once a user is registered, the process is essentially the same, except that we won't need the user to accept our application (unless we update permissions) and we won't need to create a new user record, we'll just look it up in the database.

The process looks like this:

  1. the visitor clicks some kind of link to "Login with GitHub", a link that points to /auth/github
  2. OmniAuth redirects this path to GitHub's OAuth service
  3. GitHub redirects back to our service, to /auth/github/callback
  4. this redirect comes with information about the visitor, which we can use to look up their user record in our application
  5. then once we have their user record, we respond with a session that contains the user ID for that record

Now let's work through our original list so that users can register with our application.

First up:

  • the visitor clicks some kind of link to "Login with GitHub", a link that points to /auth/github

This is easy, just some more HTML to add to our application layout. Look around to find the ul#nav we made to house the chapter dropdown menu, because we're going to want to add another <ul> as its sibling:

It actually might be easier to look for </div><!--/.nav-collapse --> and just add the new <ul> above that.

  ...
  <body>

    <div class="navbar navbar-fixed-top">
      <div class="navbar-inner">
        <div class="container">
          ...
          <div class="container nav-collapse">
            <ul class="nav">
              <li class="dropdown">
                <a class="dropdown-toggle" data-toggle="dropdown" href="#">Chapters</a>
                ...
              </li>
            </ul>

            <!-- ADD THIS-->
            <ul class="nav pull-right">
              <li class="login"><%= link_to 'Sign up or log in with GitHub', '/auth/github' %></li>
            </ul>
            <!-- ADD THIS-->

          </div><!--/.nav-collapse -->
  ...

You might notice that this <ul> has a the class pull-right. This is a class offered by Twitter Bootstrap that performs a CSS float: right on the element.

Now if you refresh your page, we'll see our login link.

The href for this link, as you can see above, is /auth/github. We won't need to set this route up ourselves, because it's already handled by Omniauth.

Next!

  • OmniAuth redirects this path (/auth/github/) to GitHub's OAuth service and asks the user if they want to allow access to our application
  • assuming the visitor allows access to our application, GitHub redirects back to our service, to /auth/github/callback, with a hash of information about the user

These are handled by OmniAuth and the provider, and require no real action from us yet, but we can take a look at what OmniAuth and GitHub (as our provider) are doing for us so far.

Head over to our application in Chrome and refresh the page to make sure you've got the latest, then click the login link we just created.

It should end up on the right side of the top nav if all went well.

Following this link will land us on a GitHub page asking Authorize [Name of Application]?, to which we will want to click Allow.

This redirects us back over to our application, which informs us that there has been a Routing Error. This might sound like a problem, but if we look closer we'll see this: No route matches [GET] "/auth/github/callback"

That's exactly where we asked GitHub to perform the OAuth callback, so everything is working perfectly.

Well, perfectly except for the fact that we're getting a Routing Error, right?

That will be resolved shortly. Bonus points if you already know the file we need to change to fix it!

Now that we've authorized the app from our GitHub account, we won't have to "Allow" it again, and it will be easy to follow the ping pong of the OAuth flow in Dev Tools now.

While viewing our app in Chrome:

  1. pop open Dev Tools
  2. click over to the "Network" tab
  3. "Clear" any HTTP requests that might be there (button for this is along the bottom)
  4. click the GitHub login link again

This will bounce us over to GitHub and back again (this time without the request for access) and result with the same Routing Error. This time though, we have a list of HTTP requests to look at in Dev Tools.

First we see the request to our application for the path /auth/github and that it is a 302, a redirect.

We then end up at http://github.com/login/oauth/authorize, but we see that the "Initiator" for this request is none other than http://localhost:3000/auth/github. This is basically "where we were redirected from", the URL of the previous request.

This HTTP transaction is where the authentication takes place on GitHub's end; it checks that the user is logged in and has allowed our application. Once that is complete, we see that this too results in a redirect, a response status of 302.

And finally, we end up back at our own application, at the path /auth/github/callback, with a 404, because we don't yet have a route for this path.

Looking at the "Initiator" for this final request, we see that it's the GitHub address we were just redirected from.

If we click on the HTTP request to http://github.com/login/oauth/authorize and make sure we're on the "Headers" tab, we can see that this request had four "Query String Parameters" sent along with it. Just like the redirect itself, these values are handled by OmniAuth. You can learn more about these parameters (and much, much more) in GitHub's OAuth documentation, but two of these are especially familiar: client_id and redirect_uri.

client_id was the value given to us after we registered our app with GitHub (the one that we put in that initializer along with the secret) and redirect_uri is the value we entered for Callback URL when we set up the app with GitHub.

Also, note the hexadecimal value of state for this request and then click over to the request to our app for /auth/github/callback. You'll see that both requests have a state query string parameter and both are the exact same value. This is a security feature of OAuth that is handled by Omniauth to ensure that no one is hijacking the request.

Omniauth keeps track of this state for us on the server and checks to make sure that the response from the OAuth provider returns a matching value.

All right, that was insightful, but not immediately useful. Let's keep moving through the list.

  • we use this information to create a user record

The logical question here is: where is this happening?

Well, from a route perspective, GitHub is redirecting everything back to /auth/github/callback like we asked it to, so we'll be working on this part there.

The next obvious question is then of course: where do we route it to?

If you sense a generator coming up, your senses are good. Except that this time we won't need a scaffold or views or even a specific resource!

The reason for this is that this controller will primarily be in charge of sessions, of logging users in and out. As it happens, we will be creating user records here, but the controller's primary responsibility is managing the session and it will be named to reflect that.

And while we could just write it by hand, why do that when there's a generator for that?

$ rails generate controller Sessions create destroy
      create  app/controllers/sessions_controller.rb
       route  get "sessions/destroy"
       route  get "sessions/create"
      invoke  erb
      create    app/views/sessions
      create    app/views/sessions/create.html.erb
      create    app/views/sessions/destroy.html.erb
      invoke  test_unit
      create    test/functional/sessions_controller_test.rb
      invoke  helper
      create    app/helpers/sessions_helper.rb
      invoke    test_unit
      create      test/unit/helpers/sessions_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/sessions.js.coffee
      invoke    scss
      create      app/assets/stylesheets/sessions.css.scss

$ rm -r app/views/sessions 

Yeah, I couldn't figure out how to tell the generator to not give me views too, so I just blew them and their directory away with a plain, old rm -r. ...like a friggin' caveman.

The -r flag in that rm stands for "recursive" by the way, one means of wiping out a populated directory.

So here we told the generator with our Sessions argument that we want a SessionsController (we apparently need to use the plural here, who knew?) and with the last two arguments also asked for stubbed create and destroy actions.

create will be the means by which we will "create" a session (log a user in) and destroy will be our way of "destroying" a session (logging the user out). The actual setting of the session is actually pretty straightforward, but create will have much more going on in it, because it will also have to check for user records or create them in the DB as necessary.

Generally speaking, session is actually a Rails feature that exists without us doing anything in particular, but when we refer to "session" here, we're talking specifically about the authentication functionality of the session cookie.

But before these actions can do anything, we need to route to them. So let's take care of that:

### config/routes.rb ###

Book::Application.routes.draw do
  match  '/auth/:provider/callback' => 'sessions#create'
  delete '/auth/logout'             => 'sessions#destroy', as: :logout

  resource  :bookmark, only: [:show], controller: :marks

  resources :chapters do
    resource  :bookmark, only: [:create],  controller: :marks, type: 'Bookmark'
    resources :errata,   only: [:create, :index], controller: :marks, type: 'Erratum'
  end
end

Here we've added two routes to the top of our config/routes, using some routing methods we haven't yet used: match and delete.

match is clearly working with a syntax of the form match '/some/path' => 'controller#action', but there is something pretty unique about match: it's HTTP verb agnostic. We can hit that route with a GET or a POST or a DELETE, whatever, and it will still route to sessions#create.

You might be thinking that sounds like it breaks from everything that we've talked about so far, but OAuth isn't RESTful and may be implemented differently from provider to provider.

If you look back at the HTTP request to our callback in Dev Tools, you'll see that it's a GET. So that's the HTTP verb GitHub decided to use, but I personally think it might be reasonable for a service to instead perform this as a POST. And since we can't control what HTTP request method the provider will use, this is a good place to leave some wiggle room.

Another interesting thing with this line is the :provider in '/auth/:priovider/callback'. This greatly resembles, for example, the :id in /chapters/:id we saw from our $ rake routes output and acts exactly the same way. By using :provider in this string representation of the path for this route, we are telling Rails that we want it to accept different strings in the place of :provider. For example, this route will not only accommodate /auth/github/callback, but will also accept /auth/facebook/callback, /auth/twitter/callback, and so on. As an added bonus, the value that is found here (e.g. 'github') will be grabbed by Rails and stuffed into params[:provider] for easy access in the controller.

The other route we added above uses the delete method which works the same as match except it only matches for HTTP DELETE requests.

get, post, and put methods also exist for their respective HTTP verbs.

And just to be sure, let's run $ rake routes just to see that everything works as we intended:

$ rake routes
                /auth/:provider/callback(.:format)  sessions#create
logout  DELETE  /auth/logout(.:format)              sessions#destroy
...

Perfect.

Here we can see that the logout route has a name, logout, which we get because we passed as: :logout into the delete method above. This way we can can use the URL helper methods logout_path and logout_url in our views.

And with that, /auth/github/callback will be routed into our new sessions#create action. We're going to need to create a new models to hold some of the data passed here by the OAuth provider, but first let's take a look at this data and figure out what we want to persist.

There are a number of ways to do this, but a quick and dirty way is to raise the OAuth hash as YAML. That is, we're going to purposefully raise an error in the action and pass in the YAML version of the OAuth hash.

We'll talk more about YAML in a bit, but think of it for now as a very human-readable JSON.

To do this, we'll need to open up our new SessionsController:

### app/controllers/sessions_controller.rb ###

class SessionsController < ApplicationController
  def create
    oauth_hash = request.env['omniauth.auth']

    raise oauth_hash.to_yaml
  end
  ...
end

Now head back to our application and click the GitHub login link again.

If all goes well, you'll get a RuntimeError in SessionsController#create.

Kind of strange that we want an error here, but we are explicitly raising it after all.

Also on the page you should see a wealth of other text about yourself, acquired from GitHub. Mine looks like this:

--- !ruby/hash:OmniAuth::AuthHash
provider: github
uid: 11111111
info: !ruby/hash:OmniAuth::AuthHash::InfoHash
  nickname: bchase
  email: ...
  name: Brad Chase
  ...
credentials: !ruby/hash:Hashie::Mash
  token: ...
  ...
extra: !ruby/hash:Hashie::Mash
  ...

At the very top level of the hash we see provider and uid. These are the values that we'll need to keep track of in order to recognize the user again through the same OAuth provider.

Moving down, we see an info hash which contains email and name. We will also be storing two values on the user record to identify the user in the system (e.g. information we can include in erratum emails from that user).

And finally, there's a token under the credentials hash. If we wanted to make subsequent requests to the GitHub API on behalf of this user (and had the permissions to), we could use this token to accomplish that.

We won't be needing to do anything like that here, but this value would be particularly useful for, say, programmatically posting something to a user's Facebook wall from our application.

Now that we know the data we're working with and what we want to keep, let's create a model to hold it.

$ rails generate model User name:string email:string provider:string uid:string
      invoke  active_record
      create    db/migrate/20120904045208_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml

$ rake db:migrate
==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0009s
==  CreateUsers: migrated (0.0010s) ===========================================

And with that, we've got everything ready to start writing a real sessions#create action. To kick things off, let's start with a simple one that dumbly creates a new user from the OAuth data passed in without checking for an existing user. This will let us focus on what basic record creation we'll need to be doing.

It looks like this:

### app/controllers/sessions_controller.rb ###

...

  def create
    # the hash of user data from the oauth provider
    oauth_hash = request.env['omniauth.auth']

    # values we want from the oauth hash
    provider = oauth_hash[:provider]
    uid      = oauth_hash[:uid].to_s
    name     = oauth_hash[:info][:name]
    email    = oauth_hash[:info][:email]

    # create the user
    user = User.create provider: provider, uid: uid, name: name, email: email

    # redirect to the chapters index
    redirect_to chapters_path
  end

...

Pretty straightforward, we're just creating a user record and then redirecting to the chapter index.

It might seem strange that we're casting the uid to a string, but this value might not always be an integer from every provider, it just happens to be one in our GitHub data.

We're definitely heading in the right direction, but there are a few critical things we're completely ignoring at the moment:

  1. we're not doing anything with the session
  2. we're just stupidly creating a new record, not checking if the user already exists in the application or not
  3. we have a fat controller

For the moment, we'll leave this action fat and our logic stupid and focus on setting the session and actually get the user properly logged in.

It's time for the next item in our list:

  • now that we have a user record, we send the visitor back a session (a special HTTP cookie) that contains the user ID for that record

As was mentioned before, the session already exists without any help from us, but it is entirely our responsibility to set is such that our application recognizes the user as "logged in".

To log our user in, we simply need to place the id of their User record somewhere in session that we can retrieve later. Doing this could not possibly be any easier:

### app/controllers/sessions_controller.rb ###

...

  def create
    ...

    # `user` was defined above
    session[:user_id] = user.id

    redirect_to chapters_path
  end

...

Now, that id will get passed into our app inside the session cookie with every request. Because of this, we can now remember who our users are!

Well, we could if we had a handy helper method to fetch the User record whose id matched the one in the session. Let's make that method now and name it current_user:

### app/controllers/application_controller.rb ###

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user

private
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
end

Putting this in ApplicationController and using helper_method :current_user will make this method accessible in all the other controllers (and by extension, their views) because they all inherit from this controller.

Looking at the current_user method, we see the caching technique of @instance_variable ||= ... used again. We will be using current_user repeatedly for each request, in both the controller and the views, so we cache the value here to prevent hitting the database repeatedly for the same record.

Now this current_user method will work great, until we blow away our database (which we'll do later).

After doing this myself, I ended up with a ActiveRecord::RecordNotFound ... Couldn't find User with id=1 when I tried this.

Any idea why this error is thrown?

Well, if I just hosed my entire DB, it shouldn't be surprising that trying to look up a User with any id would fail. ...because there aren't any. And this is exactly where the problem lies.

This error is the result of still having a session cookie with an old User id. Our current_user method then tries to use that ID to find that non-existent record in the DB. ...and then it fails. ...loudly.

But because this problem will raise an error, we can rescue it within our current_user method and respond by logging the user out (i.e. clearing out session[:user_id]).

Let's fix current_user now, to avoid this problem as we go along:

### app/controllers/application_controller.rb ###

...

  def current_user
    begin
      @current_user ||= User.find(session[:user_id]) if session[:user_id]
    rescue
      session[:user_id] = nil
    end
  end

...

We actually could have rescued this error in a variety of ways, but I like having the rescue at the method level here.

So now we'll have a user_id in the session, what can we do with it?

Well, we can do a number of things, but a good place to test it out would be the application layout.

If the user is logged in, we certainly don't want to be offering them a link to login, so let's instead display "Logged in as [User's Name], click here to logout" as a logout link. To do this we need to adjust the part of our layout where we recently added the link to login with GitHub:

### app/views/layouts/application.html.erb ###

  ...

    <ul class="nav pull-right">
      <% if current_user %>
        <li class="logout"><%= link_to "Logged in as #{current_user.name}, click here to logout", logout_path, method: :delete %></li>
      <% else %>
        <li class="login"><%= link_to 'Sign up or log in with GitHub', '/auth/github' %></li>
      <% end %>
    </ul>
  </div><!--/.nav-collapse -->

  ...

Here we are using if current_user to decide which of the two HTML fragments to render in the nav. This works because current_user will be truthy when the user is logged in and it returns a User instance, but will be falsy when the user is logged out and it returns nil.

Remember that if session[:user_id] at the end of our current_user method?

This actually gets used in a somewhat idiomatic way. Since this line ends in an if the line acts like an if statement in terms of its return.

This is to say that when an if check passes, it will return the value returned by the last line of the if. In this case, this is an instance of User returned from User.find.

However, when an if check fails, it will simply return nil.

We're also interpolating current_user.name into the text for the logout link and then using the logout_path that we made sure was defined earlier in the routes with as: :logout.

One other important thing to notice is the method: :delete at the end of the link_to used to create the logout link. This is necessary to make this a DELETE request when a user clicks the link.

And speaking of logout, we should implement that using the sessions#destroy action.

### app/controllers/sessions_controller.rb ###

...

  def destroy
    # log the user out
    session[:user_id] = nil

    # redirect back to the chapters index
    redirect_to chapters_path, notice: "You have been logged out."
  end

...

To log the user out, we simply blow away the value in sessions[:user_id] by setting it to nil.

This will result in current_user returning nil, which is exactly what we want.

Another added touch here is that we pass notice: "a notice message" here to redirect_to. We should be sure to render this notice somewhere.

Rails' default application layout will handle this all on its own, but the one that twitter-bootstrap-rails generated for us doesn't.

Look for that <div class="span12"> we made earlier and change it to look like this:

### app/views/layouts/application.html.erb ###

...

  <div class="span12">
    <% if notice %>
      <div class="alert alert-info" id="notice">
        <button class="close" data-dismiss="alert">×</button>
        <i class="icon-info-sign"></i>
        <%= notice %>
      </div>
    <% end %>

    <%= yield %>
  </div>

...

The <i> element above is used to create a Twitter Bootstrap icon.

With that, we're finally ready to test this out.

Refresh the app in the browser and click the login link.

After some sitting and spinning, I eventually get a page reload and see the link change to "Logged in as Brad Chase, click here to logout".

Clicking that link will log us out as expected.

We can log in and out ad nauseum now with no problem. ...except that each time we login we're creating a new record for ourselves in the DB.

You can see this for yourself by checking User.count in the rails console after logging in and out a few times.

We'll be working on getting our application to remember us when we log back in, but first let's deal with our fat controller, because there is absolutely way too much going on inside of sessions#create right now.

The convention is "skinny controller, fat model", so let's first move all the record creation stuff to our User model.

So where does this go in our model?

Ultimately, what we'll want is something like a fancy User.create class method that has an OAuth hash as a parameter.

Remember that Ruby instance methods are defined with def instance_method_name and class methods are defined with def self.class_method_name.

And because we will either be looking up or creating records with this method, we will name it find_or_create_by_oauth_hash.

### app/models/user.rb ###

class User < ActiveRecord::Base
  attr_accessible :provider, :uid, :email, :name

  def self.find_or_create_from_oauth_hash(oauth_hash)
    provider = oauth_hash[:provider]
    uid      = oauth_hash[:uid].to_s
    name     = oauth_hash[:info][:name]
    email    = oauth_hash[:info][:email]

    User.create provider: provider, uid: uid, name: name, email: email
  end
end

self.create or even create would also work here instead of User.create.

Remember that inside of an instance method self represents the instance that the method was called on and that inside of a class method self represents the class that the method was called on.

This logic is exactly the same as what we had in sessions#create, except that it doesn't bother to assign the new User instance to a variable, it just implicitly returns it by performing User.create on the last line of the method.

So let's make sessions#create use our new User class method now:

### app/controllers/sessions_controller.rb ###
...

  def create
    oauth_hash = request.env['omniauth.auth']

    # using our new class method to get the User instance 
    user = User.find_or_create_from_oauth_hash(oauth_hash)

    session[:user_id] = user.id

    redirect_to chapters_path
  end

...

Much cleaner!

While clean, we still can't log back in, because our code currently creates a new User for each login. We do have a method named find_or_create_by_oauth_hash now though, so let's make it do what it claims.

### app/models/user.rb ###

...

  def self.find_or_create_from_oauth_hash(oauth_hash)
    provider = oauth_hash[:provider]
    uid      = oauth_hash[:uid].to_s

    # attempt to find an User for provider/uid, create one if needed
    user = User.find_or_initialize_by_provider_and_uid provider, uid

    # if the record was initialized and not found in the database...
    if user.new_record?
      user.name  = oauth_hash[:info][:name]
      user.email = oauth_hash[:info][:email]
      user.save
    end

    user
  end

...

And with that, user login now works as it should.

Looking to the top of our find_or_create_from_oauth_hash(oauth_hash) class method, we see this interesting line towards the top: user = User.find_or_initialize_by_provider_and_uid provider, uid.

We didn't write this method, so where did it come from?

This is a dynamic method, implemented by ActiveRecord using method_missing, and it does exactly what its name implies.

First, it will attempt to find an existing record with the values matching the arguments passed to it. If nothing is found, it instead initializes a new instance of User using those same arguments.

Ultimately, this single line is something like the following:

user = User.where(provider: provider, uid: uid).first || User.create(provider: provider, uid: uid)

This is much longer and more involved to understand, so we'll go with the dynamic method instead.

Also take note that we basically specify what arguments we want the method to accept and in what order (e.g. provider_and_uid).

And if we wanted to create a record rather than just instantiate the class, we could have done find_or_create_by_provider_and_uid instead.

In fact, we can even skip the creating or initializing altogether and just use a dynamic finder like find_by_provider_and_uid.

So after that line, we have an instance of User stored in our user variable, but how do we tell if it was found in the DB or just initialized?

We simply use the new_record? method on this instance. Let's see it at work in the rails console:

> Chapter.create.new_record?
=> false

> Chapter.last.new_record?
=> false

> Chapter.last.id
=> 1

> Chapter.find(1).new_record?
=> false

> Chapter.new.new_record? 
=> true

Here we can see that any Chapter that has been persisted in the DB (either newly created or fetched with find/first/last/etc) will return false for .new_record?, whereas a Chapter that has not yet been saved to the DB will return true.

This new_record? method is available on any instance of any of our models because they all inherit from ActiveRecord::Base.

This is the reason we used a find_or_initialize method earlier and not find_or_create, because now we can use new_record? inside of find_or_create_from_oauth_hash to decide if we need to create a new record like this:

### app/models/user.rb ###

...
  # if the record was initialized and not found in the database...
  if user.new_record?
    user.name  = oauth_hash[:info][:name]
    user.email = oauth_hash[:info][:email]
    user.save
  end
...

If user contains a new record that has not yet been saved to the DB then execution will fall into the if user.new_record? block to add the name and email, then saves the record. Here we add the name and email to the record and then save it.

Finally, the last line of the method is the implicit return user.

Notice that we don't bother with an else for our if user.new_record?. The reason for this is that if the record was found in the DB, that's all we really need. In which case, the if fails and the method simply returns the User it found.

Well, that should do it.

Fire up $ rails console and run User.destroy_all so that we don't have all those junk records in the way. Then head back to the application, logout, login, logout, login.

Back over in rails console, run User.count now to see that our application has only created 1 user record for you, regardless of how many times you log in and out.

Just like it should.

9.4.2 | Authorization

Now that we have users, we need to restrict what they are able to do.

Life's like that.

Authorization can be quite involved for some systems, but our needs here are pretty modest. Because of this, we'll be rolling our own solution instead of using a Gem.

CanCan is fantastic for this though.

Let's start with our two most basic authorization needs:

  1. only paying users can view chapters
  2. only the author can create, edit, and delete chapters

As for the first one, we certainly don't have billing implemented yet, so we don't have any paying users. Still, we do have users, which means that we can start thinking about how to implement this.

On important thing to note is that in order to bill a user, it is implied that they already have a user record. This, in turn, implies that a user will be registered in our application before they have paid. Because of this, let's say that a user that has paid is "activated" and one that has not is "unactivated".

We could also just do "paid" or "unpaid", but I just don't like the sound of that.

Also, I'm not going to force my proofreaders to buy my book and then do me the favor of proofreading it. So thinking in terms of "activation" is actually more accurate.

Then in regards to the author, things get kind of fuzzy, because we may not actually mean "author" here. Perhaps the book is co-authored or the author allows other people to edit the book. What we really mean here is "admin", someone with absolute power within the site; so that's the term we'll use.

So we will need a way to keep track of activation on a per user basis, which means that we'll need to change our database table. Specifically, we need to add columns for active and admin to our users table.

And we'll need a migration to do that:

$ rails generate migration add_active_and_admin_to_users active:boolean admin:boolean
      invoke  active_record
      create    db/migrate/XXXXXXXXXXXXXX_add_active_and_admin_to_users.rb

And just for the sake of doing things properly, let's add default: false at the end of the add_column lines for these in the generated migration:

The XXXXXXXXXXXXXX in the file name will of course be replaced with a timestamp for the time you created your migration.

Protip: open migrations using Bash wildcards (e.g. $ vim db/migrate/*add_active*) to keep from having to type out annoying timestamps.

### db/migrate/XXXXXXXXXXXXXX_add_active_and_admin_to_users.rb ###

class AddActiveAndAdminToUsers < ActiveRecord::Migration
  def change
    add_column :users, :active, :boolean, default: false
    add_column :users, :admin, :boolean, default: false
  end
end

These would default to nil which is falsy and would work effectively the same as false, but we really do want boolean values here.

And then, of course, migrate the database:

$ rake db:migrate
==  AddActiveAndAdminToUsers: migrating =======================================
-- add_column(:users, :active, :boolean)
   -> 0.0004s
-- add_column(:users, :admin, :boolean)
   -> 0.0002s
==  AddActiveAndAdminToUsers: migrated (0.0008s) ==============================

Now, if these were normal attributes, we'd probably want to add :active, :admin to attr_accessor in our User model, so that they could be changed by a web request. But since we certainly don't want our users to do this themselves, we won't bother here. active will certainly be changed to true inside of a model as the result of a web request (successful payment), but we don't want users to be able to set this by web request, letting them access the book for free.

There's actually much less danger of this here because we don't even have a UsersController for them to make such a malicious request to, but I prefer to err on the side of caution.

Another thing we'll need is a page that unactivated users are allowed to visit. And since the chapter views pretty much are the entirety of the site right now, none of those really exist. We do have the static page at /, but that doesn't really count.

First, we'll blow away that static, default page we've had as the homepage

$ rm public/index.html 

then generate a new controller:

$ rails generate controller Home index about
      create  app/controllers/home_controller.rb
       route  get "home/about"
       route  get "home/index"
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      create    app/views/home/about.html.erb
      invoke  test_unit
      create    test/functional/home_controller_test.rb
      invoke  helper
      create    app/helpers/home_helper.rb
      invoke    test_unit
      create      test/unit/helpers/home_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/home.js.coffee
      invoke    scss
      create      app/assets/stylesheets/home.css.scss

We also threw in an about route and view here too, just for fun.

This generator, of course, created new routes for these actions, get "home/index" and get "home/about", but that's not really what we want. We don't want to see home in the path, HomeController just happens to be the controller we'll be using for these.

Let's fix these new routes at the top of our config/routes.rb to read like this:

### config/routes.rb ###

Book::Application.routes.draw do
  root to: "home#index"
  get "/about" => "home#about"

  ...
end

Here we use the special root routing method to declare where to route requests for our application root (/).

It's also worth noting that there's something interesting about home#index and home#about actions: they're both completely emtpy:

class HomeController < ApplicationController
  def index
  end

  def about
  end
end

The reason this is possible (and helpful) is that if a controller action has no logic inside of it, it simply renders the corresponding view for that actions. For example, home#index is empty, so when a request gets routed to it, it simply renders the home/index view.

So now when we hit http://localhost:3000, we'll see Home#index Find me in app/views/home/index.html.erb and a similar message from /about. These pages aren't much, but at least they're the result of views being rendered inside our application layout instead of flat HTML.

We also have some new route names:

$ rake routes
root       /                home#index
about GET  /about(.:format) home#about
...

In fact, let's open up app/views/layouts/application.html.erb and fix the "Book" link on the left side of the top nav to send us to the new homepage. We'll just need to change <a class="brand" href="#">Book</a> to <%= link_to 'Book', root_path, :class => 'brand' %> to accomplish this.

And while we're at it, let's toss in a link to about_path too.

### app/views/layouts/application.html.erb ###

...
  <%= link_to 'Book', root_path, :class => 'brand' %> <!-- NEW! -->
  <div class="container nav-collapse">
    <ul class="nav">
      <li><%= link_to 'About', about_path %></li> <!-- NEW! -->
      <li class="dropdown">
        <a class="dropdown-toggle" data-toggle="dropdown" href="#">Chapters</a>
...

This makes use of Twitter Bootstrap alerts and icons.

And we should also configure our logout (sessions#destroy) redirect to point to the root path too:

### app/controllers/sessions_controller.rb ###

...
  def destroy
    ...
    redirect_to root_path, notice: "You have been logged out."
  end
...

User Activation

We've got everything more or less set up for authorization now, but will we set the active and admin booleans on user records?

Well, we'll want to set active to true for a user once they've paid, but admin is a bit of an edge case.

Really, how often are you going to be adding an admin to this application? Probably not that frequently, because you're probably not going to want that many people to change your book.

So is it really worth the time to make a UI and controller and routes to accomplish this? I vote no.

Instead, we're going to do something kind of crude and assume that the author is also a Rails programmer. At least someone who can understand Heroku and rails console, because that's how we're going to flip the admin bit, so to speak.

So we could use the rails console to make ourselves active and admin users right now, but let's wait a bit and use our inactive/non-admin status to see the changes we make first-hand.

We're inactive and non-admin users because we set the default value for the new columns to false. Even if we hadn't, both values would be nil with the same result.

All right, let's lock ourselves out!

What we want is for site visitors who are not activated users to be redirected to the homepage when they try to visit a chapter show route.

Also included in this group are visitors who are not logged in.

This is easily accomplished with a before_filter and conditional redirect_to:

### app/controllers/chapters_controller.rb ###

class ChaptersController < ApplicationController
  before_filter :reject_unactivated_users, except: [:index]

  ...

private
  def reject_unactivated_users
    unless current_user.try(:active)
      redirect_to root_path, notice: "Please activate your account to read the book." 
    end
  end
end

Here we see another before_filter, but one with an additional argument: except: [:index]. Just as you would expect, this argument prevents the before_filter from being run for the index action, which will allow anyone to look at the "table of contents" offered by /chapters.

There's also an :only argument that works the opposite of :except.

This way, the reject_unactivated_users method is run before every request to this controller is processed (except for index) and if the user is unactivated (or not logged in), they get a redirect_to root_path with a notice telling them they'll need to activate their account to read the book.

We've seen redirect_to with a notice before and even the combo of a private method and before_filter but something we have not yet seen is the .try method that we're calling on current_user.

First, let's think about our current_user method; it is going to return either a User instance or nil. In the case of the User instance, we'll want to call .active, but in the case of nil, we'd just end up calling nil.active and that would just be stupid.

And by "stupid", I mean it would throw an error.

try is a method that Rails graciously adds to every object for us and it helps in situations like these where we hope to have an instance of something, but we also know that there's a chance we actually have nil. Instead of calling .active on current_user itself, we call .try on it and pass :active as an argument, indicating we would like that method called on current_user, unless it is nil.

In which case we really don't particularly want everything to blow up.

Here's a look at it in rails console:

> current_user = User.first
=> #<User ... active: false, admin: false>

> current_user.active
=> false

> current_user.try(:active)
=> false


> current_user = nil
=> nil

> current_user.active
NoMethodError: undefined method `active' for nil:NilClass
        from (irb):16
        from /home/brad/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.8/lib/rails/commands/console.rb:47:in `start'
        from /home/brad/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.8/lib/rails/commands/console.rb:8:in `start'
        from /home/brad/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.8/lib/rails/commands.rb:41:in `'
        from ./script/rails:6:in `require'
        from ./script/rails:6:in `
' > current_user.try(:active) => nil

nil.active blows up, while nil.try(:active) calmly returns nil.

Which is immensely more helpful.

So with that, we, as unactivated users, should no longer be able to hit any of the chapter routes. You can check for yourself in Chrome.

So now that we're locked out, let's let ourselves back in.

To do this, we can use the rails console to change the active attribute of our User record to true:

assuming that you are User.first...

> u = User.first
=> #<User ... active: false, admin: false>

> u.active = true
=> true

> u.save
=> true

> User.first.active
=> true

Try navigating to any of the chapter routes now and you'll be let in without problem.

Admins

Next, we'll want to make the authorization changes for author/admins. Not only will we want a redirect for non-admin's, but we'll only want to show certain UI components if the person viewing the page is an admin.

To make things easier on ourselves, let's create another helper method in ApplicationController named admin?, which we will then be able to use in all our controllers and views. In fact, we might as well make an activated? helper too.

### app/controllers/application_controller.rb ###

class ApplicationController < ActionController::Base
  ...

  helper_method :current_user
  helper_method :activated?
  helper_method :admin?

private
  def current_user
  ...

  def activated?
    return true if admin?
    !!current_user.try(:active)
  end

  def admin?
    !!current_user.try(:admin)
  end
end

This admin? method will return true if the current_user is an admin and false otherwise.

The prefixed !! is used to force the value to a boolean (e.g. !!nil == false).

The activated? method works similarly but with try(:active). The exception here is that this method will also return true straightaway if the user is an admin, due to the first line: return true if admin?.

Let's put both of these to use in ChaptersController:

class ChaptersController < ApplicationController
  before_filter :reject_unactivated_users, except: [:index]
  before_filter :reject_nonadmin_users, except: [:show, :index]

  ...

private
  def reject_unactivated_users
    unless activated?
      ...
    end
  end

  def reject_nonadmin_users
    unless admin?
      redirect_to root_path, notice: "You are not authorized to view this page." 
    end
  end
end

First, we changed our unless in reject_unactivated_users to use activated?.

Then we added a private reject_nonadmin_users method.

With the two before_filter's, we end up redirecting unactivated users away from everything except chapters#index and non-admin users away from everything except chapters#index and chapters#show.

And now if we try to visit, say, the edit route for a chapter, we get redirected to root and flashed a notice.

Admin UI Components

Now that we have the controller actions properly restricted, let's work on the UI elements we only want to display to admin.

Though we may come up with things later on that we won't want normal users to see, right now all we really need to hide is the set of Back Edit Destroy links in the show and index views.

First, let's fix show.html.erb. All we have to do is wrap the <div> at the very top:

### app/views/chapters/show.html.erb ###

<% if admin? %>
  <div class="form-actions">
    <%= link_to t('.back', :default => t("helpers.links.back")),
                chapters_path, :class => 'btn'  %>
    <%= link_to t('.edit', :default => t("helpers.links.edit")),
                edit_chapter_path(@chapter), :class => 'btn' %>
    <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
                chapter_path(@chapter),
                :method => 'delete',
                :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')),
                :class => 'btn btn-danger' %>
  </div>
<% end %>

...

Then we'll need to wrap the proper <th> as well as the New button in index.html.erb.

In fact, while we're in chapters/index, let's clean up the table a little bit to look more like a proper table of contents and have the chapter title serve as the link text instead of the chapter ID.

### app/views/chapters/index.html.erb ###

<div class="page-header">
  <!-- CHANGE H1 TEXT -->
  <h1>Table of Contents</h1>
</div>

<table class="table table-striped">
  <thead>
    <tr>
      <!-- CHANGE TH -->
      <th>Chapter Title</th>

      <!-- ADMIN ONLY TH-->
      <% if admin? %>
        <th><%=t '.actions', :default => t("helpers.actions") %></th>
      <% end%>
    </tr>
  </thead>
  <tbody>
    <% @chapters.each do |chapter| %>
      <tr>
        <!-- CHANGE TD LINK TEXT TO CHAPTER TITLE -->
        <td><%= link_to chapter.title, chapter_path(chapter) %></td>

        <!-- ADMIN ONLY CHAPTER CONTROLS -->
        <% if admin? %>
          <td>
            <%= link_to t('.edit', :default => t("helpers.links.edit")), edit_chapter_path(chapter), :class => 'btn btn-mini' %>
            <%= link_to t('.destroy', :default => t("helpers.links.destroy")), chapter_path(chapter), :method => :delete, :data => { :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')) }, :class => 'btn btn-mini btn-danger' %>
          </td>
        <% end%>

      </tr>
    <% end %>
  </tbody>
</table>

<!-- ADMIN ONLY NEW BUTTON -->
<% if admin? %>
  <%= link_to t('.new', :default => t("helpers.links.new")), new_chapter_path, :class => 'btn btn-primary' %>
<% end%>

So now if we revisit the index and show routes for chapters, we won't see the CRUD controls anymore.

It's very important to note that both the controller action restriction and the removal of UI controls are necessary here. In fact, the removal of admin controls from the views is less necessary, as its only benefit is that it doesn't clutter the UI with elements some users can't interact with.

The restriction of the controller actions is critical part, as failing to do so would leave routes like chapters#destroy dangerously open, even if there are no links to it on any of the pages.

To get our UI elements back and regain our full, CRUDy power, let's fire up rails console and make ourselves admins.

> u = User.first
=> #<User ... active: true, admin: false>

> u.admin = true
=> true

> u.save
=> true

> User.first
=> #<User ... active: true, admin: true>

To do this remotely on Heroku, the process is exactly the same, except we'd want to use $ heroku run console within Rails root to get a rails console for our production app.

We'll talk more about this when we discuss the full push to production.

After we're admins, we'll find that we can visit all of the chapter routes again and our CRUD UI controls are back.

So there you have it, authorization is all set!

Well, with one caveat: users can't activate by payment.

9.5 | Billing

Our app will need to accept credit cards as payment and to process the credit card information we'll be using an awesome service named Stripe. And anymore when you use a service like this, that probably means you're using their API (Application Programming Interface).

Many services we use every day have APIs, including GitHub, Twitter, and Facebook. Taking GitHub as an example, here is the URL you'd hit of you wanted a JSON return for DHH, creator of Ruby on Rails: https://github.com/dhh.json.

But APIs will also let you create, update, and destroy things too, if you're authorized. This is the mechanism by which there can be so many Twitter clients; they simply authenticate you with Twitter and then make requests to the Twitter API on your behalf, populating the UI as they go.

To draw a parallel to our application, we have something of an API ourselves, even though we didn't do anything special to get it. It's not particularly open or well documented, but it's an API nonetheless.

Given that we can pass along a valid session, we could interact with our application programmatically entirely through JSON requests in order to view, create, update, and destroy our resources. This has no particular use to anyone but us in developing our application (at least that I can think of), but if we were making a service that we'd want others to be able to integrate into, or even extend, then we'd definitely want to think more seriously about the API we were providing and document it so that others could easily use it.

So not only does Stripe offer a slick, RESTful (boom) JSON API, they also have great documentation for it and even offer a Ruby Gem.

And if that isn't enough, they also have incredibly fair fees in a market where that certainly isn't guaranteed.

"2.9% + 30 cents per successful charge. No setup fees, no monthly fees, no card storage fees, no hidden costs: you only get charged when you earn money." - stripe.com

Another nice thing about Stripe is that it allows us to charge customers without sending any of their credit card information to our server. This is great, because we'd otherwise have to take extra caution in securing our database, scrubbing our logs to make sure they didn't contain plaintext credit card numbers, and so on.

We will, of course, have to gather this information on a page that we serve to the user, but Stripe offers some JavaScript that will send that information to their service via Ajax. This will return a token that we can send to our application. Because this token is associated with the user's credit card information, we can then use it with the Stripe Gem (that will hit the Stripe API for us) to make one-time charges, recurring charges, refund those charges, and more.

Let's start off by making a list of the features we'll want here.

  1. make some kind of UI to collect credit card information
  2. use stripe.js to submit that credit card information to the Stripe API
  3. submit the resulting token to our Rails application
  4. use that token in the Stripe Gem to charge the customer (using the Stripe API)
  5. activate the user upon the successful charge

And finally, be sure to notice that though we have two requests to the Stripe API, the first is done with JS in the browser via Ajax, while the second is done using the Stripe Gem on the server. The request from the browser gets us the single-access token which is how we avoid letting the credit card info ever touch our server.

To start working working with Stripe, we'll first need to go to manage.stripe.com/register to create an account.

After that's done, we'll want to add their Gem to our Gemfile:

gem 'stripe'

Don't forget to $ bundle install.

Then we'll need to find the API keys for our Stripe account and create an initializer in our application to configure Stripe to use these keys:

### config/intializers/stripe.rb ###

# secret test key
Stripe.api_key = "YOUR_TEST_SECRET_KEY_HERE"

# publishable test key
Stripe::PUBLISHABLE_KEY = "YOUR_TEST_PUBLISHABLE_KEY_HERE"

Again, don't forget to drop in your actual keys to replace the strings above.

And we could have easily just used STRIPE_PUBLISHABLE_KEY here, but since we already have a Stripe namespace, I prefer to put it under there instead (as in Stripe::PUBLISHABLE_KEY).

All right, let's get working on the first item on our list.

  • make some kind of UI to collect credit card information

So this should be pretty easy, we just need some HTML to hold this information in a way that's easy to get at with JS.

But first, we'll need a route, controller action, and accompanying view to serve as our payment form.

So let's run another controller generator:

$ rails generate controller Charges new create
      create  app/controllers/charges_controller.rb
       route  get "charges/new"
       route  get "charges/create"
      invoke  erb
      create    app/views/charges
      create    app/views/charges/new.html.erb
      create    app/views/charges/create.html.erb
      invoke  test_unit
      create    test/functional/charges_controller_test.rb
      invoke  helper
      create    app/helpers/charges_helper.rb
      invoke    test_unit
      create      test/unit/helpers/charges_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/charges.js.coffee
      invoke    scss
      create      app/assets/stylesheets/charges.css.scss

$ rm app/views/charges/create.html.erb # don't need html for a create action!

Then we can remove the get "charges/new" and get "charges/create" that generator added to config/routes.rb and use the following instead:

### config/routes.rb ###

...

  resources :charges, only: [:new, :create]

...

We'll use the resources method here for convenience sake, even though we don't have a proper resource sitting behind these routes. That said, we will be using GET /charges/new to serve the credit card form HTML and POST /charges to submit the Stripe token to our server by Ajax, so resources will set us up to expect the proper HTTP verbs for these two routes.

With this in place, let's make a app/views/charges/new.html.erb that does something useful.

First, we'll want to download stripe.js and place it in our app/assets/javascripts directory so that it will be pulled into the page by application.js. Assuming you are at Rails root, you can do this easily from Bash using curl:

$ curl https://js.stripe.com/v1/ >app/assets/javascripts/stripe.js

We could actually pull this in straight from Stripe with a javascript_include_tag "https://js.stripe.com/v1/", but I had trouble with this locally and decided to just store the script in app/assets/javascripts.

There's going to be quite of bit of html.erb to follow, but most of it is the definition of the <input>'s to hold the credit card information largely repeated over and over. The top is really the most interesting.

To speed things along, I'm just going to point our interesting parts of this markup with HTML comments.

### app/views/charges/new.html.erb ###

<!-- include the stripe publishable key as a <meta> tag -->
<% content_for :meta, tag(:meta, content: Stripe::PUBLISHABLE_KEY, name: 'stripe-pub-key') %>

<h1> Payment </h1>

<!-- a noscript tag containing contents that will only be displayed if javascript is disabled -->
<noscript>
  <!-- this will be displayed in the user's browser if they have javascript disabled -->
  <div class="alert alert-error">
    <button class="close" data-dismiss="alert">×</button>
    <i class="icon-exclamation-sign"></i>
    <span class="alert-text">Please enable JavaScript in your web browser to buy the book.</span>
  </div>
</noscript>

<!-- hr stands for "horizontal rule" -->
<!-- used here to creates a horizontal line above and below the card types image -->
<hr />
<%= image_tag 'payment/cardtypes.png' %>
<hr />

<!-- our "form", pre-populated with passing stripe test values -->
<div class="form-horizontal" id="credit-card-info">
  <div class="control-group">
    <label class="control-label">Email</label>
    <div class="controls">
      <!-- auto-populate email field with current_user.email if available -->
      <input class="text_field" id="email" type="text" value="<%= current_user.email || '' %>"/>
    </div>
  </div>

  <div class="control-group">
    <label class="control-label">Credit Card Number</label>
    <div class="controls">
      <input class="text_field" id="number" type="text" value="4242424242424242" />
    </div>
  </div>

  <!-- notice the placeholder attributes below -->
  <!-- these won't be seen by default because we have actual values populated -->
  <!-- try clearing the fields out to see the defaults -->
  <div class="control-group">
    <label class="control-label">Expiration Month/Year</label>
    <div class="controls">
      <input class="text_field" id="exp_month" type="text" placeholder="mm" value="2" />
      <input class="text_field" id="exp_year" type="text" placeholder="yyyy" value="2013" />
    </div>
  </div>

  <div class="control-group">
    <label class="control-label">
      <!-- icon and tooltip, courtesy of twitter bootstrap -->
      <a id="cvc-explanation" title="Three digit code on the back of the card, usually near the signature line." rel="tooltip" href="#">CVV Code <i class="icon-question-sign"></i></a>
      <!-- tooltip text set in the `title` attribute of the `<a>` -->
    </label>
    <div class="controls">
      <input class="text_field" id="cvc" type="text" value="525" />
    </div>
  </div>

  <div class="form-actions">
    <button id="submit-credit-card" class="btn btn-primary">Pay</button>
  </div>
</div>

<!-- this html will be removed by our javascript and used as a twitter bootstrap alert template -->
<!-- we are duplicating this from above, but since we can't grab the contents of a noscript with jQuery, this can't be avoided -->
<div id="alert" class="alert alert-error">
  <button class="close" data-dismiss="alert">×</button>
  <i class="icon-exclamation-sign"></i>
  <span class="alert-text"></span>
</div>

At the very top, there is a content_for that we'll use to add our secret key as a <meta> tag.

Meta tags are mostly used for metadata pertaining to the markup, but are also a useful way of storing things like public keys (as we've done here).

Also, recall that we used content_for earlier from our chapter show view to tell the application layout that we wanted the <title> of those pages set to @chapter.title. But this doesn't work by magic, the application layout contains a yield(:title) to drop in the title text.

And when we create our <meta> tag, we use the tag method, passing in :meta as the first argument to indicate the kind of tag we want created. The rest of the arguments are used to set attributes (name and content).

Calling content_for here means that we will, of course, need to update our application layout to do something with the values that we are passing in:

### app/views/layouts/application.html.erb ###
...

  <head>
    ...
    <%= yield(:meta) if content_for?(:meta) %> <!-- somewhere in the head -->
  </head>

...

READERS - Do you understand how content_for works at this point? I feel like I might not have fully explained it earlier and that it might feel as if I'm glossing over it here.

We've also included an image above with image_tag 'payment/cardtypes.png'. This image_tag helper will look in app/assets/images/ for payment/cardtypes.png and will build an <img> element to it using the path /assets/payment/cardtypes.png.

With a view for the payment all set up, let's redirect unactivated users here immediately after OAuth/registration. To do this, we'll just need to change the redirect_to in the last line of sessions#create:

### app/controllers/sessions_controller.rb ###
...
  def create
    ...

    # if the user is active send them to the chapters_path
    # else send them to the new_charge_path
    redirect_to user.active ? chapters_path : new_charge_path
  end
...

We could have just used our activated? helper here instead of user.active, but that would have done yet another User.find when we already have the record in hand (in the variable user).

Also, keep in mind that the two _path methods above return strings for redirect_to. making the line we just added equivalent to:

redirect_to (user.active ? chapters_path : new_charge_path)

This means that the ternary operator (boolean ? pass_logic : fail_logic) is simply used to decide which path to pass to redirect_to.

Derail: Database Seeding

So we'd check to see if this redirect works, but we've already created a User record. Logging out and back in and will just send us to chapters_path without us ever seeing this page.

We could just do User.destroy_all from rails console, but that would be a pain. We could also just rebuild the DB from scratch, but then we wouldn't have any chapter data.

A nice solution to this problem is to create some seed data that will be populated into the database when it gets set up. In fact, there's even a rake task for this ($ rake db:setup) that, conveniently enough, does the following:

  1. drops the database
  2. creates a new database
  3. migrates the new database
  4. seeds the database

So let's make some seed data of our own in db/seeds.rb, because this is the file that $ rake db:setup looks to for seed data.

### db/seeds.rb ###

puts "# Seeding the DB"  

print "   # Chapters "  
(1..5).each do |i|
  chapter = {}
  chapter[:title] = "#{i} | Chapter #{i}"
  chapter[:body]  = "<h1>#{chapter[:title]}</h1><p>Some chapter text.</p>"

  50.times do |num|
    chapter[:body] << "<p>Paragraph ##{num+1} of this chapter.</p>"
  end

  Chapter.create chapter

  print '.'
end

puts

Here, we're just creating Chapter's from 1 to 5 and filling them each in with fifty lines of text. It's not much, but it will be plenty to populate the chapter dropdown in the nav, the table of contents, etc.

We're actually using (1..5).each do |i| and 50.times do |num| in much the same way here: to run through a range of numbers. But since 50.times will begin with 0 and we don't want Paragraph #0 we use num+1 to offset this in the string interpolation.

Also, the puts and prints are just there to make the $ rake db:setup keep the developer up to date on what's going on in the seeding.

In more complicated applications, seeding can take a while and you eventually begin to wonder if it's just taking a while or if the Rake task locked up. Because of this, I like to print a . for each record created.

So now we've got HTML for credit card information, a redirect to send unactivated users there, and chapter seed data for our database.

Let's drop our database and set it back up with the seed data so that we can register with our app again.

WARNING: This will irrevocably, unapologetically destroy ALL data stored in the database.

$ rake db:setup
db/development.sqlite3 already exists
db/test.sqlite3 already exists
-- create_table("chapters", {:force=>true})
   -> 0.3510s
-- create_table("marks", {:force=>true})
   -> 0.3511s
-- add_index("marks", ["chapter_id"], {:name=>"index_marks_on_chapter_id"})
   -> 0.2089s
-- create_table("users", {:force=>true})
   -> 0.3677s
-- initialize_schema_migrations_table()
   -> 0.0006s
-- assume_migrated_upto_version(20120907061204, ["/home/brad/rails/book/db/migrate"])
   -> 0.0009s
# Seeding the DB
   # Chapters .....

Now that we don't have a User record anymore, let's visit localhost:3000/ in Chrome and register again.

Sending Credit Card Data To Stripe

So now if we click our register/login link, we should be redirected to our new payment form after OAuth and registration.

The email field is pre-populated using the value we received from the OAuth hash (if one exists), but the rest are values we hardcoded with Stripe credit card test values to make things easier on us.

Speaking of Stripe, it's time for the next couple items on our list.

  • use stripe.js to submit that credit card information to the Stripe API
  • submit the resulting token to our Rails application

To do this, we're going to want to make a JavaScript file of our own at app/assets/javascripts/charges.js.

Also, be sure to delete the file app/assets/javascripts/charges.js.coffee which was created by our controller generator.

I didn't delete it at first and ended up with all of my code running twice. This is never good, but certainly not good when it's logic that is charging a customers credit card.

Also another excellent case for a thorough test suite.

There's quite a bit of JavaScript to handle all of this, but it's also commented quite thorougly.

Take careful note of what level everything is at. The top begins by making sure the code only runs after page load. Next, there are things that only run and if the page has a path of /charges/new. After that we have most of our immediately executing code, followed by the function definitions for all of the callbacks and so on.

This one will probably worth reading a couple times.

### app/assets/javascripts/charges.js ###

// contents of this function are only run after the page is ready
$(function(){
  // only run if the path matches `/charges/new`
  if(window.location.pathname.match('/charges/new')){

    // set the `Stripe` object to use the publishable key 
    // that we placed in the <meta> in the markup
    stripeKey = $('meta[name=stripe-pub-key]').attr('content');
    Stripe.setPublishableKey(stripeKey);

    // grab the alert template out of the page
    $alert = $('#alert').remove();
    // notice that `.remove` returns a jQuery object

    // immediately set focus to the card number field
    $('#number').focus();

    // tooltip to explain "CVV Code"
    $('#cvc-explanation').tooltip();

    // bind to click of pay button
    $('#submit-credit-card').click(function(evt){
      // disable the button so it cannot be pushed repeatedly
      $(this).attr('disabled', true);

      // call to our helper function to get an object
      // that contains all of the card information
      card = getCardData();

      console.log('Processing Credit Card');

      // 1st ajax request!
      // request a token from Stripe
      // second parameter designates the callback
      Stripe.createToken(card, stripeResponseHandler);
    });

    // convenience function to grab the card 
    // data and place it inside of an object 
    function getCardData(){
      card = {
        number    : $('#number').val(),
        exp_month : $('#exp_month').val(),
        exp_year  : $('#exp_year').val(),
        cvc       : $('#cvc').val()
      };

      return card;
    }

    // this is the callback registered to handle the response 
    // from our earlier call to `Stripe.createToken` 
    function stripeResponseHandler(statusText, response){
      console.log(statusText, response);

      // "run this block if the response object has a value for the key 'error'" 
      // this is the code block to run for an error state
      if(!!response.error){
        // display a twitter bootstrap alert
        text = " Stripe reports a problem: " + response.error.message;
        displayAlert(text);

        // set focus to the input that erred 
        $('#'+response.error.param).focus();

        // enable the submit button
        $('#submit-credit-card').attr('disabled', false);
      } else {
      // this is the code block to run on success

        // values we want to submit
        token = response.id;
        email = $('#email').val();

        console.log('Submitting Charge');

        // 2nd ajax request!
        // post the token to our app so we can charge the card
        $.post('/charges', {card: token, email: email})
          .complete(appResponseHandler); 
          // ^ registers the callback for this request
      }
    }
    
    function appResponseHandler(jqXHR, statusText){
      if(statusText == 'success'){
        // the user was successfully charged
        // we can redirect them on to the book
        window.location.pathname = '/read'
      } else {
        // something went wrong
        // enable the submit button and alert
        $('#submit-credit-card').attr('disabled', false);
        displayAlert(" There was a problem charging the card on the server. Please try again or email support.")
      }
    }

    // used to create a twitter bootstrap
    // alert in the page using our template
    function displayAlert(text) {
      // clear out old alerts
      $('.alert').remove();

      // copy our alert template 
      alrt = $alert.clone();
      // populate it with text
      alrt.find('.alert-text').text(text);

      // insert the alert into the page
      $('#credit-card-info').before(alrt);
    }
  }
});

There's quite a lot of JavaScript up there, but a fair amount of the lines are just comments and the logic is pretty straightforward.

Assuming a "happy path", where the user makes a succesful payment and is redirected to the book, the logic to accomplish our goal largely comes from these lines:

  • $('#submit-credit-card').click(function(evt){ - binding to the 'Pay' button
  • Stripe.createToken(card, stripeResponseHandler); - XHR to Stripe to get a token
  • function stripeResponseHandler(statusText, response){ - callback to handle the Stripe token and...
  • $.post('/charges', {card: token, email: email}) - post that token to our application
  • function appResponseHandler(jqXHR, statusText){ - callback to handle response from our server and...
  • window.location.pathname = '/read' - redirect to the book

Actually, that last part isn't quite implemented yet, so let's do that right now.

The idea with this /read route is not to create a new view or anything, but to serve as a helpful redirect to the beginning of the book with a friendly message.

Though syntactically acceptable, offensive messages are not recommended.

See also: Being A Decent Human Being

To implement this /read route idea, we'll first need to adjust config/routes.rb:

### config/routes.rb ###
...
  get "/read" => "home#read"
...

Then we'll need to make a controller action that will send the user to Chapter.first along with a notice:

### app/controllers/home_controller.rb ###
...
  def read
    redirect_to chapter_path(Chapter.first), notice: "Payment successful, enjoy the book!"
  end
...

Normally, we wouldn't want to create an action simply for a redirect, but since the "redirect" is happening via JavaScript (window.location.pathname = ...), this is the only way to display an appropriate notice.

So this is a bit of a hack, but a warranted one that demonstrates some of the flexibility we have within Rails.

Making A Stripe Charge

And with that, we have only two items remaining on our list.

  • use that token in the Stripe Gem to charge the customer (using the Stripe API)
  • activate the user upon the successful charge

To start implementing this, we'll first want to add another public instance method to User:

### app/models/user.rb ###

class User < ActiveRecord::Base
...
public
  def charge_and_activate(card, email_from_form)
    begin
      price = 30_00
      charge = Stripe::Charge.create amount: price, 
                                     currency: 'usd', 
                                     card: card, 
                                     description: "#{email_from_form} | #{self.email}"

      self.stripe_charge_id = charge.id

      self.active = true

      self.save
    rescue
      false
    end
  end
end

You won't strictly need the public keyword, but be sure to define this method somewhere that makes it public. (This allows the method to be called later as current_user.charge_and_activate(...).)

Notice that the entire contents of this new method is wrapped in a begin/rescue. The reason for this is that we are using Stripe's Gem to perform the Stripe::Charge.create, which will reach out to the Stripe API, and if it fails it will throw an error to let us know. Because of this, we want to wrap all of our charge logic in order to be able to rescue the error; if the charge fails, we won't want to do anything else.

Also, a successful self.save at the end of the method will return true, whereas a charge error will cause a false to be returned from the rescue. We'll soon use these returns inside of charges#create to determine how we respond to the charge creation HTTP request.

Another peculiarity of the code above is the 30_00 assigned to the variable price, and it's actually peculiar for a couple of reasons.

First, it should be noted that syntactically we are allowed to indiscriminately place _'s inside of number literals. Take a look in IRB:

> 1_000_000 === 10000000
=> false

> 1_000_000 === 1000000
=> true

> 30_00 === 3000
=> true

From the first two examples above, it's apparent that underscores can serve as a helpful replacement for ,'s in long numbers for increased readability.

But that doesn't explain the last part: `30_00. Are we charging customers $3000?

Hardly. We are charging customers 3000¢.

Why on earth would we represents the price in cents?

Let's just say that programming languages can do some goofy (read: terrible) things with floating point (decimal) values and it's much safer to use discrete units like cents.

And really, if we charge someone $1.99, we're not charging them one dollar and ninety-nine hundredths of a dollar so much as we're actually charging them one hundred ninety-nine cents.

So, looking back at our new method, we are also persisting charge.id. We'll have access the information for all of our charges in the Stripe dashboard as well, but its nice to have, in essence, a reference to the receipt persisted in our own DB if we need it later.

Furthermore, we're storing this charge.id on a column we don't yet have on our users table: stripe_charge_id. Let's generate a migration and then migrate the database to include it:

$ rails generate migration add_stripe_charge_id_to_users stripe_charge_id:string
      invoke  active_record
      create    db/migrate/20120907061204_add_stripe_charge_id_to_users.rb

$ rake db:migrate
==  AddStripeChargeIdToUsers: migrating =======================================
-- add_column(:users, :stripe_charge_id, :string)
   -> 0.0005s
==  AddStripeChargeIdToUsers: migrated (0.0006s) ==============================

After that, we'll just need to adjust charges#create to call this method on current_user with the proper arguments:

### app/controllers/charges_controller.rb ###

...

  def create
    if current_user.charge_and_activate(params[:card], params[:email])
      head :created
    else
      head :not_acceptable
    end
  end

...

And there it is! Everything will work for Stripe payment, at least in the Stripe test environment, which is all we need to development right now.

Again, we could do a much better job of error handling here, but for the sake of brevity, we'll move on.

Here's our list again as a look back on what we've done for billing:

  1. make some kind of UI to collect credit card information
  2. use stripe.js to submit that credit card information to the Stripe API
  3. submit the resulting token to our Rails application
  4. use that token in the Stripe Gem to charge the customer (using the Stripe API)
  5. activate the user upon the successful charge

So let's try it out.

A $ rake db:setup will get you back to a clean, unregistered slate.

Then visit our homepage, click the sign up link, hit the "Pay" button, and find yourself on /chapters/1 with your welcome notice.

Feature complete.