plugins aren't perfect either September 26th, 2007

In an attempt to wrangle the AssetPackager plugin into the Yellowpages site today, I came across this snippet.

sources = (RAILS_ENV == "production" ? 
        AssetPackage.targets_from_sources("stylesheets", sources) : 
        AssetPackage.sources_from_targets("stylesheets", sources))

RAILS_ENV == "production"? Because nobody has a staging or QA environment that should mimic the behavior of a production environment. Right?

The plugin has a lot of good things going for it. It was easy to configure and implement, but this is a pain in the ass.

My fix....

sources = (use_packaged_assets? ? 
        AssetPackage.targets_from_sources("stylesheets", sources) : 
        AssetPackage.sources_from_targets("stylesheets", sources))

....

def use_packaged_assets? 
  RAILS_ENV == "production" || USE_PACKAGED_ASSETS == true
end

Which allows me to add the USE_PACKAGED_ASSETS constant into various environments to toggle this feature on for testing purposes.

Does anybody have a better way to implement integration with various environments? I'm open to suggestion.

EDIT! Blogging my very own shame! Former Boss Steve noticed that I'd error out if the constant wasn't defined.

def use_packaged_assets? 
  RAILS_ENV == "production" || (defined? USE_PACKAGED_ASSETS && USE_PACKAGED_ASSETS == true)
end

The Next Level of Disorganization September 25th, 2007

It's easy to wrong code, but it takes creativity to put the code in the wrong location.

Pop quiz: What goes in a Rails app's tmp/ directory? Let's see:

The pattern is transient files. There are a suite of rake tasks for nuking them. You might as well keep the directory out of subversion.

Today I glance at the commit log, and there are two .rb files. Someone is using tmp as a scratch directory.

So, where should scratch files go? Trick question. Keep it out of subversion.

Mysterious Constants September 24th, 2007

<%= listing.sales_tier_summary if listing.tier == '999' %>

If you're like me, you get lost at the magical '999'. Maybe it means 'summarizable'. If so, it should be:

<%= listing.sales_tier_summary if listing.summarizable? %>

That way it's self documenting, and when the constant changes you aren't hunting through your views to update it. Welcome to MVC.

I'll stick with Rails September 23rd, 2007

Derek Sivers is pretty cool. He sponsored Hackfest back in January, so he paid for my trip to RailsConf. And he just seems like a nice guy.

So I view his switch back from Rails to PHP with an open mind. This wasn't a surprise to me. As far back as May, I'd heard murmerings about his decision to drop Rails. Now the story may get traction. So here's my respectful take.

A Language is more than its output.

1 - “IS THERE ANYTHING RAILS/RUBY CAN DO THAT PHP CAN’T DO? … (thinking)… NO.”

For 2 years, I thought Rails is genius, PHP is shit. Rails is powerful, PHP is crap. I was nearly killing my company in the name of blindly insisting Rails was the answer to all questions, timeframes be damned. But when I took a real emotionless non-prejudiced look at it, I realized the language didn’t matter that much. Ruby is prettier. Rails has nice shortcuts. But no big shortcuts I can’t code-up myself in a day if needed. Looked at from a real practical point of view, I could do anything in PHP, and there were many business reasons to do so.

As far as page output, no. For that matter, you could write your site in .Net, Smalltalk, or Assembly.

As far as high level design patterns, no, you can do what Rails does in any object oriented language. What you absolutely, positivily cannot do is alter the language itself to suite your app. Please show me a language as maliable as Ruby.

People say, "Syntax is syntax. Grow up."

In Ruby, I don't have to comment anymore. I can jump to any method, no matter how old, and instantly know what's going on. That's maintainability. That's productivity.

Yes, you can implement some of Ruby's features in another language, but it's always a hack. How about mixing in modules? .tos ? methodmissing ? Passing blocks? Whether or not you like ActiveRecord, its automatic mapping of columns to getters/setters is swashbuckling.

Most people use Ruby like any other scripting language, but there's so much more potential.

And that time you spent recreating a shortcut could have been spent on a new one.

Legacy Always Sucks

2 - OUR ENTIRE COMPANY’S STUFF WAS IN PHP: DON’T UNDERESTIMATE INTEGRATION

By the old plan (ditching all PHP and doing it all in Rails), there was going to be this One Big Day, where our entire Intranet, Storefront, Members’ Login Area, and dozens of cron shell scripts were ALL going to have to change. 85 employees re-trained. All customers and clients calling up furious that One Big Day, with questions about the new system. Instead, I was able to slowly gut the ugly PHP and replace it with beautiful PHP. Launch in stages. No big re-training.

I think this point applies whether the language is Ruby or Python or Erlang. If your entire site is based on one monolothic app, it's insane to switch it over all at once. Mind you, I've done that.

Avoid Overkill

3 - DON’T WANT WHAT I DON’T NEED

I admire the hell out of the Rails core gang that actually understand every line inside Rails itself. But I don’t. And I’m sure I will never use 90% of it. With my little self-made system, every line is only what’s absolutely necessary. That makes me extremely happy and comfortable.

This is a valid point. If you don't like frameworks, you shouldn't use Rails. Ditto for CakePHP or Django.

The Scaling Red Herring

4 - IT’S SMALL AND FAST

One little 2U LAMP server is serving up a ton of cdbaby.com traffic damn fast with hardly any load.

Writing bare bones apps does require less hardware. If your argument is your impact on the environment, I agree, but if your argument is saving money, servers are cheaper than developers.

And while it's easy to write slow Ruby code, it's easy to speed it up when it becomes an issue.

Everything looks like a nail

5 - IT’S BUILT TO MY TASTES

I don’t need to adapt my ways to Rails. I tell PHP exactly what I want to do, the way I want to do it, and it doesn’t complain. I was having to hack-up Rails with all kinds of plugins and mods to get it to be the multi-lingual integration to our existing 95-table database. My new code was made just for me. The most efficient possible code to work with our exact needs.

This is a valid argument. When you use Rails, you agree to the Rails philosophy. This philosophy is perfect for most web apps, but not 100%.

"You keeping using those words..."

6 - I LOVE SQL

Speaking of tastes: tiny but important thing : I love SQL. I dream in queries. I think in tables. I was always fighting against Rails and its migrations hiding my beloved SQL from me.

This statement makes zero sense. Migrations are a thin wrapper to your SQL, and it's easy to drop into SQL when using ActiveRecord. You could write your entire app using pure SQL.

The Telephone Game

7 - PROGRAMMING LANGUAGES ARE LIKE GIRLFRIENDS: THE NEW ONE IS BETTER BECAUSE YOU ARE BETTER

Rails was an amazing teacher. I loved it’s “do exactly as I say” paint-by-numbers framework that taught me some great guidelines. I love Ruby for making me really understand OOP. God, Ruby is so beautiful. I love you, Ruby. But the main reason that any programmer learning any new language thinks the new language is SO much better than the old one is because he’s a better programmer now! You look back at your old ugly PHP code, compared to your new beautiful Ruby code, and think, “God that PHP is ugly!” But don’t forget you wrote that PHP years ago and are unfairly discriminating against it now. It’s not the language (entirely). It’s you, dude. You’re better now. Give yourself some credit.

Rails didn't teach you. It was Rails' creators. Rails is at the forefront of innovation, and I believe the best minds have or will switch. That leaves everyone else playing catch up. Like the telephone game, the message loses meaning when it comes second hand.

Formatting September 20th, 2007

You can tell a lot about a person by their formatting.

<li class="first">
<% if listing.rateable? %><%= logged_link_to :rate_it, listing, "Rate it", new_rating_url(listing.listing_id), :onclick => 'return signInOrPopup(this.href);' %><% else %>Rate it<% end -%></li><%if listing.has_ratings? %><li><%= logged_link_to :read_reviews, listing, "Read Reviews", more_info_reviews_url(listing) %></li><% else %><li>Read Reviews</li><% end %>

This tells me: "WATCH OUT!"

Step 1: What the hell is going on?

With a little cleaning, we can survey the mess.

<li class="first">
    <% if listing.rateable? %>
        <%= logged_link_to :rate_it, listing, "Rate it", new_rating_url(listing.listing_id), :onclick => 'return signInOrPopup(this.href);' %>
    <% else %>
        Rate it
    <% end %>
</li>
<% if listing.has_ratings? %>
    <li>
        <%= logged_link_to :read_reviews, listing, "Read Reviews", more_info_reviews_url(listing) %>
    </li>
<% else %>
    <li>Read Reviews</li>
<% end %>

That's better.

