Posts Tagged ‘rails’

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.

Want it? Give.

Saturday, April 10th, 2010

Imagine this scenario. You’re waiting at a checkout at your supermarket. It’s a pretty long line, compared to your past experiences.

Directly in front of you there’s a 22-year-old guy with medium-length hair, listening to God-knows-what on his earphones, dressed in casual gear. Probably middle class.

In front of him there’s another guy, about mid thirties, wearing a “business suit”; black pants, white top and tie. Probably upper class.

Then in front of him there’s a woman, probably around the same age as the man, but dressed like a hippie. She’s got a shirt that says “There’s no place like 127.0.0.1″. You fondly remember giggling quietly to yourself the first time you saw this shirt.

Then of course there’s the cashier. Unusually, it’s a guy with a ponytail who looks like the kind who you’d see attending computer and anime conventions.

The lady at the front of the queue puts her goods on the belt and the cashier processes them. Status quo. Then when the lady hands over the money, the cashier’s drawer breaks and falls out. Money goes everywhere. Coins roll in every which direction.

Now what the fuck do you do? Do you sit back and let the cashier run about collecting up his coins or do you help out?

Well, in this analogy, you sit back and let the cashier do it all. All of you in the queue do. Nobody helps the poor cashier.

You useless, pathetic bastards.

Now imagine this scenario wasn’t a checkout line. Imagine the people in the checkout line are actually people in an open source project’s community. Imagine, for a moment, that you’re waiting for The Next Big Release for this open source project. Imagine instead of the cashier’s drawer dropping out and throwing coins like shrapnel from a frag grenade, it’s actually tickets for this open source project. Do you know of a project like this?

I do.

It’s called Ruby on Rails. And it’s suffering because of your ignorance. It is at the moment in a place which I will steal the term “Development Hell” for and use. At current writing it has over 900 open tickets. Let me state that again because it is such a pertinent fact: There are over 900 tickets still open. So what the fuck are you going to do about it?

One little piece

It’s great that (at least in this Western world) we have things called weekends. We also have time where we are not working. Some of us have (a lot) more of it than others and generally waste it playing video games or watching TV. I’m fine with that, but there is a limit I feel before it turns from “relaxing” into “slacking off”. You have to realise that you are part of a world-wide community. You are using something developed by people world wide and it is time that you gave back.

I am sure that many of you reading this will think “I don’t know enough about Rails to do anything with any of the tickets.”

Bullshit. Fucking bullshit.

Take this ticket for instance. Guy experiences hassles with rake doc:rails on Ruby 1.9.2 and edge Rails. How do you go about fixing something like that? Well, here’s that god-damned clue you’ve been missing:

  • Attempt to duplicate it. People make mistakes, because they are people. Can you see what they did wrong? Is the bug still occurring in the latest (at this point in time, 2.3.5 & 3.0.0, yes, test both!)
  • If something is broken, attempt to fix it. This continues onto my third point:
  • Fix it! This is the most simplest, basic, elementary thing. You know the saying “If it ain’t broke, don’t fix it?” the reverse applies. Fix the ever-loving shit of it.
  • Can’t fix it? Find somebody who can. They exist. There’s a great probability that the problem is not as unsolvable as certain other problems in this world.
  • Can’t find somebody? You’re not looking hard enough. Ask in the IRC channels on irc.freenode.net. Ask in #carlhuda. Ask in #rubyonrails. Ask in #rails-contrib. Ask your friends. Post on the core mailing list. Six-degrees of separation means that you have the power to find somebody with the power to fix this issue.
  • Is this ticket no longer valid? Assign it to somebody who can close it. I’m now one of these people. Assign it to me and I’ll look over it.

Another thing to look at is how long the ticket has been a problem for and if it still applies to the latest version of Rails. Take for example this ticket which has been a problem since the 9th of January last year, when Rails 2.2 was the latest version. It’s still a problem in Rails 3, and this means it needs to be fixed. If one guy is suffering, how many other people who have not seen that ticket are? We must go through and judge every ticket before a major release. If I was in charge of a release process, this is how I would run the show. When I release a major version of my project, I want a clean slate, well, at least no bugs. Admittedly there may be some people who miss out on feature requests, but that’s what future releases are for.

Pissing on the fire

I think the Bugmash events are freakin’ awesome. But they only “piss on the fire”. What we need is a concentrated stream (think Amazon River) to quell the beast of 900+ tickets. Donate some of your time to the Rails project. Spend a concerted amount of your spare time this week, and next week, and the week after that, going through the tickets and finding something you can help fix. Every little bit of effort helps.

