Messing With Ruby

I’m overriding concatenation here, which completely messes up Ruby’s interactive console:

tsadok$ irb
irb(main):001:0> class String; def +(s); "no"; end; end
=> nil
irb(main):no:0> yes
irb(main):no:0> 1 + 1
irb(main):no:0> puts "hi"
irb(main):no:0> ^D
NameError: undefined local variable or method `nononono' for main:Object
no
irb(main):no:0> ^D
tsadok$
Advertisements

Lockdown

Apple’s iPad has been drawing a lot of criticism from developers lately for its locked-down platform. Any software that a third-party wants to develop for the iPad has to go through Apple’s approval process, which has proven to be a fickle arbiter. What’s interesting about the iPad is that it uses the exact same system as the iPhone, which didn’t draw nearly as much criticism. So what gives?

The answer is that it depends on whether you see the iPad as a device or a computer. If you compare the iPad to older devices like, say, the Nintendo Entertainment System, or just about any game console, for that matter, the iPad is remarkably open. The iPad’s development tools are free to download, as compared to Nintendo’s, which cost thousands of dollars. In addition, distribution for the iPad is trivial (it’s just software – no expensive cartridges to manufacture). As far as approving and allowing content goes, Nintendo has always had the exact same approach as Apple does now. There’s no question that Apple’s platform is much more open than Nintendo’s (or Sony’s, or Sega’s).

Of course, if you look at the iPad as a general-purpose computer, the picture changes dramatically. In that case the iPad is the most locked-down, restrictive computer in history. Even Microsoft never imposed the kind of control and restrictions on software development and distribution that Apple has. It’s virtually impossible to get any software on your iPad without going through Apple first. Could you imagine if you had to get Microsoft’s approval to install, say, tax software? Or if Microsoft had to approve of Firefox in order for it to be installed on your machine? Not that I’m praising Microsoft’s openness here – they have antitrust allegations to worry about – but compared to the iPad, Microsoft Windows is a haven of freedom. Third party developers working on the iPad have to say a little prayer that their app doesn’t offend Apple’s delicate censors, or worse, compete with Apple’s functionality (since Apple doesn’t allow software that offers “duplicate functionality”). If Apple does, for whatever reason, turn down their app, developers have no recourse or alternate distribution method. What Apple says, goes.

So which is the iPad – device or computer? The iPad itself makes clear that the distinction is arbitrary. A device is simply a locked-down computer, something that device makers have known for a long time. It mostly has to do with marketing; devices are usually designed (and locked-down) for a specific application (like gaming or e-reading), while computers (and their operating systems) are open-ended and multi-functional.

The iPad lives in a weird grey area. It has almost all the capabilities of a full-fledged computer, but Apple has chosen to treat it like a device. That makes it much easier to use than the Mac (in the same way that the NES was vastly easier to use than the C64). But it also raises all the control freak issues that Apple is infamous for.

I think the main worry among developers is that other manufacturers might follow Apple’s lead. That the trend will be to move away from general-purpose computers and towards locked-down, proprietary devices and operating systems. If that’s where we’re going, then developers (particularly independent developers) could be in serious trouble. This also extends to the web, I’m afraid: if consumers are only able to use locked-down, proprietary browsers, then net neutrality is once again at risk (this time from the client side), which will primarily affect independent developers and small businesses.

Is the iPad indeed this sort of threat? Only time will tell.


My Next App

I’ve been thinking about how I would build my next killer web application, surveying what the best options are out there to get it up and running as cheaply, quickly, and cleanly as possible. So here are my thoughts on what technologies I would use:

Application Framework
Ruby on Rails 3
Application Hosting
Heroku
Database *
MongoDB OR PostgreSQL
Front-End
Haml
Sass
Compass (Blueprint CSS)
Javascript Framework
JQuery
Asset Server
Amazon S3 + Cloudfront
Source Control
Git (possibly hosted on Github)
Offsite processing
Amazon EC2

* Heroku only provides Postgres “out of the box”.

Let me know what you think!


Understanding Rails Plugins

It can be hard to write plugins for Ruby on Rails.  Here is a “simple” example to extend ActiveRecord (slightly modified), from the official Ruby on Rails site:

module Yaffle
  def self.included(base)
    base.send :extend, ClassMethods
  end

  module ClassMethods
    def acts_as_something
      send :include, InstanceMethods
    end
  end

  module InstanceMethods
    def to_yaffle
      "You are a Yaffle!"
    end
  end
end

ActiveRecord::Base.send :include, Yaffle

This code is downright baffling, if, like me, you’re not intimately comfortably with the world of Ruby metaprogramming. It’s easy enough to copy and paste your methods under ClassMethods and InstanceMethods, as the tutorial suggests, and hope things work, but it’s hard to understand what’s going on here. And I like to understand these things.

For example, are the module names ClassMethods and InstanceMethods special to Ruby, or are they arbitrary? It’s hard to tell.

And what the heck does this code mean?

def self.included(base)
  base.send :extend, ClassMethods
end

What is “base” referring to here? What is “extend” (as opposed to “include”)? This code was a complete mystery to me.

The first step was to figure out that last question – what “extend” meant. I found this very useful writeup by John Nunemaker explaining the difference. Essentially, when you “include” a module in a class, the objects created by that class have all the methods of that module (instance methods). When you use “extend” instead, the methods apply to the class (class methods).

And we can see the last line is forcing ActiveRecord::Base to “include” the module:
ActiveRecord::Base.send :include, Yaffle

So far so good.

At this point, any methods in Yaffle should be added to the class ActiveRecord::Base. But there’s a caveat: when you package your methods in submodules, however, none of those methods are included or extended or whatever. They are ignored (to the best of my knowledge).

It turns out that there is a hook you can use when you include a module in a class – you can define an “included” method in the module that is called when a class actually includes the module. It is roughly akin to a constructor for the module.

So after all that, this is how the plugin above works: The first thing that happens is that you include the module in ActiveRecord::Base. That action does one thing – it triggers the “included” method of the module. That then calls extend on ActiveRecord::Base, this time directly targeting the submodule “ClassMethods” which does contain methods. So at this point all your class methods are loaded up into ActiveRecord::Base.

What about the instance methods? In most cases, you want to control which subclasses of ActiveRecord::Base include the instance methods. So you let the user explicitly call a special class method (in this case, “acts_as_something”) that has the class call “include” on the InstanceMethods submodule, which in turn provides the instance methods for objects made from that class.

Anyway, this seems to me to be overly complicated. Calling “include” in order to indirectly call “extend” doesn’t make much sense to me. This code works perfectly well:

module Yaffle
  #class methods
  def acts_as_yaffle
    send :include, InstanceMethods
  end

  module InstanceMethods
    def to_yaffle
      "You are a Yaffle!"
    end
  end
end

ActiveRecord::Base.send :extend, Yaffle

Much more concise, in my opinion. If you don’t like the lack of symmetry between the class methods and the instance methods (*cough* OCD), you can always package the class methods in a submodule as before, and change the last line to ActiveRecord::Base.send :extend, Yaffle::ClassMethods. Same thing, and just as easy to understand.

Whew, that’s all for now! Long post – I hope it helps someone :-)

–Daniel

p.s. I know that gems are replacing plugins, but the issues are exactly the same.