views:

1856

answers:

3

How would you model the references and citations to publications (articles, books, chapters, etc...)?

A publication can be an article, book or a chapter and it has many references to other publications and other publications refer to it (call these citations)

I need to be able to list the relationships among the publications: The references in a publication and the citations from other publications to this publication

My initial understanding is that this would be a polymorphic relationship to handle the different types of publications and that it would require a bidirectionalself join.

My stab at it

Publication
belongs_to :writing, :polymorphic =>true
has_and_belongs_to_many :references 
   :class_name => "Publication"
   :join_table => 'reference_citation' 
   :foreign_key => 'reference_id'
   :foreign_key => 'citation_id'

Book,    Chapter,    Article all have:
has_many :publications :as =>writing

I find this a bit confusing so any suggestions that would help clarify it would be great. Even object and field naming suggestions.

[I asked a less clear version of this question here.]

I also probably need to use has many through because I will need the ability to destroy the relationship

+1  A: 

I have an incomplete answer over at http://github.com/francois/so-536261/tree/master

Basically, the DB schema does support your use case, but ActiveRecord doesn't. The solution will probably involve using find by sql or other tricks.

François Beausoleil
+5  A: 

Here's a solution that doesn't use Single Table Inheritance for the publications. That means that there are articles, books and chapters tables, instead of one publications table. Here are the commands to run to create the app:

$ rails myproject
$ cd myproject
$ script/generate model book name:string
$ script/generate model chapter name:string
$ script/generate model article name:string
$ script/generate model citation publication_type:string publication_id:integer reference_type:string reference_id:integer

Create this file in lib/acts_as_publication.rb:

module ActsAsPublication
  def self.included(base)
    base.extend(ClassMethods)
  end
  module ClassMethods
    def acts_as_publication
      has_many :citations, :as => :publication
      has_many :references, :as => :reference, :class_name => "Citation"      
    end
  end
end

Create this file in config/initializers/acts_as_publication.rb:

ActiveRecord::Base.send(:include, ActsAsPublication)

Then call that in each model, Article, Book and Chapter, like this:

class Article < ActiveRecord::Base
  acts_as_publication
end

Then add these relationships in app/models/citation.rb:

class Citation < ActiveRecord::Base
  belongs_to :publication, :polymorphic => true
  belongs_to :reference, :polymorphic => true
end

Now we can create the DB and try it out from the console:

$ rake db:migrate
$ script/console 
Loading development environment (Rails 2.2.2)
>> a = Article.create!(:name => "a")
=> #<Article id: 1, ...>
>> b = Article.create!(:name => "b")
=> #<Article id: 2, ...>
>> Citation.create!(:publication => a, :reference => b)
=> #<Citation id: 1, publication_type: "Article", publication_id: 1, reference_type: "Article", reference_id: 2, created_at: "2009-02-15 13:14:27", updated_at: "2009-02-15 13:14:27">
>> a.citations
=> [#<Citation id: 1, ...>]
>> a.references
=> []
>> b.citations
=> []
>> b.references
=> [#<Citation id: 1, ...>]
>> Book.create!(:name => "foo")
=> #<Book id: 1, name: "foo", created_at: "2009-02-15 13:18:23", updated_at: "2009-02-15 13:18:23">
>> a.citations.create(:reference => Book.first)
=> #<Citation id: 2, publication_type: "Article", publication_id: 1, reference_type: "Book", reference_id: 1, created_at: "2009-02-15 13:18:52", updated_at: "2009-02-15 13:18:52">
>> Book.first.references
=> [#<Citation id: 2, ...>]
>> a.citations
=> [#<Citation id: 1, publication_type: "Article", publication_id: 1, reference_type: "Article", reference_id: 2, created_at: "2009-02-15 13:14:27", updated_at: "2009-02-15 13:14:27">, #<Citation id: 2, publication_type: "Article", publication_id: 1, reference_type: "Book", reference_id: 1, created_at: "2009-02-15 13:18:52", updated_at: "2009-02-15 13:18:52">]
pjb3
+4  A: 

Here's a solution using a self-referential relationship using single table inheritance. Use these commands to create the app:

$ rails myproject
$ cd myproject
$ script/generate model publication type:string name:string
$ script/generate model citation publication_id:integer reference_id:integer

The setup the relationships this way:

class Publication < ActiveRecord::Base
  has_many :citations
  has_many :cited_publications, :through => :citations, :source => :reference
  has_many :references, :foreign_key => "reference_id", :class_name => "Citation"
  has_many :refered_publications, :through => :references, :source => :publication
end

class Citation < ActiveRecord::Base
  belongs_to :publication
  belongs_to :reference, :class_name => "Publication"
end

class Article < Publication
end

class Book < Publication
end

class Chapter < Publication
end

Now we can create the DB and try it out from the console:

$ rake db:migrate
$ script/console 
Loading development environment (Rails 2.2.2)
>> a = Article.create!(:name => "Article")
=> #<Article id: 1, ...>
>> b = Book.create!(:name => "Book")
=> #<Book id: 2, ...>
>> a.citations.create(:reference => b)
=> #<Citation id: 1, publication_id: 1, reference_id: 2, created_at: "2009-02-15 14:13:15", updated_at: "2009-02-15 14:13:15">
>> a.citations
=> [#<Citation id: 1, ...>]
>> a.references
=> []
>> b.citations
=> []
>> b.references
=> [#<Citation id: 1, publication_id: 1, reference_id: 2, created_at: "2009-02-15 14:13:15", updated_at: "2009-02-15 14:13:15">]    
>> a.cited_publications
=> [#<Book id: 2, type: "Book", name: "Book", created_at: "2009-02-15 14:11:00", updated_at: "2009-02-15 14:11:00">]
>> a.refered_publications
=> []
>> b.cited_publications
=> []
>> b.refered_publications
=> [#<Article id: 1, type: "Article", name: "Article", created_at: "2009-02-15 14:10:51", updated_at: "2009-02-15 14:10:51">]
pjb3
Just a little caution for others: referred has two Rs
srboisvert