totally classless

Posted by Jared Carroll

Nov 19

Recently I had a requirement on an app that had 2 types of groups.

  1. public
  2. private

Anyone can join a public group.

However to join a private group you have to be registered on the private group’s website, e.g. pretend Yahoo is a private group, now in order to join the Yahoo group you have to have an account with Yahoo.

Let’s pretend that every private group’s website implements a simple API that says if the given username/password is registered with that website.

I’m going to write a client for the API and put it in a module in lib

lib/api_client.rb
1
2
3
4
5
6
7
8
9

module ApiClient

  def authenticate(username, password)
    # make HTTP GET request to the private group's website's API implementation URL
    # true/false return values
  end

end

I’ll use this library in my Membership model during a validation callback on create.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Membership < ActiveRecord::Base

  belongs_to :user
  belongs_to :group

  include ApiClient

  attr_accessor :username, :password

  def validate_on_create
    unless authenticate(username, password)
      errors.add_to_base "You are not registered with this group's website"
    end
  end

end

Here we mix in the ApiClient module and use its #authenticate method to validate this membership’s username and password (username and password were added as virtual attributes using attr_accessor since I don’t want to store this remote site’s credentials in my apps db).

The problem I have with this implementation is that the #authenticate method that gets mixed into Membership via ApiClient is now a public instance method on Membership and can therefore be used anywhere. But #authenticate should only be used during validation during creation.

1
2
3

  membership = Membership.find :first
  membership.authenticate              

That’s no good. It’s polluting my Membership public interface.

Let’s try something else in our Membership model.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Membership < ActiveRecord::Base

  belongs_to :user
  belongs_to :group

  attr_accessor :username, :password

  def validate_on_create
    api_client = Object.new
    api_client.extend ApiClient
    unless api_client.authenticate(username, password)
      errors.add_to_base "You are not registered with this group's website"
    end
  end

end

Here I created an instance of Object, mixed ApiClient in just that instance and then used it to #authenticate the username/password. This is an example of per-object behavior and it gives me exactly what I want. I don’t pollute the public interface of Membership and yet still get to have my remote API behavior all in one place, ApiClient.

Can we shorten #validate_on_create a little more?

1
2
3
4
5
6
7

  def validate_on_create
    api_client = returning(Object.new) {|instance| instance.extend ApiClient}
    unless api_client.authenticate(username, password)
      errors.add_to_base "You are not registered with this group's website"
    end
  end

Oh man, an excuse to use the method all the cool kids are using, #returning. I might keep that. As long as it doesn’t go longer than my 80-column character limit but its already starting to push it!

The final implementation of this feature is an example of per-object behavior. A style similar to coding in JavaScript.

When the Rails guys tried to make JavaScript more Ruby-like in their prototype library they added a method to the Object constructor function named #extend to do the very same thing as Ruby’s Object#extend.

The above example might look like this in JavaScript.

  var ApiClient = {
    authenticate : function () {
      // make XML HTTP GET request to the private group's website API implementation URL
      // true/false return values
    };
  };

  function validateOnCreate () {
    var apiClient = new Object();
    Object.extend(apiClient, ApiClient);
    if (! apiClient.authenticate()) {
      // add/display errors
    }
  }

David A. Black recently spoke about this at RubyConf 2007. In his slides he mentions how most Ruby programmers already use it when using class methods and how its also useful as a way to avoid changing core Ruby. Dave Thomas mentioned per-object or OOP without classes at RailsConf 2007 as well.

Both of these guys got my interest in removing classes from OOP; before them all I’d seen of this technique was the language Self and its descendant JavaScript. Anyway enough bs, its an interesting technique and one I’m going to monitor to see if I see myself moving more and more in this direction.


Comments on this post

Ben Mabey

Nov 20

Ben Mabey said,

Why not just make the method protected in the module?

For the interested reader in “OOP without classes” they can read about it on wikipedia: http://en.wikipedia.org/wiki/Prototype-based_programming Also, there is is a small ruby library by Ara that allows you to easily play around with this style of programming in ruby: http://codeforpeople.com/lib/ruby/prototype/prototype-2.0.0/README

schmidt

Nov 20

schmidt said,

If the ApiClient is stateless, what seems to be the case, why not make the authenticate method a method on ApiClient. This would make things a lot easier.

module ApiClient
  def authenticate(u,p)
    # do the magical thing
    true
  end
  extend self
end

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group

  attr_accessor :username, :password

  def validate_on_create
    unless ApiClient.authenticate(username, password)
      errors.add_to_base "You are not registered with this group's website" 
    end
  end
end

And you get rid of a bit of metaprogramming, where it is not needed.

Instance-specfic behaviour is still very useful, especially in tests. But hey, defining methods on classes and modules is instance specific behaviour as well and this is always useful.

Jon Yurek

Nov 20

Jon Yurek said,

Well, I wouldn’t exactly call extending an object metaprogramming, but I do agree with the sentiment that there’s no need to mix it in to an Object before calling it.

chako

Nov 20

chako said,

What is the point of the returning call? Wouldn’t this do the same thing:

api_client = Object.new.extend ApiClient

theJareCare

Nov 20

theJareCare said,

that’s true chako but i think we both would agree that

1
2
  object = Object.new
  object.extend ApiClient

or


  object = returning(Object.new) {|instance| instance.extend ApiClient}

is a lot more better looking than chaining a ton of messages together like


  object = Object.new.extend ApiClient
Yossef

Nov 23

Yossef said,

Jare: I don’t necessarily agree that the first two look better (and if you’re worried about the 80-character limit, try |o| instance of |instance|) than chaining messages together. I also don’t think that’s “a ton” of messages.

I do think it’s not all that nice to depend on Object#extend returning self. I know that’s what it does, but it just seems an odd thing to do.


Sorry, comments are closed for this article.

© 2000 - 2008 by thoughtbot, inc.
written by a bushel of tiny robots