The garbage collection bug we encountered on Monday has popped up in new places:
Rspec version < 1.1.11 + Linux ruby 1.8.6 patchlevel > 114 = (small explosion)
libxml version 0.5.4 (11 versions out of date) + Linux ruby 1.8.6 patchlevel > 114 = (another small explosion)
Also, speaking of libxml:
"Turning off cache-busting for test environment replaces cache-busting junk with ??. Is there a way to make it replace it with nothing instead?"
With cache busting on, you have would have to test like this:
admin_button.should have_tag("a > img[src ='/images/v3/admin/image_name.png?12121212?']")which is awkward.
We turned it off by adding the following to config/environments/test.rb (google said to do it)
ENV['RAILS_ASSET_ID'] = ''But, then we had to do this:
admin_button.should have_tag("a > img[src ='/images/v3/admin/image_name.png??']")Is there a way to turn off cache-busting that doesn't put weird/ugly question marks at the end of your image names?
"with_tag & have_tag don't work in non-controller (e.g. model) rspecs?"
Yes, they don't. Use Hpricot instead. Or maybe assert_elements.
If you're new to Tracker, or are considering trying it out but haven't signed up yet, check out the Tracker 101 screencast. Thanks to Ian McFarland for recording it.
We've got a few mantras at Pivotal. One of them has to do with testing all the time. It's a Good Thing, for sure. Until recently though, I had always inserted a tacit "except for views" to the end of it. The reason for my reservations wasn't the fact that view tests can be brittle. Any test can be brittle. I didn't like testing views because it seemed like the test I was writing never really described the code I was writing. Let's look at a typical view test to see what I mean:
describe "/posts/index.html.erb" do def render_view render "/posts/index.html.erb" response.body end before(:each) do assigns[:posts] = [ stub_model(Post, :name => "First!", :body => "first body."), stub_model(Post, :name => "Second!", :body => "second body.") ] end describe "assertions using have_tag" do it "renders posts" do render_view response.should have_tag(".post", 2) end it "renders post headers" do render_view response.should have_tag(".post .post-name", "First!", 1) response.should have_tag(".post .post-name", "Second!", 1) end it "renders post bodies" do render_view response.should have_tag(".post .post-body", "first body.", 1) response.should have_tag(".post .post-body", "second body.", 1) end end endThis snippet uses the have_tag helper. It's somewhat slow, and to my eyes, expresses intent about as well as an apple floating in a top-hat filled with perfume. The "tag" is a selector? The content filter is just the second argument? And the last argument is the amount of "tag" the response should have? You can test like this, but why would you?
I've also seen a more manual pattern, using a library like Hpricot or Nokogiri to parse the response body, then asserting on the results of that:
describe "assertions using Nokogiri" do def doc @doc ||= Nokogiri(render_view) end it "renders posts" do doc.search('.post').should have(2).nodes end it "renders post headers" do headers = doc.search('.post .post-name') headers.should have(2).elements headers.detect { |element| element.text == "First!" }.should_not be_nil headers.detect { |element| element.text == "Second!" }.should_not be_nil end it "renders post bodies" do bodies = doc.search('.post .post-name') bodies.should have(2).elements bodies.detect { |element| element.text == "first body." }.should_not be_nil bodies.detect { |element| element.text == "second body." }.should_not be_nil end endIt's faster, since it's not using have_tag, but still not very expressive. CSS selectors are still littered across the it statements, but at least it's only once per test. Still, using detect to find content is no good. And I don't think CSS selectors have any business in it statements at all. That seems like asserting on the name of a method being called, not its behavior.
The solution!Given my problems with the above approaches, I created a gem that allows the following assertion syntax:
it "renders posts" do result.should have(2).posts end it "renders post headers" do result.should have(2).post_headers result.should have(1).post_header.with_text("First!") result.should have(1).post_header.with_text("Second!") end it "renders post bodies" do result.should have(2).post_bodies result.should have(1).post_body.with_text("first body.") result.should have(1).post_body.with_text("second body.") endWhat's a result? And how does it know how many posts, post_headers, and post_bodies it has? The result is defined in a before block like so:
require 'elementor' require 'elementor/spec' include Elementor attr_reader :result before(:each) do @result = elements(:from => :render_view) do |tag| tag.posts ".post" tag.post_headers ".post .post-name" tag.post_bodies ".post .post-body" end endThe elements method allows you to name your CSS selectors using the tag block argument. The tag object uses method_missing to register your names. The :from option specifies a method to be called that will return some raw markup.
Naming selectors alone was a huge win for me, but there are a few other cool bits about the @result object. First, you get to use the with_text helper for filtering content. You'll also get a with_attrs helper for filtering based on a hash of attribute values.
The project is called Elementor, and you can install it like so:
[sudo] gem install elementorThe code is on the GitHub here: github.com/nakajima/elementor (and you can see the CI build here). Take a look at the specs for all of the examples of what you can do. Hopefully, you'll find it as useful as I have. If not, please share your reasons in the comments!
You have probably noticed that the changes view is unusable in RubyMine because of all the .svn files showing.
Well if you refresh (
) the changes view they go away...Hurray!
"Does anyone know about recent changes to Ruby 1.8.6 garbage collection?"
We're seeing a strange test failure on one of our CI boxes:
[BUG] object allocation during garbage collection phase ruby 1.8.6 (2008-08-11) [i686-linux]
ruby -v gives this:
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-linux]
On the developer's OSX machine, which doesn't experience this bug, ruby -v gives:
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
There have apparently been changes to garbage collection in 1.8.7 and 1.9 -- does anyone know if these have been backported to 1.8.6 somewhere between patchlevels 114 and 287?
Rails 2.2.2 is released!
Even Rails 2.2.2 isn't always threadsafe. I found this out by running a script with JRuby from the command line. The script loaded the Rails environment and then launched two threads that simply tried to resolve an ActiveRecord class constant. Fireworks (in the form of LoadError) ensued deep inside of const_missing. I'll post the full example later today.
Tsearch2 is now built into Postgres (as of 8.3). This means you must remove the metadata from your tables, since Postgres now stores it in a separate place.
Matthew O'Connor and I recently worked on a project that sent SMS messages to mobile customers. Unfortunately the SMS aggregator we used on the project rejected messages with non-ascii characters.
One approach we considered was to strip our messages of any characters that were not ascii and send them as is. After looking through some of the rejected messages we realized most of the problems occurred with unicode punctuation. Instead of simple deleting the characters we tried transliterating them to their ascii equivalent.
Our first approach used IConv:
require 'iconv' module SmsEncoder def self.convert(utf8_text) text = Iconv.iconv("US-ASCII//TRANSLIT", "UTF-8", utf8_text).first text.gsub(/`/, "'") rescue Iconv::Failure "" end endFor some reason the backtick ` also caused problems so we converted that after using Iconv.
This approach worked perfectly on OS X but as soon as we moved to the Linux servers the libiconv characteristics changed and most untranslatable characters became question marks instead of empty strings.
Instead of wrestling with libiconv we looked for a solution entirely in ruby. We found unidecode which got us most of the way there. Unidecode did a little more than we wanted though and translated Chinese and Japanese characters to their approximate sounds. e.g. 今年1月 gets transliterated to Jin Nian 1Yue
We decided to only transliterate extended latin charaters, punctuation and money symbols.
Here is the final code with the unidecode monkey patch:
require 'set' require 'unidecode' module SmsEncoder def self.convert(utf8_text) Unidecoder.decode(utf8_text.to_s).gsub("[?]", "").gsub(/`/, "'").strip end end module Unidecoder class << self def decode(string) string.gsub(/[^\x20-\x7e]/u) do |character| codepoint = character.unpack("U").first if should_transliterate?(codepoint) CODEPOINTS[code_group(character)][grouped_point(character)] rescue "" else "" end end end private # c.f. http://unicode.org/roadmaps/bmp/ CODE_POINT_RANGES = { :basic_latin => Set.new(32 .. 126), :latin1_supplement => Set.new(160 .. 255), :latin1_extended_a => Set.new(256 .. 383), :latin1_extended_b => Set.new(384 .. 591), :general_punctuation => Set.new(8192 .. 8303), :currency_symbols => Set.new(8352 .. 8399), } def should_transliterate?(codepoint) @all_ranges ||= CODE_POINT_RANGES.values.sum @all_ranges.include? codepoint end end endand tests:
class SmsEncoderTest < Test::Unit::TestCase def test_transliteration_of_blank assert_equal "", SmsEncoder.convert(nil) assert_equal "", SmsEncoder.convert("") end def test_transliteration_of_whitespace assert_equal "", SmsEncoder.convert(" \t\n") end def test_transliteration_of_text_surrounded_by_space assert_equal "abc", SmsEncoder.convert(" abc ") end def test_transliteration_of_ascii orig_text = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~" conv_text = SmsEncoder.convert(orig_text) assert_equal orig_text.gsub(/`/, "'"), conv_text end def test_transliteration_of_unicode_punctuation utf8_text = "“foo” ‹foo› ‘foo’ ,foo, –foo— {foo} (foo) `foo`" ascii_text = SmsEncoder.convert(utf8_text) assert_equal "\"foo\" <foo> 'foo' ,foo, foo-- {foo} (foo) 'foo'", ascii_text end def test_transliteration_of_common_latin1_characters utf8_text = "ñ ò ^ ¡ ¿ Æ æ ß Ç §" ascii_text = SmsEncoder.convert(utf8_text) assert_equal "n o ^ ! ? AE ae ss C SS", ascii_text end def test_transliteration_of_money_characters utf8_text = "€ £ $ ¥" ascii_text = SmsEncoder.convert(utf8_text) assert_equal "EU PS $ Y=", ascii_text end def test_untransliterable_characters utf8_text = "ɏ \x1f \x01 \x00 Ʌ \x7f" ascii_text = SmsEncoder.convert(utf8_text) assert_equal "", ascii_text end def test_transliteration_of_chinese_characters utf8_text = "ウェブ全体から検索" ascii_text = SmsEncoder.convert(utf8_text) assert_equal "", ascii_text end end"How do you get Selenium to work with Firefox 3?"
If you know how, pull the jar files out of a later release and use those. Good luck!
This is a cross post from my personal blog, because I'd like to hear from other Pivots about why they test.
First about unit and integration tests.
Then about interaction and state based testing.
So, now it's your turn - why do you test?
Engine Yard has made some changes to their ssh-key setup:
...any non-approved keys will be removed from the root user's authorized_keys file. It should be noted that customers should not log in directly as root but rather should log in as their user and use sudo for any commands that need super user privilege. If you are currently using the root user to log in and have your key in roots authorized_keys file it will be removed when this change is made.
If you are having any ssh problems, contact them.
Pivotal Tracker tip: as the Holiday Season approaches, you can edit your Team Strength to account for vacationing team members. For example, if you are missing 1 out of a team of 4 next week, set next week's Team Strength to 75%

Rescuing inside a transaction: ActiveRecord relies on catching a Rollback error in order to perform transaction rollbacks. If you are performing a begin..rescue block within a transaction, make sure you either (a) specify the Exception or Errors you want to catch, or (b) re-raise the Rollback error if caught.
Bring it! The Pivot Pong Tournament of Champions is on! Games will be played on the Pivotal breakfast tables/ping pong table.

"What is the life-cycle of test fixtures when using transactional fixtures?"
One team discovered a jaw-dropping issue with has_many :through. Given the following:
class User < ActiveRecord::Base has_many :user_photos has_many :photos, :through => :user_photosRails 2.2: Test::Unit::TestCase extentions have been removed from Rails Core and are now in ActiveSupport::TestCase. As stated in the Groups Thread about this, use ActiveSupport::TestCase instead of Test::Unit::TestCase in test/test_helper.rb.
Pivot Jonathan and I were recently working on support for Google Chrome in Pivotal Tracker. Tracker's extensive JsUnit test suite made this a lot easier.
Here's some quick notes I took on the issues we ran into.
Don't try to directly mock the 'reset' method on a Form ElementThis was the original mocking code in one of our JsUnit tests:
var resetCalled = false; widget._uploadForm.reset = function() { resetCalled = true; };This permanently blew away the "reset" method, so it was undefined when called in a subsequent. To fix it, we did this in our form builder method:
var element = Element.create("form"); element.nativeReset = element.reset; element.reset = function() { element.nativeReset() }; Hash keys sort differentlyWe had a testHash.keys() being compared to a hardcoded array. Chrome sorted the keys differently (apparently non-deterministically, so we had to do an explicit sort:
assertArrayEquals(['10001', '10002', '10003', 'endOfList'].sort(), $H(itemListWidget.draggables).keys().sort());It wasn't good to depend on the keys order in the first place, but it worked under IE, Firefox, and Safari.
The same hash sorting bug bit us in a much more obscure way. There was some threading test code that simulated timeouts/concurrency using a mock clock. Previously, the test code was dependent on the order in which the functions were added to a hash the mock "clock". This broke with a different hash sorting order. We had to simulate some additional "ticks" to make the test pass.
Mozilla, but not GeckoThe browser string returned for Chrome by one of our utility functions, BrowserDetect.browser(), is "Mozilla". However, for some of our simulated keypress events in tests, the "Gecko" version did not work.
Specifically, we had to use "KeyboardEvent" instead of "KeyEvents", and "initKeyboardEvent" instead of "initKeyEvent". See the table in this mozilla doc page.
Here's the code we used to handle both cases:
evt = document.createEvent('KeyboardEvent'); if (typeof(evt.initKeyboardEvent) != 'undefined') { evt.initKeyboardEvent(eventName, true, true, window, false, false, false, false, options.keyCode, options.keyCode); } else { evt.initKeyEvent(eventName, true, true, window, false, false, false, false, options.keyCode, options.keyCode); }The UserAgent (request.user_agent) returns
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.3.154.9 Safari/525.19 The 'sort' function does not preserve order of equivalent elementsThe following page outputs 'ACBD' under Chrome:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <a href="#" onclick="alert(doSort()); return false;">Sort</a> <script type="text/javascript"> function doSort() { var myArray = [ {id: "A", sortVal: 0}, {id: "B", sortVal: 1}, {id: "C", sortVal: 1}, {id: "D", sortVal: 2} ]; var sorted = myArray.sort(function(a,b) {return a.sortVal - b.sortVal}); return sorted[0].id + sorted[1].id + sorted[2].id + sorted[3].id; } </script> </body> </html>We recently updated Pivotal Tracker's extensive JSUnit test suite to be compatible with the Google Chrome browser. Check out the extensive Notes on Google Chrome Compatiblity post by Pivot Chad.
ActiveScaffold + Rails 2.2 = BOOM. ActiveScaffold will work with Rails 2.1 if you get the version from github. Read about it.
Here's an interesting mental exercise that recently I've found more and more valuable:
You might be surprised how often your assumptions turn out wrong. And, you might be surprised what you learn when you explicitly state those assumptions and then have to justify them when they do turn out wrong.
If you don't test drive, or you don't always test drive, (and you're still reading), ask yourself this: what happens those times when my assumptions are wrong and I don't have tests to protect me?
Multithreaded programming was a hot topic at RubyConf this year, and a common theme in many talks was the use of functional languages to prevent contention between threads. This totally makes sense to me, to the limited extent that I can wrap my head around truly functional programming, and I'm sure it's an excellent approach. However, imagine a case in which we can't just drop in a new language, so we need to write some multithreaded code in Ruby. I'm sure you won't have to think too long or hard.
Now, one point several speakers at RubyConf made that I would like to reiterate is this: multithreaded programming is difficult. Gosh darn difficult. People who write software tend to thrive on determinism and linearity. After all, computers always do what we tell them to, right? They don't make mistakes or change their minds; not like those silly, silly humans. But now big, bad concurrent programming comes along and suddenly computers can come up with different answers depending on, oh, the alignment of the planets. Chaos.
So, functional programming languages aside for the moment, what tools do we have to rein in the inevitable entropy that will, more than likely, eventually bring the planets into alignment against us?
Several years ago Andrei Alexandrescu wrote this excellent article on how to use the C++ type system (stick with me, it's all Ruby after this paragraph) to automatically prevent race conditions at compile time. I recommend you read it; it's quite short. Now, however you feel about static typing, you must admit that the approach he describes is a beautiful use of the expressiveness of the C++ type system. The question is, can we do something analogous in Ruby?
First off, lets start with some thread-unsafe code, similar to what Jim Weirich used in his talk on threads at RubyConf:
account = Account.new threads = [] 1000.times do |i| threads << Thread.new(account) do |account| account.credit(1) end end threads.each { |thread| thread.join } puts account.balanceThis code results in an account balance somewhere between 1 and 1,000. The exact value depends, of course, on the planets.
Now, to make this thread-safe. I came up with a few attempts using #alias_method and #undef_method, but didn't find anything satisfying. After that, I figured I'd try a simple proxy to approximate the effect. Here's a first cut:
class VolatileProxy attr_reader :obj def initialize(obj) @obj = obj end def method_missing(method, *args) raise "Unsynchronized message '#{method}' sent to volatile object" end end def volatile(obj) VolatileProxy.new(obj) end def locked_scope(volatile, mutex) mutex.synchronize do yield(volatile.obj) end endNow, calls to methods on objects you declare as volatile (shared across threads) will fail messily unless you use them within a locked_scope. To make this work the example code now becomes:
mutex = Mutex.new account = volatile Account.new threads = [] 1000.times do |i| threads << Thread.new do locked_scope(account, mutex) { |safe_account| safe_account.credit(1) } end end threads.each { |thread| thread.join } puts account.balanceThis example has some issues, namely:
I really like the idea of using blocks to scope behavior like this. This particular example doesn't feel particularly clean to me yet, but hopefully it will give some people something to think about. If you have a better approach, please don't be afraid to shout it out.
"How big should a typical mongrel be? Ours is starting out at over 200 megs but is not leaking from that point."
Everyone agreed that 50MB to 70MB is standard and anything over 100MB is considered pretty big. People suggested RubyProf for inspecting object counts and possibly tracking down the memory hogging code.
"What pattern does everyone use for non-model Reports with ActiveRecord? We are trying to create a report that counts a single model and groups by two associated models"
There was consensus around modeling a distinct report object and calling the referenced models. For example, FooReport and FooReportController fit nicely in a RESTful Rails world.
Regular Selenium tests (in Java) might look like:
selenium.open("/login"); selenium.type("id=username", "bob"); selenium.type("id=password", "password"); selenium.click("Login"); selenium.waitForPageToLoad(); selenium.click("My Account"); selenium.waitForPageToLoad(); assertEquals("bob", selenium.getText("//table[2]/tr[3]/td[2]/");After a few tests, this kind of thing becomes painful to manage. The typical solution is to create a bunch of constants for IDs and Xpaths, but that doesn't help too much.
Fellow Pivot Mike Grafton came up with a cool pattern for improving on this. The idea is to create a class representing each page of your web app. Each class contains two types of methods: a bunch of action methods (clickMyAccountLink(), typeUsername()), and a bunch of inspection commands (isLoginButtonEnabled(), getLoggedInUsername()).
When an action takes you to a new page, the corresponding action method returns a new class representing that page. When it stays on the same page, the method just returns "this". This allows methods to be chained to make the tests more readable.
Here's how that test would look using this new pattern:
MyAccountPage myAccountPage = new LoginPage(selenium) .typeUsername("bob") .typePassword("password") .clickLoginButton() .clickMyAccountLink(); assertEquals("bob", myAccountPage.getLoggedInUsername());The constructor of each page class should validate that it's on the correct page (waiting if necessary, and perhaps asserting on the page title).
"What are your current favorite Object Mother implementations or patterns?"
There are several approaches:
"Does anyone know how to disable caching in Safari? Caching can often break JSUnit between test runs"
Go to Develop -> Disable Caches and you get a fresh non-caching Safari.
Here's a hot Fluid icon for Pivotal Tracker. Thanks, Ted!
![]()
Check out a couple of other icons in this Flickr upload.
JSON version 2 has problems with its JavaScript implementation json2.js, specifically when calling JSON.stringify() on arrays:
// JSON version 1 [1] => "[1]" // JSON version 2 [1] => "\"[1]\""We're not sure if this is a bug or a feature. We're sticking with JSON version 1 until further notice.
Pivot Adam has a very interesting blog post about functors called Give up the func. Check it out!
"We were having problems when mocking an ActiveRecord association under Mocha when we called expects on a method that calls a method."
The solution was basically to not mock associations.
"Mod_rewrite + SSL was causing problems when redirecting to a subdomain behind SSL."
A wild-card SSL certificate solves the problem.
IE7 sends odd Accept headers (*.* instead of an explicit text/html) which can cause undesired behavior in Rails. Someone suggested manually setting the format in a before_filter:
params[:format] ||= 'text/html' This was discouraged because it can cause problems elsewhere. A better solution was to put the html format at the top of any `respond_to` blocks: def show respond_to.html # run by default when type cannot be determined respond_to.js endThe Ruby MySQL Gem version 2.7 leaks memory for very large queries. The solution is to remove the 2.7 gem and manually install version 2.8 from source. This library is no longer a gem and must be installed from the mysql-ruby-2.8 tarball.
"As a followup to Firefox SSL certificate problems..."
It turns out that our server running nginx had an old version of OpenSSL installed. Upgrading OpenSSL solved the problem.
"We're having an issue with a long running Ruby process consuming too much memory and failing. What tools are available for finding and patching Ruby memory leaks?"
Several tools were suggested:
"Rake will often silently 'fail' when running RSpec. It will not blow up but rather silently quits in the middle of the suite. This seems to happen intermittently, usually on the first run of our test suite. If we run the suite again, it works."
It was suggested that this might be a Rake version issue since there have been other test suite problems with Rake 0.8.3. Though, this particular type of problem was not identical to previous Rake versioning issues and may be something altogether different.
Keep reading for a more detailed description of this weird RSpec + Rake issue.
More on the RSpec + Rake issue:
"It always manages to go through the integration tests, but when it hits the main tests, it will randomly quit (without a failure or error message) before all the tests are complete. At this point it'll give us a summary message (X tests run, X tests passed) as if no other tests exist. We've worked on the problem a little bit this morning and tried the following things, all without success: >1) upgraded to the latest version of rspec and rspec-rails (1.1.11) 2) downgraded from rake 0.8.3 to 0.8.1 3) tried running with rspec and rspec-rails gems only 4) tried running with rspec and rspec-rails plugins only.
We've tried it on another machine with similar results. Right now, I want to say it only happens when we've modified a file in the project, but that may be confirmation bias (we ran about 60 full rakes on one machine where we have not been mucking about in the codebase and it seems to have successfully run all of them).
There was one other mention of this online in this ticket: http://rspec.lighthouseapp.com/projects/5645/tickets/587-conflict-with-loadby-and-reverse#ticket-587-3, paraphrased below:
'This was caused by an error in handling a certain mal-formed spec; it was causing rspec to exit silently in the middle of one spec file. As I would edit files, --loadby mtime would cause them to load in different orders, which would then run different #s of examples because it would hit the file, and then quit silently, in different places'
You can close this one, I'll report another bug that's appropriate for the silent-exit-without-exception problem."
Anyways, at this point we're out of ideas and are leaving the problem behind for now, but we'd love to fix it, as it makes testing somewhat unpredictable."
Nivi, from Venture Hacks, wrote a great article about why Pivotal Tracker is great for startups. It's worth reading even if you're already a fan of Tracker, and nails down the key ingredients to successful project management. Check it out:
http://venturehacks.com/articles/pivotal-tracker
I wrote a bit about function objects here. However, if you don't buy that the persistent state of function objects provides something that anonymous functions cannot, how about this: readability. In some cases.
Anonymous functions are boss and cool, and extremely common in idiomatic Ruby. However, in some cases they can get a little... esoteric. Consider:
people.sort do |lhs, rhs| lhs, rhs = rhs, lhs if ascending? result = lhs.name <=> rhs.name if result == 0 result = lhs.date_of_birth <=> rhs.date_of_birth end # etc... endSometimes, anonymity isn't the answer. Consider:
class ByNameAscending def self.to_proc Proc.new { |lhs, rhs| rhs.name <=> lhs.name } end endThis allows you to write this:
people.sort(&ByNameAscending)Or, to push the example to the extreme:
class SortOrder def initialize(direction = :descending) @direction = direction end def by(attribute) attributes << attribute self end alias_method :and, :by def to_proc Proc.new do |lhs, rhs| lhs, rhs = rhs, lhs if ascending? return lhs <=> rhs if attributes.empty? attributes.each do |attribute| result = lhs.send(attribute) <=> rhs.send(attribute) return result if result != 0 end 0 end end private def attributes @attributes ||= [] end def ascending? @direction == :ascending end end def ascending; SortOrder.new(:ascending); endWhich gives us:
people.sort(&ascending.by(:name).and(:date_of_birth))A DSL for generating sort order function objects. It could be useful.
I've been a C++ developer ever since I discovered the language in the early 90's and I realized that my beloved Pascal had nothing on objects. I've spent plenty of time working with other languages, of course, and over the past year or so I've written almost exclusively Ruby. But, one thing I've missed about C++ is the ease with which you can make objects act like functions.
In case your C++ is a little rusty, here's an example:
class Fibonacci { public: Fibonacci(): n1_(1), n2_(1) {} int operator()() { int result = n1_; n1_ = n2_; n2_ = result + n2_; return result; } private: int n1_, n2_; };What you're looking at there is an overload of the function call operator. No, I'm not kidding; that will compile and run. Instances of the Fibonacci class are called function objects, or functors.
Now, you're wondering to yourself why anyone would care. The answer is, this function can now carry state around with it:
Fibonacci fibonacci; fibonacci(); // 1 fibonacci(); // 1 fibonacci(); // 2 Fibonacci()(); // 1 vector<int> v(5); generate(v.begin(), v.end(), Fibonnaci()); // [1, 1, 2, 3, 5] generate(v.begin(), v.end(), fibonnaci); // [3, 5, 8, 13, 21]This may not seem particularly compelling for generating Fibonnaci numbers, but consider generators that may carry more complex state, or references to state owned by other objects. Consumers can also set initial state, such as a seed value for a random number generator, via the ctor.
Also, consider the generate method above. It expects a third parameter that supports function call semantics with arity of zero. And nothing else. That parameter could be a function pointer (should you desire statelessness and impenetrable syntax), a functor of any type, or anything else that supports operator (). That's duck-typing, my friends. In a statically-typed language. Dogs and cats sleeping together, and all that.
Again, you cry, why would anyone care? Well, blocks in Ruby carry around the state of the context in which they were created, but sometimes you want more. For instance, if you pass your Proc object around your code may be clearer with the state explicitly encapsulated. The initial state may simply not make sense as local variables when you create the Proc. You may want to save some secondary value that a consumer can query the functor for (how many Fibonacci numbers has this generator generated). Or, perhaps you want consumers to be able to mutate the state in some way.
In any case, this functor approach wacked me over the head recently while I was looking at some code that used the Rails Symbol#to_proc. We all know that Rails adds voodoo to symbols so that
User.find(:all).collect(&:name)is equivalent to
User.find(:all).collect { |u| u.name }And, we all know that this works because the & operator, when applied to an object in a parameter list, will implicitly call #to_proc on that object and then convert the result to a block. This is vanilla Ruby functionality, Rails just adds #to_proc to the Symbol class.
So, duh. Check it out:
class Fibonacci def initialize @n1 = @n2 = 1 end def to_proc @proc ||= Proc.new do result = @n1 @n1, @n2 = @n2, @n1 + @n2 result end end end fibonacci = Fibonacci.new fibonacci.call # 1 fibonacci.call # 1 fibonacci.call # 2 Fibonacci.new.call # 1 (1..5).collect(&Fibonacci.new) # [1, 1, 2, 3, 5] (1..5).collect(&fibonacci) # [3, 5, 8, 13, 21]Voilà, a functor. I'd love to hear from anyone who has used this technique to do something really cool.
I've been a C++ developer ever since I discovered the language in the early 90's and I realized that my beloved Pascal had nothing on objects. I've spent plenty of time working with other languages, of course, and over the past year or so I've written almost exclusively Ruby. But, one thing I've missed about C++ is the ease with which you can make objects act like functions.
In case your C++ is a little rusty, here's an example:
class Fibonacci { public: Fibonacci(): n1_(1), n2_(1) {} int operator()() { int result = n1_; n1_ = n2_; n2_ = result + n2_; return result; } private: int n1_, n2_; };What you're looking at there is an overload of the function call operator. No, I'm not kidding; that will compile and run. Instances of the Fibonacci class are called function objects, or functors.
Now, you're wondering to yourself why anyone would care. The answer is, this function can now carry state around with it:
Fibonacci fibonacci; fibonacci(); // 1 fibonacci(); // 1 fibonacci(); // 2 Fibonacci()(); // 1 vector<int> v(5); generate(v.begin(), v.end(), Fibonnaci()); // [1, 1, 2, 3, 5] generate(v.begin(), v.end(), fibonnaci); // [3, 5, 8, 13, 21]This may not seem particularly compelling for generating Fibonnaci numbers, but consider generators that may carry more complex state, or references to state owned by other objects. Consumers can also set initial state, such as a seed value for a random number generator, via the ctor.
Also, consider the generate method above. It expects a third parameter that supports function call semantics with arity of zero. And nothing else. That parameter could be a function pointer (should you desire statelessness and impenetrable syntax), a functor of any type, or anything else that supports operator (). That's duck-typing, my friends. In a statically-typed language. Dogs and cats sleeping together, and all that.
Again, you cry, why would anyone care? Well, blocks in Ruby carry around the state of the context in which they were created, but sometimes you want more. For instance, if you pass your Proc object around your code may be clearer with the state explicitly encapsulated. The initial state may simply not make sense as local variables when you create the Proc. You may want to save some secondary value that a consumer can query the functor for (how many Fibonacci numbers has this generator generated). Or, perhaps you want consumers to be able to mutate the state in some way.
In any case, this functor approach wacked me over the head recently while I was looking at some code that used the Rails Symbol#to_proc. We all know that Rails adds voodoo to symbols so that
User.find(:all).collect(&:name)is equivalent to
User.find(:all).collect { |u| u.name }And, we all know that this works because the & operator, when applied to an object in a parameter list, will implicitly call #to_proc on that object and then convert the result to a block. This is vanilla Ruby functionality, Rails just adds #to_proc to the Symbol class.
So, duh. Check it out:
class Fibonacci def initialize @n1 = @n2 = 1 end def to_proc @proc ||= Proc.new do result = @n1 @n1, @n2 = @n2, @n1 + @n2 result end end end fibonacci = Fibonacci.new fibonacci.call # 1 fibonacci.call # 1 fibonacci.call # 2 Fibonacci.new.call # 1 (1..5).collect(&Fibonacci.new) # [1, 1, 2, 3, 5] (1..5).collect(&fibonacci) # [3, 5, 8, 13, 21]Voilà, a functor. I'd love to hear from anyone who has used this technique to do something really cool.
STI-weirdness. Rails surprise of the day: given a query of a has_many :photos where Photos has STI subclasses (got that?) Rails will build a SQL query that includes the subclass types of Photo, which you might not want:
foo.photos.find_by_type("Photo") # query will have "... WHERE type IN ('Photo', 'OriginalPhoto', 'ThumbnailPhoto')"It appears that the retardase_inhibitor might not work with Rails 2.1.X due to fixes in ActionMailer.
Check out Pivot Jonathan's wife's art exhibit at Artist-Xchange Gallery in San Francisco, Friday 11/7 from 7-10pm:

3169 16th Street
San Francisco
CA 94103
"I want to create a custom launcher for Firefox 2 and Firefox 3 with different profiles. Perhaps the real question is how do we create a custom version of a Mac application launcher, passing in the arguments we need?"
... without having to invoke it on the command line every time.
"We're trying to delete cookies in our Controller, but they keep appearing in the headers anyway."
Suggestion: make sure you are specifying your URL paths and domains correctly.
"Why won't our CSS and other assets load the first time when accessing an SSL-protected domain on Engine Yard?"
It's most likely not Engine Yard or Firefox 3's fault. More research needed.
STI-weirdness. Rails surprise of the day: given a query of a has_many :photos where Photos has STI subclasses (got that?) Rails will build a SQL query that includes the subclass types of Photo, which you might not want:
foo.photos.find_by_type("Photo") # query will have "... WHERE type IN ('Photo', 'OriginalPhoto', 'ThumbnailPhoto')"It appears that the retardase_inhibitor might not work with Rails 2.1.X due to fixes in ActionMailer.
Check out Pivot Jonathan's wife's art exhibit at Artist-Xchange Gallery in San Francisco, Friday 11/7 from 7-10pm:

3169 16th Street
San Francisco
CA 94103
"I want to create a custom launcher for Firefox 2 and Firefox 3 with different profiles. Perhaps the real question is how do we create a custom version of a Mac application launcher, passing in the arguments we need?"
... without having to invoke it on the command line every time.
"We're trying to delete cookies in our Controller, but they keep appearing in the headers anyway."
Suggestion: make sure you are specifying your URL paths and domains correctly.
"Why won't our CSS and other assets load the first time when accessing an SSL-protected domain on Engine Yard?"
It's most likely not Engine Yard or Firefox 3's fault. More research needed.
"console.log seems to be broken again in Firebug with 1.2.1?"
People have had some success with 1.3 beta. Get Firebug
"Suggestions for creating PDF's in Ruby?"