Solution : Denormalize into Text Fields
Take a look at the following Article model and the associated State and Category models:class Article < ActiveRecord::Base
belongs_to :state
belongs_to :category
validates :state_id, :presence => true
validates :category_id, :presence => true
end
class State < ActiveRecord::Base
has_many :articles
end
class Category < ActiveRecord::Base
has_many :articles
end
Given these models, the specific set of available states and categories would be loaded into the production application's respective database tables, and code for working with these associations would be as follows:
@article.state = State.find_by_name("published")
Of course, repeating the finder and the string for the published state is bad practice, so it might be wise to abstract that out into a custom finder on the State model:
@article.state == State.published
The code for dynamically defining these custom finder methods might be as follows:
class State < ActiveRecord::Base
validates :name => :presence => true
class << self
all.each do |state|
define_method "#{state}" do
first(:conditions => { :name => state })
end
end
end
end
There is quite a bit of functionality associated with these types of models (states, categories, and so on) and, therefore, it's not desirable to allow end users or even administrators - to add or remove available states in the database.
For Example, if the article publication workflow changes and a new state needs to be added, it's unlikely that an administrator can simply add the state to the database and have everything as desired.
You would denormalize the data from the state and category tables into the article table itself, and you would remove the State and Category models.
When you do all this, the Article model looks as follows:
class Article < ActiveRecord::Base
STATES = %W(draft review published archived)
CATEGORIES = %w(tips faqs misc)
validates :state, :inclusion => {:in => STATES}
validates :category, :inclusion => {:in => CATEGORIES}
STATES.each do |state|
define_method "#{state}?" do
self.state == state
end
end
CATEGORIES.each do |category|
define_method "#{category}?" do
self.category == category
end
end
class << self
STATES.each do |state|
define_method "#{state}" do
state
end
end
CATEGORIES.each do |category|
define_method "#{category}" do
category
end
end
end
end
As you can see, the total code shown here for the normalized version is very similar to the code for the denomalized version. The dynamic methods are still being defined but the difference here is that the Article model now has state and category columns that contain a string representing the state instead of foreign key columns to hold the ID of the State and Category.
No comments:
Post a Comment