my recent reads..

#Amazon, #Audible: can you get your global act together?

I bitched about Audible for not doing a good job of serving the global audience.

Well. I just got an email today that reminded me not to forget lambasting Amazon (now audible's parent company).

Over 800 Albums for $5 Each..

..from the Amazon mp3 store. Or so it said. It was a lie and grand deception.

I so want to buy from Amazon's mp3 store - heaven save me from even considering the Apple iTunes Store - but guess what? I can't. Not authorized outside the US (even though I can buy the exact same thing on a bit of plastic and have it shipped to me).

Now, I know it is not Audible and Amazon that set these policies. It's the RIAA and the rest of the old-fashioned publishing industry (be it books or music). And judging by The Washington Post's recent article "E-books spark battle inside the publishing industry", it seems things may get worse before they get better.

But I wish Audible and Amazon were a little more aggressive in championing consumer rights. In particular, take close aim at the notion of regional distribution deals.

Once upon a time, it was reasonable to ink regional deals. After all, someone needed to provide the warehouse, retail frontage and so on. In far off, foreign lands. But in the digital age, we have global retail frontage. Local distribution deals (and all their attendant evils such as DVD region coding) are an anachronism.

To put it simply: When Amazon, Audible or any other internet distributor puts a product in their stores, it should be available (and have been sold on) a global basis. If publishers are not able to make such a deal, don't stock their stuff. Send them packing and tell them to come back when they've got a deal that works for a global audience.

But is there an incentive for Amazon, Audible and the like to take such a stand against the publishers? Well here's one: the other 80% of the world market. I loo-ve Audible (props @jason), and Amazon has been a favoured source for years. But if you keep jilting me under the control of US-centric publishers, I'll be the first to jump to a regional/truly-global competitor. Your future growth will be limited to the shores of the continental US.



Soundtrack for this post: Can't Take Me Home - Pink
read more and comment..

Understanding Authlogic Plugin Dynamics

authlogic is by far and away my favourite authentication framework for Rails. I've raved enough in my slides on Authlogic_RPX.

It's true beauty is making authentication so unobtrusive for application developers.

However, the same can't be said for Authlogic plugin developers. I spent quite a bit of time meandering through the authlogic source and other plugins in order to produce Authlogic_RPX (the RPX plugin for authlogic, to support JanRain's RPX service).

I recently returned to the Authlogic_RPX in order to provide an update that finally adds identity mapping (with contributions from John and Damir; thanks guys!).

Luckily my previous exploits were recent enough that much of what I learned about authlogic were still pretty fresh. But before I forget it all again, I thought it would be worthwhile to write up a few of the "insights" I had on the authlogic source.

Hence this post. I'm just going to focus on one thing for now. Since authlogic is so "unobtrusive", one of the big conceptual hurdles you need to get over if you are attempting to write an authlogic plugin is simply:

Just how the heck does it all get loaded and mixed in with my models??

(To follow this discussion, I'd recommend you have a plugin close to hand. Either my previously mentioned Authlogic_RPX, or another like Authlogic_OAuth, or Authlogic_openid)

By unobtrusive, I mean like this. Here is the minimal configuration for a user model that uses Authlogic_RPX:
  class User < ActiveRecord::Base
acts_as_authentic
end

Pretty simple, right? But what power lies behind that little "acts_as_authentic" statement?

What follows is my attempt at a description of what goes on behind the scenes..

First: get loaded


The main file in an authlogic plugin/gem is going to have the relevant requires to the library files. But they do squat. We start mixing in our plugin with the includes and helper registrations:
require "authlogic_rpx/version"
require "authlogic_rpx/acts_as_authentic"
require "authlogic_rpx/session"
require "authlogic_rpx/helper"
require "authlogic_rpx/rpx_identifier"

ActiveRecord::Base.send(:include, AuthlogicRpx::ActsAsAuthentic)
Authlogic::Session::Base.send(:include, AuthlogicRpx::Session)
ActionController::Base.helper AuthlogicRpx::Helper

Note that your plugin ActsAsAuthentic module get's mixed in with ActiveRecord itself (not just a specific ActiveRecord model). That's crucial to remember when considering class methods in your plugin (they are basically global across all ActiveRecord).

What including ActsAsAuthentic in ActiveRecord::Base does..


What happens when the previous lines included the plugin's ActsAsAuthentic module?
The self.included method handles the initial bootstrap..


module AuthlogicRpx
module ActsAsAuthentic
def self.included(klass)
klass.class_eval do
extend Config
add_acts_as_authentic_module(Methods, :prepend)
end
end
..

Here we see we do a class_eval on the class that the module is included in (i.e. ActiveRecord::Base). You'll immediately get the sense we're doing some kind of mixin with the Config and Methods modules. The Config / Methods module structure is a common pattern you will see throughout authlogic.

extend Config takes the Config module (AuthlogicRpx::ActsAsAuthentic::Config) and add it to the ActiveRecord::Base class cdefinition. i.e. methods defined in Config become class methods of ActiveRecord::Base. (If you add a def self.extended(klass) method to Config you'll be able to hook the extension).

add_acts_as_authentic_module(Methods, :prepend) adds the Methods module (AuthlogicRpx::ActsAsAuthentic::Methods) to the authlogic modules list. That's all. Take a look at add_acts_as_authentic_module:


def add_acts_as_authentic_module(mod, action = :append)
modules = acts_as_authentic_modules
case action
when :append
modules << mod
when :prepend
modules = [mod] + modules
end
modules.uniq!
write_inheritable_attribute(:acts_as_authentic_modules, modules)
end


Ready to launch..


It is only when we add the acts_as_authentic in our model class that things start to happen. This method loads all the modules from the list built up by all the call(s) to "add_acts_as_authentic_module". Note the include in the last line of the method:

def acts_as_authentic(unsupported_options = nil, &block)
# Stop all configuration if the DB is not set up
return if !db_setup?

raise ArgumentError.new("You are using the old v1.X.X configuration method for Authlogic. Instead of " +
"passing a hash of configuration options to acts_as_authentic, pass a block: acts_as_authentic { |c| c.my_option = my_value }") if !unsupported_options.nil?

yield self if block_given?
acts_as_authentic_modules.each { |mod| include mod }
end


Ignition..


Once the include is invoked, our plugin will usually hook the event and do some setup activity in our module's def self.included method.


module Methods
def self.included(klass)
klass.class_eval do
..
end
..
end
..

Unlike the Config extension, the class you are including in (the klass parameter in the example), is the specific ActiveRecord model you have marked as "acts_as_authentic".

In other words, the methods in the Methods module get included as instance methods for the specific ActiveRecord models class (User in the example I presented earlier).

Hanging it on the line..


Let's hang it all out in a simplified and contrived example. Take this basic structure:

module AuthlogicPlugin
module ActsAsAuthentic
def self.included(klass)
klass.class_eval do
extend Config
add_acts_as_authentic_module(Methods, :prepend)
end
end
module Config
def config_item
end
end
module Methods
def self.included(klass)
klass.class_eval do
def self.special_setting
end
end
end
def instance_item
end
end
end
end

If we add this to our User model, then the result we'd end up with is this:

  • config_item: will be a class method on ActiveRecord::Base

  • instance_item: will be an instance method on User

  • special_setting: will be a class method on User



Conclusions & Implications?


I've covered the main points in bootstrapping authlogic. There's obviously a lot more that goes on, but I think once you get these basics it makes authlogic-related code so much easier to read and understand. It's a pretty neat demonstration of dynamic ruby at work.

Understanding the loading process is also makes it possible to be definitive about how your application will behave, rather than just treating it as a heuristic black box.

Take authlogic configuration settings for example. Say we have a configuration parameter in our plugin called "big_red_button" that takes values :on and :off.

Syntactically, both of these user model definitions are valid:


class User < ActiveRecord::Base
acts_as_authentic do |c|
c.big_red_button :on
end
end

class User < ActiveRecord::Base
acts_as_authentic
big_red_button :on
end

However, the behaviour is slightly different, and the difference will be significant if you have any initialisation code in the plugin that cares about the setting of the big_red_button.

In the second case, it should be clear that setting big_red_button :on only happens after all the plugin initialisation is complete.

But in the first case, it is a little more subtle. If you go back to review the acts_as_authentic method you'll see that setting the big_red_button occurs at yield self if block_given?. Implications:

  • Config extension of ActiveRecord::Base takes place before the big_red_button is set

  • Method methods are included in the User model before the big_red_button is set

  • Method's def self.included is called after the big_red_button is set (meaning you can safely do conditional initialisation here based on the big_red_button setting)


How's that? Pretty cool stuff, but thankfully as I mentioned before, these details only really concern plugin authors and anyone who just loves to read dynamic ruby code.

There's much more to authlogic that what I've discussed here of course (and RPX). Perhaps good fodder for a future post? Let's see..


Soundtrack for this post: Because it's There - Michael Hedges
read more and comment..

Watching people shop


I've been a long time Amazon customer, but a while ago I stumbled upon The Book Depository in the UK. Not only are their prices competitive with Amazon (especially when you consider the free shipping), but I was totally sucked in by their live "Watch people shop" widget - a very cool Google maps mashup.

..although it does look a bit strange when all the book buyers in Australia seems to be based in Alice Springs;-)

Soundtrack for this post: Someone To Watch Over Me - Blossom Dearie
read more and comment..

The #joojoo is coming (with or without the true story)

Michael Arrington hasn't been shy about arguing his position on the CrunchPad/joojoo story, and until recently that's all we've really heard.

Reading the filing, I had the distinct feeling that everything wasn't so cut and dried as Mike claimed, and there's another story to tell.

Andrew Warner's Mixergy interview with Chandra (and subsequent discussion on TWiT) finally starts to bring some balance. I'm sure it all won't come out until the dust has settled around the court case, but I reckon there's a book in this story, a lá The Accidental Billionaires ("Crunch!"?)

Besides, now having seen it .. I want a joojoo! Short of an injunction, the joojoo is due to ship in the US in 8-10 weeks. It will be a real sad thing if the legal wrangle scuttles the joojoo's chances to have a serious shot in the market.

Business Tips via Mixergy, home of the ambitious upstart!
read more and comment..