It's a simple pattern: "If that link applies to the record, make it a link. Otherwise, make it plain text." There should be a helper for that. Like link_to_if.

link_to_if(listing.has_ratings?, "Read Reviews", more_info_reviews_url(listing))

Except, we're using our own helper for logging purposes called "logged_link_to." Writing logged_link_to_if is a sign something is wrong with our overall logging system, and we should be overriding link_to with an alias_method_chain, but that's a project for another day. I just need something so I can sleep at night.

<li class="first">
  <%=
    if listing.rateable?
      logged_link_to(:rate_it, listing,
        "Rate it", new_rating_url(listing.listing_id),
        :onclick => 'return signInOrPopup(this.href);')
    else
      'Rate it'
    end %>
</li>
<li>
  <%=
    if listing.has_ratings? 
      logged_link_to(:read_reviews, listing,
      "Read Reviews", more_info_reviews_url(listing))
    else
      'Read Reviews'
    end
   %>
</li>

When a method becomes an epic journey September 18th, 2007

It's going to be hard to read it, because the the blog of shame's column can't handle the might of this snippet. Trust me when I say that there are things that are wrong here.

Snippet might be the wrong word. It makes it sound quaint or small...

def get_recycle_centers
        begin
        zip_code = h params[:zip]
        #if it has the -dddd then get rid of the last part of that zip
        if (zip_code =~ /^\d{5}-\d{4}$/) == 0
          zip_code = zip_code[0...-5]  
        end

        recycle_centers = Zip.find_by_zip(zip_code).recycling_affiliates

        if recycle_centers.size == 0
           render :text => "<div style='margin-left:10px;' id='no_zip'><h4 style='padding-top:15px;'>There Are No Affiliates Near ZIP Code "+ zip_code+"</h4>
                            For recycling information and resources near you, you can search for 
                            <a href='/"+zip_code+"/Recycling-Centers?t=Recycling+Centers'>Recycling Centers</a> or 
                            <a href='/"+zip_code+"/Recycling-Equipment-Services?t=Recycling+Equipment+%26+Services'>Recycling Equipment & Services.
                            </a>
                            <div style='padding-bottom:100px;'></div>" and return  
        end
        @centers = recycle_centers
        render :partial => 'recycle_centers'        
      rescue
          render :text => "<div style='margin-left:10px;' id='no_zip'><h4 style='padding-top:15px;'>We did not recognize the ZIP code you entered. Please check it and try again.</h4></div><div style='padding-bottom:100px;'></div>"       
      end
    end

To summarize:

  1. That zip code regex seems like a bit of overkill when we only care about the first five chars
  2. We're using a begin/rescue block as a logical control
  3. render :text => WTFAWHOLEBUNCHOFSTUFF
  4. Plus, the HTML in the render :text is missing one of the closing div tags
  5. Building links to specific actions by hand
  6. Assigning the temp variable recycle_centers, checking its size, and then assigning it to @centers

Let me know if I forgot anything.

How about this?

def get_recycle_centers    
    zip_code = h params[:zip]
    zip_code = zip_code[0..4] unless zip_code.nil? #just grab the five digit zip code
    if @zip_centroid = ZipCentroid.find_by_zip(zip_code)
      @centers = @zip_centroid.recycling_affiliates
      render :partial => 'recycling_centers'
    else
      render :text => "<div id='no_zip'><h4>We did not recognize the ZIP code you entered. Please check it and try again.</h4></div>"
    end
end
  1. No more rescue! We're checking for nil on @zip_centroid like a good citizen
  2. Moving the big bulky error message into the view, which really only depends upon @centers being empty or not
  3. The links, which are now in the view, actually use link_to
  4. Reduced extra assignments, renders, ifs and other such items
  5. Got rid of inline styles that were identical in the two original render :text calls

I could probably do more. It's a good start, though.

Dinosaurs September 17th, 2007

Don't browse slashdot below +5, or you'll come across this:

Read the documentation and tutorials of Rails. Read the opinions people leave on various forums. You'll notice something frequently recurring: they keep talking about "magic" and "magical features" (literally).

Rails became popular since the MVC architecture it enforces allows you to feel you're using a "more professional" paradigm for developing web applications, and lots of newcomers were attracted by the promise for "worry free development" that Rails promotes.

In fact, Rails has very little real world usage. Trying to adapt it to an existing database schema can make you cry, and their entire "convention over configuration" idea falls completely.

