Archive for the ‘ruby’ Category

Where did I put that puts?

Wednesday, November 4th, 2009

This is the question I ask after I’ve just finished a massive debugging session and I run the tests and halfway through there’s something vague like “S3″ printed out. So I do a Cmd+Shift+F looking for that string and of course it doesn’t exist. What’s a guy to do?

Well, at Mocra we put this in our config/environment.rb file (although a better location would be in a required file located somewhere in lib, probably named debug.rb):

# Print the location of puts/p calls so you can find them later
def puts str
  super caller.first if caller.first.index("shoulda.rb") == -1
  super str
end

def p obj puts caller.first super obj end

And when we don’t want it we comment it out. This will give us the exact location of the puts so we can track it down and remove it.

Size != Count

Friday, October 30th, 2009

When using a has_many :through in Rails with a counter_cache an interesting thing can occur. When you call size on the association, it can return a seemingly incorrect number. This is caused by the following code in activerecord/lib/has_many_through_association.rb:

def size
  return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?
  return @target.size if loaded?
  return count
end

It’ll reach the counter_cache column which could be incorrect, giving you all the objects returned when you look up the association, but an invalid number. This was only in my tests where data was being created through Machinist. Just watch yourself for this, this is the second time it has caught me, and ideally the last.

Connecting to Multiple Databases Using ActiveRecord

Saturday, October 17th, 2009

You can call establish_connection with the key that points to another database config in your config/database.yml file

class Person < ActiveRecord::Base
  establish_connection(:hr)
end
class Ticket < ActiveRecord::Base
  establish_connection(:bug_tracker)
end

If you have a whole bunch of models that need to connect to another database:

class HR < ActiveRecord::Base
  establish_connection(:hr)
end

class People < HR
  # ...
end

class Resource < HR
  # ...
end

distance_of_time_in_words

Friday, August 7th, 2009

I was infatuated with this method when I first saw Rails but I’ve seen a couple of people recently express that Rails’ built in distanceoftimeinwords is not accurate enough, showing something like “about 2 years” rather than “2 years, 21 days, 5 hours, and 6 minutes”. With some help from chendo at Mocra (where I work) I’ve made a new distanceoftimeinwords which should be a drop-in replacement for the old crappy one. To install it, use: script/plugin install git://github.com/radar/dotiw.git. This also comes with another method if you’re still picky about the output: distanceoftimeinwords_hash which gives you a Hash containing keys in your native tongue. The README should give you a good guide of what other options it supports too.

sort_by

Sunday, July 12th, 2009

Fresh on the heels of yesterdays cantouchthis I’ve released another plugin today called sort_by. It provides you a way of sorting a table by user selected fields and it even paginates *ooh* *ahh* *pause for ego boost*. Inspired by hearing people talk about having to make stuff from scratch to do this I worked on it for about the last 3 hours. Tell me what you think!

humanize

Saturday, July 4th, 2009

I was inspired earlier in the year by Brenton Fletcher’s humanize that I decided to make my own. Today, I “finished” it with the help of Mitchell Riley and other Actionhackers. Humanize will convert numbers up to a very large number of digits long into their string versions. This means given the number:

444,333,222,111,999,777,666,555,444,333,222,111,999,777,666,555,444,333,221,111,112,176,514,321,007,310, 444,333,222,111,999,777,666,555,444,333,222,111,999,777,666,555,444,333,221,111,112,176,514,321,007,310

Yes it’s the same sequence repeated twice, I got lazy in my testing. imagine it conjoined into one very large number.

Coincidentally, this is my IQ.

I get:

four hundred and forty four quinquagintillion, three hundred and thirty three novenquadragintillion, two hundred and twenty two octoquadragintillion, one hundred and eleven septenquadragintillion, nine hundred and ninety nine sesquadragintillion, seven hundred and seventy seven quinquadragintillion, six hundred and sixty six quattuorquadragintillion, five hundred and fifty five trequadragintillion, four hundred and forty four duoquadragintillion, three hundred and thirty three unquadragintillion, two hundred and twenty two quadragintillion, one hundred and eleven novemtrigintillion, nine hundred and ninety nine octotrigintillion, seven hundred and seventy seven septentrigintillion, six hundred and sixty six sextrigintillion, five hundred and fifty five quintrigintillion, four hundred and forty four quattuortrigintillion, three hundred and thirty three trestrigintillion, two hundred and twenty one duotrigintillion, one hundred and eleven untrigintillion, one hundred and twelve trigintillion, one hundred and seventy six novemvigintillion, five hundred and fourteen octovigintillion, three hundred and twenty one septenvigintillion, seven sexvigintillion, three hundred and ten quinvigintillion, four hundred and forty four quattuortillion, three hundred and thirty three trevigintillion, two hundred and twenty two duovigintillion, one hundred and eleven unvigintillion, nine hundred and ninety nine vigintillion, seven hundred and seventy seven novemdecillion, six hundred and sixty six octodecillion, five hundred and fifty five septendecillion, four hundred and forty four sexdecillion, three hundred and thirty three quindecillion, two hundred and twenty two quattuordecillion, one hundred and eleven tredecillion, nine hundred and ninety nine duodecillion, seven hundred and seventy seven undecillion, six hundred and sixty six decillion, five hundred and fifty five nonillion, four hundred and forty four octillion, three hundred and thirty three septillion, two hundred and twenty one sextillion, one hundred and eleven quintrillion, one hundred and twelve quadrillion, one hundred and seventy six trillion, five hundred and fourteen billion, three hundred and twenty one million, seven thousand three hundred and ten.

Props to this site which gives names to unimaginably large numbers.

I have yet to find a use for this, but I’m sure somebody out there can.

NOW WITH FASTER PEFORMANCE! Thanks to Jack Chen.

SQL Display

Thursday, June 11th, 2009

Sometimes you want to see a query (or queries) that were executed by a model call that you just did. To do this you have to either set up some hax in your irb configuration or you could now use SQL Display.

SQL Display’s been an idea that has been thrown around in the Mocra offices for a while and after Bo Jeanes’ and Ben Hoskings’ conversation on twitter last night about the syntax I decided to try my hand at it. The code itself is… not pretty. I warn you of this now before you go trumping through the source code and discover something analogous to a large heap of elephant dung; but it’s functional!

One of the fun things about implementing this plugin was how ActiveRecord implements its logger. When it initialises a new connection it passes the logger object at the time to the connection adapter object so it’s set there For All Time.

SQL Display works by storing what the old logger was, setting the new logger to a file called tmp/sql_display.log, re-establishing the connection, running your query, removing all the colour & extraneous crap from the logs and stores it, removes the log, sets the logger back and again re-establishes the connection.

Patches welcome.

Cucumber Failing Scenarios

Tuesday, June 9th, 2009

I submitted a ticket for Cucumber, and I hope that it gets accepted:

I encountered a problem when I was using cucumber today. I changed a small detail in one of my Rails controllers and when I ran the features many of them turned red! Disaster! So I scrolled back up, found one that was failing and copy and pasted the file:line syntax into the cucumber command and made them all work! What I didn’t like about this process was that I had to scroll back up through a mixed mountain of red and green features. I only wanted to know which of my features were failing. Trudging through this mess, I thought “If only cucumber showed me the failing features in the summary, along with all the other useful information it displays there” and then I remembered “Cucumber is an open-source project, I could fork it and add it during my spare time!”. So I did. My commit that adds this functionality is added here: http://github.com/radar/cucumber… for all to gaze upon, criticise and ideally improve upon. At the moment it does everything but exceptions that are raised during Scenarios, something I intend to look on later this evening or perhaps one of you lovely people could do that. I’d really love to see this merged into the core since I believe (along with my coworkers and friends) this is “extremely fucking useful”. Thanks for reading!

And now the ticket’s been accepted and applied! Thanks to Aslak for this!

Pretty-ifying URLs

Saturday, April 18th, 2009

In today’s modern world we are not limited (cough, FORTRAN) to storing information in just a couple of bits. Some say that having pretty URLs is good SEO. I say it’s just common sense. I want the URL to tell a story, not give me some number that is only important to the system it’s coming from.

This is where to_param comes in.

By default this is defined like this:

def to_param
  id
end

Pretty high-tech stuff there. I won’t explain it to you. Give it a moment to sink it. et-cetera. This method is called on an object when it is passed to a url helper such as link_to or redirect_to. It is important to note that you should NEVER call @object.id as that will give the id of the object, not the to_param version of that name.

You can override this in your model in order to give you pretty urls! The way you do this is:

def to_param
  "#{id}-#{name.parameterize}"
end

Where id is the id of the model and name is the text you want into the URL. We call parameterize on this text in order to make it URL friendly.

The reason why we put in the id in the URL is two-fold. Firstly, (/forums/1-best-forum-ever) is so that when it gets passed to our controller as params[:id] or similar, we’re able to pass it to a finder:

Forum.find(params[:id])
# will be passed in as...
Forum.find("1-best-forum-ever")

Now you may go “Hey… wait a second! I don’t have an ID in my database that says ‘1-best-forum-ever’!” and you’d be right, you don’t. But you do have an ID of 1 for a record somewhere. Rails will call to_i on this value and convert it simply down to 1 and you’ll be able to treat the pretty URL as if it were a real ID.

Secondly, if two objects in your database have the same name they won’t clash for the parameterized versions, since the id will always be unique.

But I don’t want my ID in my URL

