Hobo Fields: Lots of ActiveRecord Goodness
What? You’re still generating migrations manually? I don’t anymore. I have tools which do it for me.
Hobofields is a set of extensions to ActiveRecord that gives us lots of goodies, including the ability to generate migrations automatically. The key to achieving this automatic property declaration is schema declaration in the model.
1 class Post < ActiveRecord::Base 2 fields do 3 title :string 4 published_at :datetime 5 timestamps 6 end 7 end
If you ever used DataMapper, the idea is similar. Where Hobofields shines though is in reading your model and inferring many, many conventions:
1 class Comment < ActiveRecord::Base 2 belongs_to :post 3 end
Running the hobo_migration generator results in:
1 $ script/generate hobo_migration 2 3 ---------- Up Migration ---------- 4 add_column :comments, :post_id, :integer 5 6 add_index :comments, [:post_id] 7 ---------------------------------- 8 9 ---------- Down Migration -------- 10 remove_column :comments, :post_id 11 12 remove_index :comments, :name => :index_comments_on_post_id rescue ActiveRecord::StatementInvalid 13 ----------------------------------
If you use acts_as_list, Hobofields also knows to add a position column (or whatever name is needed):
1 class Comment < ActiveRecord::Base 2 acts_as_list 3 end
Running the migration generator, you get:
1 $ script/generate hobo_migration 2 3 ---------- Up Migration ---------- 4 add_column :comments, :position, :integer 5 ---------------------------------- 6 7 ---------- Down Migration -------- 8 remove_column :comments, :position 9 ---------------------------------- 10 What now: [g]enerate migration, generate and [m]igrate now or [c]ancel?
Another thing I love about Hobofields is rich type support. Need Textile?
1 class Post < ActiveRecord::Base 2 fields do 3 body :textile 4 end 5 end
1 content = <<EOTEXTILE 2 This is some Textile content 3 4 h1. Automatically formatted 5 6 To your: 7 8 * specifications, 9 * using standard tools 10 EOTEXTILE 11 12 Post.new(:body => content).body.to_html 13 #=> "<p>This is some Textile content</p>\n<h1>Automatically formatted</h1>\n<p>To your:</p>\n<ul><li>specifications,</li>\n<li>using standard tools</li>\n</ul>\n"
Or maybe you need an enumeration?
1 class Comment < ActiveRecord::Base 2 fields do 3 status enum_string(:spam, :ham), :default => :spam, :required => true 4 end 5 end
1 $ script/generate hobo_migration 2 3 ---------- Up Migration ---------- 4 add_column :comments, :status, :string, :default => "ham", :required => true 5 ---------------------------------- 6 7 ---------- Down Migration -------- 8 remove_column :comments, :status 9 ----------------------------------
1 >>Comment.new.status 2 #=> "ham" 3 >> Comment.create!(:status => "foo") 4 #=> ActiveRecord::RecordInvalid: Validation failed: Status must be one of spam, ham
Admittedly, this last example could be better handled with Hobo’s lifecycle (state machine) implementation, but still, it’s nice that the validation already exists. Using :null => false, :required => true creates the appropriate validation as well:
1 class Comment < ActiveRecord::Base 2 fields do 3 author :string, :required 4 email :string, :required 5 status enum_string(:spam, :ham), :required => true, :default => "ham" 6 timestamps 7 end 8 end
And Hobofields is smart enough to know when no changes are required:
1 $ script/generate hobo_migration 2 Database and models match -- nothing to change
But with this model definition, we now have new validation automatically:
1 >> Comment.create! 2 #=> ActiveRecord::RecordInvalid: Validation failed: Author can't be blank, Email can't be blank
Oh wait, just realized that the email address should be an email address:
1 class Comment < ActiveRecord::Base 2 fields do 3 email :email_address, :required 4 end 5 end
1 >> Comment.create!(:email => "a") 2 #=> ActiveRecord::RecordInvalid: Validation failed: Author can't be blank, Email is invalid
I really enjoy Hobofields for all the goodies it brings to the table. I haven’t even started on all the automatic scopes goodies, the lifecycle methods (which I haven’t used yet), or scoped associations.
Until we meet again tomorrow, I suggest you read Hobofields Reference, as well as Hobofields rich types to get the full goods.