Modular Tables? DataMapper is Remixable.

One of my favorite things about Ruby is mixins.  I love the pseudo multiple inheritance it provides.  One thing that is frustrating to me though when working on developing back-ends is the amount of un-DRY code I am forced to create when working with the database.

For example, comments.  Look at sites like MySpace, Facebook, and SocialGiant3.0 (sarcasm), everything is commentable.  A user can comment on pictures, profiles, videos, and just about any other resource you can think of.

In this scenario you generally have one of two choices:

A.) You create a comments table for each of the resources: user_comments, profile_comments, etc.

B.) You create a giant comments table and horizontally partition it based on the comment_type.

I prefer the choice ‘A’, but the downfall, a very wet, anti-DRY set of models.  This is were dm-is-remixable comes in.  It gives you the ability to remix datamapper models into other models.

Whats that mean?

You write one model describing what a comment looks like and you mix it into User, Profile, etc; like magic, you’ve got a dry ‘comment’ model and a nice normalized table structure to store it in.

Here are some examples:

Create a remixable database model named Commentable


module Commentable
  include DataMapper::Resource

  is :remixable,
    :suffix => "comment"

  property :id,         Integer, :key => true, :serial => true
  property :comment,    String
  property :created_at, DateTime
end

Create a remixable image model and mix it into User


module Image
  include DataMapper::Resource

  is :remixable

  property :id,           Integer, :key => true, :serial => true
  property :description,  String
  property :path,         String
end

class User
  include DataMapper::Resource

  property :id,             Integer,
    :key          => true,
    :serial       => true

  property :first_name,     String,
    :nullable     => false,
    :length       => 2..50

  property :last_name,      String,
    :nullable     => false,
    :length       => 2..50

  remix n, :images, :as => "pics" #:as is optional and gives you an additional accessor
end

user = User.new
user.user_images
=>[] #Array of images
user.pics
=>[] #Array of images

Enhance the functionality of UserImages


class User
  enhance :images do
    def dimensions
      # ... get the dimensions of the image
    end

    def resize(x,y)
      # ... resize the image
    end
  end
end

user = User.first
user.pics.first.dimensions
=> "300x500"
user.pics.first.resize(100,300)
=> "100x300"

Remix commentables into Videos


class Video
  include DataMapper::Resource
  is :remixable

  property :id,           Integer, :key => true, :serial => true
  property :description,  String
  property :path,         String

  remix n, :commentables, :as => "comments" # as is optional, provides add'l accessor name
end

video = Video.new
video.comments
=>[] # Array of comments
video.video_comments
=>[] # same array of comments

Remixing commentables into User;


class User
  # ... from declaration above ...

  # This is some of the more complex syntax
  # here we are remixing a module between two of the same class, so we need some sort of alias
  # :as => is the accessor method
  # :for => is the class that this class remixes the module with
  # :via => what we want to call the other user object

  remix n, :commentables, :as => "comments", :for => "User", :via => "commentor"

end

commentee = User.first
commentor = User.get(2)
comment = UserComment.new
comment.comment = "Zomg!  I talk non-stop, you are awesome!"
comment.commentor = commentor
commentee.comments << comment
commentee.save

How do you get it?  Well its in dm-more.  If you dont know what DataMapper is, then git with the program.

From your favorite command line:

git clone git://github.com/sam/dm-more.git

Then from your source:

require ‘dm-is-remixable’

Tags: , ,

8 Responses to “Modular Tables? DataMapper is Remixable.”

  1. epoch Says:

    Nice write up, thanks.

    Quick question, can we define relationships in the remixable modules?

  2. Cory Says:

    Yeah, you can define relationships, but its kinda wonky. Since the remixable is actually a module, you have to ’send’ the relationship to the class that is remixing the module via the ‘extended’ method.

    module MyRemixable
    def self.extended(klass)
    klass.send :has, n, :yada_yadas
    end
    end

  3. Loy81 Says:

    And these new notes were not just plain bank notes. ,

  4. Dillon Messan Says:

    WHAT LUCK! I I came across this site on google. this is really great stuff! I have added your site to my faves. I will be back!

  5. Sonya Doakes Says:

    !!!..a different great article, I might count on practically nothing much less from you although :]

  6. Edwin Aragoni Says:

    Decent reading. I like your web design also. Keep up your good work.

  7. Devon Correira Says:

    Yeah I need to agree with the guy a couple of feedback up. Indiscriminate advertising is for noobs!

  8. Ute Pago Says:

    Really informative weblog right here. I could not make a much better 1 if my life depended on this.

Leave a Reply