Skip to content

Instantly share code, notes, and snippets.

@dscataglini
Created August 19, 2010 01:16
Show Gist options
  • Save dscataglini/536717 to your computer and use it in GitHub Desktop.
Save dscataglini/536717 to your computer and use it in GitHub Desktop.
# this is an experiment that I tried about 3 years ago.
# instead of calling asset.attachable.post.user.login just calling asset.login
class User < ActiveRecord::Base #fields are :login, :email
has_many :posts
has_many :channels
end
class Channel < ActiveRecord::Base #fields are :private(bool), :channel_type, :name
belongs_to :user
has_many :posts
end
class Post < ActiveRecord::Base #fields are :title, :body, :comments_count
belongs_to :user
belongs_to :channel
has_many :assets, :as => :attachable
end
class Asset < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
include AssociationDelegation
set_delegation_chain :attachable, :channel, :user
end
require 'active_support'
module AssociationDelegation
def self.included(base)
base.send :cattr_accessor, :class_delegation_chain
base.extend ClassMethods
def method_missing(method_name, *args, &block)
super(method_name, *args, &block)
rescue NoMethodError => error
puts "the error is #{error}"
if delegated = self.find_delegated(method_name)
return delegated.send(method_name, *args, &block)
end
raise error
end
def find_delegated(method)
self.class_delegation_chain.inject(self) do |cur, association|
cur = find_next_in_chain_or_start_new_chain(cur, association)
return cur if cur.respond_to? method
cur
end
return nil
end
def find_next_in_chain_or_start_new_chain(cur, association)
return cur.send(association) if cur.respond_to?(association)
return self.send(association) if self.respond_to?(association)
nil
end
end
module ClassMethods
def set_delegation_chain(*classes)
self.class_delegation_chain = [*classes]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment