Posts Tagged ‘ruby’

I want you to give

Monday, April 19th, 2010

Greetings. I want to “answer” to you as I feel you deserve an answer for my previous post.

I won’t start this post with a terrible analogy. I will use swear words throughout though.

I will, however, start it with an apology. The apology is for my most successful post yet. I am honestly sorry for anyone that was offended by the language of the post. Let me explain my thoughts, and I hope we can resolve our differences.

It was positively successful. It drove a lot of traffic to my blog which is what all bloggers truly want: to be noticed. To have people leaving comments. To have an effect on the world around them. If you want to really, really hurt a blogger: stop reading what they write. Your “un-attention” is blogger Kryptonite. To those who have commented positively on not only this post, but any other post on this blog, thank you. You give me the motivation to continue writing.

Then there was the negative reaction that I did see coming, but did nothing to prevent it. I welcomed it. Simply put: negative reactions are more noticed than positive reactions of the same strength.

I will dip into my history as a supermarket checkout attendant again here temporarily. I would receive thanks from the customers who I served regularly. I thought I was good at my job. The people whom I worked with thought I was good at my job. But when a customer complained that I packed something incorrectly, that’s when the shit hit the fan. I would be brought into the manager’s office and we would have a discussion about ensuring that all customers are happy. This is impractical. We are all humans, and therefore we make mistakes, or take offense to something when there was never any given. It only takes a small negative thing to set off a large enough reaction that you’ll tell you friends about it, where as it would take a huge positive thing for a similar-sized reaction to occur. Never, ever, was I called into the manager’s office for him to compliment me on any of the exceptional things I did. He was an asshole.

As a programmer, I attempt to see the logic in everything. I can understand that, for example, when I press the “a” key on my keyboard, that an “a” is going to appear on the screen, always. It’s just logical. This is why I sometimes get frustrated with human behaviour, as it is illogical. My intention for the post was to get people interested in working on the core of Ruby on Rails rather than the ecosystem around it. You cannot have a stable ecosystem without a stable core. Yes, you are correct. I was arrogant in my post. But I implore you to think of it from my side of the fence. I am trying to convey a point. To get some motion going. Do you honestly think my post would have generated that much traffic had I been saying things such as “Hey guys, there’s 900 open tickets in Ruby on Rails. Would you mind, you know, taking some time out of your very, very busy schedules and fixing it”? I think not. Before you go reaching for that comment form, read that again.

I think not.

The operative words, of course, being “I think”. You know this. I don’t need to explain this to you. I am not stating it as fact that the post would not have been successful had I used positive terminology rather than the words I used. It is simply my interpretation. You have yours, and I have mine. I willingly used terminology that was offensive to evoke a large enough response in order to get some more eyes on Rails tickets.

I want to thank those of you who reacted negatively to this post, too. Why? You guys are the ones who generated the media coverage. You got it very highly ranked on the Ruby subreddit, Hacker News and many other technology media sites. You are the people who I want to talk to the most. You are the ones who have great ideas of how I could better go about conveying my point, and I have talked with some of you, such as Eric Florenzano. Eric was the writer of the aforementioned “Axe body spray” tweet. I politely asked him if I could email him, and he said yes and so I’ve posted the transcript if you would like to have a read. This is the kind of back-and-forth I am really looking forward to having with you all.

Then finally, my favourite response of all from this post was from a guy called Matthew Joiner (yes, I did my research too ;)) who was the only person out of all 10,000-and-something unique visitors to my blog to track down my email address and write me an email. Matthew starts off his email like this:

No one special sending you a random email, I know how people hate it, but I also am not really into commenting on blog posts, so I hope this doesn’t annoy you too much.

When I read this, I was annoyed. Annoyed because he thought I would be annoyed! Why would sending me an email, annoy me? I want you to notice me. The one way you can inform me that you’ve noticed something that I’ve done is to email me. You can also buy me cider in real life (FYI, favourites so far are Magners and Aspley and Scrumpy Jack). I digress. You can come up to me at any of the Ruby/Rails meetups I go to and shake my hand and say “I really liked that.” It’s my fuel. I want to be noticed. Maybe I’ll post about the “why”s of it later, but I’m sure you can guess those.

Matthew was the only person to send me an email which instantly disqualifies him from the “no one special” category forever. He has gone out of his way to track down my email and tell me what he thought. Nobody else did. So I wrote him a reply effectively thanking him, and discussing a couple of things. A 1500-word reply. You can find the transcript from this email here. He mentioned my email was nowhere on the site, but now it is in the sidebar. This is the most direct-line of contact you can have with me just short of my mobile number.