Lots of the "magic" it does, like Active Record and routing / route generation, add significant overhead to each page request, for the sake of abstracting and keeping things flexible that don't need to be flexible in the first place (especially with modern refactoring tools found in modern IDE-s).

RoR is and WILL remain a niche, since there are already too many attempts at people employing it for large sites with very poor results.

It's a popular fact that the official site of Rails itself runs on PHP [rubyonrails.org], to which the excuse is that RoR is "for web apps and not for web sites"... which of course is a very poor excuse when the difference between both nowadays is only superficial.

What I like about Rails is that it resulted in a huge movement towards MVC in plenty of other languages and frameworks.

What I don't like is that those same languages don't try to apply MVC in a smart, efficient, light and performant way, but just rip Rails' features one by one, with routes and all.

People assume Rails and "web MVC" are the same thing, but they are not. A lot of the architecture in Rails is poorly conceived, and takes huge amount of resources to run abstractions of questionable use in real world application.

I suppose this will auto-correct itself as the "Rails fad" passes, but one thing's for certain: after Rails, web development will never be the same.

All these posts seem to come from the same playbook.

Break it down

...lots of newcomers were attracted by the promise for "worry free development" that Rails promotes.

Its sensible defaults do cut down worries, but its staying power is productivity and happiness.

In fact, Rails has very little real world usage.

In June, we relaunched yellowpages.com, becoming one of the world's largest sites on Rails.

Trying to adapt it to an existing database schema can make you cry, and their entire "convention over configuration" idea falls completely.

You can easily override Rails' naming conventions. If it's an uphill battle, your app is either too niche, or just poorly designed.

Lots of the "magic" it does, like Active Record and routing / route generation, add significant overhead to each page request, for the sake of abstracting and keeping things flexible that don't need to be flexible in the first place (especially with modern refactoring tools found in modern IDE-s).

Flexible routing generation makes RESTful architectures easy, and it's a godsend for SEO.

IDEs are overrated.

RoR is and WILL remain a niche, since there are already too many attempts at people employing it for large sites with very poor results.

Since the relaunch, yellowpages.com been breaking its traffic records.

It's a popular fact that the official site of Rails itself runs on PHP, to which the excuse is that RoR is "for web apps and not for web sites"... which of course is a very poor excuse when the difference between both nowadays is only superficial.

rubyonrails.org could be written entirely in flat HTML. However, the Official Rails Blog runs on Rails.

What I don't like is that those same languages don't try to apply MVC in a smart, efficient, light and performant way, but just rip Rails' features one by one, with routes and all.

Because Rails is at the forefront of innovation.

People assume Rails and "web MVC" are the same thing, but they are not. A lot of the architecture in Rails is poorly conceived, and takes huge amount of resources to run abstractions of questionable use in real world application.

Servers are cheaper than developers.

I suppose this will auto-correct itself as the "Rails fad" passes, but one thing's for certain: after Rails, web development will never be the same.

Yes. Those who don't evolve will die.

dry? September 7th, 2007

<% if params[:auto_play] %>
    <%= render :partial => 'more_info_listing_box_video', 
                :locals => {:listing => @listing, :auto_play => true} %>
<% else %>
    <%= render :partial => 'more_info_listing_box_video', 
                :locals => {:listing => @listing, :auto_play => false} %>
<% end %>

Why not...

<%= render :partial => 'more_info_listing_box_video',
            :locals => {:listing => @listing, 
            :auto_play =>(!params[:auto_play].nil? && params[:auto_play] == true)} %>

or something along those lines?

Wouldn't it be great if... September 3rd, 2007

Mea culpa. Mea maxima culpa.

We want to override sections of a page layout on a per-action level. Stuff like the title tag or some javascript includes. Right now we've got a class called PageParameters. Want to include the javascript for ajax maps? Call...

page_parameters.set_map_scripts!

...in your controller. Or your before_filter. Or in some other PageParameters method. There's a dozen places people call those methods, so when there's a change you're playing Where's Waldo.

A better solution is overriding sections of layout right in the action's view. It's the principle of least surprise.

So I spent an hour creating a helper method called "for_layout." I was so happy that I bundled it in a plugin, and then found a bug. I checked the Rails API docs, and discovered content_for, which does everything I did, but better.

The best solution is avoiding the work in the first place.