The Web Doesn’t Suck. Browsers Are Innovating.

This week saw a flurry of back-and-forth about the future of the web platform. In the “web sucks” camp were Sachin Agarwal of Posterous (The Web Sucks. Browsers need to innovate) and Joe Hewitt (Sachin summarized some of his tweets at @joehewitt agrees with me).

Chris Blizzard responded with a few narrow examples of what Firefox has been doing lately (geolocation, orientation, WebGL).

On Hacker News, most of the comments took Sachin to task for his argument that standards don’t matter, pointing people back to the “bad old days” of the browser wars.

In my opinion, both camps kind of miss the point.

Sachin made some very pointed factual claims which are just complete, pure hokum. Close to the top of his post, he says:

Web applications don’t have threading, GPU acceleration, drag and drop, copy and paste of rich media, true offline access, or persistence. Are you kidding me?

Really? Are you kidding me. In fact, browsers have implemented, and the WHAT-WG has been busily standardizing, all of these things for quite a number of years now. Threading? Check. GPU acceleration? Check and check. Drag and drop? Check. Offline access and persistence? Check and check.

Almost universally, these features, which exist in shipping browsers today, were based on experiments conducted years ago by browsers (notably Firefox and Safari, more recently Chrome), and did not wait for approval from the standards bodies to begin implementing.

In fact, large swaths of HTML5 are based on proposals from browsers, who have either already implemented the feature (CSS-based animations and transitions) or who then build the feature without waiting for final approval. And this is transparently obvious to anyone, since HTML5 has not not yet been approved, and some parts are the subject of internal W3C debate, yet the browsers are implementing the parts they like and innovating in other areas.

You can see this explicitly in the features prefixed with -moz or -webkit, because they’re going ahead and implementing features that have not gotten consensus yet.

In 2010, the WHAT-WG is a functioning place to bring a proposal for further review once you’re conducting some experiments or have a working implementation. Nobody is sitting on their hands waiting for final approval of HTML5. Don’t confuse the fact that people are submitting their ideas to the standards bodies with the misguided idea that only approved standards are being implemented.

And in fact, this is basically never how it’s worked. Apple implemented the <canvas> tag and <input type="search" /> in 2004 (here’s a 2004 rant from the editor of the then-brand-new HTML5 spec). Opera and Apple worked on the <video> tag in 2007. The new CSS flexible box model is based on XUL, which Firefox has had for over a decade. HTML5 drag and drop support is based on implementations shipped by the IE team over a decade ago. Firefox has been extending JavaScript for almost its entire history (most recently with JavaScript 1.8).

CSS transition, transforms and animations were built by Apple for the iPhone before they were a spec, and only later ported to the desktop. Firefox did the initial experimentation on WebGL in 2006, back when they were calling it Canvas 3d (here‘s a working add-on by Mozilla developer Vladimir Vukićević).

Google Gears shipped what would become the Application Cache, Web Storage, and Web Workers, proving out the concepts. It also shipped geolocation, which is now part of the HTML5 spec.

@font-face originally shipped in Internet Explorer 5.5. Apple has added new mobile events (touchstart, touchmove, touchend, onorientationchange) with accompanying JavaScript APIs (Apple developer account needed) without any spec activity that I can discern. Firefox, at the same time, added support for hardware accelerometers.

Even Microsoft bucked the standards bodies by shipping its own implementation of the W3C’s “cross-origin-resource-sharing” spec called Cross domain request, and shipped it in IE8.

It’s perfectly understandable that people who haven’t been following along for the past few years might have missed all of this. But the fact remains that browser vendors have been moving at a very rapid clip, implementing all kinds of interesting features, and then sharing them with other browsers for feedback, and, one hopes, consensus.

Some of these implementations suck (I’ve heard some people say not-nice things about WebGL and drag-and-drop, for instance). But that’s quite a bit different from saying that browsers are sitting on their hands waiting for the W3C or WHAT-WG to tell them what to do.

MongoHQ Add-on Public Beta

Let’s cut straight to the chase: MongoHQ is launching their add-on to all Heroku users as a public beta.

The details

Over the last six months we have seen persistent demand for MongoDB support on Heroku, so we are incredibly excited that MongoHQ is releasing their highly anticipated add-on into public beta today. The add-on interfaces seamlessly with their successful hosted service, and allows developers to use MongoDB as a first-class-citizen data store in any of their Heroku apps. Using it is just as easy as you’ve come to expect from Heroku: simply add the add-on, and you’re good to go!

The first available plan is free and includes one database up to 16MB. Soon, you will have your pick of the full range of MongoHQ plans.

Getting Started

Add the add-on to your app:

$ heroku addons:add mongohq:free

This will create a fresh new MongoHQ database for you and set the proper environment variable in your app. Follow the detailed instructions in our MongoHQ docs page for specifics on using MongoDB in your app.

Transferring data in/out

Since MongoDB isn’t a SQL database, taps won’t work. Luckily Pedro has created a Heroku client plugin that offers you push/pull functionality to a MongoDB. If you have MongoDB running locally on your development machine, with this plugin you can easily push and pull data just like with taps.

If you have any questions using the MongoHQ add-on, or any Heroku add-on, our support staff is available.

Node.js Feedback

The response to yesterday’s Node.js announcement continues to be absolutely amazing.

First and foremost, we’re thrilled to see the community share our excitement about Node.js and its potential on the Heroku platform.

We do, however, also want to be mindful that we’re still in the experimental phase with this technology here. For this reason, we’re going to proceed carefully and invite testers in small batches.

So, if you don’t hear from us right away, despair not. It’ll likely take us a few weeks to get through the current list, and if you’re reading this for the first time, please don’t hesitate to register your interest at nodejs@heroku.com.

Find out what it takes to become a Rocketeer

In this week’s “Ask a Rocketeer” Obie Fernandez answers @fredlee’s question about the hiring process at Hashrocket. Obie explains what he is looking for in a candidate, walks us through a typical Hashrocket interview and lets you know the steps you need to take if you want to become a Rocketeer.

In this week’s “Ask a Rocketeer” Obie Fernandez answers @fredlee’s question about the hiring process at Hashrocket. Obie explains what he is looking for in a candidate, walks us through a typical Hashrocket interview and lets you know the steps you need to take if you want to become a Rocketeer.

You should also get a chuckle watching our Rocketeers being put on the spot to answer the question: “Where do babies come from?” submitted by @thechrizp.

Thanks to everyone who submitted their questions, please keep ‘em coming. Tweet @hashrocket #ask and we’ll select the best question(s) and get you an answer.

Paul Barry Winner RPCFN #8

In this brief interview, Satish Talim of RubyLearning talks to Paul Barry of USA, winner of the eighth Ruby Programming Challenge For Newbies.

Paul Barry

Satish>> Welcome Paul and thanks for taking out time to share your thoughts. For the benefit of the readers, could you tell us something about your self?

Paul>> Sure, I’m a Web Developer from Baltimore, MD. I’ve been doing web development for over 10 years in a variety of languages and frameworks including Perl, PHP, Java and Ruby.

Satish>> How did you get involved with Ruby programming?

Paul>> I got started with Ruby programming the same way I suspect many other Ruby developers did, which is through Rails. At the time Rails hit the scene, I was pretty entrenched as a Java developer. My initial reaction to Rails was that it seemed to have a lot of good ideas in it, but I figured we could find a way to implement those idea in Java. The more I dug into Rails, the more I realized that it was the Ruby language that made the clear, powerful abstractions that make up Rails possible and that I could use Ruby to create useful abstractions in my own code as well.

Satish>> Could you name three features of Ruby that you like the most, as compared to other languages? Why?

Paul>>

  1. Blocks: The best thing about blocks is that when you first start using Ruby, Ruby’s block syntax gets you to start using anonymous functions and higher-order functions all over the place in your code without even realizing it.
  2. Everything is an object: This is makes some aspects of the language simple to understand in that everything follows a few simple rules. String, Integers, Regexs, and most importantly Classes, are all just objects, just data, that you can manipulate at runtime.
  3. Class definitions: These are just expressions that are evaluated at runtime, expressions that have access to the class that is being defined. This allows you to create methods that when called will generate other methods, such as has_many in Rails.

When you put these three features together, you have a metaprogramming environment that I would argue is as powerful as what Lisp macros give you, although I’m sure there are some hardcore Lisp programmers out there that would disagree with me.

Satish>> How was experience of taking part in the Ruby Programming Challenge For Newbies (RPCFN)?

Paul>> I’ve really enjoyed it. It’s a great exercise to come up with a solution to a problem and then be able to compare your solution to what other developers came up with. Most of the challenges so far have been just the right size, which is hard enough to make you think about it, but short enough to where you can work out an interesting solution without having to invest too much time in it.

Satish>> You are based in Baltimore, USA. How is the Ruby and Rails scenario there?

Paul>> Baltimore has a great Ruby and Rails community. Of course, RailsConf is coming to town, which we’re all excited about. Many of us that are part of the B’More on Rails group, are working on various initiatives to make sure everyone has a great time at RailsConf. The Baltimore Ruby on Rails group has a bunch of smart people that meet regularly at various events. We’re always trying to do everything we can to help new people become part of the community as well. There’s also a lot of overlap with the DC/Northern Virginia communities which are close by and very good as well.

Satish>> What are your future plans?