The Rails core team has only so much effort it can apply in any given timeframe, how about applying some of your own to speed up the process of bringing us closer to a final Rails 3 release?

I ask you, not as a Ruby on Rails core member (clarification: I’m not, nor desire to ever be “core”), but as a fellow Rails guy: let’s keep this Rails beast alive and kicking. Take some time out of watching your porn and actually do something useful. Otherwise, you are just another leech hanging on the side and whining about “it’s been so long between releases”. Did you ever stop to think that: “maybe there’s something holding up the release?” or “maybe there’s something holding up the release that I can help with?”.

Give back to the community that has provided this wonderful framework for your benefit.

Please.

Extra Links

Dan Pickett also has similar advice on contributing back to Rails 3. He makes a good point that if you are able to demonstrate that you have contributed to Rails that it looks awesome on your CV / Resume.

Kristopher Murata writes from the point of view who hasn’t contributed all that much to Rails, but really wants to.

Greetings YCombinator people! Thanks for making this story one of your favourites (peaked at #4). I have commented on your comments left on YCombinator and I appreciate the time you have spent writing them. I look forward to keeping up the correspondence.

Greetings also to the Ruby sub-reddit crew. Thank you for making this your 3rd most popular post and your comments (no matter how critical, all comments are good)

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

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!

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.

Finding SQL Type of Column

Sunday, December 14th, 2008

exlibris asked tonight in #rubyonrails how to find the type of a column in the table for a model. I didn’t know this and maybe someone else out there may not know how either. The solution lies within the columnshash method from ActiveRecord:Base which returns a hash containing all kinds of useful information about all the columns in your table such as:

  • Precision (integer, notes the precision of the column if it’s decimal-based)
  • Primary (boolean, identifies if column is primary key)
  • default
  • limit (integer, denotes how long the field can be)
  • type (symbol, the class type in lowercase)
  • name (string, fairly obvious)
  • null (boolean, identifies if column can be set to null)
  • scale (integer, does something)
  • sqltype (returns the sql type of the column)
That final column is the magic attribute we’re looking for. To get to it we access it like Model.columnshash["attribute"].sqltype

Single Table Inheritance

Sunday, September 28th, 2008

This tutorial was written using Ruby 1.8.6, Rails 2.1.

A lot of the time I see people asking how they can do something like access levels for their Rails applications and this usually boils down to some STI (single table inheritance) love. You’ll need to have restful_authentication installed.

What is Single Table Inhertiance?

Single Table Inheritance is where you have multiple models that inherit from a single table, hence the name. It’s great for situations like this where we want multiple user access levels, but we don’t want to create multiple tables for the different kinds of users of our application.

Okay.. so how do I use it?

Firstly you start off with one model that inherits from ActiveRecord::Base, and I would advise running script/generate authenticated person session to get this:

app/models/person.rb

class Person < ActiveRecord::Base

end

And then you subclass some other classes from Person app/models/student.rb
class Student < Person

end

app/models/teacher.rb
class Teacher < Person

end

app/models/admin.rb
class Admin < Person

end

The great thing about splitting these into individual files is because they’re more easily managed and closely follows the convention of one model per file.

Now you have a person, student and teacher model and you should have a brand new migration for your people table in db/migrate so go on and open that up and you’ll see something like this:

class CreatePeople < ActiveRecord::Migration
  def self.up
    createtable "people", :force => true do |t|
      t.column :login,                     :string, :limit => 40
      t.column :name,                      :string, :limit => 100, :default => '', :null => true
      t.column :email,                     :string, :limit => 100
      t.column :cryptedpassword,          :string, :limit => 40
      t.column :salt,                      :string, :limit => 40
      t.column :createdat,                :datetime
      t.column :updatedat,                :datetime
      t.column :remembertoken,            :string, :limit => 40
      t.column :remembertokenexpiresat, :datetime

end
add_index :people, :login, :unique => true

end

def self.down droptable "people" end end

To enable STI for this table, just add:
t.column :type, :string
into the create
table block, just after t.column :remembertokenexpires_at, :datetime. This type column will be set to whatever class the record is, so if we create a new Student object, this type column will be set to “Student”.

Controllers

To create new objects for these subclasses I would recommend building a people controller and using the form partial from that to base your form off from the other controllers, such as StudentsController and TeachersController. These controllers should NOT subclass from PeopleController, they should be their own independent controllers because Teachers, People and Students are all individual resource types.

Associated Models

If you have associated models (e.g. Person hasmany :enrolments), any subclass of Person will inherit this relationship. For the relationship, this will be referenced via personid still in the enrolments table.