A bidirectional polymorphic merge model in Rails?

I am working on a multi-site CMS that has the concept of cross-publishing among sites. Several types of content (articles, events, biographies, etc.) can be associated with many sites, and sites can have many pieces of content. The many-to-many relationship between pieces of content and sites should also support a couple of common attributes for each related content item - the concept of creating a site (is this the source site on which the content appeared?), As well as the concepts of "primary" and " secondary "content content for a given piece of content on a given linked site.

My idea was to create a polymorphic association model called ContentAssociation, but it's hard for me to make polymorphic associations behave the way I expect them, and I wonder if it's possible that I will do it all wrong.

Here is my setup for the connection table and models:

create_table "content_associations", :force => true do |t|
  t.string   "associable_type"
  t.integer  "associable_id"
  t.integer  "site_id"
  t.boolean  "primary_eligible"
  t.boolean  "secondary_eligible"
  t.boolean  "originating_site"
  t.datetime "created_at"
  t.datetime "updated_at"
end

class ContentAssociation < ActiveRecord::Base
  belongs_to :site
  belongs_to :associable, :polymorphic => true
  belongs_to :primary_site, :class_name => "Site", :foreign_key => "site_id" 
  belongs_to :secondary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :originating_site, :class_name => "Site", :foreign_key => "site_id"
end

class Site < ActiveRecord::Base
  has_many :content_associations, :dependent => :destroy 
  has_many :articles, :through => :content_associations, :source => :associable, :source_type => "Article"
  has_many :events, :through => :content_associations, :source => :associable, :source_type => "Event"

  has_many :primary_articles, :through => :content_associations, 
                              :source => :associable, 
                              :source_type => "Article", 
                              :conditions => ["content_associations.primary_eligible = ?" true]

  has_many :originating_articles, :through => :content_associations, 
                                  :source => :associable, 
                                  :source_type => "Article", 
                                  :conditions => ["content_associations.originating_site = ?" true]

  has_many :secondary_articles, :through => :content_associations, 
                                :source => :associable, 
                                :source_type => "Article", 
                                :conditions => ["content_associations.secondary_eligible = ?" true]
end

class Article < ActiveRecord::Base
  has_many :content_associations, :as => :associable, :dependent => :destroy
  has_one :originating_site, :through => :content_associations, 
                             :source => :associable, 
                             :conditions => ["content_associations.originating_site = ?" true]

  has_many :primary_sites, :through => :content_associations, 
                           :source => :associable
                           :conditions => ["content_associations.primary_eligible = ?" true]

  has_many :secondary_sites, :through => :content_associations, 
                             :source => :associable
                             :conditions => ["content_associations.secondary_eligible = ?" true]                         
end

I have tried many variations of the above association declarations, but no matter what I do, I cannot get the behavior I want

@site = Site.find(2)
@article = Article.find(23)
@article.originating_site = @site
@site.originating_articles #=>[@article]

or

@site.primary_articles << @article
@article.primary_sites #=> [@site]

Is Rails built-in polymorphism the wrong mechanism for influencing these connections between sites and their various pieces of content? It seems like this would be useful because I need to connect several different models to the same general model in many ways, but it was difficult for me to find examples using it that way.

, , - , , , , . has_many_polymorphs, , . Rails 3 , .

, .

!

+3
3

, , STI, , .

belongs_to, has_many has_one .. , :

class Article < ActiveRecord::Base
  has_many :associations, :as => :associable, :dependent => :destroy
  has_many :sites, :through => :article_associations

  scope :originating_site, lambda { joins(:article_associations).where('content_associations.originating_site' => true).first }
  scope :primary_sites, lambda { joins(:article_associations).where('content_associations.primary_eligable' => true) }
  scope :secondary_sites, lambda { joins(:article_associations).where('content_associations.secondary_eligable' => true) }
end

class Site < ActiveRecord::Base
  has_many :content_associations, :as => :associable, :dependent => :destroy do
    def articles
      collect(&:associable).collect { |a| a.is_a? Article }
    end
  end
end

class ContentAssociation < ActiveRecord::Base
  belongs_to :site
  belongs_to :associable, :polymorphic => true
  belongs_to :primary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :secondary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :originating_site, :class_name => "Site", :foreign_key => "site_id"
end

, , :

module Content
  class Procs
    cattr_accessor :associations
    @@associations = lambda do
      def articles
        collect(&:associable).collect { |a| a.is_a? Article }
      end

      def events
        collect(&:associable).collect { |e| e.is_a? Event }
      end

      def bios
        collect(&:associable).collect { |b| b.is_a? Bio }
      end
    end
  end
end


class Site < ActiveRecord::Base
  has_many :content_associations, :as => :associable, :dependent => :destroy, &Content::Procs.associations
end

, , DRY :

module Content
  class Procs
    cattr_accessor :associations
    @@associations = lambda do
      %w(articles events bios).each do |type_name|
        type = eval type_name.singularize.classify
        define_method type_name do
          collect(&:associable).collect { |a| a.is_a? type }
        end
      end
    end
  end
end

, . , .

+9

, - , , , . . , , - . , , .

class Article < ActiveRecord::Base
  has_many :article_associations, :dependent => :destroy
  has_many :sites, :through => :article_associations

  scope :originating_site, lambda { joins(:article_associations).where('content_associations.originating_site' => true).first }
  scope :primary_sites, lambda { joins(:article_associations).where('content_associations.primary_eligable' => true) }
  scope :secondary_sites, lambda { joins(:article_associations).where('content_associations.secondary_eligable' => true) }
end

class Site < ActiveRecord::Base
  has_many :content_associations, :dependent => :destroy
  has_many :article_associations
  has_many :articles, :through => :article_associations
end

class ContentAssociation < ActiveRecord::Base
  belongs_to :site
  belongs_to :primary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :secondary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :originating_site, :class_name => "Site", :foreign_key => "site_id"
end

class ArticleAssociation < ContentAssociation
  belongs_to :article
end

, , . , , site.articles, site.content_assocations .

STI type:string. , ContentAssociation. ArticleAssociation article_id, , .

+1

Source: https://habr.com/ru/post/1760107/


All Articles