In 2007, I gave a talk about using Presenters with Rails. Here I re-visit the topic drawing on experiences developing an application for the UK Government.
was inspired by the various GUI patterns documented by Martin Fowler. The Presenter pattern addresses bloated controllers and views containing logic in concert by creating a class representation of the state of the view. An architecture that uses the Presenter pattern provides view specific data as attributes of an instance of the Presenter. The Presenter's state is an aggregation of model and user entered data.” – Jay Fields (2007)
aggressive! Try to keep your controller actions and views as slim as possible. A one-line action is a thing of wonder, as is a template that is mostly HTML. It is also much more maintainable than a view that is full of assignment statements and chained method calls.” – Jamis Buck (2007)
Lender.find_by_id(id) end def received_on=(value) @received_on = QuickDateFormatter.parse(value) end def loans @loans ||= lender.loans.demanded.includes(:lending_limit).map {|loan| SettleLoan.new(loan) } end def loans_attributes=(values) values.each do |_, attributes| loan = loans_by_id[attributes['id'].to_i] loan.settled = (attributes['settled'] == '1') loan.settled_amount = attributes['settled_amount'] end end def attributes=(values) sanitize_for_mass_assignment(values).each do |attr, value| public_send("#{attr}=", value) end end def save # This is intentionally eager. We want to run all of the validations. return false if invalid?(:details) | invalid?(:save) | settled_loans.map(&:invalid?).any? ActiveRecord::Base.transaction do
Lender.find_by_id(id) end def received_on=(value) @received_on = QuickDateFormatter.parse(value) end def loans @loans ||= lender.loans.demanded.includes(:lending_limit).map {|loan| SettleLoan.new(loan) } end def loans_attributes=(values) values.each do |_, attributes| loan = loans_by_id[attributes['id'].to_i] loan.settled = (attributes['settled'] == '1') loan.settled_amount = attributes['settled_amount'] end end def attributes=(values) sanitize_for_mass_assignment(values).each do |attr, value| public_send("#{attr}=", value) end end def save # This is intentionally eager. We want to run all of the validations. return false if invalid?(:details) | invalid?(:save) | settled_loans.map(&:invalid?).any? ActiveRecord::Base.transaction do
Lender.find_by_id(id) end def received_on=(value) @received_on = QuickDateFormatter.parse(value) end def loans @loans ||= lender.loans.demanded.includes(:lending_limit).map {|loan| SettleLoan.new(loan) } end def loans_attributes=(values) values.each do |_, attributes| loan = loans_by_id[attributes['id'].to_i] loan.settled = (attributes['settled'] == '1') loan.settled_amount = attributes['settled_amount'] end end def attributes=(values) sanitize_for_mass_assignment(values).each do |attr, value| public_send("#{attr}=", value) end end def save # This is intentionally eager. We want to run all of the validations. return false if invalid?(:details) | invalid?(:save) | settled_loans.map(&:invalid?).any? ActiveRecord::Base.transaction do
invoice.reference = self.reference invoice.period_covered_quarter = self.period_covered_quarter invoice.period_covered_year = self.period_covered_year invoice.received_on = self.received_on invoice.created_by = self.creator end end def settle_loans! settled_loans.each do |loan| loan.settle!(invoice, self.creator) end end def loans_by_id @loans_by_id ||= loans.index_by(&:id) end def settled_loans loans.select(&:settled?) end end
invoice.reference = self.reference invoice.period_covered_quarter = self.period_covered_quarter invoice.period_covered_year = self.period_covered_year invoice.received_on = self.received_on invoice.created_by = self.creator end end def settle_loans! settled_loans.each do |loan| loan.settle!(invoice, self.creator) end end def loans_by_id @loans_by_id ||= loans.index_by(&:id) end def settled_loans loans.select(&:settled?) end end