shoulda 2.0.0, now with gem power

Shoulda has long been available as a plugin. It's core was yanked out at some point, and made available as a gem. Unfortunately, the codebases diverged, with main development occurring in the plugin.

What was once was divided is now united, as of this day. Shoulda 2.0.0. Check it:

$ gem install thoughtbot-shoulda

Nothing new there, but you can use this gem in a Rails app:

# in config/environment.rb
Rails::Initializer.run do |config|
  config.gem 'thoughtbot-shoulda', :lib => 'shoulda/rails', :source => 'http://gems.github.com'
end

Using gems instead of plugins isn't perfect though, specifically, rake tasks don't get loaded yet. Hopefully that will be fixed eventually, but in the mean time, you can do:

# in Rakefile
begin require 'shoulda/tasks' rescue LoadError end

You can also use this gem in any non-Rails app. In your test_helper.rb, you'd just do something like:

require 'shoulda'

So what else is new? I'm not sure exactly what was in the last release, so I'll leave that for a more official announcement.

Making factory_girl even easier on Rails

factory_girl is pretty sweet. I'm not going over to rehash its awesomeness, but you can check out its website.

Not that it was difficult to use with Rails, but now it's even easier.

I present to you the-oh-so-creatively-named factory_girl_on_rails.

Install it

script/plugin install git://github.com/technicalpickles/factory_girl_on_rails.git

What it does

This plugin allows you to place factory definitions in test/factories, and the plugin will automatically load them.

Additionally, it provides a simple generator for creating new factories:

./script/generate factory account

This creates an Account factory: test/factories/account_factory.rb

./script/generate factory post title:string body:text published:boolean

This creates a Post factory with a string title, text body, and published flag.

The source

It lives on GitHub: technicalpickles/factorygirlon_rails

Summary

Overall, this plugin doesn't do a whole lot, but I had to do this kind of thing by hand on enough projects that I figured it was worth it.

Manage your markup with has_markup

I recently decided to extract some plugins, in an effort to clean up the codebase for my blog.

The first thing I tackled was generation of HTML from my Post model.

In the beginning...

Here's the starting point for my model:

class Post < ActiveRecord::Base
  # --- SNIP ---

  # TODO move into own file... and plugin?
  def self.validates_markdown(*attrs)
    validates_each(*attrs) do |record, attr, value|
      begin
        BlueCloth.new(value).to_html unless value.nil?
      rescue BlueCloth::FormatError => e
        errors.add attr, "has #{e}"
      end
    end
  end
  validates_presence_of :content
  validates_markdown :content
  before_save :cache_content

  # Use BlueCloth to generate HTML ahead of time.
  def cache_content
    html = BlueCloth.new(self.content).to_html
    self.cached_content = html
  end

  # --- SNIP ---
end
  • validates_markdown really doesn't belong here
  • Nothing particularly interesting is happening, but the lines of code start to add up

Let's look at the partial for Post.

%div{ :id => dom_id(post), :class => dom_class(post) }
  %h2.title= link_to(post.title, post)
  .content~ post.cached_content
  • Eh, nothing particuarly interesting

Goals

  • One-liner for specifying markup, similar to has_many or acts_as_taggable
  • Optionally require the markup
  • Optionally cache the markup
  • Allow for different syntaxes

A whole new look on things

I was able to extract the logic out of my model and hit all of these goals.

The refactored model:

class Post < ActiveRecord::Base
  # --- SNIP ---

  has_markup :content, :required => true, :cache_html => true

  # --- SNIP ---
end
And the view is more or less the same:
%div{ :id => dom_id(post), :class => dom_class(post) }
  %h2.title= link_to(post.title, post)
  .content~ post.cached_content_html

Thoughts

  • Plugins are really easy to make
  • Plugins can pull a good amount of code out of your model, which results in it being more readable
  • I think I finally understand the difference between include and extend

Get it

If you're interested in trying this plugin, it is hosted on GitHub. You can install it by with:

script/plugin install git://github.com/technicalpickles/has_markup.git