So I didn't go to whatever was going on in Baltimore this week, but I did do a whole bunch of open source coding over the past week or two:
expectalias now plays nicer with RSpec's
- Rerun got a few new command-line options, including
--exitso you can now easily rerun regular scripts (like
rerun -cx rake test) when their files change
- I submitted a patch to RubyGems to make the warningitis less ZOMG and more KTHXBY
- Fonzie is a bookmarklet that tells you what font you're looking at
- Twitter RSS is a bookmarklet that brings back the RSS link to the New Twitter UI
- Showoff works better with nested bullets and missing
showoff.jsonfiles -- so go nag Scott if you want him to accept my patches
- I also finally got my git pre-commit hook correctly stripping whitespace, and
Have you upgraded RubyGems lately? Is your console suddenly filled with warnings like this?
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01. Gem::Specification#default_executable= called from /Users/chaffee/.rvm/gems/ruby-1.9.2-p0/specifications/thin-1.2.7.gemspec:10.
You may be showing signs of a new malady known as Warningitis! So far there is no cure, but doing the following will temporarily cure your symptoms:
gem update --system 1.7.2
This has been a public health alert. Please do not panic. SARS masks and iodine pills are not recommended at this time.
I'm updating Erector to RSpec 2 and came across two problems for which solutions were surprisingly difficult to Google. Here are my (finally successful) results.
no such file to load -- spec/rake/spectask
require "spec/rake/spectask" # RSpec 1.3
require "rspec/core/rake_task" # RSpec 2.0
undefined method `spec_files=' for #<RSpec::Core::RakeTask:0x00000101550aa8>
# RSpec 1.3 Spec::Rake::SpecTask.new(:core) do |spec| spec.spec_files = FileList['spec/erector/*_spec.rb'] spec.spec_opts = ['--backtrace'] end
# RSpec 2.0 RSpec::Core::RakeTask.new(:core) do |spec| spec.pattern = 'spec/erector/*_spec.rb' spec.rspec_opts = ['--backtrace'] end
See also http://github.com/rspec/rspec-core/blob/master/Upgrade.markdown (curiously cloaked from Google searches for the above problem strings).
ERROR: While executing gem ... (Gem::RemoteFetcher::FetchError) bad response Moved Permanently 301 (http://gems.rubyforge.org/latest_specs.4.8)
gem sources -a http://rubygems.org/ gem sources -r http://gems.rubyforge.org/
Looks like they weren't kidding when they said to switch from rubyforge to rubygems.org (née gemcutter)!
[Edited to change "http://production.s3.rubygems.org/" to "http://rubygems.org/". Note that the trailing slash is significant!]
/Library/Ruby/Gems/1.8/gems/json_pure-1.4.3/lib/json/pure/generator.rb:232:in `__send__': undefined method `except' for #<JSON::Pure::Generator::State:0x102f245b0> (NoMethodError)
The environment: Ruby 1.8.7, DataMapper, dm-types, ActiveSupport, or just
require 'json/pure' require 'active_support'
(as seen in http://gist.github.com/339528)
# workaround for activesupport vs. json_pure vs. Ruby 1.8 glitch if JSON.const_defined?(:Pure) class JSON::Pure::Generator::State include ActiveSupport::CoreExtensions::Hash::Except end end
When two strings fail to match, if the difference is somewhere in the middle of the strings, it can be annoying/impossible to track down the
actual difference. I've written a little Comparison object that
overrides the failure message for
.should == like this:
Strings differ at position 12: expected: ..."efghijklmnopqrst"... actual: ..."efghijklXXopqrst"...
It shows a "prelude" of a few characters, then the difference, lined up on successive lines so they're easy to visually scan. It also does the right thing (or tries to) if the difference is near the beginning or end of the string (i.e. does or doesn't show ellipses).
For people who can't wait for this to get incorporated into RSpec
proper, you can grab the code from github and
require "comparison" in your
spec_helper.rb and it'll override the existing RSpec
== matcher. Or wait for Issue 9 to be pulled into version 2.1 (maybe). Or if you want to use it in your favorite testing framework, the object is completely self-contained and should be easy to call from your own
assert_equals or whatever.
One open question is whether the exception message should show the full actual string as well as the comparison... On one hand, it adds to screen clutter, but on the other hand, it can be important in tracking down the problem, especially if the prelude is ambiguous.
My company launched our app, Cohuman, a few weeks ago. The rush of finishing features, fixing bugs, and responding to user feedback has subsided a bit, and it's time to go back and give the little baby a tune-up. I find that a good development process will ebb and flow, and as long as you don't let something slide for too long, it's perfectly acceptable to let bugs, or performance issues, or development chores pile up for a bit and then attack them concertedly for an entire day or two. A bug-fest or chore-fest or tuning-fest can actually increase efficiency as you get in a rhythm... and it feels really good at the end of the day when you see all the bugs you slayed or all the milliseconds you shaved.
In this article I'd like to describe some of my techniques. I make no claim of originality or great expertise; I just want to share what I know, and hear (in comments) what other people have learned. I'm using Sinatra and ActiveRecord, but not Rails; hopefully this discussion will help people no matter what framework they're using.
That's it. I'm done with TextMate. It hasn't been updated in over 2 years, either for essential functionality (replace in path) or performance fixes (searching through log files) or UI issues (how many boxes must you click to enable autosave?) or bug fixes. Every few months the author pokes his head up and says "I'm working on TextMate 3.0!" and then disappears again, happily accepting new license fees into his PayPal account.
I've just been bitten twice in two weeks by a bug that caused not just data loss, but data mangling in a way that was very difficult to fix. Here's the rough steps to reproduce:
- Edit some files in TextMate
- Leave TextMate running in the foreground
- Switch to console and "git pull" in the latest code from your workmates
- Run a search-and-replace that edits a file that was changed (by someone else) during the merge
- Save that edit
You'll see (with "git diff") that your version of the file has your new post-merge changes... but it also has reverted your buddy's changes from the merge. It's like you decided that those changes were no good and reverted them yourself and then added your own.
If you fail to notice this before checking in, you will totally hose your version control... but just for that one file. You can't just revert a whole commit... you'll have to step through change by change to figure out which change was theirs, which change was yours, and which change looks like it was yours but was actually an inadvertent revert of theirs!
I can't work under these conditions. I'm switching to RubyMine today. See http://bjclark.me/2010/03/10/rubymine-a-love-story/ for why.
P.S. I just found a bug report for this dated 2008-04-02. That's just about 2 years ago for a critical data-losing bug. Woot.
Now that I'm starting to use DelayedJob to perform jobs in the future in my Heroku Sinatra app, its important that they happen at the scheduled time. But unless you pay attention, you'll find that times get mysteriously changed -- in my case, since I'm in San Francisco in the wintertime, by +/-8 hours -- which means that some conversion to or from UTC is being attempted, but it's only working halfway.
Trying to keep a handle on which libraries are attempting, and which are failing, to convert times is a losing battle, so I'm trying to do the right thing and save all my times in the database in UTC, and convert them to and from the user's local time as close to the UI as possible. Unfortunately, a variety of gotchas in Ruby and ActiveRecord and PostgreSQL makes this trickier than it should be. Here's a little catalog of my workarounds.
The rules for how to make parts of your HTML page translucent are kind of hard to understand -- in other words, the opacity rules are pretty opaque. (Anyone who can make that into a good pun, let me know and I'll change the title of this article accordingly.) The following represents the results of a couple of days of empirical research and as such may be incomplete or inadequate, but here goes.
In the brave new HTML5 world, with all the CSS gizmos supported by Safari and Chrome and Firefox, there are now three ways to make things translucent. And none of them works quite the way I naïvely expected.
One. Use the "opacity" CSS attribute. This attribute works pretty well... at first. It applies to an element and all its children, but according to the spec it's meant to act as an upper bound on the opacity of all its children, and while it can technically be overridden, the overridden value is applied as a multiplier to the previous value, not as a whole separate value. So if you want some fully opaque children inside a translucent container, you can't get there from here. The children are always going to be at least as transparent as the parent -- in other words, they can't transcend their parent's transparency.
This is spelled out in detail in https://developer.mozilla.org/En/Useful_CSS_tips/Color_and_Background and as a solution they propose either pulling the child out of the normal hierarchy (ugh -- that means you lose all the other CSS inherited styles and positioning), or ...
Two. Make an alpha channel PNG and use it as the parent's background, probably with
background-repeat:repeat. This is adequate, except that there's now another, cleaner way...
Three. For the parent, use
background-color: rgba(255, 255, 255, 0.5) (where '0.5' is the opacity and '255,255,255' is the decimal RGB value) -- that will work the same as an alpha PNG but without needing to go round-trip to Photoshop every time you want to change the color or level. Much better.
I have no idea what the level of support for rgba background colors is, but it seems to work in the latest Safari and Firefox so I'm happy.