Monthly Archives: September 2007

Ouch! You broke my site

When James asked me if he could send RefactorMyCode to Digg, I said: sure. After all I spent lot of time on caching parts of the site so it would be fast. Ben and Fred also helped me spread the word with their networking ninja skills. By the time I went home that day it gained 30-40 diggs, but disappeared on the other link sites (reddit and ycomb hacker news). The server had no problem handle the load.

I kept checking my stats until I fell asleep.

7 AM: Checking my site… Huh! Not responding…

Ouch

It’s on programming front page of Digg, Reddit and Delicious! 20 000 visits! My heart is pumping!

What can I do ?

I tried multiple things on my poor little 200 MB of RAM Dreamhost account. Whenever a new dispatcher process is started it’s killed right away due to the incredible number of simultaneous visits. I read some comments on Digg, damn those people are mean, :(. I make some coffee and realize the only solution it to put an index.html file at the root and turn Rails dispatching off. Doesn’t seem to work, I can’t even serve a static page! 15 minutes later it works! The home page shows the last snapshot of the live site I could take. And visits keep going through the roof, 30 000.

It was better then nothing and I think it was less frustrating for people to see at least the facing page.

On my way home I was pretty desperate that the only solution was to move everything to a new provider. That means it will be down until I can get an account and resetup the whole app (installation of Ruby gems for syntax highlithing require some black magic and lots of luck). That’s too long!

So there I was, yesterday night, trying to find a better solution with my site busting the 50 000 visits mark.

I was caching almost everything on the front page, but with fragment caching, the request had to go through ActionPack. What prevented me from using full page caching was the login and account link and the “Your submissions sidebar”. Each are specific to a user, so it’s impossible to cache those. But still, security is checked on the server. So I though: I can show everything and show/hide relevant links with javascript. This way everyone would have the same HTML and it could be cached. That’s what I did. You can check the HTML source of the page and see the trick. I’ll go into more details in a future post.

After all this, the site is now back online. Everything seems to be working well now. Even though the traffic is going down a little bit, I still got 5800 visits today (at 1PM).

Thanks to all the people who talked, commented or blogged about or digged/voted for RefactorMyCode! You made it happend (made it popular that is, not crashing it, that was only me)!

3 Comments

Filed under Misc, refactormycode

Refactor my Code

If there’s one thing that current blogs suck at it’s sharing code. Ok, some have plugins to highlight code in your post, but what about the comments? Blogging is all about having conversations! How many times have you seen on blogs the author asking to replace > and < with something else. Oh! and forget about indentation HTML eats those, WordPress replace " with some weird character, etc, etc and etc! (etc)

Wouldn’t it be great to have a site that cares about your code ? That threats it as a piece of art and at the same time, encourage people to make it better and more beautiful ?


RefactorMyCode.com logo

That’s why I created RefactorMyCode.com. You submit your code, people refactor it!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Behave like +link_to_remote+, but shows a spinner while the request is processing.
#   link_to_remote_with_spinner 'Hi', :url => hi_path
# You can specify the spinner and the container to hide
#   link_to_remote_with_spinner 'Hi', :url => hi_path, :container_id => 'container', :spinner => 'spinner'
def link_to_remote_with_spinner(title, options)
  element_id = options.delete(:id) || ('link_to_' + title.underscore.tr(' ', '_'))
  container_id = options.delete(:container_id) || element_id
  
  returning '' do |out|
    unless spinner = options.delete(:spinner)
      spinner = "#{element_id}_spinner"
      out << image_tag('spinner.gif', :id => spinner, :style => 'display:none')
    end
    options[:complete] = "$('#{spinner}').hide(); " + (options[:complete] || "$('#{container_id}').show()")
    
    out << link_to_remote(title, { :loading => "$('#{container_id}').hide(); $('#{spinner}').show()" }.merge(options),
                                 { :id => element_id })
  end
end

Small_logo

On RefactorMyCode, you’ll find:

  • Gorgeous syntax highlighting to show your code in all its glory!
  • Split your code in sections with more then 150 supported syntaxes.
  • Have the honour of being the best refactorer of the net.
  • Pastable version of your beautifully highlighted code to put on your blog.
  • Trackback support for sending comments back to your blog (hey! why not drive traffic to your blog at the same time ?)
  • Atom Feeds for everything you should care about.
  • Spam filtering on comments (by Defensio) so you know it stays clean
  • No account creation! Enter your OpenID and you’re done! How’s that? Plus you get an avatar if your email is registered on Gravatar. All that in one step!
  • And when you get to be one of the best refactorers out there, be sure to put your badge on your blog! (See mine on the side =>, Boooo! I’m 5th :( )

But apart from all those Web-2.0-AJAX-semantic-aggregated compliant features, good things happened since I started using it with Daniel, Gary, James and Hampton (!). I learned new tricks, refactored some poor and ugly code in my apps and shared what I knew at the same time. It’s the best learning tool ever, but mostly it’s the best place to brag about your skills and have fun while doing what we love: coding!

I have a dream! That someday, ugly code will be no more! I know we can do it. This is a call for action to kill all ugly code that pollute our lives! Spread the news and help bad code disappear forever!

Now on to some refactoring!

Update: Help spread the word, Digg RefactorMyCode, Reddit it and Hacker News it (what?) !

30 Comments

Filed under rails, refactormycode, ruby, web 2.0

The Migrations for .NET project is not dead yet!

I know, I know, since I got the chance to work full-time with Ruby I pretty much abandoned all of my .NET projects. Most of them died silently, in piece. They are now gone to a better world, with full coverage and never failing tests, clean and usable design, fast and beautiful code, funny and complete documentation, transcendent and logical structure… I hope, *tears*!

But one refuses to die!

MigratorI keep getting patches and questions for the Migrator. And hopefully for my fellow .NET readers, Nick Hemsley will be the future maintainer of that project. And by the refactorings he’ve already done in a branch, I can tell you that great things are still to come for that project! Please send all your love to Nick and thank him for rescuing another useful project from dying!

Thanks Nick!

2 Comments

Filed under C#

A day in the life of a Standout Jobs hacker

In case you don’t know it yet, we’re hiring more Ruby hackers at Standout Jobs!

Instead of trying to convince you that this is the best place to work, I’ll describe my typical day of work and let you judge if this is the right thing for you.

7 AM

Sleeping

7:30 AM

Sleeping

8 AM

Wake up!

8:15 AM

Back to sleep

8:30 AM

All right, I feel like an early-riser today!

Narrator: In fact at Standout Jobs, there’s not schedule, arrive when you want! Just get things done!

9:30 AM

Standout FridgeArrive at the office, grab a juice in the fridge. Say hi to Ben. Ben has one of the 5K most popular blog in the world. He’s so famous, people ask him for autographs on the beach! He’s at the office at like 4 AM, he never sleeps (he has 2 kids).

9:32 AM

My workstationPut my Mac Book Pro next to my Apple Cinema Display, my keyboard and mouse and sit on my CEOish chair.

Narrator: as a Standout Jobs employee, you’ll get a all the Apple stuff you’re dreaming of!

10 AM

View from my placeReading some news in my Google Reader while looking at the Mount Royal.

10:15 AM

Check out tickets in Trac to see what needs to be done. Accept one, svn up, mate ., put my headphones on and I’m gone in my coder’s bubble.

10:20 AM

Fred arrives at bicycle and does a little swing dance while going to his desk. (Fred can dance, code, bicycle all at the same time, plus he’s always in the newspaper or some internet tv show. He’s famous too!)

10:30 AM

Daniel arrives not at bicycle. Daniel dances too, has a blog, code and presented at more DemoCamps then you would think humanly possible. He’s a famous Ruby Guru too!

11 AM

code, code, code, code

11:30 AM

Most of the time we communicate throught our Campfire chatroom. This is a lot better then behing continuously interupted. You can ignore if you’re deep into some code, but most of the time, it’s serious stuff!

Fred N.
So… if a train stops at a train station… and a bus stops at a bus station…
Fred N.
what happens at a work station
Marc-André C.
hard one
Marc-André C.
you stop working ?
Fred N.
you also park on a driveway
Fred N.
and drive on a parkway

… ok bad example.

12 PM

Feeling hungry and realize Fred and Daniel are there.

12:30 PM

Standout KitchenEating spaghettis in Ikea plates while watching some Ted Talk videos on youtube.

1 PM

Making coffee, while talking about some styling issues with Fred.

1:30 PM

Discuss (argue) some design decisions with Daniel.

Narrator: unlike many places, we believe in beautiful code and writing tests. We’re hackers and we sometimes get emotional when something is one pixel off or one space is missing or not properly aligned. We do more then care about our craft, we’re passionate about it!

2 PM

In our Campfire chat room:

Bot marc commited r1756…
Bot
Adding some code and fixing things

A   standoutjobs/trunk/--------
A   standoutjobs/trunk/-----
A   standoutjobs/trunk/-------/---------

Narrator: Whenever someone commits something to the subversion tree a message is sent in the chat room. This way we know what everyone is doing. We also get notified of build failures. Hey the chat room can display useful stuff too!

3 PM

But chat room is only for serious, corporate stuff, always:

Daniel H.
cool
Fred N.
cool
Marc-André C.
cool
Daniel H.
67 errors => 0
Marc-André C.
oh! you broke the cool chain!!
Daniel H.
oh no
Daniel H.
solving 67 errors in one go is very cool
Marc-André C.
you’re right
Marc-André C.
very cool
Daniel H.
very cool
Fred N.
very very cool

… ok bad example again! But I swear we have some deep tech discussions in the chatroom!

4 PM

Coding and fixing more tickets on Trac.

5:30 PM

OfficeGoodbye pretty office, I’ll miss you!

(While Marc is on his way home, in the Standout Chatroom …)

Bot
Why did you do this marc ? You broke standoutjobs-trunk build !

9:30 PM

Back on the computer, see the build failing!!! Aaaaah!

Bot
Fix the damn build

  U   standoutjobs/trunk/------------------

Bot Holy cow! marc fixed standoutjobs-trunk build again and again and again! *WOW!*

The End

You too wanna live the life of a Rails rockstar ?

Apply now!

You don’t need to be a Ruby “Guru”, but you do need to be a great hacker.

And also be sure to include code samples or references to some of the most gorgeous code you’ve written (not necessarily in Ruby) or else put [VIAGRA] at the start of your email subject to help us sort the applicants.

17 Comments

Filed under rails, ruby, StandoutJobs

Ruby to Javascript in 100 LOC, is it possible ?

The goal of this tutorial is to show you how, with Ruby, we can turn this:

if ($('a').value == 'a') {
  doThis(true, 'maybe');
  andThis($('a').innerHTML);
}
var MyClass = Class.create();
MyClass.prototype = {
  myMethod: function(arg1, arg2) {
    cool();
  },
  anotherOne: function() {
    say('hi');
  },
};

Into Ruby code, with a 100 lines Ruby script.

Go grab a beer / coffee / Guru / water / Asian pear / Pad Thai and get ready !

# First we build a base Page class that will handle the common statement and hold the output.
# This is pretty simple, as you can see, we only output text.
class Page
  def initialize
    @output = ''
  end
  
  def if(condition)
    self << "if (#{condition}) {"
    yield self
    self << "}"
  end
  
  def def(method, *args)
    self << "#{method}: function(#{args * ', '}) {"
    yield self
    self << "},"
  end
  
  def class(name, &block)
    self << "var #{name} = Class.create();"
    self << "#{name}.prototype = {"
    yield self
    self << "};"
  end
  
  def <<(output)
    @output << output << "\n"
  end
  
  def to_s
    @output
  end
end

page = Page.new

# Using those methods looks pretty cool
# but we still need to write some js code.
page.if "$('a').value == 'a'" do
  page << "doThis(true, 'maybe');"
  page << "andThis($('a').innerHTML);"
end
page.class :MyClass do
  page.def 'myMethod', :arg1, :arg2 do
    page << "cool();"
  end
  page.def 'anotherOne' do
    page << "say('hi');"
  end
end

puts page

# Outputs
if ($('a').value == 'a') {
doThis(true, 'maybe');
andThis($('a').innerHTML);
}
var MyClass = Class.create();
MyClass.prototype = {
myMethod: function(arg1, arg2) {
cool();
},
anotherOne: function() {
say('hi');
},
};

# Iteration 2 - Make the outputed code beautiful

# Lets add some helper methods to all the objects
# so we can make beautiful Ruby code output as
# beautiful Javascript.
class Object
  # This method will allow to use Ruby naming convention
  # to declare Javascript methods.
  # Converting my_method to myMethod
  def to_js_method
    to_s.gsub(/_(\w)/) { $1.upcase }
  end
  
  # This one will convert an object to it's Javascript
  # equivalent. This is pretty basic here. Wrap string in '
  # or outputing if any other type.
  def to_js_arg
    self.is_a?(String) ? "'#{self}'" : self
  end
end

class Page
  def initialize
    @output = ''
    @indent = 0
  end
  
  # We make the outputed code a little more pretty
  # by indenting it! See the indented method bellow.
  def if(condition)
    self << "if (#{condition}) {"
    indented { yield self }
    self << "}"
  end
  
  ...
  
  # If an unexisting method is called, output it
  # as a Javascript method call.
  def method_missing(method, *args)
    self << "#{method.to_js_method}(#{args.collect { |arg| arg.to_js_arg } * ', '});"
  end
  
  def <<(output)
    @output << '  ' * @indent << output << "\n"
  end
  
  private
    def indented
      @indent += 1
      yield
      @indent -= 1
    end
end

page = Page.new

page.if "$('a').value == 'a'" do
  # As you can see, we use the method_missing magic
  # to create method calls
  page.do_this true, 'maybe'
  page.and_this "$('a').innerHTML"
end
page.class :MyClass do
  page.def :my_method, :arg1, :arg2 do
    page.cool
  end
  page.def :another_one do
    page.say 'hi'
  end
end

puts page

# Nice camelized method names, clearly indented = sweet!
if ($('a').value == 'a') {
  doThis(true, 'maybe');
  andThis('$('a').innerHTML');
}
var MyClass = Class.create();
MyClass.prototype = {
  myMethod: function(arg1, arg2) {
    cool();
  },
  anotherOne: function() {
    say('hi');
  },
};

# Iteration 3 - converting Ruby expressions to Javascript

# Now lets take care of that if statement.
# In Ruby, even operators are methods. We can take advantage of this
# and override those and output what we want!

# This module will override all comparison operators
# and output a string representation of the expression.
# So calling a == 'a' will return the string "a == 'a'"
module JSComparable
  def self.included(base)
    base.class_eval do
      %w(== < > <= >=).each do |operator|
        define_method operator do |other|
          [self.to_js_arg, operator, other.to_js_arg] * ' '
        end
      end
    end
  end
end

# We need to defined a simple class in which we will
# override those operators.
class Element
  attr_reader :id
  include JSComparable
  
  def initialize(id)
    @id = id
  end
  
  def to_s
    "$('#{id}')"
  end
end

# Since we're lazy, we'd like to type E[:a] ratter then Element.new(:a)
module E
  def self.[](id)
    Element.new id
  end
end

# All this allows us to rewrite this:
page.if "$('a') == 'a'" do
# into this:
page.if E['a'] == 'a' do
# Pure Ruby, how cool ?

###############
# Iteration 4 #
###############
# Now one line is still pretty annoying:
#   page.and_this "$('a').innerHTML"
# To turn this into Ruby code we can use a little
# method_missing magic again.

# We'll create a small class that will handle
# the calls to methods on element.
# When rendered, parent.method is outputed.
class MethodProxy
  include JSComparable
  
  def initialize(parent, name)
    @parent = parent
    @name = name
  end
  
  def to_s
    [@parent, @name.to_js_method] * '.'
  end
end

# Add a simple hook into Element
# so any unknow method calls are rendered as
# Javascript method calls.
class Element
  ...
  
  def method_missing(method, *args)
    MethodProxy.new(self, method)
  end
end

# All this allows us to come to the almost perfect:
page.if E['a'].value == 'a' do
  page.do_this true, 'maybe'
  page.and_this E[:a].innerHTML
end
page.class :MyClass do
  page.def :my_method, :arg1, :arg2 do
    page.cool
  end
  page.def :another_one do
    page.say 'hi'
  end
end

# Iteration 5 - Final touch, making this a real language !

# The 'page.' thing is pretty annoying I know!
# What's the simpless thing we could do to remove this ?

page = Page.new

code = File.read(ARGV[0])
code.gsub! /(if|class|def) (.*)$/, 'page.\1 \2 do'
eval code

puts page

# Now place this in a seperate file:
if E[:a].value == 'a'
  page.do_this(true, 'maybe')
  page.and_this(E[:a].innerHTML)
end

class :MyClass
  def :my_method, :arg1, :arg2
    page.cool
  end
  def :another_one
    page.say('hi')
  end
end

# And run with: ruby js.rb javascript.rjs
# and you'll get:

if ($('a').value == 'a') {
  doThis(true, 'maybe');
  andThis($('a').innerHTML);
}
var MyClass = Class.create();
MyClass.prototype = {
  myMethod: function(arg1, arg2) {
    cool();
  },
  anotherOne: function() {
    say('hi');
  },
};

If you're serious about this, I'd suggest you take a look at ParseTree which allows you to explore the parse tree of some Ruby code.

I hope this helps anyone understand better some of Ruby meta-programming capability, beauty and power!

Ruby, I love you!

Amen

Get the full script here (rename it to js.rb)

11 Comments

Filed under js, ruby, tutorial

The Secret of Happiness : Constraint

If you have one video to watch today, this week, this month or even this year, make it this one. Both enlightening and funny, it’s a scientific proof that the way to acheive happiness is not the one you think.

6 Comments

Filed under links, Misc, video

Defensio on Rails

I know what you’re thinking: what kind of lazy ass are your Marc ? Did you get a life, lost your computer, your girlfriend went into labour ? More then a month since I released a project! Wow, I must have been pretty busy…

While talking with Carl at the last Montreal on Rails meeting, it occurred to me that it would be cool to integrate spam filtering with Defensio in my next project (more details on this eventually).

Defensio has received good and bad reviews, but from what I understand, it really gets better with time as opposite to Akismet who plateau pretty soon. I think one of the reason why Defensio might be better is that it uses much more data to classify your comments. Not just the content and user IP but the author email, the date of the article and the color of the sox the author’s sister’s father in law wore last weekend. That might be hard and tortuous to supply, especially when he’s on vacation. So that’s why I though I could make a Rails plugin that make it easy to supply all this info without hunting into your sister’s father in law wardrobe.

Get to the point already

1) The first thing your need to do to use the defensio Rails plugin is to install it:

script/plugin install http://code.macournoyer.com/svn/plugins/defensio/

2) Then you might want to create a config file in /yourapp/config/defensio.yml

