Reputation: 1473
A rails app contains many different Pages of content. The Pages are organized into small groups known as Sections:
class Page < ActiveRecord::Base
attr_accessible: section_id #etc..
belongs_to :section
end
class Section < ActiveRecord::Base
attr_accessible :title #, etc...
has_many :pages
end
The Sections need to be organized too, but what is the best way to do this - re-use sections themselves, or create a new Unit model?
Option 1 - Re-use Section
Allow Section to have children and parent Sections. That way, you don't need to create another model with similar fields as Section. Some sections will have_many pages, and other sections will have_many children sections:
class Section < ActiveRecord::Base
attr_accessible :parent_id :title # etc...
has_many :pages
belongs_to :parent, class_name: "Section"
has_many :children, class_name: "Section", foreign_key: "parent_id"
end
Option 2 - new Unit model
Create another model called Unit to organize the sections. It will have many similar fields to section, but it will be a clearly separate entity.
class Section < ActiveRecord::Base
attr_accessible :title, :unit_id # etc...
has_many :pages
belongs_to :units
end
class Unit < ActiveRecord::Base
attr_accessible :title # etc...
has_many :sections
end
The advantage of Option 1 is it avoid some duplication, and could be adapted in the future if even more levels are needed. However, Option 2 clearly separates the roles of Sections, which have_many pages, from Units, which have_many Sections, which can help keep other code clear. Which is the best approach?
Update
It seems Option 2 would have clearer code, such as when going through all the Sections. Is it worth re-using Sections if it would make some code more complicated? For example, here's how to list all the Sections in an organized manner:
Option 2 - For each Unit, list all the child sections. Then list any Sections that aren't in any Unit.
Option 1 - For each parent Section, list all the children Sections. Then list any Section that has with no parent Section or child Section.
Upvotes: 5
Views: 2112
Reputation: 5182
It really depends on how far you want to go. If it's just one extra level of hierarchy, then definitely go with the new model. If you're looking to be able to go 2+ levels deep, definitely go with the option of reusing sections.
Upvotes: 2
Reputation: 5644
It will be worth reusing Section(using Option 1), if you see Section and its children having exactly the same methods defined in them. Otherwise, you should go for Option 2.
Regarding the concerns you had on how to list all the Sections in an organized manner:
Option 1 - This isn't and can be done unless you want to iterate through one collection which has the parent section and the children sections. See how we could do some of the queries in ActiveRecord below:
sections_with_parent = Section.joins(:parent)
sections_with_children = Section.joins(:children).uniq
parent_key_with_children_values = Section.joins(:children).uniq.inject({}) do |result, section|
result.merge({section => section.children})
end
sections_with_no_parent = Section.where(parent_id: nil)
Option 2 - Below are some code for comparison with above:
sections_with_parent = Section.joins(:unit)
units_with_children = Unit.joins(:sections).uniq
parent_key_with_children_values = Unit.joins(:sections).uniq.inject({}) do |result, unit|
result.merge({unit => unit.sections })
end
sections_with_no_parent = Section.where(unit_id: nil)
As you can see, both options will have very similar code for listing children and parents so that shouldn't be a concern when deciding on what option to go for.
Upvotes: 3
Reputation: 7172
You don't have to use a relational database for all of your data storage.
Mongodb (mongoid: http://mongoid.org/en/mongoid/index.html) may be a good solution to your problem.
class Page
include Mongoid::Document
embeds_many :sections, :class_name => 'Sections', :inverse_of => :page
end
class Section
include Mongoid::Document
field :title, :type => String, :default => ''
embedded_in :page, :class_name => 'Page', :inverse_of => :sections
end
Upvotes: 0
Reputation: 13521
I would go with the nested set of sections using awesome_nested_set. By going this route you reduce the number of database calls needed to get a section and all of its child sections. The Unit class doesn't do much other than group sections and seems like it also duplicates columns common to section e.g. title... Another thing to note is if your requirements include the ability to have arbitrarily deep nested sections. With the Unit approach you're stuck at 1 level deep.
Upvotes: 1