O'Reilly logo

RESTful Web Services by Sam Ruby, Leonard Richardson

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Figuring Out the Data Set

Because I’m basing my service on an existing one, it’s fairly easy to figure out the parameters of the data set. If what follows is confusing, feel free to flip back to del.icio.us: The Sample Application” in Chapter 2 for an overview of del.icio.us.

The del.icio.us site has four main kinds of data: user accounts, bookmarks (del.icio.us calls them “posts”), tags (short strings that act as metadata for bookmarks), and bundles (collections of tags for a user). The web site and the web service track the same data set.

Unlike an S3 bucket, or a user account on my map service, a del.icio.us user account is not just a named list of subordinate resources. It’s got state of its own. A del.icio.us account has a username and password, but it’s supposed to correspond to a particular person, and it also tracks that person’s full name and email address. A user account also has a list of subordinate resources: the user’s bookmarks. All this state can be fetched and manipulated through HTTP.

A bookmark belongs to a user and has six pieces of state: a URI, a short and a long description, a timestamp, a collection of tags, and a flag that says whether or not it’s public (the previous chapter’s “custom place” resource has a similar flag). The client is in charge of specifying all of this information for each bookmark, though the URI and the short description are the only required pieces of state.

The URIs in users’ bookmarks are the most interesting part of the data set. When you put a bunch of peoples’ bookmarks together, you find that the URIs have emergent properties. On del.icio.us these properties include newness, a measure of how recently someone bookmarked a particular URI; “popularity,” a measure of how many people have bookmarked that URI; and the “tag cloud,” a generated vocabulary for the URI, based on which tags people tend to use to describe the URI. The del.icio.us web site also exposes a recommendation engine that relates URIs to each other, using a secret algorithm.

I’m not going to do much with the emergent properties of URIs, properties that account for much of del.icio.us’s behind-the-scenes code. My implemented service will have a notion of newness but it won’t have popularity, tag clouds, or recommendation algorithms. This is just so I can keep this book down to a manageable size instead of turning it into a book about recommendation algorithms.

Tags have only one piece of state: their name. They only exist in relation to bookmarks—and bundles, which I haven’t described yet. A bundle is a user’s decision to group particular tags together. A user with tags “recipes,” “restaurants,” and “food,” might group those tags into a bundle called “gustation.” I’ll show the RESTful design of bundles, just for completeness, but I won’t be implementing them when it comes time to write code.

At this point I know enough about the data set to create the database schema. I create an empty database called bookmarks_development in my MySQL installation, and put this data in the file db/migrate/001_initial_schema.rb, shown in Example 7-1.

Example 7-1. The bookmark database schema as a Rails migration

class InitialSchema < ActiveRecord::Migration

  # Create the database tables on a Rails migration.
  def self.up
    # The 'users' table, tracking four items of state 
    # plus a unique ID.
    create_table :users, :force => true do |t|
      t.column :user_id, :string
      t.column :name, :string
      t.column :full_name, :string
      t.column :email, :string
      t.column :password, :string
    end

    # The 'bookmarks' table, tracking six items of state,
    # plus a derivative field and a unique ID.
    create_table :bookmarks, :force => true do |t|
      t.column :bookmark_id, :string
      t.column :uri, :string
      t.column :uri_hash, :string   # A hash of the URI.
                                    # See book text below.
      t.column :short_description, :string
      t.column :long_description, :text
      t.column :timestamp, :datetime
      t.column :public, :boolean
    end

    # This join table reflects the fact that bookmarks are subordinate
    # resources to users.
    create_table :user_bookmarks, :force => true do |t|
      t.column :user_id, :integer
      t.column :bookmark_id, :integer
    end

    # These two are standard tables defined by the acts_as_taggable
    # plugin, of which more later. This one defines tags.
    create_table :tags do |t|
      t.column :name, :string
    end

    # This one defines the relationship between tags and the things
    # tagged--in this case, bookmarks.
    create_table :taggings do |t|
      t.column :tag_id, :integer
      t.column :taggable_id, :integer
      t.column :taggable_type, :string
    end

    # Four indexes that capture the ways I plan to search the
    # database.
    add_index :users, :name
    add_index :bookmarks, :uri_hash
    add_index :tags, :name
    add_index :taggings, [:tag_id, :taggable_id, :taggable_type]
  end
  # Drop the database tables on a Rails reverse migration.
  def self.down
    [:users, :bookmarks, :tags, :user_bookmarks, :taggings].each do |t|
      drop_table t
    end
  end
end

I’ve used Ruby code to describe five database tables and four indexes. I create the corresponding database schema by running this command:

$ rake db:migrate

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required