development:
  api_key: 1234abc
  owner_url: http://code.macournoyer.com/svn/plugins/defensio

test:
  api_key: 1234abc
  owner_url: http://code.macournoyer.com/svn/plugins/defensio

production:
  api_key: 6789xyz
  owner_url: http://code.macournoyer.com/svn/plugins/defensio

You'll need to get an API key at this point. Ask the guys at Defensio, or spam Carl until he gives you one.

3) Next step is to add the columns used to store the comment spaminess level

script/generate defensio_migration comment

(Replace comment with the name of your model that you want to be marked as spam/ham)

Run it like there's no tomorrow !

rake db:migrate

4) In your article class (the one that holds the comments), add:

acts_as_defensio_article

By default the following fields will be looked up for values: author, author_email, title, content, permalink.
You can change those fields with the :fields options, like this:

acts_as_defensio_article :fields => { :content => :body }

The content value will be fetched from the body method.

5) In your comment class (the one that can be spam or ham), add:

acts_as_defensio_comment

Same things apply with the :fields thinggy. Looking in the following fields by default: author, content, title, author_email, author_url, article.permalink, trusted_user and logged_in.

6) Add some magic in your controller:

class CommentsController < ApplicationController
  def index
    @ham  = @article.comments.find_all_by_spam(false)
    @spam = @article.comments.find_all_by_spam(true, :order => 'spaminess desc')
  end

  def create
    @comment = @article.comments.build(params[:comment])
    @comment.env = request.env

    if @comment.save
      if @comment.spam
        flash[:notice] = 'Your comment has been marked for review'
      else
        flash[:notice] = 'Comment created'
      end
      redirect_to article_url(@article)
    else
      render :action => 'new'
    end
  end

  def report_as_spam
    @comment = @article.comments.find(params[:id])
    @comment.report_as_spam
  end

  def report_as_ham
    @comment = @article.comments.find(params[:id])
    @comment.report_as_ham
  end

  def stats
    @stats = Comment.defensio_stats
  end
end

You're done! Now go find some spammers !

Beta aka buggy

I've started to use this plugin myself in one of my personal project but not for very long. It might contain some bugz. Although I doubt it... I never write bugs. If you encounter any unwanted features (aka bug) please let me know in the comments here.

Thanks to the guys at Karabunga for making Defensio, using the API was a real pleasure!

Update: There are more detailed code sample in the plugin svn: http://code.macournoyer.com/svn/plugins/defensio/example/

Update: Documentation is online.

12 Comments

Filed under rails, ruby