About

James Golick

James Golick is an engineer, entrepreneur, speaker, and above all else, a grinder.

As CTO (or something?) of BitLove, he scaled FetLife.com's traffic by more than an order of magnitude (and counting).

James spends most of his time writing ruby and scala, building infrastructure, and extinguishing fires.

He speaks regularly at conferences and blogs periodically, but James values shipping code over just about anything else.

Latest Tweets

follow me on Twitter

James on the Web

Friendly 0.4: scopes, named_scopes, associations, and more

Dec 22 2009

Friendly has gotten a bunch of attention since we released it last week. We're already over 200 watchers on the github project. Thanks to everybody who wrote or tweeted about it.

Since then, I've been hacking away on features that were sorely missing. Today, I've got a handful of those to release.

Scopes

The star of Friendly 0.4.0 is scopes. Here's how they work.

Assuming the following model:

class Post
  attribute :author, String

  indexes   :author, :created_at
end

To create a scope on the fly, just call YourDocument.scope.

@scope = Post.scope(:author => "Stewie Griffin")

You'll get back an instance of Friendly::Scope. That object supports almost all of the query and creation API that a Document class does.

@scope.all == Post.all(:author => "Stewie Griffin")
@scope.first == Post.first(:author => "Stewie Griffin")

# these won't actually evaluate to true, but I mean to say that the syntaxes are identical
@scope.build == Post.new(:author => "Stewie Griffin")
@scope.create == Post.create(:author => "Stewie Griffin")

It's also possible to create reusable, named scopes.

Named Scopes

The named scope syntax is pretty well identical to ActiveRecord's:

class Post
  # ... snip ...
  named_scope :by_stewie, :name => "Stewie Griffin"

Then, you can get an instance of that scope with:

Post.by_stewie

A named scope instance is just another instance of Friendly::Scope. Unlike with ActiveRecord, you must call scope.all to get the target of the scope. You can also use any of the other scope methods:

Post.by_stewie.all == Post.all(:name => "Stewie Griffin")
  Post.by_stewie.create.name == "Stewie Griffin"
  # ... and so on

Named scopes are also chainable arbitrarily.

class Post
  # ... snip ...
  named_scope :recent, :order! => :created_at.desc
end

Post.by_stewie.recent == Post.all(:name => "Stewie Griffin", :order! => :created_at.desc)

If there are conflicts, the right-most scope takes precedence.

class Post
  # ... snip ...
  named_scope :by_peter, :name => "Peter Griffin"
end

Post.by_stewie.by_peter == Post.all(:name => "Peter Griffin")

Associations

Currently, only has_many associations are supported.

class User
  has_many :posts
end

class Post
  # ... snip ...
  attribute :user_id, Friendly::UUID

  indexes   :user_id, :created_at
end

The cool thing is that associations are just another instance of Friendly::Scope.

User.new.posts # => #<Friendly::Scope>

So, all of the Friendly::Scope methods are supported. You can also chain named_scopes on an association.

@user = User.create
  @user.posts.recent == Post.all(:user_id => @user.id, :order! => :created_at.desc)

Those are the basics. Check out the docs for Friendly::Scope and Friendly::Associations::Association for more info.

Misc Stuff

Jeff Rafter contributed a few awesome patches.

Most notably, Friendly was previously unable to create an index table for a field of type Friendly::UUID. Jeff added support for mapping custom types to database field types.

There are no docs yet, but the method signature for Friendly::Attribute.register_type should be clear enough. For most purposes, though, the big win is that Friendly should now be able to create tables for you more consistently.

Jeff also added a spec/config.yml for putting your spec db settings in.

Get it while it's hot!

  • Install Friendly:
    sudo gem install friendly
    
  • Watch the github project.
  • Join us in irc at #friendlyorm on freenode.

Introducing Friendly: NoSQL With MySQL in Ruby

Dec 16 2009

I've been a big proponent of NoSQL for a while. I have played with just about all of the new generation of data stores. We almost got cassandra running in production once, and we've been running mongodb in production for about six months now.

But, here's the thing: as awesome as these new dbs are, they're still young. Our app generates a ton of data and gets pretty serious traffic. So, we started hitting walls quickly.

To make a long story short, we decided to fall back to MySQL. It's battle hardened. We know its production characteristics and limitations. Backups are a science. We know we can count on it.

But, we have a lot of data, and adding fields and indexes was starting to get painful. Flexible schemas are one of the things that attracted me to NoSQL in the first place. Then, I remembered this article about How FriendFeed uses MySQL to store schema-less data. So, I decided to implement the system they describe in the article.

Since we put Friendly in to production, we've seen a significant increase in site performance. Friendly's built-in caching has achieved a hit rate of 99.8%. We're pretty happy with things so far.

Introducing Friendly

Friendly makes MySQL look like a document store. When you save an object, it seralizes all of its attributes to JSON and stores them in a single field. To query your data, Friendly creates and maintains indexes in separate tables. It even has write-through and read-through caching built right in.

Let's build a model:

class Post
  include Friendly::Document

  attribute :author, String
  attribute :title,  String
  attribute :body,   String
end

We can create objects using a familiar syntax:

Post.create :author => "Stewie Griffin",
            :title  => "World Domination Plot",
            :body   => "All the gory details here..."

At this point, we can only query our models by id, which isn't great. So, let's add an index:

class Post
  # ... snip ...
  
  indexes :author, :created_at
end

Note that all friendly tables have automatically managed created_at and updated_at fields. Now we can query our model by author:

Post.all(:author => "Stewie Griffin", :order! => :created_at.desc)

With Friendly's query syntax, all parameters are conditions, except modifiers, which end in !.

Since everybody obviously wants to read all about Stewie's world domination plot, his site is getting hammered. Let's add some caching to make this outrageously fast:

class Post
  # ... snip ...

  caches_by :id
end

Now, the Post.all query we performed above will only hit the database once, and the rest of the data will come from a single memcached multiget. When we modify the row, Friendly will automatically update the cache. If we try to access the row in cache and it's missing, Friendly will query the database for it, and stick the result in cache.

Currently, only caching by id is supported, but arbitrary caches are on the way.

Now what?

  • Install Friendly and start playing:
    sudo gem install friendly
    
  • Check out the site (designed by the awesome John Baku) for a more detailed tutorial.
  • Follow the github project.
  • Subscribe to my blog for further updates.
  • Join #friendlyorm on freenode.