Posts from Late to the Party...

Aptana Studio 1.0 Released!

Wow it's been quite a while since I last posted. In the meantime I've been working hard on RDT and RadRails at Aptana and the job has been great. I'm very lucky to have found a way to work on the open source projects I love full-time.

In that vein I'd like to announce that we've released the 1.0 of Aptana. This release is important for a number of reasons. First, we think the product is good-to-go for everyone. Second, we're announcing a Pro version of the IDE. This version is for users who want to support the project so we can keep going, or who want the extra features and perks that come with a license: nightly build access, priority support, IE Javascript Debugger, SFTP/FTPS support and all sorts of other goodies. The support, nightly builds, SVN access also apply to the other components of what we're now calling Aptana Studio: RadRails, iPhone, PHP and AIR. So if you want to be on the bleeding edge of RadRails/RDT development you'll probably want to look into getting a license.

Keep in mind that while we do offer a pro version for those who'd like to support us or the extra stuff, we are still shipping the same codebase (minus the commercial features) as an open source project under GPL. And we plan to remain an open-source company with an open source product. Here's hoping that model will work for us!

Tags: programming, personal, rails, aptana, rdt, radrails, ide

Aptana backs RDT, hires me

I'm proud to announce today that Aptana has hired me to work full-time on RDT, RadRails and integrating that work with their existing Aptana IDE which focuses on CSS, HTML and Javascript.

This announcement means that RDT will now have commercial backing (but will remain open-source and free!) and that you should see RDT and RadRails move forward at a much quicker pace than in the past.

This is also great news for RadRails users and Rails developers in general as integrating the two will give you code completion, outlines, help, debugging and much more across the entire stack - from model to controller to the HTML, ruby code, CSS and Javascript that make up your views.

Tags: programming, personal, web_design, ruby, rails, aptana, commercial, sponsor, rdt, radrails, ide, eclipse, announcement

RadRails dying off?

Kyle Shank of the RadRails team has mentioned that he and Matt are both working on a web startup. Looks like the priority of RadRails is lower for them - after all, RadRails doesn't make money.

It's a shame that this sort of thing happens, but I can't say I'm all that surprised. I've been working on RDT for nearly 4 years now and I can definitely say that people just don't pay for free things. You can beg for donations, but you shouldn't expect them. Given the amount of time and effort - and the sheer number of downloads - it just doesn't pay the bills to run an open source project that passively solicits donations. I estimate the per-user donations for RDT to be at about 1.4 cents*. And if we take out the one large donor? .00071 cents per user.

That doesn't quite cut it for rent and food, unless of course you get the entire world to use your product.

I wish Kyle and Matt well and hope that others from the community step forward and help lead the project onward.

Update: Looks like RadRails isn't dying off - it's getting new ownership.

* This estimate assumes we count RadRails users as RDT users, because RadRails contains RDT. It also uses just the raw zip downloads from Sourceforge for both projects. There is a large number of users we are not counting here who have downloaded via Eclipse's update site mechanism, and who use RDT from other distributions available.

Tags: programming, personal, ruby, java, rdt, radrails, open, source

SVN and SVK

Dear Lazyweb:

I would like to take an existing SVN repository of a project (like say, Typo), check out a tagged version, create local modifications and save the modified version in a local/home SVN repository(my blog). Later, I'd like to sync up the local version to a new tagged version of the original repository (Typo), handle any merges locally and then check in the result into my local repository again. Rinse and repeat, ad infinitum.

Is SVK the right job for this? Has anybody done something like this? Essentially its the equivalent of creating a branch on a SVN repository but having that branch in an entirely separate SVN repository instance. I don't have experience with this, so I'd greatly appreciate any pointers anybody out there might have.

Tags: programming, personal, svn, svk, scm, repository, development, subversion

Steve Jobs says Apple would love DRM-free music

Wow. Steve Jobs has posted an open letter on Apple's website essentially calling for the big four music companies to abolish their restriction that music sold through iTunes be done with DRM. This combined with hints from Bill Gates that he'd love to see DRM dropped as well, may actually move the discussion to the right players - the music companies themselves. (After all, not only have they pushed for DRM, they actually blackmailed Microsoft into giving them money for every Zune sold!)

It's nice to see the big technology companies are starting to push for the right thing - even if it is because the hassle of keeping up the DRM and recent court battles have pushed them into this position.

Tags: personal, apple, itunes, ipod, music, drm

