Ruby has a powerful set of libs and tools that we don't use in our day to day jobs as ruby developers. The main reason for this is because we force logic where it should not be: the template. By refactoring a simple application I'll show you how to unlock the power of Ruby and how to write code that you will love to work with in the future.
9. A set of helpers that takes us from a url to a method for
both incoming requests and outgoing responses (HTTP) An ORM
(DoneRecord) that serves as a database adapter Corey Haines
27. div#dates div class="date #{@contract.signed_at.past? ?
'done' : 'pending'}" - month =
@contract.signed_at.strftime('%B').downcase - day =
@contract.signed_at.day.to_s div class=month div class=day div
class="date #{@contract.activation_date.past? ? 'done' :
'pending'}" - month =
@contract.activation_date.strftime('%B').downcase - day =
@contract.activation_date.day.to_s div class=month div class=day
div class="date #{@contract.first_invoice_sent_at.past? ? 'done' :
'pending'}" - month =
@contract.first_invoice_sent_at.strftime('%B').downcase - day =
@contract.first_invoice_sent_at.day.to_s div class=month div
class=day
28. div#dates div - month = - day = div div div - month = - day
= div div div - month = - day = div div SLIM LOGIC-LESS
29. div#dates div - month = - day = div div div - month = - day
= Programming by Wishful Thinking div div div - month = - day = div
div
30. div#dates - @dates div class="date #{status}" div
class=month div class=day
31. div - div Ok cool. But we do need that logic! div div
32. div - div div M V C div
33. div - div div M V C div
34. div - div div M V C div
35. div - div div M V C div
36. div - div div PORO div
37. div#dates - @dates div class="date #{status}" div
class=month div class=day
38. describe Presenters::CalendarWidget do let(:date) {
DateTime.parse '2014/09/26 08:00:00' } ! describe '#status' do it
'is "done" when the date is in the past' do calendar_widget =
described_class.new date expect(calendar_widget.status).to
eq('done') end end end
39. class Presenters::CalendarWidget def initialize date end !
def status "done" end end
40. describe Presenters::CalendarWidget do let(:date) {
DateTime.parse '2014/09/26 08:00:00' } ! describe '#status' do it
'is "done" when the date is in the past' do calendar_widget =
described_class.new date expect(calendar_widget.status).to
eq('done') end ! it 'is "pending" when the date is in the future'
do calendar_widget = described_class.new date + 1.day
expect(calendar_widget.status).to eq('pending') end end end
41. class Presenters::CalendarWidget def initialize date @date
= date end ! def status return "done" if @date.past? "pending" end
end
42. describe Presenters::CalendarWidget do let(:date) {
DateTime.parse '2014/09/26 08:00:00' } ! . . . ! describe '#month'
do it 'is the month downcased' do calendar_widget =
described_class.new date expect(calendar_widget.month).to
eq('september') end end end
43. class Presenters::CalendarWidget . . . ! def month
@date.strftime('%B').downcase end end
44. describe Presenters::CalendarWidget do let(:date) {
DateTime.parse '2014/09/26 08:00:00' } ! . . . ! describe '#day' do
it 'is the day of the month' do calendar_widget =
described_class.new date expect(calendar_widget.day).to eq('26')
end end end
45. class Presenters::CalendarWidget ! . . . ! def day
@date.day.to_s end end
46. class Presenters::CalendarWidget def initialize date @date
= date end ! def status return "done" if @date.past? "pending" end
! def month @date.strftime('%B').downcase end ! def day
@date.day.to_s end end
47. div#dates - @dates div class="date #{status}" div
class=month div class=day
48. Where does @dates come from?
49. class DashboardController def index @dates = [
Presenter::CalendarWidget.new(current_user.contract.signed_at),
Presenter::CalendarWidget.new(current_user.contract.activation_date),
Presenter::CalendarWidget.new(current_user.contract.invoices.first.sent_at)
] end end
50. class DashboardController def index @dates = [
Presenter::CalendarWidget.new(current_user.contract.signed_at),
Presenter::CalendarWidget.new(current_user.contract.activation_date),
Presenter::CalendarWidget.new(current_user.contract.invoices.first.sent_at)
] end end
51. class DashboardController def index @dates =
Presenters::CalendarWidget.for([ current_user.contract.signed_at,
current_user.contract.activation_date,
current_user.contract.invoices.first.sent_at ]) end end
52. class DashboardController def index @dates =
Presenters::CalendarWidget.for(
current_user.relevant_contract_dates ) end end
53. describe Presenters::CalendarWidget do let(:date) {
DateTime.parse '2014/09/26 08:00:00' } ! . . . ! describe '.for' do
it 'returns a list of widgets' do dates = [ date, date + 2.month ]
widgets = described_class.for dates ! expect(widgets.count).to
be(2) expect(widgets.first.status).to be('done')
expect(widgets.last.status).to be('pending') end end end
54. class Presenters::CalendarWidget . . . ! def self.for dates
dates.each.map do |date| new date end end end
55. class User < ActiveRecord::Base . . . ! def
relevant_contract_dates [ contract.signed_at,
contract.activation_date, contract.invoices.first.sent_at ] end
end
56. class User < ActiveRecord::Base . . . ! delegate:
:signed_at, :activation_date, :invoices, to: :contract ! def
relevant_contract_dates [ signed_at, activation_date,
invoices.first.sent_at ] end end
57. class User < ActiveRecord::Base . . . ! delegate:
:signed_at, :activation_date, :first_invoice_date, to: :contract !
def relevant_contract_dates [ signed_at, activation_date,
first_invoice_date ] end end
58. class Contract < ActiveRecord::Base . . . ! def
first_invoice_date invoices.first.sent_at end end
59. class Contract < ActiveRecord::Base . . . ! def
first_invoice_date first_invoice.sent_at end ! def first_invoice
invoices.first end end
60. class Contract < ActiveRecord::Base . . . ! def
first_invoice_date first_invoice.sent_at end ! def first_invoice
invoices.first || invoices.new( sent_at: activation_date + 2.month)
end end
61. class Presenters::CalendarWidget def self.for dates
dates.each.map {|date| new date } end ! def initialize date @date =
date end ! def status return "done" if @date.past? "pending" end !
def month @date.strftime('%B').downcase end ! def day
@date.day.to_s end end
62. class Collection::CalendarWidgets def self.for dates new
dates end ! def all @dates end ! private def initialize dates
@dates = dates.map do |date| Presenter::CalendarWidget.new date end
end end
63. class Collection::CalendarWidgets . . . ! def pending
all.select { |date| date.status == "pending" } end ! def done
all.select { |date| date.status == "done" } end end