require 'facet/string/singular' require 'facet/string/demodulize' require 'facet/string/underscore' require 'og/relation' require 'og/collection' module Og class JoinsManyCollection < Collection end # A 'joins_many' relation. # This objects is associated with an other using an intermediate # join table. # # === Examples # # joins_many Category # joins_many :categories, Category class JoinsMany < Relation def enchant self[:owner_singular_name] = owner_class.to_s.demodulize.underscore.downcase self[:target_singular_name] = target_plural_name.to_s.singular store = owner_class.ogmanager.store join_table_info = store.join_table_info(owner_class, target_class) if through = self[:through] # A custom class is used for the join. Use the class # table and don't create a new table. through = resolve_symbol(through, owner_class) if through.is_a?(Symbol) join_table = self[:join_table] = through.table else # Calculate the name of the join table join_table = self[:join_table] = join_table_info[:table] # Create a join table. owner_class.meta :join_tables, join_table_info end owner_key = join_table_info[:owner_key] target_key = join_table_info[:target_key] owner_class.module_eval %{ attr_accessor :#{target_plural_name} def #{target_plural_name}(options = nil) reload = options and options[:reload] unless @#{target_plural_name} @#{target_plural_name} = JoinsManyCollection.new( self, :add_#{target_singular_name}, :remove_#{target_singular_name}, :find_#{target_plural_name}, :count_#{target_plural_name}, options ) end @#{target_plural_name}.find_options = options @#{target_plural_name}.reload(options) if options and options[:reload] @#{target_plural_name} end def add_#{target_singular_name}(obj, options = nil) obj.save obj.class.ogmanager.store.join(self, obj, "#{join_table}", options) end def remove_#{target_singular_name}(obj) obj.class.ogmanager.store.unjoin(self, obj, "#{join_table}") end def find_#{target_plural_name}(options = {}) find_options = { :join_table => "#{join_table}", :join_condition => "#{join_table}.#{target_key}=#{store.table(target_class)}.oid", :condition => "#{join_table}.#{owner_key}=\#\{@#{owner_pk}\}" } if options if condition = options.delete(:condition) find_options[:condition] += " AND (\#{condition})" end find_options.update(options) end #{target_class}.find(find_options) end #-- # TODO: optimize this!! #++ def count_#{target_plural_name} find_options = { :join_table => "#{join_table}", :join_condition => "#{join_table}.#{target_key}=#{store.table(target_class)}.oid", :condition => "#{join_table}.#{owner_key}=\#\{@#{owner_pk}\}" } #{target_class}.find(find_options).size end } if through owner_class.module_eval %{ def #{target_singular_name}_join_data(t) #{through}.find_one(:condition => "#{owner_key}=\#{@#{owner_pk}} and #{target_key}=\#{t.pk}") end } end end end end # * George Moschovitis # * Ysabel