Paul>> I’m going to Disneyland!

Just kidding. There is so much stuff I want to learn more about, it’s hard to find time for all of it. I really like where Rails is going with Rails 3. As much as I love Ruby, I’m also interested in other languages as well, especially functional ones like OCaml, Erlang, Clojure, Scala and Haskell. I think asynchronous IO/evented frameworks like Node.js and EventMachine provide a better model for building network servers. Technologies like XMPP and the WebSocket API that’s part of HTML5 are also going to be key in the next generation of real-time, connected web and mobile applications.

Thank you Paul. In case you have any queries and/or questions, kindly post your questions here (as comments to this blog post) and Paul would be glad to answer.

Technorati Tags: , , , , ,

RPCFN: Interactive Fiction (#9)

Ruby Programming Challenge For Newbies

RPCFN: Interactive Fiction (#9)

By Avdi Grimm

About Avdi Grimm

Avdi GrimmAvdi Grimm is a husband, father, software cultivator living in southern Pennsylvania, USA. He has been been working with the Ruby language for almost ten years, and is still finding new reasons to love it. He is the author of NullDB, Hammertime, AlterEgo, HookR and numerous other Rubygems and has contributed code to Gemcutter, UtilityBelt, the DataMapper/SimpleDB adapter, and other projects. He writes about Ruby and software development at his blog Virtuous Code.

Avdi has this to say about the challenge:

One of the hardest parts of getting started with a new programming language is picking problems to practice on. The problems need to be difficult enough to give you an opportunity to apply the features of the language, but small enough to complete in a reasonable amount of time. RPCFN is a great way to find practice problems which have been scoped to that “just right” size by experienced Rubyists. Complete these challenges and you will be sure to gain confidence and proficiency in the Ruby language.

Our Awesome Sponsors

This monthly programming challenge is co-sponsored by ELC Technologies and Backup My App.

A leading software development firm based in Santa Barbara, California

Founded in 1991, ELC Technologies delivers the value of next-generation Web technologies to today’s businesses, harnessing the power of mobile applications, cloud computing, and software as a service (SaaS). ELC Technologies’ leadership in agile software development processes has brought success to business-critical implementations for clients ranging from Cisco to Tribune Interactive to LiveNation. The company is a worldwide leader in Ruby on Rails development and has pioneered dynamic language development across multiple platforms. Short iterations, demos early and often, and constant client communication regarding vision and progress are all part of the ELC promise and what makes them such a valuable development partner for Global 2000 companies.

ELC Technologies is headquartered in Santa Barbara, California.

Backup My App

Backup My App is an automatic backup service for Ruby on Rails applications. You simply install the plugin to your Rails application and they handle the rest of the process. They store backup history for several weeks and you can restore any of them automatically. Try out their 1 GB plan for free. Backup My App has co-sponsored this challenge and is proud to make this contribution to the Ruby community.

Prizes

  • The participant with the best Ruby solution (if there is a tie between answers, then the one who posted first will be the winner) will be awarded any one of PeepCode’s Ruby on Rails screencasts and a free 10 GB account for a year from Backup My App.
  • From the remaining working Ruby solutions, three participants would be selected randomly and each one would be awarded any one of Pragmatic’s The Ruby Object Model and Metaprogramming screencasts.
  • All the participants in this challenge (except the participant with the best Ruby solution) will get a free 5 GB account for 6 months from Backup My App.

The four persons who win, can’t win again in the next immediate challenge but can still participate.

The Ruby Challenge

RPCFN

The Challenge

The entire challenge details are available at: http://github.com/avdi/rpcfn-interactive-fiction.

How to Enter the Challenge

Read the Challenge Rules. By participating in this challenge, you agree to be bound by these Challenge Rules. It’s free and registration is optional. You can enter the challenge just by posting the following as a comment to this blog post:

  1. Your name:
  2. Country of Residence:
  3. GIST URL of your Solution (i.e. Ruby code) with explanation and / or test cases:
  4. Code works with Ruby 1.8 / 1.9 / Both:
  5. Email address (will not be published):
  6. Brief description of what you do (will not be published):

Note:

  • As soon as we receive your GIST URL, we will fork your submission. This means that your solution is frozen and accepted. Please be sure that is the solution you want, as it is now recorded in time and is the version that will be evaluated.
  • All solutions posted would be hidden to allow participants to come up with their own solutions.
  • You should post your entries before midnight of 24th May 2010 (Indian Standard Time). No new solutions will be accepted from 25th May onwards.
  • On 25th May 2010 all the solutions will be thrown open for everyone to see and comment upon.
  • The winning entries will be announced on this blog before 30th May 2010. The winners will be sent their prizes by email.

More details on the RPCFN?

Please refer to the RPCFN FAQ for answers to the following questions:

Donations

RPCFN is entirely financed by RubyLearning and sometimes sponsors, so if you enjoy solving Ruby problems and would like to give something back by helping with the running costs then any donations are gratefully received.

Click here to lend your support to: Support RubyLearning With Some Love and make a donation at www.pledgie.com !

Acknowledgements

Special thanks to:

  • Avdi Grimm.
  • Sponsors ELC Technologies and Backup My App.
  • GitHub, for giving us access to a private repository on GitHub to store all the submitted solutions.
  • The RubyLearning team.

Questions?

Contact Satish Talim at satish [dot] talim [at] gmail.com OR if you have any doubts / questions about the challenge (the current problem statement), please post them as comments to this post and the author will reply asap.

The Participants

There are two categories of participants. Some are vying for the prizes and some are participating for the fun of it.

In the competition

  1. Aldric Giacomoni, USA
  2. Benoit Daloze, Belgium
  3. James Martin, Australia

Just for Fun

  1. Tanzeeb Khalili, Canada
  2. Vojto Rinik, Slovakia

The Winners

Winners

Congratulations to the winners of this Ruby Challenge. They are:

Previous Challenge

RPCFN: XML Transformer (#8) by Jamie van Dyke.

Note: All the previous challenges, sponsors and winners can be seen on the Ruby Programming Challenge for Newbies page.

Update

  • This challenge is now closed.
  • The (#10) challenge by Ryan Bates, USA is scheduled for 1st June 2010.

Technorati Tags: , , , , ,

Experimental Node.js Support

Today we’re offering experimental support for node.js to a limited set of users. We know there is a lot of demand, and will work with as many users as we can. See below for details.

A natural complement to Ruby

Yesterday we posted about how we think about the platform and make roadmap decisions. We are always looking for the next set of use cases to support, and lately we’ve been thinking about realtime apps and event-driven architectures.

Today, most Ruby apps are synchronous. By default, all I/O blocks. If you’re uploading a file, polling a service, or waiting on data, your app will be blocked. While it’s possible to fix this by meticulously eliminating all blocking I/O from your code and dependencies, and using a library such as EventMachine, it’s tedious and as Adam points out: “Libraries like eventmachine will never be truly intuitive to use, because event-driven I/O is enough of a fundamental shift that it requires deep language integration. JavaScript, it turns out, is a fundamentally event-driven language because of its origins in the browser”

Node.js is evented I/O for JavaScript, built on top of the blazingly fast V8 engine. It makes handling event-driven I/O incredibly simple, and aligns perfectly with our maniacal focus on simplicity and developer productivity. The Ruby community has quickly adopted node, and with great reason. Complimenting existing apps with node.js for components that require real-time event handling or massive concurrency is both easy and elegant – in part thanks to the availability of frameworks such as express.

Simple to use

Node fits well into our existing architecture. It’s just another runtime selectable as part of our stacks:

Open Questions

Supporting node opens many questions on the platform: how are we going to charge, what version will we support, how can I integrate it with my current apps? We’re releasing this in experimental state now to collaborate with the Node.js and Ruby communities on this. Over the next months we will work together to provide answers to all these questions and more.

How to Participate

If you want to participate and use node on an experimental basis, drop an email to nodejs@heroku.com with your email address, what you want to use Node for, and we’ll work to include you in the program.

Visually Inspect Ruby Object Models with DrX

When you want to inspect your objects in Ruby, Object#inspect, p, or awesome_print are all valuable. You’re stuck with plain-text, though, and primarily designed to look at object data rather than object models. If you want to drill down into parent classes, see object and class relationships, etc, then, check out DrX, a visual object inspector for Ruby!

DrX bills itself as a “small object inspector”, but its key features are that it shows results visually (in a GUI interface) and that it focuses on showing the object model behind your objects, rather than the data contained within. A visual example of a DrX session should give you the idea:

Usage

Once DrX is installed (more on that in the next section), you just require 'drx' it into your app (or even within irb) and then use the Object#see method to get DrX into action:

require 'drx'
123.see

Even this rudimentary example will bring up an interesting graph. The DrX author does, however, provide a more interesting example to show off DrX’s introspective features:

s = "wizard of oz"
def s.strong
"<strong>" + self + "!</strong>"
end
s.see

Installation and Prerequisites

Depending on your setup, DrX might take some serious work to get going. If you’re running Linux and are heavy on your development experimentation, you might have everything ready to go. Just try gem install drx and see if the above examples work in irb.

Failing that, DrX uses Tk for its GUI work in order to be cross-platform and also requires GraphViz to be present. Install these with your package manager of choice and ensure that your Ruby installation has the Tk bindings installed (again, easier said than done).

On OS X 10.6 (Snow Leopard) I discovered that the stock Ruby installation does not include the Tk bindings, even though Tk is present otherwise. Rather than mess it up, I relied on the always-wonderful RVM and installed Ruby 1.9.2-preview1 (rvm install ruby-1.9.2-preview1). With this, Tk worked “out of the box” and gem install rbx was OK. For the Graphviz dependency, sudo port install graphviz did the trick and also “just worked.” If you’re one of the anti-Macports crowd, though, you might need to find a different approach.

Foy Savas – Ruby on Rails Podcast

Director of the Boston-based consultancy Assembly and author of The Merb Way, Foy Savas talks about why sometimes Rails sucks and suggests what we might do to improve the framework, make it easier for new people to learn, and continue to popularize the Ruby language.
Interviewed by Rails evangelist Gregg Pollack at RubyConf Taiwan.

Links Mentioned in the Show

Foy Savas – Ruby on Rails Podcast

Director of the Boston-based consultancy Assembly and author of The Merb Way, Foy Savas talks about why sometimes Rails sucks and suggests what we might do to improve the framework, make it easier for new people to learn, and continue to popularize the Ruby language.
Interviewed by Rails evangelist Gregg Pollack at RubyConf Taiwan.

Links Mentioned in the Show

Cocoa Programming: A Quick-Start Guide for Developers in print

Cocoa Programming: A Quick-Start Guide for Developers in now and print and shipping; new Mac roadmap

Nestful: A Simple Ruby HTTP/REST Client Library

Nestful is a simple HTTP/REST client library for Ruby, developed by Alex MacCaw (of Juggernaut) fame. Nestful allows you to consume basic Web services easily, usually in a single line of code. It can deal with JSON, buffered downloads, and callbacks out of the box.

HTTParty is the current, de-facto simple HTTP/REST client library used by most Rubyists (when net/http won’t do or when Typhoeus is too overkill) but Nestful differs enough from HTTParty to live alongside it. While HTTParty encourages you to build up some structure and separate the types of resources you’re accessing into classes (that HTTParty then extends), Nestful offers a simpler, “just call a method from anywhere” approach.

Some basic Nestful examples:

Nestful.get 'http://example.com' #=> "body"
Nestful.post 'http://example.com', :format => :form #=> "body"
Nestful.get 'http://example.com', :params => {:nestled => {:params => 1}}
Nestful.get 'http://example.com', :format => :json #=> {:json_hash => 1}
Nestful::Resource.new('http://example.com')['assets'][1].get(:format => :xml) #=> {:xml_hash => 1}

Nestful’s features include:

  • Simple API
  • File buffering
  • Before/Progress/After Callbacks
  • JSON & XML requests
  • Multipart requests (file uploading)
  • Resource API
  • Proxy support
  • SSL support

Cinch: A Ruby IRC Bot Building Framework

Cinch (or GitHub repo) is a new Ruby “microframework” for creating IRC bots. Effectively, Cinch is a library that both abstracts away all of the complexities of dealing with IRC servers and presents a DSL for rolling out your own functionality.

Cinch’s Hello Bot example demonstrates how you can easily create a bot that connects to an IRC server (irc.freenode.org), joins a channel (#cinch) and then replies to greetings:

irc = Cinch.setup :verbose => true do
  server "irc.freenode.org"
  nick "Cinchbot"
  channels %w(#cinch)
end

irc.plugin "hello" do |m|
  m.reply "Hello, #{m.nick}!"
end

irc.run

Cinch isn’t the first attempt at building a DSL for creating bots in Ruby. Autumn is perhaps the most famous existing library, but it’s extremely heavy compared to Cinch. Cinch vs Autumn is almost like Sinatra vs Rails. Other libraries include Rbot and Butler.

Update: I’ve been reminded in the comments that Isaac is another IRC bot DSL that’s very much like Cinch.

If you prefer to get nearer the wire and see how the IRC protocol works, this Ruby code snippet provides basic IRC functionality.

Update & Roadmap

It’s been a great first quarter for us, and it’s time for a brief update on where we are and where we’re headed.

Growth

Heroku’s growth has continued to be huge. 1,500 new apps were deployed to Heroku last week alone, and that number increases every week. Next week we will cross the 60,000 application mark.

As you can imagine, traffic is growing even more quickly, serving billions of requests per month. In fact, traffic has grown by 4x over the last four months:

Many are finding great value in the platform and paying for features and scale. Our customer count and revenue have similar growth curves.

Roadmap

Where is Heroku’s platform going next? How can you plan for our next releases or influence our direction? When we launch new features, what’s the best way to think about how they fit into our overall strategy?

Here’s how we think about our roadmap and decide on big new areas to work on: it’s all about use cases.

We started with the simplest use case: making it drop-dead easy for developers to deploy applications, and have grown into more complex ones (like multi-tier complex composite apps). We continue to try and expand the number of use cases that we provide a complete solution for.

Here’s a brief look at our historical roadmap from the perspective of expanding use cases:

Provisionless Deployment. Instant deployment with the now famous “git push heroku master” is at the heart of Heroku, enabling the basic use case: a frictionless application platform that just works.

Caching and Instant Scaling. We introduced high-speed HTTP caching built into every app, and added dynos which can be scaled up and down instantly, enabling large scale and variable scale apps.

Asynchronous Patterns. We added background processing with Delayed::Job, followed shortly by workers which can be scaled up and down just like dynos, enabling many use cases around modern asynchronous architecture.

Platform Extensibility. We launched the Add-ons system, a way to extend Heroku apps with core functionality like full-text search or memcache, and to consume external services like New Relic, Zerigo, or Sendgrid. Use cases here are literally endless. Add-ons allow the growing ecosystem of startups and established companies building cloud services to add new features to our platform – many more than we could do on our own.

Flexible Runtime. We recently introduced deployment stacks, which enable choice between multiple runtime environments.

What’s Next?

Over the next days, weeks, and months, we will release new features that continue to expand the number of use cases supported by Heroku, whether for startups or large enterprises.

You can be sure that each time we build a feature we will be maniacally focused on simplicity and developer productivity, and will always try to maintain the cohesiveness and quality of the platform.

From our core focus on developer productivity and frictionless deployment, we’ll be expanding the footprint to include areas like realtime and event-driven apps, more complex multi-tier applications, and a broader platform for deploying advanced applications. Stay tuned, and let us know where you’d like to see us go.

#115: Deflector Dish

In this episode, Jason and Dan bring you info on the latest and greatest Ruby gems and Rails plugins.

Links for this episode:

Making CRUD less “Cruddy”, one step at a time

One of the great “new” features of Rails (as of 2.3) is accepts_nested_attributes_for, allowing you to build cross-model CRUD forms without “cruddying” your controller. There are some great examples out there about how to do this, but I’d like to walk thorough a particular use case — managing the “join” records in a has_many :through relationship.

Consider the following database schema:

class Villain < ActiveRecord::Base
  has_many :gifts
  has_many :super_powers, :through => :gifts
end

class Gift < ActiveRecord::Base
  belongs_to :villain
  belongs_to :super_power

  validates_uniqueness_of :super_power, :scope => :villain_id
end

class SuperPower < ActiveRecord::Base
  has_many :gifts
  has_many :villains, :through => :gifts
end

In our dataset, there are a relatively small number of super powers which we wish to present as a list of checkboxes on the villain management form. Checking/unchecking the boxes will manage the gift records for that villain, effectively managing the list of super powers available to the baddy.

To get started, we need to add accepts_nested_attributes_for :gifts to the Villain class — piece of cake. To complete the implementation, we need to change the params hash that our form generates. Let’s review the cases that we need to support and the associated params hash format needed to implement the correct functionality.

The first case is a super power record that is not currently associated with the villain. Here, the UI should display an unchecked checkbox. If we check it and submit the form, a gift record should be created linking the villain with the super power, making this bad guy that much badder. Here is an example of the params hash we should be sending to accomplish this:

  {
    'villain' => {
      'name' => 'Lex Luthor',
      ...
      'gifts_attributes' => {
        1 => { 'super_power_id' => 5 },
        2 => { 'super_power_id' => 7 },
        ...
      }
    }
  }

The alternate case is a super power this villain already possesses. In this instance, the UI should display a checked checkbox, and if we uncheck it, the existing gift record should be deleted, diminishing the villain’s capacity for evil. And our params hash needs to look like:

  {
    'villain' => {
      'name' => 'Two-Face',
      ...
      'gifts_attributes' => {
        1 => { 'id' => 101, '_delete' => true },
        ...
      }
    }
  }

Note that the keys for the gifts_attributes hash are arbitrary; we can use any scheme to generate unique keys for the hash.

So how can we craft a form that sends the params hash that Rails wants to see? Here’s my implementation:

  <%- SuperPower.all.each_with_index do |super_power, index| -%>
    <label>
      <%- if gift = @villain.gifts.find_by_super_power_id(super_power.id) -%>
        <%= hidden_field_tag "villain[gifts_attributes][#{ index }][id]", gift.id %>
        <%= check_box_tag "villain[gifts_attributes][#{ index }][_delete]", false, true %>
        <%= hidden_field_tag "villain[gifts_attributes][#{ index }][_delete]", true %>
      <%- else -%>
        <%= check_box_tag "villain[gifts_attributes][#{ index }][super_power_id]", super_power.id %>
      <%- end -%>

      <%= super_power.name %>
    </label><br />
  <%- end -%>

If the gift is detected, the villain has the super power, and we handle our second case from above, using the checkbox / hidden field hack Rails employs in the check_box helper method to make sure a value is sent whether or not the checkbox is checked. The else block handles the other case, setting up our params hash to create the gift if the checkbox is checked.

This works, but we probably don’t want to copy and paste that code everywhere we use this pattern. How can we reuse this in a DRY fashion? Here’s a helper method that encapsulates the logic:

  def has_join_relationship(model, join_collection_name, related_item, collection_index, options={})
    returning "" do |output|
      relationship_name = options[:relationship_name] || related_item.class.table_name.singularize + "_id"
      tag_prefix = "#{ model.class.class_name.underscore }[#{ join_collection_name }_attributes][#{ collection_index }]"

      if join_item = model.send(join_collection_name).find(:first, :conditions => { relationship_name => related_item.id })
        output << hidden_field_tag("#{ tag_prefix }[id]", related_item.id)
        output << check_box_tag("#{ tag_prefix }[_delete]", false, true)
        output << hidden_field_tag("#{ tag_prefix }[_delete]", true)
      else
        output << check_box_tag("#{ tag_prefix }[#{ relationship_name }]", related_item.id, false)
      end
    end
  end

Drop that in a helper, and then your form code becomes:

  <%- SuperPower.all.each_with_index do |super_power, index| -%>
    <label>
      <%= has_join_relationship(@villain, :gifts, super_power, index) %>
      <%= super_power.name %>
    </label><br />
  <%- end -%>

Much nicer … although I’m not sold on the name has_join_relationship. Any suggestions?

Making CRUD less "Cruddy", one step at a time

One of the great “new” features of Rails (as of 2.3) is accepts_nested_attributes_for, allowing you to build cross-model CRUD forms without “cruddying” your controller. There are some great examples out there about how to do this, but I’d like to walk thorough a particular use case—managing the “join” records in a has_many :through relationship.

Consider the following database schema:

class Villain < ActiveRecord::Base
  has_many :gifts
  has_many :super_powers, :through => :gifts
end

class Gift < ActiveRecord::Base
  belongs_to :villain
  belongs_to :super_power

  validates_uniqueness_of :super_power, :scope => :villain_id
end

class SuperPower < ActiveRecord::Base
  has_many :gifts
  has_many :villains, :through => :gifts
end

In our dataset, there are a relatively small number of super powers which we wish to present as a list of checkboxes on the villain management form. Checking/unchecking the boxes will manage the gift records for that villain, effectively managing the list of super powers available to the baddy.

To get started, we need to add accepts_nested_attributes_for :gifts to the Villain class—piece of cake. To complete the implementation, we need to change the params hash that our form generates. Let’s review the cases that we need to support and the associated params hash format needed to implement the correct functionality.

The first case is a super power record that is not currently associated with the villain. Here, the UI should display an unchecked checkbox. If we check it and submit the form, a gift record should be created linking the villain with the super power, making this bad guy that much badder. Here is an example of the params hash we should be sending to accomplish this:

  {
    'villain' => {
      'name' => 'Lex Luthor',
      ...
      'gifts_attributes' => {
        1 => { 'super_power_id' => 5 },
        2 => { 'super_power_id' => 7 },
        ...
      }
    }
  }

The alternate case is a super power this villain already possesses. In this instance, the UI should display a checked checkbox, and if we uncheck it, the existing gift record should be deleted, diminishing the villain’s capacity for evil. And our params hash needs to look like:

  {
    'villain' => {
      'name' => 'Two-Face',
      ...
      'gifts_attributes' => {
        1 => { 'id' => 101, '_delete' => true },
        ...
      }
    }
  }

Note that the keys for the gifts_attributes hash are arbitrary; we can use any scheme to generate unique keys for the hash.

So how can we craft a form that sends the params hash that Rails wants to see? Here’s my implementation:

  <%- SuperPower.all.each_with_index do |super_power, index| -%>
    <label>
      <%- if gift = @villain.gifts.find_by_super_power_id(super_power.id) -%>
        <%= hidden_field_tag "villain[gifts_attributes][#{ index }][id]", gift.id %>
        <%= check_box_tag "villain[gifts_attributes][#{ index }][_delete]", false, true %>
        <%= hidden_field_tag "villain[gifts_attributes][#{ index }][_delete]", true %>
      <%- else -%>
        <%= check_box_tag "villain[gifts_attributes][#{ index }][super_power_id]", super_power.id %>
      <%- end -%>

      <%= super_power.name %>
    </label><br />
  <%- end -%>

If the gift is detected, the villain has the super power, and we handle our second case from above, using the checkbox / hidden field hack Rails employs in the check_box helper method to make sure a value is sent whether or not the checkbox is checked. The else block handles the other case, setting up our params hash to create the gift if the checkbox is checked.

This works, but we probably don’t want to copy and paste that code everywhere we use this pattern. How can we reuse this in a DRY fashion? Here’s a helper method that encapsulates the logic:

  def has_join_relationship(model, join_collection_name, related_item, collection_index, options={})
    returning "" do |output|
      relationship_name = options[:relationship_name] || related_item.class.table_name.singularize + "_id"
      tag_prefix = "#{ model.class.class_name.underscore }[#{ join_collection_name }_attributes][#{ collection_index }]"

      if join_item = model.send(join_collection_name).find(:first, :conditions => { relationship_name => related_item.id })
        output << hidden_field_tag("#{ tag_prefix }[id]", related_item.id)
        output << check_box_tag("#{ tag_prefix }[_delete]", false, true)
        output << hidden_field_tag("#{ tag_prefix }[_delete]", true)
      else
        output << check_box_tag("#{ tag_prefix }[#{ relationship_name }]", related_item.id, false)
      end
    end
  end

Drop that in a helper, and then your form code becomes:

  <%- SuperPower.all.each_with_index do |super_power, index| -%>
    <label>
      <%= has_join_relationship(@villain, :gifts, super_power, index) %>
      <%= super_power.name %>
    </label><br />
  <%- end -%>

Much nicer … although I’m not sold on the name has_join_relationship. Any suggestions?

April 27, 2010, Now Writing About Cucumbers

Top Story

For me, the top story is still Rails Test Prescriptions on sale, and my discussion yesterday of the raffle for the old Lulu customers.

Book Status

Now re-doing the Cucumber chapter, which was written long enough ago that it didn’t consider tags. Cucumber has had approximately seventy-million releases in the interim, so there’s some writing to do. This is the first chapter where I’m adding Rails 3 setup instructions, which will eventually go everywhere in the book, of course.

Tab Dump

Have to say, RVM support in RubyMine is potentially really cool.

Kent Beck would like to analogize goat farming and software development. I’ve heard worse.

I know you all have been following this story closely, so you’ll be pleased to know that you can now bring your iPad into Israel with impunity. Again, carrying two of them with the roman numerals I to X as wallpaper.

Macworld has released an epub-formatted, iBooks compatible, user guide to the iPad.

Webrat bumped it’s version to 0.7.1.

I frequently complain that there’s no good visualizer for git repositories. This fork of GitX looks like it comes pretty close, though.

Finally

I’m pretty sure I disagree with some of this article by Josh Clayton talking about integration tests being more useful than unit tests. He’s probably right about integration tests being more useful for ultimate correctness, but that’s not everything that TDD is about. Unit tests are critical for the development process, and writing great code in the moment of development, and for supporting design changes and refactoring. Unit and integration tests have two complementary functions, just because they cover the same code doesn’t mean they are redundant.

Filed under: Cucumber, Git, iPad, Kent Beck, RailsRx, RubyMine, testing, Webrat

Shutting down Usage Report!

Oops, I did it again. Abandoning an idea after I invested quite some time on it even before it was released. This morning I had a good talk with my wife on where I should go with this project. It’s always hard to call it quit after spending quite some time on something but I changed once too many direction in the last two month that I don’t have a product to put on the market yet and to reach a point where I could sell something would take me a few more months (working on Mondays only) to get there…So I think I will call it a day on this project. Now that doesn’t make me feel great as I start having a track record of starting things and not finishing them…at least for project I want to commercialize. I seem to have an easier time to create stuff Continue reading “Shutting down Usage Report!”

Making CRUD less "Cruddy", one step at a time

One of the great “new” features of Rails (as of 2.3) is accepts_nested_attributes_for, allowing you to build cross-model CRUD forms without “cruddying” your controller. There are some great examples out there about how to do this, but I’d like to walk thorough a particular use case — managing the “join” records in a has_many :through relationship.

Consider the following database schema:

class Villain < ActiveRecord::Base
has_many :gifts
has_many :super_powers, :through => :gifts
end

class Gift < ActiveRecord::Base
belongs_to :villain
belongs_to :super_power
validates_uniqueness_of :super_power, :scope => :villain_id
end

class SuperPower < ActiveRecord::Base
has_many :gifts
has_many :villains, :through => :gifts
end

In our dataset, there are a relatively small number of super powers which we wish to present as a list of checkboxes on the villain management form. Checking/unchecking the boxes will manage the gift records for that villain, effectively managing the list of super powers available to the Continue reading “Making CRUD less "Cruddy", one step at a time”