Testing.has_many :purposes slides
January 15th, 2010
I’ve posted slides from the talk I gave at LARuby tonight, titled “Testing.has_many :purposes”. Enjoy.
Goals for 2010
December 25th, 2009
I’ve never publicized my New Year’s resolutions/goals, but I think it’s a really insightful exercise. I’d like to see what other rubyists out there are planning for 2010, so please be encouraged to do so, and leave links in the comments if you do!
Run more
Run my first 10k and half marathon.
Write more
Mostly about software, on this blog. Related: move off of mephisto (see upcoming post on my blog engine wishlist).
Speak more
I’ll be submitting talks to Mountain West Ruby Conf 2010, LA Ruby Conf 2010, and RailsConf 2010. I’d also like to speak more at my local user group(s).
Read more
Here’s my tech reading list for 2010:
1. Working Effectively with Legacy Code by Michael Feathers
2. Agile Estimating and Planning by Mike Cohn- I just bought these first two, and I’m excited to dig in.
- Seriously, I challenge you to find a more all-star group of authors collaborating on one book. This one is a must-read!
- Paul gave a fantastic talk at RubyConf 09 on designing SOA apps, and I trust his book will be just as informative.
- I love targeted titles like this, and having spent some time hanging out with Tammer at RubyConf this year, not only is he a great guy but a fantastic rubyist. Looks like Addison-Wesley is coming correct this year.
Release more
I’ve got a slew of ideas for open source stuff, as well as a few things for personal apps.
Live more
I spend a lot of time camped out in front of my laptop. Yes, I work long hours, and there’s plenty of work to be done, blah, blah, blah. While I’ve really improved as a rubyist in the past year, I haven’t done quite as good of a job at maintaining a good “work-life balance”, as the saying goes. This is probably because I love creating ideas by writing software, and I love doing it in Ruby.
Still, there are other things I really enjoy doing; like snowboarding, and exercising, and traveling, and even once upon a time, playing music. I need to make an effort to do these things regularly. Jamis Buck has published and spoke recently a lot on this topic, and he has some very worthwhile things to say. Because the last thing I want to happen is to get burned out from writing software, as Jamis warns will happen if you don’t invest yourself in some non-tech hobbies.
Those are my goals for 2010! 2009 was an exciting year for me, but I’ve got a great feeling about the new decade.
Bundler is the new hotness: deploying rails apps with bundler and capistrano
December 22nd, 2009
This post is a compilation of useful tidbits I’ve learned from a few different sources as I’ve moved a rails app over to the new gem dependency hotness, bundler. There are a few steps involved here, and seeing that I’ve done the legwork in figuring them out, I figured I’d share what I’ve learned. Still, if you’d prefer to cut to the chase, here are the relevant links:
UPDATE: Bundler requires ruby 1.8.7, so make sure you have that installed.
UPDATE 2: Looks like bundler works with 1.8.6 again (commit)
- Bundler github page
- Using the New Gem Bundler Today (via Yehuda Katz)
- My fork of jerryvos’ gist (I literally changed one character—the credit goes to jerryvos)
- The Gemcutter source—specifically, check out Nick’s config/boot.rb
- Managing gems in a rails project (via Kerry Buckley)
Why?
Because bundler is the new hotness—THAT’S WHY. Also, dependency resolution, and system independence (i.e. no more relying on gems that may or may not be installed on your production box). Gem bundler does a fantastic job of managing both of these problems for you. For more details on how, check out the README on the bundler github page.
Still here? Sweet, let’s get started.
We’ll need to touch a couple of different areas to get a deployment strategy up to snuff with bundler. WARNING: monkey patches are involved. Get over it.
Steps:
1. Create a Gemfile in RAILS_ROOT, and populate it with your gem dependencies.
Here’s a sample. The syntax is a little different that what you’re likely used to, be I find it much more intuitive. Also, note the first line, where we set a custom vendor path. This is important—more on that in a bit.
2. Create RAILS_ROOT/config/preinitializer.rb.
If you’re using passenger to serve up your app, stick only this line in it (despite what some of above posts may say—read the first and fourth comments on Yehuda’s post):
require "#{File.dirname(__FILE__)}/../vendor/bundler_gems/environment"
And place the following at the bottom of RAILS_ROOT/config/boot.rb, but STILL ABOVE the last line, which is Rails.boot!:
# bundler
class Rails::Boot
def run
load_initializer
extend_environment
Rails::Initializer.run(:set_load_path)
end
def extend_environment
Rails::Initializer.class_eval do
old_load = instance_method(:load_environment)
define_method(:load_environment) do
Bundler.require_env RAILS_ENV
old_load.bind(self).call
end
end
end
end
Here’s a gist of our entire config/boot.rb file for reference. Yep, that’s the nasty monkey patch. Oh well. Bundler is worth it.
If you’re running passenger to use the “smart” spawn method (which I believe is the default method—because I haven’t added much to the passenger config), then placing the above chunk of code in the config/preinitializer.rb file as Yehuda suggested, will not work.
3. Tell git to ignore the build gems, but keep the .gem files
bin/* vendor/bundler_gems/* !vendor/bundler_gems/cache/
This ensures that everything in vendor/bundler_gems (where we’ll be vendoring our gems) except the .gem files which bundler places in vendor/bundler_gems/cache will be ignored by git. The .gem files are all we need to be able to build our gems.
As pointed out in the bundler README, make sure you add something to vendor/bundler_gems/cache before you commit these changes to .gitignore. The easiest way to do this is to simply run the gem bundle command, which will indeed, bundle your gems. Sweet.
Also, it is important that you don’t try to use vendor/gems to store your new bundled gems. According to the bundler README, rails gives some special treatment to this location, and you may run into trouble by bundling your gems here.
4. Modify capistrano strategy
We need to make a few changes to our capistrano deploy strategy to make sure we’re using bundler in an intelligent way. First, we need to create a couple of new cap tasks so capistrano knows how to 1.) bundle your gems and 2.) symlink the proper directories on your current release so that you’re not bundling the same gems on every deploy.
Finally, we need to a hook to capistrano so it knows when in our deployment lifecycle to execute these tasks. Here’s the code:
namespace :bundler do
task :install do
run("gem install bundler --source=http://gemcutter.org")
end
task :symlink_vendor do
shared_gems = File.join(shared_path, 'vendor/bundler_gems')
release_gems = "#{release_path}/vendor/bundler_gems"
%w(cache gems specifications).each do |sub_dir|
shared_sub_dir = File.join(shared_gems, sub_dir)
run("mkdir -p #{shared_sub_dir} && mkdir -p #{release_gems} && ln -s #{shared_sub_dir} #{release_gems}/#{sub_dir}")
end
end
task :bundle_new_release do
bundler.symlink_vendor
run("cd #{release_path} && gem bundle --only #{rails_env}")
end
end
# hook into capistrano's deploy task
after 'deploy:update_code', 'bundler:bundle_new_release'
The above code creates three symlinks in your current release folder to a shared gem folder. This way, towards then end of a deploy cycle when capistrano goes to run gem bundle in your current release, it only downloads/builds/removes gems that have changed. This makes for a much more efficient deployment strategy. Nice.
Controller spec session variables
November 24th, 2009
When writing a controller spec recently, I ran into a frustrating little gotcha. We’re using Authlogic for user authentication and rspec for our test suite. We have a controller that requires the user to be logged in but also requires the user to have a practice assigned. Here’s what the controller looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class UsersController < ApplicationController before_filter :require_user before_filter :require_practice def new # ... end end describe UsersController, "#new" do it "should render the new template" do user = Factory(:active_user) UserSession.create(user) get :new, {}, { :current_practice => user.practice.id } end end |
With the above code, we’ll be redirected to the user login page. Why? When you alter the session by passing it into the third argument to the http verb (in this case, get) method, it overwrites the entire session object. In this case, the above would overwrite the session information that Authlogic relies on to persist user sessions. Instead of passing it into the request, i.e. overwriting the entire session object, we must set only that session variable explicitly:
1 2 3 4 5 6 7 8 9 10 11 |
describe UsersController, "#new" do it "should render the new template" do user = Factory(:active_user) UserSession.create(user) session[:current_practice] = user.practice.id get :new end end |
This way we’re not overwriting the entire session object.
Make your tests less brittle by unit testing your factories
September 4th, 2009
If you haven’t already, stop using fixtures immediately and switch to factories on the quick. Personally, I use factory girl courtesy of the tiny robots over at thoughtbot, but I’ve heard object daddy is pretty kick ass too, brought to you by them playaz at OG. Regardless of the solution you use, you’ll find yourself using these babies throughout your test stack, and as such, you ought to make sure your factories are as solid and trustworthy as your production code.
I think unit testing your factories is hugely important (and easily overlooked) in maintaining a good TDD work-flow. You’ll be using your factories all throughout your test suite, and since they’re unique to your application, and they’re created by you, they need to be tested by you. More importantly, especially if you’ve got even mildly complex models with more than a few associations (which might have some fairly complex validation criterion on their own) keeping track of your factories can get pretty hairy pretty quick. Better yet, delegate the task of ensuring your factories stay up to date to your tests.
Consider this all too familiar scenario: you’re doing some integration testing of your controllers, and you hit an error in your tests you just can’t nail down. Finally, you track it down to the factory creating object X, but you can’t figure out why the object won’t validate. Then, finally, you realize that object X isn’t validating/saving because associated object Y isn’t validating/saving (classic example: a polymorphic Address model). Before you know it, you’ve lost two hours, you’re frustrated, and all you really want to do is assert that the “new.haml.html” template is being rendered when you send a GET request to /users/new! Sheesh!
I find this nightmarish scenario particularly prone to occur when I’m working on an app that I haven’t touched in a few weeks (i.e. legacy). Well excuse me if I don’t have the object dependency chart burned into my brain!
In addition to the actual hours lost trying to nail down the error, the lingering uncertainty that your brittle factories might break your tests is a productivity killer. Remember, the whole point of using factories over fixtures was to make your tests less brittle, not more. Even worse than the man hours lost, brittle factories might incentivize you to write fewer tests, which, of course, would be complete and total blasphemy, and time might literally stop.
Luckily, testing your factories is super easy, takes no time and in my opinion, yields a massive benefit (re: preventing the time stopping madness briefly mentioned in previous paragraph). In Test::Unit you can do something like the following (the following examples assume you’re using factory girl):
class UserTest < Test::Unit::TestCase
context "a valid user model" do
should "have a valid Factories" do
%w(admin non_admin).each do |u|
assert Factory.build(u.to_sym).valid?
assert Factory(u.to_sym)
end
end
end
end
# in your user factory definition file
Factory.sequence :email do |n|
"email#{n}@address.com"
end
Factory.define :admin, :class => User do |f|
f.email { Factory.next(:email) }
f.name 'John Doe'
f.password 'password'
f.password_confirmation 'password'
f.admin true
f.email_confirmed true
end
Factory.define :non_admin, :class => User do |f|
f.email { Factory.next(:email) }
f.name 'John Doe'
f.password 'password'
f.password_confirmation 'password'
f.admin false
end
Now you can rest (test) easy as night knowing that your factories are doing what they’re supposed to do (create object instances), your tests are less brittle, and most importantly, the sky isn’t falling. Sweet.
xclip: send standard output to the clipboard
September 4th, 2009
I’ve been wanting this for a while now. http://www.cyberciti.biz/faq/xclip-linux-insert-files-command-output-intoclipboard/
Focus on what your object DOES, not what it IS
August 24th, 2009
I just picked up the beta book of The RSpec Book and I thought I’d share this very insightful quote on TDD/BDD:
The problem with testing an object’s internal structure is that we’re testing what an object is instead of what it does. What an object does is significantly more important.
Goodbye ugly boilerplate RESTful controller code
August 12th, 2009
Goodbye ugly boilerplate RESTful controller code: http://ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with
New CI server - CI Joe
August 10th, 2009
Continuous Integration Installation Gotchas
July 25th, 2009
This weekend I thought I’d test out the two leading ruby-based continuous integration (CI) servers, Integrity and CruiseControl.rb (at least according to my new favorite ruby hotness resource). Both tools boast ease-of-installation, and except for a couple of gotchas, they are pretty damn easy to install. I’m not going to go through how to install these tools, I’ll leave that to the tools’ respective sites and a couple of other posts, which you can find at the end of this post.
Gotcha #1 – git: command not found
Depending where git is installed on your system, your web server (apache in my case, though it would be just the same for nginx) may not know where to find git, in which case your apache logs will show some like sh: git: command not found. Boo.
If memory serves me correct, I think I used this installer back when I re-installed git after I had to replace my hard drive. For whatever reason, this otherwise extremely convenient installer placed git at /opt/git/bin/git, and the apache user doesn’t have that path added to it’s PATH. The articles linked at the end of this post suggest adding a SetEnv PATH line in your apache vhost config file, but I think a symlink is easier.
Solution: Add a symlink in /usr/bin to my the git install location, like so:
sudo ln -s /opt/git/bin/git /usr/bin/git
The above creates a symlink to your git location, so now apache should be able to use it.
Note: Integrity is particularly bad with this error because it doesn’t display any sort of useful error message, and nothing appears in the integrity application log files. Rather, I had to dig into my apache logs to find out that it couldn’t find the git command. CruiseControl.rb displays this message promptly, which is helpful.Gotcha #2 – Database.yml files
Most likely, you exclude your database.yml config file from your git repo. That said, you’ll need to set one up for the cloned version of your repo that your CI server will create or your builds won’t run. Below lists where your app will be cloned by the CI server.
- CruiseControl.rb: ~/.cruise/projects/[APP_NAME]/work/
- Integrity: [LOCATION OF INTEGRITY INSTALL]/builds
Solution: The CI server just does a git clone of your application, so setting up your database.yml file is the same as setting it up for any other app.
Gotcha #3 – Some weird rake task error (CruiseControl.rb only)
My impression is that this is probably an edge case that is only applicable to people brave enough to run gem cleanup (doh!), but who knows, maybe you’ll find this useful. For some reason pertaining to the OS environment compatibility with rake, CruiseControl.rb uses a very long-winded rake command, while the default task it runs is cc:build. I’m not sure which part of this was causing the error – the rake task or the rake command itself – but nonetheless I got a very odd error:
undefined method `reenable' for #<Rake::Task:0x335db0>
Solution: We need to tell CruiseControl.rb to run a different rake task. Open up the cruise_config.rb file located in ~/.cruise/projects/[PROJECT NAME] and in the Project.configure block do the following:
Project.configure do |project| #... project.rake_task = 'test' # this can be the name of any rake task #... end
Conclusion…or lack thereof
In terms of features, both CruiseControl.rb and Integrity seem to offer about the same. As far as performance goes, I have no idea which is better, but if anyone has done some testing on this please include your results in the comments. However, the one thing that I definitely appreciate about CruiseControl.rb is they provide much more documentation on their website than does Integrity.
I hope you found these tips helpful.
Other Helpful Posts
Rails 2.3 Backtrace Cleaning
July 23rd, 2009
I’m pretty late to the party on this one, but there are probably others out there like me who simply couldn’t live without thoughtbot’s quietbacktrace gem. When I upgraded to Rails 2.3, this gem of a gem (pun intended) stopped working. I think I’ve figured out why, thanks to this post from Mike Gunderloy. Shoulda googled that one a while ago.
Time Machine Tip: Disable Antivirus
February 26th, 2009
My initial Time Machine backup seemed to be taking far too long. After a quick glance in the Console, the reason was painfully obvious: Symantec Antivirus AutoProtect. For whatever reason, it eats up CPU processing power and brings the speed of a backup to a halt. Disable it while backing up.
Two quick ways to make testing much more enjoyable
December 11th, 2008
If you’re doing TDD, and especially if you’re just starting out, you know how frustrating debugging your code can be. These tools/tips make TDD much, much more enjoyable.
1. autotest + quiet backtrace
This is a really a two-fer, but in my opinion, these two tools together make me exponentially more productive. autotest keeps your test suite running in the background, and re-runs them every time a file changes. The reasons for using autotest are beyond the scope of this short post, because there are so damn many good ones. If you’re not using it already, just take my word for it and start, now. Right now.
quiet backtrace is a nifty little tool developed by the generous geniuses over at thoughtbot. In short, quiet backtrace filters all the irrelevant backtrace “noise” that are produced when a test fails or hits an error. When a test fails, the only information you really want is the fail message, and more importantly, the actual line number in the test file that failed! Instead of over-working your eyes trying to dig through the wrapped backtrace lines telling you which path ruby took to get to the failed assertion, quiet backtrace filters out all the crap and outputs only the usable information.
Now, what really makes these two powerful together is that since using autotest and quiet backtrace together, I never have to leave my text editor to mess with the console window running my tests. By itself, autotest eliminates the need to manually re-run your test suite every time. Still, quite often I found myself not only needing to leave the text editor, but also use my mouse (gasp!) to scroll up in the console window because the backtrace scrolled so long that I could no longer see the actual error message.
2. tail -f log/test.log
This isn’t really a “tool”, but more of a massively overlooked convenience on my part. It’s easy to overlook the test log because the development log is always front and center, as it’s output by default when you run script/server. However, the test log gets updated on every request, just like the development log. So, when you’ve hit a weird error in functional or integration tests, and you just can’t figure out why you’re getting a redirect where you’re expecting successful request, turn to the test log. Better yet, use the tail -f command to keep up as it is updated.
I’ll say briefly that virtually every testing product put out by thoughtbot has made testing a much more enjoyable experience. I want to keep this post short, so I’m going to cut it off here. But seriously, go check out all the goodness being produced by those Giant Robots.
Nevermind that last article
November 20th, 2008
If you happened to read my post on managing plugins with git, pretend you didn’t because it doesn’t work as expected. I thought ignoring the .git directories in my clones plugin repositories would do the trick, but I was mistaken. My quest continues…
Ugh...
November 18th, 2008
From an email I just received from Google:
New Gmail code base available to IE6 users: Users of Internet Explorer 6 can now access significant Gmail performance improvements as well as new features like colored labels, group chat, an updated contact manager, remote sign out and more. To access these features, simply sign in to Gmail. If you don’t see these new features immediately, you may still need to get the latest IE6 updates from Microsoft.
Message to you poor souls still using this garbage browser: please stop. Ugh…