Skip to content

Instantly share code, notes, and snippets.

@rbranson
Created May 15, 2010 00:31
Show Gist options
  • Select an option

  • Save rbranson/401870 to your computer and use it in GitHub Desktop.

Select an option

Save rbranson/401870 to your computer and use it in GitHub Desktop.
#
# To use this, drop it into your lib directory and add a require to an initializer
# in config/initializers.
#
# This little extension to Mongoid allows belongs_to_related associations to
# have an "in" option, which will traverse the object graph to find the
# collection that will contain the related model object.
#
# The goal is to allow relations to be created that reference objects embedded
# in documents, instead of only objects that are contained within collections.
#
# For instance, if you have an object model defined as such (totally contrived):
#
# class Blog
# ...
# embeds_many :authors
# embeds_many :posts
# end
#
# class Author
# ...
# embedded_in :blog, :inverse_of => :authors
# embeds_many :pictures
# end
#
# class Picture
# ...
# embedded_in :author, :inverse_of => :pictures
# end
#
# class Post
# ...
# embedded_in :blog, :inverse_of => :posts
# belongs_to_related :author, :in => :blog
# end
#
# post.author can now be used to relate to any author within Blog. The accessor
# uses the blog method on the Post instance and looks for an authors method on
# whatever the blog method returned.
#
# For relations on objects that are embedded several layers deep, an array can
# be passed to :in, and it will traverse the objects, calling the passed methods
# in order to arrive at the object with the accessor that will return the embedded
# collection that will contain the object we're looking for. Phew.
#
module Mongoid
module Associations
class Options
def in
@attributes[:in]
end
end
end
end
module Mongoid
module Associations
class BelongsToRelated < Proxy
def initialize(document, foreign_key, options, target = nil)
@options = options
if target == nil and options.in
if options.in.is_a?(Array)
# If in is an array, traverse the object graph to find the
# right container.
container = options.in.inject(document) do |obj, sym|
obj.send(sym)
end
else
# If not, just access it directly
container = document.send(options.in)
end
okdcpts = options.klass.name.downcase.pluralize.to_sym
# Look for a has_many style plural method
if container.respond_to?(okdcpts)
@target = container.send(okdcpts).find(foreign_key)
else
raise "belongs_to_related :in target does not contain the method :#{okdcpts.to_s}"
end
else
@target = target || options.klass.find(foreign_key)
end
extends(options)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment