RailsConf Recap: Named Callbacks

Posted over 7 years back at The Rails Way - all

Another topic we touched briefly on at RailsConf was the idea of named callbacks.

Consider this snippet (also from Brian Cooke’s expense tracking application):

1
2
3
4
5
6
7
8
class Expense < ActiveRecord::Base
  protected
    def before_create
      if self.created_at == Time.now.to_date.to_time
        self.created_at = Time.now
      end
    end
end

One thing to keep in mind here is that when a new Expense record is created, the created_at column is used to track when the expense originally occurred, not when the record was created. As a special case, if the timestamp is 00:00 of the current day, then it is assumed to actually be the current time.

Now, looking at that code, it’s definitely not immediately obvious what it is trying to do. In fact, it took me a few minutes of steady concentration (and cross-referencing other parts of the project) to understand it. The fact that it uses a generic “before_create” callback makes it hard to know the purpose of the method, and the use of “Time.now.to_date.to_time” (though effective) is pretty intention-obscuring.

Here’s a clearer, more self-documenting approach, using a named callback:

1
2
3
4
5
6
7
8
9
10
class Expense < ActiveRecord::Base
  before_create :make_created_now_if_created_today

  protected
    def make_created_now_if_created_today
      if self.created_at == Time.now.beginning_of_day
        self.created_at = Time.now
      end
    end
end

The named callback helps make it clearer what the purpose of the method is (though in this case, an additional comment would not be amiss). Also, ActiveSupport comes to the rescue, allowing us to convert the convoluted “Time.now.to_date.to_time” into the more self-documenting “Time.now.beginning_of_day”. (Alternatively, you might prefer “Time.now.midnight”, though I find “beginning_of_day” to be clearer, since it reveals the intention better.)

Always look for ways to make your code document itself. Ruby is one of the most readable programming languages I’ve ever used, and it’s a pity to not take advantage of that readability as often as you can.

Rails Consultant wanted

Posted over 7 years back at RailsExpress.blog

I’m looking for an experienced Rails consultant to complement the team working for Autoscout24 on the International Sales Synergy Project which I am managing.

Requirements:

  • brightness
  • ability to get things done
  • willingness to dive into an existing code base, fix stuff and extend existing functionality
  • good social skills
  • ability to speak English (German is a plus)

If you’re interested, send me your credentials.

Update: the open position has been filled. Thanks for sending in your emails.

Episode 41: Conditional Validations

Posted over 7 years back at Railscasts

By default, validations will take place every time the model is saved. Sometimes you only want a validation to happen when certain conditions are met. See how to do that in this episode.

LOLCODE

Posted over 7 years back at zerosum dirt(nap) - Home

[3:44pm] strager: BUKKIT is the array type. [3:44pm] zapnap: O RLY?

Draw with Ruby and Scribble!

Posted over 7 years back at Spejman On Rails

Looking at the new blog of _why (http://hackety.org) I found Scribble! that it's a Ruby version of NodeBox. Its aim is to make cool graphics using Ruby.

If you want to test it using windows, you can follow the instructions at http://nex3.leeweiz.net/posts/3

I wanted to test it in Linux but I didn't found related information, then I did some research on how to install it. Finally, I made it work in Ubuntu following these steps:

  1. Install cairo and gtk2 ruby libraries and get the scribble code:

    sudo apt-get install libcairo-ruby1.8 libgtk2-ruby
    svn co svn://hamptoncatlin.com/scribble/trunk scribble

  2. Execute scribble:

    cd scribble
    bin/scribble

With these steps we'll execute Scribble! and we can do graphs like this:

Scribble! Screenshot

This drawing is uniq because it is randomly generated from this code:

brush.fill = rand(0.1) + 0.9, rand(0.4) + 0.6, rand(0.1) + 0.9, rand(0.1)+0.1
blanket

brush.fill = rand(0.1) + 0.9, rand(0.4) + 0.6, rand(0.1) + 0.9, rand(0.1)+0.1
brush.stroke = rand(0.4) + 0.6, 0, 1, 0.2
brush.width = 2

100.times do
circle :center => [rand(size[0]), rand(size[1])], :radius => rand(50) + 10
end


If you like Scribble! and make some cool graphics, share it putting its code as a comment in this post ;)

See you there!

Posted over 7 years back at The Hobo Blog

Dear Tom,

Congratulations! You have been accepted as a presenter for

the RailsConf Europe 2007 at the Maritim proArte, September 17, 2007 - September 19, 2007.

The following has been accepted as a 45 Minute Conference Session for the event:

“Exploring very rapid web devleopment techniques with Hobo”

Yay!

See you there!

Posted over 7 years back at The Hobo Blog

Dear Tom,

Congratulations! You have been accepted as a presenter for

the RailsConf Europe 2007 at the Maritim proArte, September 17, 2007 - September 19, 2007.

The following has been accepted as a 45 Minute Conference Session for the event:

“Exploring very rapid web devleopment techniques with Hobo”

Yay!

Handling *the* four letter words

Posted over 7 years back at work.rowanhick.com

The boys over at 37signals have put the danger flag up over the following words. Need, Must, Can’t, Easy, Just, Only, Fast. The following comments thread has been an interesting read. But it does highlight what to do about when you hear these words, and whether they necessarily are bad. I tend to think of them as helping you, the business analyst, or developer, to extract exactly what the client wants to acheive. Harking back to the general philosophy that a client/user (be it a your paying client, your designer, your developer) just wants to get things done, when they can't the frustration level can start to increase and you start to see these words - by the time you personally get involved things might be getting a little heated... So, what do they really mean ? And how do we deal with it ? Take the case of our proverbial user. Our user is tring to do something, our system is (hopefully) helping them to do this but along the way, boom, somebody can't get past step Y, because it won't let them enter x. The following call is made "why can't it just do this..." so what's wrong here ? Well... maybe the original developer for not figuring out the requirements, or maybe the business model has changed. Either way our poor end user has gotten stuck. And we need to fix it for them. It's a mindset shift all dependent on your audience - sure you can continue down the Mr UnPleasant road and not deal with it. However, if you're an enterprise system where things have to get done, no matter what. Then simple, get down to business. If you're running a public web app with a wide audience you've got a tough decision on your hands to make. So what do they really mean? "I just need it to do X, I can't complete my task" [translation: I'm trying to do something, I know what I want to do, and I think I know what the software needs to do in order for me to do it.. so I'm telling you what you need to do, I think this will speed up the process if I express it to you like this] First off, before saying yes or no, it's part of my job to figure out what they actually need. Nearly always I ask these key questions. 1. "What are you trying to acheive?" 2. "What's the steps they took to get to X, so I know context" 3. "How did you do X before today ?" 4. "If in a perfect world, what would your preferred way of doing this.. " All of the above should provide a gold mine of information in helping you to design the best solution, and to work out whether it is really needed, or a work around can be provided. Okay so after a bit of backwards and forwards figuring out what they want to do, and the best solution we've realised that our 'please just do this' is a bit of a kicker, we've got tonnes of code to rewrite (unless you're rolling on rails of course!). So do we go straight back to our office and start coding. No. Not yet. You now explain to the user what you've got planned out in the next short term, all of which will be impacted by this change, and then how long the change will take. Ie "we'll I'm currently working on the new inventory feature, which I have 3 weeks left for. if I implement this it will take probably 1 week" and ask the really important question "What do you want me to work on ?" You will be suprised how often the decision won't be this 'critical urgent' problem that was just raised. Now, if you get the do it now, do everything else you're working on, and I don't give a damn type attitude there are two ways to handle it. One involves lots of this stuff and all nighters, the other involves giving them a business card of your competitors. Problem solved, go off and find a client who values your relationship. Going back to wether those word's are dangerous, in my world no, it's just a way of expressing a need. I think the occurence of those words is inversley proportional to the happiness of our client - a good system that is returning value and have content users will see very little of these. A system constantly hindering our users workflow will see a tonne of those words.....

has_much :confusion, :about => "string #{interpolation}"

Posted over 7 years back at zerosum dirt(nap) - Home

No matter how flexible ActiveRecord’s associations become, there’s always going to be a time when you want to override it’s baked-in smarts with your own custom SQL query. Fortunately, we can do just that with the :finder\_sql option. Use it to manually specify the association that should be returned.

There’s one gotcha to be aware of though: if you need to do any variable interpolation in the string (and you most likely will), make sure to use single quotes instead of the usual doubles:

has_many :transfers, :finder_sql => 
  'SELECT * FROM transfers ' +
  'WHERE sender_id = #{id} or receiver_id = #{id}'

When you use double quotes, the string interpolation happens immediately (when the class is first loaded), thus obtaining the object ID of the class in memory. Using single quotes, the interpolation occurs within the context of your object instance, which is what you’re expecting.

Revolution Health Traffic Doubles in 4 Weeks

Posted over 7 years back at Revolution On Rails

Hitwise has recently publish traffic statistics on our growth over the past month.

This Wednesday AOL Founder Steve Case spoke at D5 about his new venture, Revolution Health. Revolution Health is setting out to be far more than just a health information source on the web, but it is already succeeding on that front. For the week ending May 26, 2007, Revolution Health was ranked #11 in the Health & Medical - Information category, and traffic increased by 113% since the week ending April 28, 2007, when it was ranked #23.

Revolution Health Traffic Doubles in 4 Weeks

Posted over 7 years back at Revolution On Rails

Hitwise has recently publish traffic statistics on our growth over the past month.

This Wednesday AOL Founder Steve Case spoke at D5 about his new venture, Revolution Health. Revolution Health is setting out to be far more than just a health information source on the web, but it is already succeeding on that front. For the week ending May 26, 2007, Revolution Health was ranked #11 in the Health & Medical - Information category, and traffic increased by 113% since the week ending April 28, 2007, when it was ranked #23.

Episode 40: Blocks in View

Posted over 7 years back at Railscasts

If you try to create a helper method which accepts a block, you will run into a few gotchas. Learn the secrets of blocks in views in this episode.

Ruby vs. JRuby - The path to consistent behavior + OpenSSL

Posted over 7 years back at Revolution On Rails

In Ola Bini's recent post There can be only one, a tale about Ruby, IronRuby, MS and whatnot, he stresses the importance of documenting Ruby's API so that other implementations can match MRI's behavior. I have to strongly agree. We've recently been bitten by this, which I'll outline using OpenSSL as an example.

Ruby's OpenSSL library does not strictly enforce a ciphers's initialization vector (IV) size specification. It simply truncates to the appropriate size.

Ola's JRuby OpenSSL library (which is awesome btw!) leverages the Bouncy Castle's JCE implementation which strictly enforces IV specifications.

Below is a simple example that works fine on MRI, and fails on JRuby.

require 'openssl'
text = "abc123"
cipher = OpenSSL::Cipher::Cipher.new("des-ecb")

#The DES Cipher specifies a length of 8 bytes, the below IV exceeds that
key, iv = "1234567890", "1234567890"

cipher.encrypt
cipher.key, cipher.iv = key, iv
encrypted = cipher.update(text) << cipher.final

cipher.reset
cipher.decrypt

#I double the length of the cipher here, just to prove that MRI truncates it.
cipher.key, cipher.iv = key*2, iv*2
decrypted = cipher.update(encrypted) << cipher.final
puts "#{text} == #{decrypted}"



> jruby test_openssl_iv.rb
java.security.InvalidKeyException: DES key too long - should be 8 bytes
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at javax.crypto.Cipher.init(DashoA12275)
at javax.crypto.Cipher.init(DashoA12275)
at org.jruby.ext.openssl.Cipher.doInitialize(Cipher.java:416)
at org.jruby.ext.openssl.Cipher.update(Cipher.java:432)

> ruby test_openssl_iv.rb 
Result is: abc123 == abc123



Yes, this is obviously an edge case, and one could argue that the Java implementation is more correct than MRI's, but the goal is any piece of code should run the same on either platform.

Charles Nutter (Headius) has started a RubySpec Wiki where I've noted this issue there.

I've submitted a bug against JRuby to track the issue, but arguably its not a JRuby bug. Its a difference between BouncyCastle and SSLeay's implementation. I'd be interested in some community feedback on how to correctly implement this. For now, we've worked around this by simply truncating if the RUBY_PLATFORM =~ /java/, which I know if horrible, but it allows me to move on.

Ruby vs. JRuby - The path to consistent behavior + OpenSSL

Posted over 7 years back at Revolution On Rails

In Ola Bini's recent post There can be only one, a tale about Ruby, IronRuby, MS and whatnot, he stresses the importance of documenting Ruby's API so that other implementations can match MRI's behavior. I have to strongly agree. We've recently been bitten by this, which I'll outline using OpenSSL as an example.

Ruby's OpenSSL library does not strictly enforce a ciphers's initialization vector (IV) size specification. It simply truncates to the appropriate size.

Ola's JRuby OpenSSL library (which is awesome btw!) leverages the Bouncy Castle's JCE implementation which strictly enforces IV specifications.

Below is a simple example that works fine on MRI, and fails on JRuby.

require 'openssl'
text = "abc123"
cipher = OpenSSL::Cipher::Cipher.new("des-ecb")

#The DES Cipher specifies a length of 8 bytes, the below IV exceeds that
key, iv = "1234567890", "1234567890"

cipher.encrypt
cipher.key, cipher.iv = key, iv
encrypted = cipher.update(text) << cipher.final

cipher.reset
cipher.decrypt

#I double the length of the cipher here, just to prove that MRI truncates it.
cipher.key, cipher.iv = key*2, iv*2
decrypted = cipher.update(encrypted) << cipher.final
puts "#{text} == #{decrypted}"



> jruby test_openssl_iv.rb
java.security.InvalidKeyException: DES key too long - should be 8 bytes
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at javax.crypto.Cipher.init(DashoA12275)
at javax.crypto.Cipher.init(DashoA12275)
at org.jruby.ext.openssl.Cipher.doInitialize(Cipher.java:416)
at org.jruby.ext.openssl.Cipher.update(Cipher.java:432)

> ruby test_openssl_iv.rb 
Result is: abc123 == abc123



Yes, this is obviously an edge case, and one could argue that the Java implementation is more correct than MRI's, but the goal is any piece of code should run the same on either platform.

Charles Nutter (Headius) has started a RubySpec Wiki where I've noted this issue there.

I've submitted a bug against JRuby to track the issue, but arguably its not a JRuby bug. Its a difference between BouncyCastle and SSLeay's implementation. I'd be interested in some community feedback on how to correctly implement this. For now, we've worked around this by simply truncating if the RUBY_PLATFORM =~ /java/, which I know if horrible, but it allows me to move on.

Hobo Sightings

Posted over 7 years back at The Hobo Blog

Hi folks, sorry to be a bit quiet of late. Just thought I’d update you with a few bits and bobs from the Hobo world.

There’s been a few interesting sightings of Hobo out there. By now you’ve probably seen Dr Nic’s myconfplan, which uses Hobo. I’ve been meaning to ping some questions over to Dr Nic about the experience of using Hobo in that app, so hopefully that’ll be a blog post soon.

If you’ve paid attention to RailsConf, you may have noticed that a very tiny corner of Hobo – the compact create_table migration syntax – has found it’s way into core Rails. It seems DHH came across the “Sexy Migrations” post on err the blog. Nice!

The guys over at Bugzilla have taken a look at Hobo as one option (of many!) for a new implementation of Bugzilla.

Niko Dittmann will be doing a presentation on Hobo at the German language Rails Konferenz (German language link, here’s the hilarious automatic translation)

Meanwhile I’m hoping to get my proposal accepted for a talk on Hobo at RailsConf Europe.

Behind the scenes things are very busy at Hobo Central. We have a ton of client work on, all of which will be pushing the quality and feature-set of Hobo along in leaps and bounds. And of course, all this professional work puts HoboTech Ltd. on a sound financial footing, so the Hobo project will continue to be very active for the foreseeable future.

Good times!