This is also the kind of back-and-forth I want to have with you all. I want you to ask me questions about Rails 3, and in return I get something from you automatically, which is the feeling of satisfaction of being noticed. Then if I help you, further satisfaction that I’ve improved your life somehow.

Finally, when a lot of you start asking questions, then I have more material to write down for a book I’m thinking of writing. Yup, you heard it here: I’m going to take on writing a book. This is why I want you all working on Rails and the community, because I simply will not have the time to sit in the IRC channel or on Stack Overflow to help you out anymore. Somebody else needs to step up and “be me” temporarily if I am to complete this book by the end of the year. I want your feedback on this! So contact me on twitter or e-mail me with ideas of what you think should go in a book about Rails.

So let me finish on a positive note. Since that post last weekend, over one hundred Rails tickets have been marked off the list, be it them being marked as invalid and then all the way up to patches being submitted and a lot of people getting things done. That’s an amazing amount of effort and it is these types of people the community needs more of. If you want to nominate somebody for the Ruby Hero award, do not nominate me (ok, you can if you want). Nominate the collective of people making the community a better place. I am not the only person doing this. I fear for that situation. I do not want to be the go-to guy for all of your issues as that is the community’s job. But, in order to build a knowledgeable community, those with the knowledge need to be out there spreading it, rather than sitting in their “ivory towers”.

Side-note: I’d like to think I know for a fact that this post won’t gain the level of recognition that the original did. That’s fine with me. If you could go about proving me wrong, please do: I enjoy being wrong.

All I ask is that I want you to help one person a day with Ruby or Rails. Be it a co-worker, a friend, or a complete stranger. Just go out there and do something. C’mon, you can do it. It’s an awesome feeling.

has_and_belongs_to_many double insert

Wednesday, April 14th, 2010

This is a story about my work with GetUp, in particular the past week. It’s about a problem that I’ve been putting off help one of the guys (James) solve, it didn’t seem all that important to me. So last night I kind of promised that I’d sit down with him this morning and help him work out what it was. Hopefully it was something silly either of us did and it would only take us an hour.

You know how this story is going to end up already.

It didn’t take us an hour. It’s now 5pm and I’ve only just figured out what it was.

Symptoms

We have two models who’s names aren’t important so excuse me if I use the name Person and Address to represent them. They are nothing of the sort. In their purest form to replicate this issue, they are defined like this:

class Address < ActiveRecord::Base
  has_and_belongs_to_many :people
end

class Person < ActiveRecord::Base
  has_and_belongs_to_many :addresses
  accepts_nested_attributes_for :addresses

end

When we go to create a new Person record:

Person.create(:addresses_attributes => { "0" => { :suburb => "Camperdown" } }) 

It inserts 1 Person record, 1 Address record but 2 join table records.

So, wtf?

We originally thought it was a bug in our application. How, in all realities, could Rails have a bug, right?

Wrong!

I should know how many bugs Rails could have. I should have been more wary. I was not. And it bit me in the arse. So out of curiosity I googled the issue and saw that others came across it and then I tried checking out to v2.3.4, which worked!. So there was a regression between v2.3.5 and v2.3.4. A simple git bisect bad v2.3.5 with git bisect good v2.3.4 put me on the way to finding out what this was. A couple of bisects later, I found the offending commit was 6b2291f3, by Eloy Duran.

A “solution(?)”

So I generated an application to simply demonstrate that this was a 2.3.5 regression. As I say in the README, I suggest using 2-3-stable if this bothers you. Alternatively there’s always Rails 3, or simply specifying the :uniq => true option on your has_and_belongs_to_many.

That was a fun 7 hours.

As I found out this (the next) morning and Tim Riley points out in the comments the ticket for this bug is #3575 and the related commit is 146a7505 by Eloy Duran also. Freezing rails to v2.3.5 and git cherry-picking this commit into this frozen version fixes it.

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

Driver

Sunday, August 30th, 2009

Over the past 24 hours I’ve worked (not constantly, in bursts) on a little Sinatra app called Driver. My inspiration for this was that Passenger Preference Pane was hanging in Mac OS X. Driver acts as an alternative to Passenger Preference Pane and it was a fun little project I did to see if it was possible, and it is. Now your challenge is to get it to work with Linux. Go for it!

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.

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!