Say I have a Statement
model, which has_many :months
. There are always 12 months associated with a statement, but the first month can vary (eg months = [Mar, Apr, May...Jan, Feb])
Given a certain month, what's the MVC way to find the previous month?
I find myself accessing it through the statement
which feels dirty:
# statement.rb
has_many :months
def previous_month(month)
if months.index(month) == 0
return nil
else
return months[months.index(month) - 1]
end
end
# blergh
prev_month = month.statement.previous_month(month)
Should I have a pr开发者_C百科evious_month_id
column in my database? How would you implement this functionality? I'm using Rails 2.3.x
.
I would define it on the Month
model to cut back on the roundtrips.
# month.rb
class Month < ActiveRecord::Base
belongs_to :statement, :include => :months
def previous
return nil if self.index == 0
find_or_create_by_index_and_statement_id(self.index - 1, self.statement.id)
end
def index
statement.months.index self
end
end
so that you can get june.previous
. This should even work on unsaved records.
How are these months added? If they're added separately in the chronological order of the months then you can simply do what you have but you should define the order in the relationship.
#statement.rb
has_many :months, :order => 'created_at ASC'
If they're added some other way then you might need to think about having an order column and using acts_as_list to maintain the order.
To do this the MVC way, I would probably push this logic on the thing that 'owns' the statements. After all a statement usually belongs to something. After reading the comments though, it sounds like this is an inherited project. If not you have to ask why would you have a 'months' relationship, when the statement has a created_at
column you can tie into? Here is what i came up with, it may not be helpful to you. Although do checkout Date::MONTHNAMES
at the very least it sounds like that might be helpful to you.
describe User do
before(:each) do
@user = User.create!
end
it "should know about months" do
Statement.create(:user => @user)
@user.statements.last.month_name.should == "November"
end
it "should report last months statement as nil when there is no statement" do
@user.last_months_statement.should be_nil
end
it "should report last months statement as nil if there is only one for this month" do
Statement.create(:user => @user)
@user.last_months_statement.should be_nil
end
it "should report a statement from the previous month if there is one" do
target = Statement.create(:user => @user, :created_at => 1.month.ago)
Statement.create(:user => @user)
@user.last_months_statement.should == target
end
it "should report last months statement if there a several" do
Statement.create(:user => @user, :created_at => 1.month.ago)
Statement.create(:user => @user)
Statement.create(:user => @user, :created_at => 2.months.ago)
@user.last_months_statement.month_name.should == "October"
end
end
class User < ActiveRecord::Base
has_many :statements, :order => "created_at"
def last_months_statement
if statements.size <= 1 || statements.last.created_at.month < Time.now.month
nil
else
index = statements.index(statements.last)
statements[index - 1]
end
end
end
class Statement < ActiveRecord::Base
belongs_to :user
def month
created_at.month
end
def month_name
Date::MONTHNAMES[created_at.month]
end
end
精彩评论