Skip to content

Commit

Permalink
ActiveRecord compatible association reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
balexand committed Dec 14, 2011
1 parent 15067e5 commit f589066
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 30 deletions.
61 changes: 31 additions & 30 deletions lib/mongo_mapper.rb
Expand Up @@ -26,36 +26,37 @@ module Middleware
end

module Plugins
autoload :ActiveModel, 'mongo_mapper/plugins/active_model'
autoload :Associations, 'mongo_mapper/plugins/associations'
autoload :Accessible, 'mongo_mapper/plugins/accessible'
autoload :Callbacks, 'mongo_mapper/plugins/callbacks'
autoload :Caching, 'mongo_mapper/plugins/caching'
autoload :Clone, 'mongo_mapper/plugins/clone'
autoload :Dirty, 'mongo_mapper/plugins/dirty'
autoload :Document, 'mongo_mapper/plugins/document'
autoload :DynamicQuerying, 'mongo_mapper/plugins/dynamic_querying'
autoload :EmbeddedCallbacks, 'mongo_mapper/plugins/embedded_callbacks'
autoload :EmbeddedDocument, 'mongo_mapper/plugins/embedded_document'
autoload :Equality, 'mongo_mapper/plugins/equality'
autoload :IdentityMap, 'mongo_mapper/plugins/identity_map'
autoload :Inspect, 'mongo_mapper/plugins/inspect'
autoload :Indexes, 'mongo_mapper/plugins/indexes'
autoload :Keys, 'mongo_mapper/plugins/keys'
autoload :Logger, 'mongo_mapper/plugins/logger'
autoload :Modifiers, 'mongo_mapper/plugins/modifiers'
autoload :Pagination, 'mongo_mapper/plugins/pagination'
autoload :Persistence, 'mongo_mapper/plugins/persistence'
autoload :Protected, 'mongo_mapper/plugins/protected'
autoload :Querying, 'mongo_mapper/plugins/querying'
autoload :Rails, 'mongo_mapper/plugins/rails'
autoload :Safe, 'mongo_mapper/plugins/safe'
autoload :Sci, 'mongo_mapper/plugins/sci'
autoload :Scopes, 'mongo_mapper/plugins/scopes'
autoload :Serialization, 'mongo_mapper/plugins/serialization'
autoload :Timestamps, 'mongo_mapper/plugins/timestamps'
autoload :Userstamps, 'mongo_mapper/plugins/userstamps'
autoload :Validations, 'mongo_mapper/plugins/validations'
autoload :ActiveModel, 'mongo_mapper/plugins/active_model'
autoload :Associations, 'mongo_mapper/plugins/associations'
autoload :Accessible, 'mongo_mapper/plugins/accessible'
autoload :Callbacks, 'mongo_mapper/plugins/callbacks'
autoload :Caching, 'mongo_mapper/plugins/caching'
autoload :Clone, 'mongo_mapper/plugins/clone'
autoload :Dirty, 'mongo_mapper/plugins/dirty'
autoload :Document, 'mongo_mapper/plugins/document'
autoload :DynamicQuerying, 'mongo_mapper/plugins/dynamic_querying'
autoload :EmbeddedCallbacks, 'mongo_mapper/plugins/embedded_callbacks'
autoload :EmbeddedDocument, 'mongo_mapper/plugins/embedded_document'
autoload :Equality, 'mongo_mapper/plugins/equality'
autoload :IdentityMap, 'mongo_mapper/plugins/identity_map'
autoload :Inspect, 'mongo_mapper/plugins/inspect'
autoload :Indexes, 'mongo_mapper/plugins/indexes'
autoload :Keys, 'mongo_mapper/plugins/keys'
autoload :Logger, 'mongo_mapper/plugins/logger'
autoload :Modifiers, 'mongo_mapper/plugins/modifiers'
autoload :Pagination, 'mongo_mapper/plugins/pagination'
autoload :Persistence, 'mongo_mapper/plugins/persistence'
autoload :Protected, 'mongo_mapper/plugins/protected'
autoload :Querying, 'mongo_mapper/plugins/querying'
autoload :Rails, 'mongo_mapper/plugins/rails'
autoload :ReflectOnAssociation, 'mongo_mapper/plugins/reflect_on_association'
autoload :Safe, 'mongo_mapper/plugins/safe'
autoload :Sci, 'mongo_mapper/plugins/sci'
autoload :Scopes, 'mongo_mapper/plugins/scopes'
autoload :Serialization, 'mongo_mapper/plugins/serialization'
autoload :Timestamps, 'mongo_mapper/plugins/timestamps'
autoload :Userstamps, 'mongo_mapper/plugins/userstamps'
autoload :Validations, 'mongo_mapper/plugins/validations'

module Associations
autoload :Base, 'mongo_mapper/plugins/associations/base'
Expand Down
1 change: 1 addition & 0 deletions lib/mongo_mapper/document.rb
Expand Up @@ -23,6 +23,7 @@ module Document
include Plugins::Accessible
include Plugins::Protected
include Plugins::Rails
include Plugins::ReflectOnAssociation
include Plugins::Safe # needs to be after querying (save_to_collection)
include Plugins::Sci
include Plugins::Scopes
Expand Down
1 change: 1 addition & 0 deletions lib/mongo_mapper/embedded_document.rb
Expand Up @@ -17,6 +17,7 @@ module EmbeddedDocument
include Plugins::Accessible
include Plugins::Protected
include Plugins::Rails
include Plugins::ReflectOnAssociation
include Plugins::Sci
include Plugins::Serialization
include Plugins::Validations
Expand Down
19 changes: 19 additions & 0 deletions lib/mongo_mapper/plugins/reflect_on_association.rb
@@ -0,0 +1,19 @@
# encoding: UTF-8
require 'mongo_mapper/plugins/reflect_on_association/active_record_association_adapter'

module MongoMapper
module Plugins
# Provides a reflect_on_association method that returns an ActiveRecordAssociationAdapter for an association.
# This adapter has an API that is a subset of ActiveRecord::Reflection::AssociationReflection. This allows
# MongoMapper to be used with the association helpers in gems like simple_form and formtastic.
module ReflectOnAssociation
extend ActiveSupport::Concern

module ClassMethods
def reflect_on_association(name)
ActiveRecordAssociationAdapter.for_association(associations[name]) if associations[name]
end
end
end
end
end
@@ -0,0 +1,33 @@
# encoding: UTF-8
module MongoMapper
module Plugins
module ReflectOnAssociation
class ActiveRecordAssociationAdapter
attr_reader :klass, :macro, :name, :options

def self.for_association(association)
macro = case association
when MongoMapper::Plugins::Associations::BelongsToAssociation
:belongs_to
when MongoMapper::Plugins::Associations::ManyAssociation
:has_many
when MongoMapper::Plugins::Associations::OneAssociation
:has_one
else
raise "no #{name} for association of type #{association.class}"
end

new(association, macro)
end

def initialize(association, macro)
@klass, @name = association.klass, association.name
# only include compatible options
@options = association.options.slice(:conditions, :order)

@macro = macro
end
end
end
end
end
118 changes: 118 additions & 0 deletions test/unit/test_reflect_on_association.rb
@@ -0,0 +1,118 @@
require 'test_helper'

module ReflectOnAssociationTestModels
class Tree
include MongoMapper::Document
many :birds, :class_name => "ReflectOnAssociationTestModels::Bird"
end

class Bird
include MongoMapper::Document
belongs_to :tree, :class_name => "ReflectOnAssociationTestModels::Tree"
end

class Book
include MongoMapper::Document
many :authors, :class_name => "ReflectOnAssociationTestModels::Author", :in => :author_ids
end

class Author
include MongoMapper::Document
end

class Employee
include MongoMapper::Document
one :desk, :class_name => "ReflectOnAssociationTestModels::Desk"
end

class Desk
include MongoMapper::Document
belongs_to :employee, :class_name => "ReflectOnAssociationTestModels::Employee"
end

class Order
include MongoMapper::Document
many :line_items, :class_name => "ReflectOnAssociationTestModels::LineItem"
end

class LineItem
include MongoMapper::EmbeddedDocument
end

class Body
include MongoMapper::Document
one :heart, :class_name => "ReflectOnAssociationTestModels::Heart"
end

class Heart
include MongoMapper::EmbeddedDocument
end
end

class ReflectOnAssociationTest < Test::Unit::TestCase
context "one-to-many association" do
should "return :has_many association for Tree#birds" do
association = ReflectOnAssociationTestModels::Tree.reflect_on_association(:birds)
association.klass.should == ReflectOnAssociationTestModels::Bird
association.macro.should == :has_many
association.name.should == :birds
association.options.should == {}
end

should "return :belongs_to association for Bird#tree" do
association = ReflectOnAssociationTestModels::Bird.reflect_on_association(:tree)
association.klass.should == ReflectOnAssociationTestModels::Tree
association.macro.should == :belongs_to
association.name.should == :tree
association.options.should == {}
end
end

context "many-to-many association" do
should "return :has_many association for Book#authors" do
association = ReflectOnAssociationTestModels::Book.reflect_on_association(:authors)
association.klass.should == ReflectOnAssociationTestModels::Author
association.macro.should == :has_many
association.name.should == :authors
association.options.should == {}
end
end

context "one-to-one association" do
should "return :has_one association for Employee#desk" do
association = ReflectOnAssociationTestModels::Employee.reflect_on_association(:desk)
association.klass.should == ReflectOnAssociationTestModels::Desk
association.macro.should == :has_one
association.name.should == :desk
association.options.should == {}
end

should "return :belongs_to association for Desk#employee" do
association = ReflectOnAssociationTestModels::Desk.reflect_on_association(:employee)
association.klass.should == ReflectOnAssociationTestModels::Employee
association.macro.should == :belongs_to
association.name.should == :employee
association.options.should == {}
end
end

context "embeds one" do
should "return :has_one association for Body#heart" do
association = ReflectOnAssociationTestModels::Body.reflect_on_association(:heart)
association.klass.should == ReflectOnAssociationTestModels::Heart
association.macro.should == :has_one
association.name.should == :heart
association.options.should == {}
end
end

context "embeds many" do
should "return :has_many association for Order#line_items" do
association = ReflectOnAssociationTestModels::Order.reflect_on_association(:line_items)
association.klass.should == ReflectOnAssociationTestModels::LineItem
association.macro.should == :has_many
association.name.should == :line_items
association.options.should == {}
end
end
end

0 comments on commit f589066

Please sign in to comment.