# File lib/has_many_polymorphs/class_methods.rb, line 59
      def acts_as_double_polymorphic_join options={}, &extension

        collections, options = extract_double_collections(options)

        # handle the block
        options[:extend] = (if options[:extend]
          Array(options[:extend]) + [extension]
        else
          extension
        end) if extension

        collection_option_keys = make_general_option_keys_specific!(options, collections)

        join_name = self.name.tableize.to_sym
        collections.each do |association_id, children|
          parent_hash_key = (collections.keys - [association_id]).first # parents are the entries in the _other_ children array

          begin
            parent_foreign_key = self.reflect_on_association(parent_hash_key._singularize).primary_key_name
          rescue NoMethodError
            raise PolymorphicError, "Couldn't find 'belongs_to' association for :#{parent_hash_key._singularize} in #{self.name}." unless parent_foreign_key
          end

          parents = collections[parent_hash_key]
          conflicts = (children & parents) # set intersection
          parents.each do |plural_parent_name|

            parent_class = plural_parent_name._as_class
            singular_reverse_association_id = parent_hash_key._singularize

            internal_options = {
              :is_double => true,
              :from => children,
              :as => singular_reverse_association_id,
              :through => join_name.to_sym,
              :foreign_key => parent_foreign_key,
              :foreign_type_key => parent_foreign_key.to_s.sub(/_id$/, '_type'),
              :singular_reverse_association_id => singular_reverse_association_id,
              :conflicts => conflicts
            }

            general_options = Hash[*options._select do |key, value|
              collection_option_keys[association_id].include? key and !value.nil?
            end.map do |key, value|
              [key.to_s[association_id.to_s.length+1..-1].to_sym, value]
            end._flatten_once] # rename side-specific options to general names

            general_options.each do |key, value|
              # avoid clobbering keys that appear in both option sets
              if internal_options[key]
                general_options[key] = Array(value) + Array(internal_options[key])
              end
            end

            parent_class.send(:has_many_polymorphs, association_id, internal_options.merge(general_options))

            if conflicts.include? plural_parent_name
              # unify the alternate sides of the conflicting children
              (conflicts).each do |method_name|
                unless parent_class.instance_methods.include?(method_name)
                  parent_class.send(:define_method, method_name) do
                    (self.send("#{singular_reverse_association_id}_#{method_name}") +
                      self.send("#{association_id._singularize}_#{method_name}")).freeze
                  end
                end
              end

              # unify the join model... join model is always renamed for doubles, unlike child associations
              unless parent_class.instance_methods.include?(join_name)
                parent_class.send(:define_method, join_name) do
                  (self.send("#{join_name}_as_#{singular_reverse_association_id}") +
                    self.send("#{join_name}_as_#{association_id._singularize}")).freeze
                end
              end
            else
              unless parent_class.instance_methods.include?(join_name)
                # ensure there are no forward slashes in the aliased join_name_method (occurs when namespaces are used)
                join_name_method = join_name.to_s.gsub('/', '_').to_sym
                parent_class.send(:alias_method, join_name_method, "#{join_name_method}_as_#{singular_reverse_association_id}")
              end
            end

          end
        end
      end