RDT gets Refactoring support

Well the cat is out of the bag: Mirko Stocker and his cohorts have committed their refactoring support to RDT's Subversion repository.

This means we'll be able to roll out 0.9.0 with this support. Right now we're working to get it integrated into the build process, so that it will begin showing up in our new builds. I'm pretty excited myself, because I've had little chance to try out their work.

This refactoring support joins other recent work in RDT which allows us to do some occurence marking of variables, code completion and other exciting features (thanks Jason!). There's certainly a long way yet to go to get the tools polished - for instance we still have a hard time doing code completion (or much else) on a file which is being edited while the syntax is temporarily incorrect (the JRuby parser is great, but not so forgiving) - but we're constantly marching forward.

Look for 0.9.0 to come out sometime this month (we're aiming for the 15th)!

Tags: programming, personal, ruby, java, refactoring, eclipse, ide

Rosie our cleaner

So, I was lucky enough to get a Roomba Discovery for Christmas from my parents. My wife and I both love her and we've decided she'd be named Rosie.

My father also grabbed a strange TV remote alarm, which is supposed to learn your remote's infrared signals for turning the TV on and off and then send that signal when the alarm goes off (thereby waking you up by turning your TV on). It also should be able to learn the Roomba power/clean command.

But it won't.

Or at least it says it's got it by flashing it's little red lights, and when the alarm goes off those wonderful little lights go off again - but no movement from Rosie. I can't get it to actually learn the TV remote control either, so I guess it's no fault of Rosie.

So, dear lazyweb, are there any other cheap solutions for setting Rosie up on a timed schedule to clean my house while I'm away? Or do I have to buy some accessories from iRobot (which will be more expensive)?

Tags: personal, rosie, jetsons, roomba, irobot, vaccuum, cleaner, remote

Patterns in Ruby: Decorator revisited

A while back I wrote an article describing some possible ways to implement a Decorator pattern in Ruby. I've stumbled across several mentions of yet another idiom used so often in the Rails codebase that they've extracted it into the latest ActiveSupport. That idiom is alias_method_chain, and it's a good example of a decorator implementation.

This idiom is a codified example of using the alias approach I briefly mentioned in the earlier article. In that article we aliased the original implementation with anew name, and set up a new implementation of our method (often delegating to the original) with the original's name. A quick example makes this clear:

class Window
  def draw
    # do some drawing here...
  end

  # some code...

# creates a 'copy' of draw method, but gives it
# the name/selector 'original_draw'
  alias :original_draw :draw 

  def draw
    draw_vertical_scrollbar
    original_draw
  end
end

alias_method_chain

In Rails 1.2 (ActiveSupport specifically), the Rails core team found many instances of this pattern and codified a new method on the class Module, alias_method_chain. This class-level method encapsulates this pattern of wrapping existing methods with additional behavior.

Here's a specific example, showing how they would wrap rendering with layouts:

alias_method :render_with_no_layout, :render
alias_method :render, :render_with_a_layout

In this small code snippet, they are creating a small chain of methods to wrap the existing render behavior. Now calls to render will be routed to render_with_a_layout and then on to the original render implementation (which is now aliased to render_with_no_layout). So they coded up alias_method_chain which simply does the wrapping for them (using naming conventions):

class Module
  # Encapsulates the common pattern of:
  #
  #   alias_method :foo_without_feature, :foo
  #   alias_method :foo, :foo_with_feature
  #
  # With this, you simply do:
  #
  #   alias_method_chain :foo, :feature
  #
  # And both aliases are set up for you.
  def alias_method_chain(target, feature)
    alias_method "#{target}_without_#{feature}", target
    alias_method target, "#{target}_with_#{feature}"
  end
end

Please note that if they were to replace their existing two calls to alias_method above, they would need to tweak the naming a little (the methods would become render_without_layout and render_with_layout as opposed to render_with_no_layout and render_with_a_layout respectively).

Tags: programming, ruby, rails, decorator, patterns, alias, chain, method

Free Annual Credit reports

Since I talk about real estate with some regularity I thought I should share some personal experience around getting your credit report.

First things first, get your credit report when you are beginning to think about buying a home. If you have a low score you will need to address any inaccuracies or problems the report brings up or you won't get approved by a mortgage lender (or you will get charged a higher rate). In fact, you should probably get one annually anyways to avoid higher rates on credit cards and loans and to help avoid landlords from renting to you, etc.

Next, if you want a free credit report, use www.annualcreditreport.com. The federal government passed legislation allowing you to request a free report every 12 months and this is the site to do so. Those of you who try the others (like www.freecreditreport.com) beware - Read the fine print and you'll see that getting the report will sign you up for a subscription of some sorts that will charge you on a monthly basis until canceled. They typically say that if you cancel within 30 days you won't be charged. I'm not alleging that they're outright lying, but I canceled within 3 days and was charged for 4 months and was never able to recover the charges. Your mileage may vary, but why bother?

Tags: real_estate, real, estate, credit, report, free, annual

Real Estate predictions for 1Q 2007

I occasionally write columns on real estate here and given the latest news I thought I should post an update. CNN and other major outlets have all been reporting the news for some time - the housing market is stagnant and the bubble has burst.

They're right. The bubble has officially burst and those of you who got into the speculative game in Florida, California, Las Vegas or some of the other areas who saw incredible gains are now paying the price. I don't have much first-hand knowledge of those markets, but as I mentioned before they were well overpriced, they continue to be overpriced and I don't imagine that we'll see any appreciable gains (real gains, adjusted for inflation) in home prices in those markets in the coming quarter, year, and probably not in the next five years. Definitely nothing like those ridiculous 20%+ YOY gains of the past. Gains of that nature are unsustainable in the long term and we're seeing a natural correction in the market.

As for the secondary and tertiary markets, I'm predicting that as a result of the general malaise felt by the aftershocks of the major-market bubble-burst prices will remain at current rates or, in some markets, achieve single-digit gains. Investment in homebuilders and other real estate related holdings will be dropping as returns disappoint (no more flipping properties for a 100% profit!?) and this will ripple into some of these smaller markets holding down price growth.

While this is bad short term news for many owners who bought into the market in the past couple years, assuming you bought into the market for the right reasons (not to flip, the real estate equivalent of day trading), time frame (more than 5 years), and with the right financing (fixed rate mortgages that fit in your budget) you should feel comfortable holding onto your holdings. Those of you who hold ARMs may want to consider dropping properties and even forfeiting deposits on properties in construction which were intended to be sold for profit from appreciation.

Tags: real_estate, real, estate, homes, house, buying, price, predictions

Patterns in Ruby: Observer Pattern

Another easy to implement pattern in Ruby is the Observer pattern. The Observer pattern is a publish/subscribe mechanism where an objects can register to be notified of state changes (or observe changes) on another observed object. This pattern may often become refactored into a more general event framework (where objects fire events off into queues to which there are listeners subscribed).

The basic implementation

Here's a look at a simple ruby implementation:

class Observable
  def initialize
    @listeners = []
  end

  def register_listener(listener)
    @listeners << listener
  end

  def unregister_listener(listener)
    @listeners.remove(listener)
  end

  def run
    notify_listeners("Hello!")
  end

  protected
  def notify_listeners(event)
    @listeners.each {|l| l.notify(event) }
  end
end

class Listener
  def initialize(observable)
    observable.register_listener(self)
  end

  def notify(event)
    puts "Notified of '#{event}'"
  end
end

observable = Observable.new
listener = Listener.new(observable)
observable.run                        #=> Notified of 'Hello!'

The pattern itself in this form is pretty general. So general, in fact, that there is a module mixin of Observer inside the standard ruby library (observer.rb). There's some good documentation in there, and it provides a simpler path to this implementation:

require "observer"

class TV
  include Observable
  def initialize(channel)
    @channel = channel
  end

  def up
    @channel += 1
    changed
    notify_observers(@channel)
  end
end

class ChannelWatcher
  def initialize(tv)
    tv.add_observer(self)
  end

  def update(channel)
    puts "Changed channel to #{channel}"
  end
end

tv = TV.new(160)
watcher = ChannelWatcher.new(tv)
tv.up                              #=> Changed channel to 161

Please be aware that the API is a little different from my initial example.

Moving towards events

Both of the above implementations rely on a generic observer pattern, but the Observer pattern can often evolve into a simple event mechanism. The difference is that instead of firing a generic event object via a generic notify method, the move towards events uses unique method names, and filters events to notify only those interested in the type of event occurring.

Let's see a TV example where we move towards a more specialized event firing version of the pattern:

require "observer"

class TV
  def initialize(channel)
    @channel = channel
    @listeners = []
  end

  def add_listener(listener)
    @listeners << listener
  end

  def up
    @channel += 1
    @listeners.each {|l| l.channel_increased(@channel) }
  end

  def down
    @channel -= 1
    @listeners.each {|l| l.channel_decreased(@channel) }
  end
end

class ChannelUpWatcher
  def initialize(tv)
    tv.add_listener(self)
  end

  def channel_increased(channel)
    puts "Changed channel to #{channel}"
  end

  def channel_decreased
    # do nothing...
  end
end

tv = TV.new(160)
watcher = ChannelUpWatcher.new(tv)
tv.up                              #=> Changed channel to 161

In this instance we can fire off events for surfing the Tv upwards or downwards (in channels) separately, though we still register listeners into a generic pool, and listeners are expected to contain both event methods. Variations of this can be done to register listeners into sub-groups upon registration by calling unique methods names for each registration, or by passing in a Filter object that can be used to filter to the events the listener cares about. In filtering at registration we can avoid listeners having to implement every event firing method and minimize the number of events fired off.

One illustration of this observer based event model is the Java Swing events API.

Extending the pattern towards this event firing mechanism even further we'd likely move into using queues and firing events off to the queues themselves rather than directly to observers. Observers would then become subscribers to the queues.

Using blocks and procs

The Observer pattern as described above is the typical pattern followed in most languages without closures, lambdas or functors. In Ruby we have the ability to throw around closures/blocks so we can take the pattern a little further.

Let's revisit our original implementation, but let's add the ability to register the callback function to be performed upon notification.

class Observable
  def initialize
    @listeners = []
  end

  def register_listener(&blk)
    @listeners << blk
  end

  def unregister_listener(&blk)
    @listeners.remove(blk)
  end

  def run
    notify_listeners("Hello!")
  end

  protected
  def notify_listeners(event)
    @listeners.each {|l| l.call(event) }
  end
end

class Listener
  def initialize(observable)
    observable.register_listener {|event| "Notified of '#{event}'"}
  end
end

observable = Observable.new
listener = Listener.new(observable)
observable.run                        #=> Notified of 'Hello!'

This is the model illustrated in the Tk bindings for Ruby - you can see examples of usage in Programming Ruby's section on binding events in Tk. That section and their section on blocks as closures begin to broach the how closures capture the context in which they were defined - allowing for some very interesting and complex behavior in using blocks and procs as event or observer callbacks (allowing you to refer to objects available at the scope of the block definition, not when the callback/block execution occurs).

Tags: programming, ruby, patterns, observer, design

Patterns in Ruby: Singleton Pattern

The Singleton pattern is the black sheep of the pattern family. It was easy to grasp, developers everywhere applied it liberally, and an inevitable backlash came against its overuse.

I won't make any judgments or reccomendations on when to use it - but I will show you just how easy it is to apply in Ruby.

The literal translation of the pattern is to create a class level instance method and to hide the new method.

    
class Example
  def initialize
    # do something?
  end

  def self.instance
    return @@instance if defined? @@instance
    @@instance = new
  end
  private_class_method  :new
end

puts Example.instance.object_id  #=> 21783380
puts Example.instance.object_id  #=> 21783380

This example gives you the basic idea, but it doesn't cover many cases you'd like to handle, like cloning or duping the singleton. It also doesn't hide the class level allocate method, which means a sneaky coder could still create another instance through some hacking. Lastly, it's not thread safe.

Luckily, Ruby already provides a module for making classes singletons. It's in the standard library, inside 'singleton.rb'. Here's how you use it:

    
require 'singleton'

class Example
  include Singleton
end

This module will do the same thing as my example above but will also handle hiding allocate, overriding the clone and dup methods, and is thread safe. The library file itself contains a bunch of examples of its usage, and those interested should definitely read through it.

One thing to note about these implementations is that the instance method takes no arguments, so none are passed on to the object's constructor. This makes sense because the first time instance is called those will be the arguments used for this global instance. Setters are typically more appropriate for most singletons.

Since singletons are global in nature setters should be at the class level. As an extra bonus here's the implementation of the class level attr_ methods to generate the vanilla getter/setter methods (stolen from Rails).

class Class # :nodoc:
  def cattr_reader(*syms)
    syms.flatten.each do |sym|
      class_eval(<<-EOS, __FILE__, __LINE__)
        unless defined? @@#{sym}
          @@#{sym} = nil
        end

        def self.#{sym}
          @@#{sym}
        end

        def #{sym}
          @@#{sym}
        end
      EOS
    end
  end

  def cattr_writer(*syms)
    syms.flatten.each do |sym|
      class_eval(<<-EOS, __FILE__, __LINE__)
        unless defined? @@#{sym}
          @@#{sym} = nil
        end

        def self.#{sym}=(obj)
          @@#{sym} = obj
        end

        def #{sym}=(obj)
          @@#{sym} = obj
        end
      EOS
    end
  end

  def cattr_accessor(*syms)
    cattr_reader(*syms)
    cattr_writer(*syms)
  end
end

Now we can create a more realistic singleton:

    
require 'singleton'

class JimmyGrimble
  include Singleton
  cattr_reader :boots
  cattr_accessor :football
end
Tags: programming, ruby, design, patterns, singleton

Patterns in Ruby: Decorator Pattern

I've been a fan of the work that was done by the Gang of Four on Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series), Martin Fowler's Refactoring: Improving the Design of Existing Code and the bridge book by Joshua Kerievsky, Refactoring to Patterns. I haven't seen a lot of information out there on how Ruby changes the game: ways to apply these patterns using Ruby idioms, new patterns that show up, patterns that fall away. So I've decided that as I go along I'll try and document the new twists as I see them.

Today's article is the twist on the Decorator pattern. The Decorator pattern wraps the original object in a new one which will add functionality to some of the methods and then delegate to the original object. The prototypical example is decorating a window object.

A Decorator Example

public interface Window {
  public void draw();
}

In Java, we'd probably use a decorator to add scroll bars.

public Class VerticalScrollWindow implements Window {
  private Window window;

  public VerticalScrollWindow(Window window) {
    this.window = window;
  }

  public void draw() {
    drawScrollBar();
    window.draw();
  }
}

The basic concept is that we'll usually want to be adding some behavior around a particular method call to extend behavior, while retaining the same interface.

Ruby

In ruby, we have a number of options to achieve this pattern. First, let's define our original Window in Ruby code:

class Window
  def draw
    # do some drawing here...
  end
end

Translating the pattern literally

Given ruby's duck-typing nature, we could easily create a VerticalScrollWindow that wraps the original Window when we create the original window object, and pass that around. In fact we could patch only the single method and add a method_missing implementation that always delegated to the original Window.

class VerticalScrollWindow

  def initialize(window)
    @window = window
  end

  def draw
    draw_vertical_scrollbar
    window.draw
  end

  def method_missing(method, *args, &block)
    @window.send(method, *args, &block)
  end
end

This approach could be evolved to a much higher level using method_missing tricks and dynamic modifications. One could create a generic Proxy class which took a target class and intercepted all method calls, executing pre- and post- method blocks for specific methods. I'll leave that as an exercise for the reader for now...

Using Alias

Another option we have is to "monkeypatch" the original class (or specific instances of it). The idea here is to rename the old implementation of the method, insert a new implementation and have that refer to the renamed original.

class Window
  def draw
    # do some drawing here...
  end

  # some code...

  alias :original_draw :draw
  def draw
    draw_vertical_scrollbar
    original_draw
  end
end

Building pre and post hooks

Our last option is to build pre and post method hooks into the original class definition. Obviously, this approach requires the original class' author to explicitly build in callback hooks. This approach can be found in Capistrano (you can add tasks which get executed before or after well known tasks), or in ActiveRecord (lifecycle type callbacks - i.e. before_save, after_destroy).

This option is a bit more advanced and differs in the approach taken. To learn how Capistrano does it, dive into capistrano/actor.rb, line 118. Each task is defined as a method which explicitly calls before and after methods if they exist.

For ActiveRecord, please refer to activerecord/callback.rb. ActiveRecord goes a little further by allowing class level methods to add pre- and post- code blocks to be executed which will be inherited down the class hierarchy. They also allow instance level methods to be defined for each hook which would not be inherited.

Update: Francisco points us to a great article on Decorator showing some other possible Ruby implementations.

Tags: programming, ruby, patterns, idioms

Getting Capistrano to play with Oracle on Red Hat Enterprise Linux

I've been working on an internal project that connects to a legacy Oracle database. The process of getting the driver and client set up was a bit annoying, but I'd done it before and I managed to get through it again. (For those of you braving it, be sure you don't include a trailing slash on your ORACLE_HOME environment variable).

The Oracle driver and client are like most Oracle products - unnecessarily complex. One of its requirements is that you set an ORACLE_HOME environment variable. I'd managed to do this and add it to my .bashrc file so that when I manually logged into the app server via SSH I could get the rails app up and running. The problems was that remotely deploying via Capistrano was causing errors - anything that loaded the rails framework, and therefore the Oracle driver, would die with a stack trace about an undefined method. Luckily in my past troubles with the driver, I knew that meant that it didn't have the ORACLE_HOME environment variable set.

In case anyone else is running into this (or I do again), here's a patch:

First, add the following to your deploy.rb recipe:

set :use_sudo, false

Next, modify your capistrano/actor.rb file to force all run commands through bash with BASH_ENV set:

alias :run_without_env :run
def run(cmd, options={}, &block)
  cmd = <<-CMD
  BASH_ENV=.bashrc /bin/bash -c -- "#{cmd}"
  CMD
  run_without_env cmd, options, &block
end

These two changes will force the environment to be loaded and also make all command be run as the original user (which should have rights to do so).

You can read more about it on the Google group for Capistrano

Tags: programming, ruby, raisl, deployment, capistrano, oracle, redhat, linux

Google Summer of Code Swag

A box

Hmm what is this mysterious package?

GSoC T-shirt

It's my Google Summer of Code T-Shirt! That feeling you're having - pure jealousy.

Tags: programming, personal, gsoc, google, summer, code, tshirt, swag

Note to Subaru Marketing: Part Two

Dear Subaru Marketing:


Hello again! It's been two weeks since we last spoke, and I miss you already. As I mentioned then, I appreciate your efforts to market your vehicles in my area and your attempts at regional advertisements. Since that time you've introduced a new ad referring to I-95. While I'm no expert on geography, I'm quite sure that I-95 runs by New York City and just barely touches a corner of our state - the opposite corner from where you are advertising. Here, I've provided a helpful illustration:

Rochester is far away from I-95
I-95 is the red winding line. Rochester is located at the tip of the arrow.

Perhaps you are trying to save money by referring to a 1,927 mile long interstate highway that spans the entire east coast and running the ad in all those markets as "local". While this is ingenius, to reach Western New York you would likely want to reference I-90, or as we New Yorkers call it, the Thruway. It has the added bonus of stretching the entire continental US's northern states.

But those are really just semantics, after all you've discovered a way to make "regional" ads that span a huge swath of the country. Maybe you can subdivide the audience into similar niches beyond region by referencing very specific interests. Targetting such a niche would make the listener feel unique and passionate about your product. I suggest using in-jokes about Seinfeld, or maybe sharply dividing the audience and creating passionate responses by professing a love of puppies and babies.


Sincerely,
A member of your target audience

Tags: personal, rochester, subaru, marketing, promotion, regional, advertising

Multi-stage deployments with Capistrano

I'm seeing a recurring theme in my feed reader today - multi-stage deployments with Capistrano. It seems that those of us working in larger companies have all hit the same issue - we have some form of user acceptance, staging or other system which mirrors the production in configuration; and we want to use Capistrano to deploy to it as well as production. I know that my current company has a 5-stage process for pushing out releases (we're in financial services, and very paranoid. Whether they'd ever accept Capistrano is another issue).

So here are my lazy pointers to the discussions:

Update: Michael Buffington shares his Mongrel deployment for multiple stages

Tags: programming, ruby, rails, capistrano, deployment, production, webdev

The Reverse Captcha

Tim O’Reilly posed an interesting side-question on the O’Reilly Radar blog in a write-up on Google’s Image Labeler.

This is an interesting variation on the Turing test, in which humans generate and grade tests that most humans can pass, but current computer programs cannot pass. Is there another variation in the future, in which computers generate and grade tests that computers can pass, but humans cannot pass?

I’m not sure of the applicability of such a test, but the idea is intriguing none the less. I would think that there are plenty of problems that would be too difficult to solve by a human, but they are based on the assumption that a time limit is given for the production of a solution. You could simply give a very difficult mathematical question with a subsecond solve time.

One possible example without a need for a time-limit: a picture with a randomly generated steganographic message embedded and a text field with no explanation - computers fill it with the embedded message, humans would likely try to ascertain the image’s contents as the answer. Sort of a reverse captcha. The assumption here is that the algorithm for generating the random message is truly random, or not able to be reverse-engineered and the next value guessed by a human (sort of like those random card shufflers in Vegas that aren’t supposed to be beatable).

In any case, the idea is thought provoking. Anyone else have examples?

Tags: programming, personal, steganography, captcha, turing, oreilly

Prototype based Tag Autocompletion

I've been working on a website that uses tags internally. The acts_as_taggable plugin has been great, but it only takes care of the ActiveRecord portion of tagging. One of the pieces of functionality I needed was to create an autocompleter for users to enter a list of tags on a record. Below is a pure copy-paste dump of my implementation. I'm not claiming it's good code or the right way top do things, but for those looking to use an autocompleter for a text field holding tags might find it useful. The implementation is close to that used by delicious in the tags field on posting new bookmarks.

Here's an example usage:


<%= text_field_tag 'tags', @tags.join(' '), :size => 80, :autocomplete => "off" %>

And the prototype-based class in javascript. You'll probably want to stick this in your application.js file if you're using Rails.

String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g,'') }

Autocompleter.Tag = Class.create();
Autocompleter.Tag.prototype = Object.extend(new Autocompleter.Base(), {
  lastEdit: '',
  currentTag: {},
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },
  
  getCurrentTag: function() {
  if(this.element.value == this.lastEdit) return true // no edit
  if(this.element == '') return false
  this.currentTag = {}
  var tagArray=this.element.value.toLowerCase().split(' '), oldArray=this.lastEdit.toLowerCase().split(' '), currentTags = [], matched=false, t,o
  for (t in tagArray) {
    for (o in oldArray) {
      if(typeof oldArray[o] == 'undefined') { oldArray.splice(o,1); break }
      if(tagArray[t] == oldArray[o]) { matched = true; oldArray.splice(o,1); break; }
    }
    if(!matched) currentTags[currentTags.length] = t
    matched=false
  }
  // more than one word changed... abort
  if(currentTags.length > 1) {
    // hideSuggestions(); TODO Fix this!
    return false;
  }
  this.currentTag = { text:tagArray[currentTags[0]], index:currentTags[0] }
  return true
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },
  
  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
         return;
      }
     else 
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
    this.lastEdit = this.element.value;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer = 
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },
  
  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    
    var lastTokenPos = this.findLastToken();
    if (lastTokenPos != -1) {
      var newValue = this.element.value.substr(0, lastTokenPos + 1);
      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value;
    } else {
      tagArray = this.element.value.trim().split(' ')
      if (!this.getCurrentTag()) return;  // We had a problem getting the current tag, just return?
      tagArray[this.currentTag.index] = value;
      this.element.value = tagArray.join(' ');
    }
    this.element.focus();
    
    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
          var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        
        entry = entry.trim().split(' ').pop()
        
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) { 
              ret.push("
  • " + elem.substr(0, entry.length) + "" + elem.substr(entry.length) + "
  • "); break; } else if (entry.length >= instance.options.partialChars && instance.options.partialSearch && foundPos != -1) { if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { partial.push("
  • " + elem.substr(0, foundPos) + "" + elem.substr(foundPos, entry.length) + "" + elem.substr( foundPos + entry.length) + "
  • "); break; } } foundPos = instance.options.ignoreCase ? elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : elem.indexOf(entry, foundPos + 1); } } if (partial.length) ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) return "
      " + ret.join('') + "
    "; } }, options || {}); } });
    Tags: programming, personal, web_design, ruby, rails, tags, delicious, autocomplete, javascript, prototype

    Note to Subaru Marketing

    Dear Subaru Marketing:


    I appreciate your efforts to market your vehicles in my area; and I also appreciate your effort in creating regional specific advertisements - it lends a nice local feel to them. Bravo! However, you may want to actually make sure that the region you're airing the commercials within actually makes sense.

    There are other cities in New York state besides New York city. Upstate / Western New York is not referred to as the tri-state area. So, your slogan to tell use that we "try everything" and maybe that's why we're the "try-state" area is cute as buttons and puppy dogs - and horribly wrong. The area you're referring to is a 400 mile, 7-hour drive across nowheresville from us. Perhaps you could create one specific for our area: Since we are "Western" New York you may want to mention cowboys and indians. Or perhaps use the title "Finger Lakes region" and talk about rude gestures.


    Sincerely,
    A member of your target audience

    Tags: personal, rochester, subaru, advertising, marketing, commercial, ny
    next page »