Hey, that’s cool too! Just a little tougher… Before you save your records you’ll want to write out the permalink as a new field, so define a field called permalink in your table if you want to take this route. Then you do a before_save and define to_param like this:

before_create :set_permalink

def set_permalink
  self.permalink = name.parameterize
end

def to_param
  permalink
end

Now when you call the finder you’re going to need to find_by_permalink! instead of just find:

Forum.find_by_permalink!(params[:id])

We have to use the bang version of find_by_permalink because this will raise an ActiveRecord::RecordNotFound exception if the record is not found, just like find. (Thanks to Yarsolav Markin for mentioning this)

So there you have it. to_param overriding for pretty URLs.

Testing Gems with multiruby

Thursday, April 2nd, 2009

A couple of days ago I was requested politely and with acknowledgement of my humanity, by Dr Nic to write a post about testing gems on 1.9 and because he’s my boss I’m obliged to do the things he tells me to do. He told me to read his post about Future-proofing Your Ruby Code and in particular the instructions about installing multiruby.

How to install multiruby

You can install multiruby, different versions of ruby, rubygems and useful gems using these commands:

sudo gem install ZenTest
multiruby_setup mri:svn:tag:v1_8_6_114
multiruby_setup mri:svn:tag:v1_8_7_153
multiruby_setup mri:svn:tag:v1_9_1_0

multiruby_setup update:rubygems
multiruby -S gem install --no-ri --no-rdoc --development test-unit

This will:

  1. Install ZenTest (or the latest version)
  2. Install Ruby 1.8.6, build 114
  3. Install Ruby 1.8.7, build 153
  4. Install Ruby 1.9.1, build 0
  5. Setup Rubygems for all three versions and;
  6. install test-unit for all three versions!

You may also want to multiruby -S gem install –no-ri –no-rdoc –development rails mocha rspec.

multiruby is awesome because it sticks this all in your home directory in a folder called .multiruby, so don’t worry about it clashing with your existing version(s) of ruby. Different versions go in different directories inside .multiruby/versions.

numero!

Today we’re going to be testing a gem that I recently made use of on this site. For those of you that visited the site yesterday you would’ve seen something like this:

  112.117.116.115.32.34.104.101.108.108.111.32.119.111.114.108.100.34

This is numero! (exclamation mark compulsory). I don’t know where the idea came from. According to the MSN logs I keep, at 8:03am Friday morning I went and made breakfast and then at 8:13am I had the idea. But it’s awesome. The “language” uses the ASCII representation of all your favourite characters and separates them using a dot. This is numero! script. You run it using the numero! gem. This is how you get it:

sudo gem install radar-numero --source http://gems.github.com

Test it out by running it on your favourite ruby script!

numero favourite.rb

This will generate a file called favourite.numero and then evaluate the newly generated numero! code. Now you can throw away your ugly ruby file and bathe in the glory that is numero by then running:

numero favourite.numero

So it appears we’ve gone off on a tangent here! Fear not! For I want you to test my gem on your new multiruby setup:

git clone git://github.com/radar/numero
cd numero
git checkout -b broken origin/broken

Now this sensible naming schema will probably dissolve the mystery surrounding what’s going to happen next. You’re going to be testing my gem, and it’s going to break.

Testing numero!

When I wrote this particular branch of numero!, I used “a”[0] to get the ASCII representation (97) of the letter a. Generally, I would’ve used use a variable rather than “a”, but this is just an example. The problem is that this doesn’t work the same way in Ruby 1.8.* and Ruby 1.9. If we run multiruby -S test/test_numero.rb we see that it passes on both versions of Ruby 1.8, but fails on Ruby 1.9!

Ruby 1.8.*

Loaded suite test/test_numero
Started
.
Finished in 0.001186 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

Ruby 1.9 Loaded suite test/test_numero Started F

Finished in 0.025228 seconds.

1) Failure: testnumerocompiles(TestNumero) [test/test_numero.rb:9]: <”[\"p.u.t.s. .\\".h.e.l.l.o. .w.o.r.l.d.\\"\"]“> expected but was <”112.117.116.115.32.34.104.101.108.108.111.32.119.111.114.108.100.34″>.

1 tests, 1 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications

Not good! This is something I wouldn't have found if I didn't test the functionality on multiple Rubies. Using multiruby is essential to ensure that your code works for everybody. We don't want to end up like Internet Explorer.

The solution, of course, is to use str.unpack("C").first instead. You have two options: 1) change the code yourself in lib/numero.rb to use the afore-mentioned example or 2) git checkout master. Now we run the tests again and now Ruby 1.9 passes:

Ruby 1.8.7

Finished in 0.001109 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

Ruby 1.9.1

Finished in 0.001353 seconds.

1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications

Lovely!