ruby over rails

73
Ruby over Rails Ruby Day 2014

Upload: giuseppe-modarelli

Post on 02-Dec-2014

215 views

Category:

Education


0 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: Ruby over Rails

Ruby over RailsRuby Day 2014

Page 2: Ruby over Rails

Giuseppe Modarelli

Ruby DeveloperJavascript Developer

Drummer

gmodarelli gmodarelli gmodarelli.com

Page 3: Ruby over Rails
Page 4: Ruby over Rails
Page 5: Ruby over Rails

xml2json

Page 6: Ruby over Rails
Page 7: Ruby over Rails

Why Ruby over Rails?

Page 8: Ruby over Rails

Your app uses Rails

Page 9: Ruby over Rails

Your app is not Rails

Page 10: Ruby over Rails

What’s Rails?

Page 11: Ruby over Rails

Corey Haines

Page 12: Ruby over Rails

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

Page 13: Ruby over Rails

HTTP DBYOUR APP

Page 14: Ruby over Rails

A real life example

Page 15: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 16: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 17: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 18: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 19: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 20: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 21: Ruby over Rails

WHAT THE SHISH!!?!?!?

Page 22: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 23: Ruby over Rails

<div id= <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> </div>

How can we solve this?

Page 24: Ruby over Rails

<div id= <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> </div>

Don’t start over!

Page 25: Ruby over Rails

<div id= <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> </div>

Clean up existing code

Page 26: Ruby over Rails

<div id="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> <div class="<%= day %>"></div> </div> ! <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> <div class="<%= day %>"></div> </div> ! <div class="date <%= @contract.invoices.first.sent_at.past? ? 'done' : 'pending' %>"> <% month = @contract.invoices.first.sent_at.strftime(‘%B’).downcase %> <% day = @contract.invoices.first.sent_at.day.to_s %> <div class="<%= month %>"></div> <div class="<%= day %>"></div> </div> </div>

Page 27: Ruby over Rails

<div id= <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> ! <div <% month = <% day = <div <div </div> </div>

HAML

Page 28: Ruby over Rails

%div{ id: "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}" }

Page 29: Ruby over Rails

%div{ id: "dates" } %div{ class: "date #{@contract.signed_at.past? ? 'done' : 'pending'} - month = @contract.signed_at.strftime('%B') - day = %div{ class: "#{month} %div %div{ class: "date #{@contract.activation_date.past? ? 'done' : 'pending'} - month = @contract.activation_date.strftime('%B') - day = %div{ class: "#{month} %div %div{ class: "date #{@contract.first_invoice_sent_at.past? ? 'done' : 'pending'} - month = @contract.first_invoice_sent_at.strftime('%B') - day = %div{ class: "#{month} %div

SLIM

Page 30: Ruby over Rails

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

Page 31: Ruby over Rails

div#dates div - month = - day = div div div - month = - day = div div div - month = - day = div div

SLIM LOGIC-LESS

Page 32: Ruby over Rails

div#dates div - month = - day = div div div - month = - day = div div div - month = - day = div div

Programming by Wishful Thinking

Page 33: Ruby over Rails

div#dates - @dates div class="date #{status}" div class=month div class=day

Page 34: Ruby over Rails

div - div div div

Ok cool. But we do need that logic!

Page 35: Ruby over Rails

div - div div div

M V C

Page 36: Ruby over Rails

div - div div div

M V C

Page 37: Ruby over Rails

div - div div div

M V C

Page 38: Ruby over Rails

div - div div div

M V C

Page 39: Ruby over Rails

div - div div div

PORO

Page 40: Ruby over Rails

div#dates - @dates div class="date #{status}" div class=month div class=day

Page 41: Ruby over Rails

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

Page 42: Ruby over Rails

class Presenters::CalendarWidget def initialize date end ! def status "done" end end

Page 43: Ruby over Rails

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

Page 44: Ruby over Rails

class Presenters::CalendarWidget def initialize date @date = date end ! def status return "done" if @date.past? "pending" end end

Page 45: Ruby over Rails

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

Page 46: Ruby over Rails

class Presenters::CalendarWidget . . . ! def month @date.strftime('%B').downcase end end

Page 47: Ruby over Rails

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

Page 48: Ruby over Rails

class Presenters::CalendarWidget ! . . . ! def day @date.day.to_s end end

Page 49: Ruby over Rails

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

Page 50: Ruby over Rails

div#dates - @dates div class="date #{status}" div class=month div class=day

Page 51: Ruby over Rails

Where does @dates come from?

Page 52: Ruby over Rails

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

Page 53: Ruby over Rails
Page 54: Ruby over Rails

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

Page 55: Ruby over Rails

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

Page 56: Ruby over Rails

class DashboardController def index @dates = Presenters::CalendarWidget.for( current_user.relevant_contract_dates ) end end

Page 57: Ruby over Rails

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

Page 58: Ruby over Rails

class Presenters::CalendarWidget . . . ! def self.for dates dates.each.map do |date| new date end end end

Page 59: Ruby over Rails

class User < ActiveRecord::Base . . . ! def relevant_contract_dates [ contract.signed_at, contract.activation_date, contract.invoices.first.sent_at ] end end

Page 60: Ruby over Rails

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

Page 61: Ruby over Rails

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

Page 62: Ruby over Rails

class Contract < ActiveRecord::Base . . . ! def first_invoice_date invoices.first.sent_at end end

Page 63: Ruby over Rails

class Contract < ActiveRecord::Base . . . ! def first_invoice_date first_invoice.sent_at end ! def first_invoice invoices.first end end

Page 64: Ruby over Rails

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

Page 65: Ruby over Rails

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

Page 66: Ruby over Rails

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

Page 67: Ruby over Rails

class Collection::CalendarWidgets . . . ! def pending all.select { |date| date.status == "pending" } end ! def done all.select { |date| date.status == "done" } end end

Page 68: Ruby over Rails

When to use these techniques

Page 69: Ruby over Rails

DHH Style

Page 70: Ruby over Rails

BULLSHIT!!!

Page 71: Ruby over Rails

ALWAYS

Page 72: Ruby over Rails

Thank You

Page 73: Ruby over Rails

Giuseppe Modarelli

Ruby DeveloperJavascript Developer

Drummer

gmodarelli gmodarelli gmodarelli.com