On recent rails projects, I found myself clinging to a few useful helpers and additions. Here’s a few.
hide_unless
Often in views, I find I want to hide a particular div or element, but only if certain conditions are met. An ajax call might later reveal it, or replace the contents of the div with something.
Lately, rubygems seems to be slow when updating. I guess there’s a lot more gems being released than ever before. As a result, running gem update is painful:
$ sudo gem update
Updating installed gems...
Updating metadata for 345 gems from http://gems.rubyforge.org
.......................
Argh! Turns out that there’s a “buik update” setting, and the default threshold is 1000. Metadata will be downloaded a gem at a time if there are 999 to get. Fortunately, it can be changed, by passing the -B flag to gem commands, or you can put this in ~/.gemrc :
update: -B 10
install: -B 10
Now your gem updates will be much faster.
Update:zenspider notes: the latest (last 2 actually) version of rubygems has http keepalive so it should be much much much faster and the bulk update threshold setting shouldn’t be necessary.
Ken Collins put together an awesome update to the autotest sound plugin. His version supports a playlist directory, so you can easily cycle through different init, red and green sounds. His sounds are hilarious!
Some collected recipes to make irb and script/console a bit nicer to use:
pretty printing (pp whatever)
enhanced tab completion
rails logging to console
saves command history between sessions
use readline extensions module
Tips were collected from posts by “Dr. Nic”:http://drnicwilliams.com/2006/10/12/my-irbrc-for-consoleirb/, “Toolman Tim”:http://toolmantim.com/article/2007/2/6/system_wide_script_console_logging and “Dzone Snippets”:http://snippets.dzone.com/posts/show/4371.
~/.irbrc
1234567891011121314151617181920
require'pp'require'irb/completion'ARGV.concat["--readline","--prompt-mode","simple"]IRB.conf[:AUTO_INDENT]=true# load console_with_helpers if possiblescript_console_running=ENV.include?('RAILS_ENV')&&IRB.conf[:LOAD_MODULES]&&IRB.conf[:LOAD_MODULES].include?('console_with_helpers')rails_running=ENV.include?('RAILS_ENV')&&!(IRB.conf[:LOAD_MODULES]&&IRB.conf[:LOAD_MODULES].include?('console_with_helpers'))irb_standalone_running=!script_console_running&&!rails_running# log Rails stuff to STDOUTifscript_console_runningrequire'logger'Object.const_set(:RAILS_DEFAULT_LOGGER,Logger.new(STDOUT))end# Keep command-line history between startupsrequire'irb/ext/save-history'IRB.conf[:SAVE_HISTORY]=100IRB.conf[:HISTORY_FILE]="#{ENV['HOME']}/.irb-save-history"
Update: my friend “Joannou”:http://joannou.tumblr.com/ pointed out Utility Belt which looks pretty nice also.
Update 2 (a month later): Been using Utility Belt for a while and noticed some problems… it has a tendency to conflict with ActiveRecord validations and trigger bogus errors during callbacks. Also seems to destroy some of the init process with certain plugins like ActiveScaffold. Perhaps it’s too clever. I ended up rolling back to my old .irbc - YMMV.
We’ve all been enjoying autotest, part of the ZenTest gem. If you’ve tricked out your kit, then you have plugins configured, so at minimum you’re red, green and growling.
Now, things get really fun.
I’m stoked to announce the sound plugin for autotest. This simple chunk of code will fire off sounds for different events in autotest. I’ve provided a set of custom-made sounds, produced with my trusty Nord Modular synthesizer and fine-tuned for an optimal testing experience. You should be able to use these all day without annoying your neighbors too much.
Here’s what you need to do:
1. Install mgp321
in OS X:
$ sudo port install mpg321
for Linux:
$ sudo apt-get install mpg321
2. Download and extract the plugin
The starter sound fx are in the zip file. Extract it in your home directory, it will create ~/autotest/sound.
I’ve been using this setup for several weeks now. I initially wrote it as a gag, but I have since found it to be incredibly useful. It’s nice know what your testing status via audio - you don’t have to switch windows or take your eyes off the code. I’ve even turned off Growl, I don’t need it any more. audio makes testing more fun. :)
If there are any problems or feedback, please post a comment here.
UPDATE: Plugin instructions and zip file updated, now with Windows support. Thanks, John and Jamie.
UPDATE #2: Fixed bad path in instructions and doc fixes in zip file. (thanks, Matt)
Continuing with the fixtures/test theme, I want to focus on the place where fixtures actually live - the database. Migrations are the blueprint, however, they often break and we don’t notice. You should alway be able to do this:
12
$ rake db:migrate VERSION=0
$ rake db:migrate
I used to say “what does it matter? We’re never going back to migration 3, we’re on 156 now!” This kind of thinking showed how I didn’t understand the usefulness of migrations:
Setting up a new development system should not require a recent database snapshot.
Automated tests and build notifications are simpler when the migrations are clean.
cap rollback will save your life some day
Migrations are not just a historical record of your database design. They instead give you a way to build your database up from scratch, doing more than just creating a schema. You can seed data, create indexes, and make transformations.
When you first start a rails project, and everything is golden, migrations are easy. Eventually, you run into problems. It happens a lot, because we typically don’t test the entire migration sequence. For example:
A model changes somewhere, and breaks a dependent migration
Using models in migrations is a common way to seed the database, or manipulate things:
If you delete or refactor the Section model later, this migration will likely break. The solution for this one is to define the model in the migration:
Someone on the team checks in a migration that has a bug
If the problem is trivial, they might be tempted to skip reporting it and just fix it in the database to keep things moving. Or, they may not even notice the problem, depending on when they updated. These issues can lead to inconsistencies, and tests that pass for one developer, but not another!
Developers only migrate up
Migrating down should work too, what if you need to roll back to fix something in production? Always write a sensible down method and test it. It does not have to perfectly reverse the database, it just needs to return it to a state that will enable the previous migration to run it’s down method. I’ve seen horrific migrations checked in like this:
123
defself.down# no need to do thisend
The team works from a production db snapshot based on a deployed site
This is bad, because it means the team is probably not using TDD, and are instead relying on browser interaction to develop the app. At minimum, they are blind to migration issues. Relying on an external database for development is an unwise dependency. It also complicates setup for testing.
Keep Migrations Working!
Each time you add a migration, or refactor a number models, you should check that all the migrations are working. There are a number of solutions for doing this - the most obvious is to drop the dev db and migrate up from scratch, see if it works.
I just got back from RailsConf 2007, it was a brain-expanding conference. I spent a lot of time talking to Rails coders about how they do testing and use fixtures. I saw some patterns emerge. Generally, people agree that fixtures are painful:
referencing ids in the fixtures is annoying and prone to error
the lack of a grouping mechanism makes selecting them harder
fixtures get out of date with model changes
I plan to do a few posts about fixtures in the next coming weeks, to share some of what I have learned. For now, I’ll focus on validation.
Fixtures Break
The project I’m currently working on has a large number of fixtures (over 100 models). It’s become really hard to manage them all. Over time, some fixtures busted, and it became hard to diagnose random problems in tests.
We are fortunate to be working with “zenspider”:http://blog.zenspider.com/, he’s helping us get our tests, dev process and performance in better shape. I complained a lot about invalid fixtures, and how I longed for a Rake task that could identify the broken ones. He offered up this:
namespace:dbdonamespace:fixturesdotask:validate=>:environmentdoname_map=Hash.new{|h,k|h[k]=k}Dir.chdir("app/models")domap=`grep set_table_name *.rb`.gsub(/[:\'\"]+|set_table_name/,'').splitHash[*map].eachdo|file,name|name_map[name]=file.sub(/\.rb$/,'')endendDir["test/fixtures/*.yml"].eachdo|fixture|fixture=name_map[File.basename(fixture,".yml")]beginklass=fixture.classify.constantizeklass.find(:all).eachdo|thing|unlessthing.valid?thenputs"#{fixture}: id ##{thing.id} is invalid:"thing.errors.full_messages.eachdo|msg|puts" - #{msg}"endendendrescue=>eputs"#{fixture}: skipping: #{e.message}"endendend# validateend# fixturesend# db
Put it in your project Rakefile, and you can then run:
1
$ rake db:fixtures:validate
You will get back a list of all the fixtures that are not valid, with the validation messages.
But what about edge cases? What about “bad” form data? DHH has declared that most folks want all fixtures loaded at the start of the tests. The data in fixtures does not have to pass model validation before it is loaded into the db.
After discussing this with a number of folks, I’m starting to believe:
All fixtures should be valid at all times
Fixtures should provide stuff required by your app to run tests (your admin user, default options, etc)
Edge cases should be created in tests, not fixtures
Test invalid data by loading a good fixture and changing it
rake db:fixtures:validate is helpful for keeping them valid. I plan to use it whenever I do a migration, just to ensure that nothing got broken.
I have not really been getting into blogging yet, obviously. It’s been several months since the most recent post. I’m in New York at the moment, getting ready to launch a rails web project. More details about that later.
For now, I just had to post something I did recently that was handy: Using rake to run a specific group of files.
There are some great built-in tasks provided by Rake. Let’s look at the testing-related ones (run rake -T to see them all):
123456
rake test:functionals # Run the functional tests in test/functional
rake test:integration # Run the integration tests in test/integration
rake test:plugins # Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)
rake test:recent # Test recent changes
rake test:uncommitted # Test changes since last checkin (only Subversion)
rake test:units # Run the unit tests in test/unit
These are all very useful. But on the site I’m currently working on, I wanted to run a set of tests that were related to a section of the web site: the web store. I wanted to be able to run a specific combination of unit, functional, and integration tests.
I looked at the source for the tasks that rails provides for rake. The testing tasks are defined in lib/tasks/testing.rake in the rails distribution. The code for the unit testing task, for example:
123456
desc"Run the unit tests in test/unit"Rake::TestTask.new(:units=>"db:test:prepare")do|t|t.libs<<"test"t.pattern='test/unit/**/*_test.rb't.verbose=trueend
The files are specified as a glob. But, I did not want to use a file pattern. I consulted the rake API docs and discovered the test_files= setter method of Rake::TestTask. Here’s what I ended up with:
I saved this task in lib/tasks/store_tests.rake under my project directory. Now, I can do this:
123456789
$ rake test:store
(in ~/rails_project)/opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb""test/integration/store_test.rb""test/unit/product_test.rb""test/unit/product_category_test.rb""test/unit/product_categorization_test.rb"[... etc ...]Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader
Started
....................................................................
Finished in 10.799886 seconds.
68 tests, 682 assertions, 0 failures, 0 errors
All the store tests are run as a group. This is a nice way to verify that things are working in the money-making part of the site.
However, I have to remember to update the task whenever I refactor or make additions. I’m thinking about symbolic links, now. Perhaps the tests could be grouped together in a directory (like test/groups/store) by making symbolic links to them in there. And, subversion deals with symlinks well…
I’ve been looking for a great javascript/css editor for a long time. I have tried out several of them, but I was turned off by how busy and complicated they seemed. Too many icons, or not css-centric. A site with a clean CSS design can get easily bungled by badly formated content.
I was fortunate to meet Peter Krantz at Railsconf 2006. Peter runs the Standards-Schmandards website, which I have been following for years. He recently posted aboutWYSiwym, a visual editor built in javascript that uses pure css to power a creative approach to the problem of web-based rich text editing. Take a minute to check out the demo.
I believe this project has a bright future. Web-based rich text editing should respect the CSS rules put in place by the site’s designers. Less is more when it comes to formatting, and the “block-based” approach to formatting makes a lot of sense for most rich text web applications. I definitely plan to try this out on my next CMS project!