2. Thiago Pradi
• Desenvolvedor Web na ZInfinit
• Ruby/Rails a 3 anos
• Participante do Ruby Summer of Code
• Sócio-Fundador da THP Informática
3. Padrões no Rails
• No inicio, era só trevas...
• Rails, nascido com o MVC
• Simplicidade ao máximo
4. Primórdios
• Muitos desenvolvedores vindo de .net / java
• Atraídos por simplicidade e produtividade
• Nascem aplicações cada vez mais
complexas
• Surgindo os plugins...
8. Caso Octopus
• Abusa de Monkey patch.
• Parecia legal, no ínicio
• as coisas foram mudando e...
• terá que ser totalmente refatorado para
Rails 3.1 =/
9. Discussões sobre
código
• Vários blogs criados
• Discussões sobre códigos
• Padrões surgindo...
10. Fat Model, Skinny
Controller
• Introduzido pelo blog Rails best practices
• Mover código do controller para o model
• Favorecendo testabilidade
11. Parece bom, porém...
• Alto acoplamento no model
• Model com 1001 utilidades
• e 328193892189 linhas de código! ZONG!
• 1325 Callbacks, com zilhões de ifs.
16. Design patterns!
• Não realmente
• Falarei sobre experiências de trabalho
• Coisas que podem melhorar seu dia a dia
• Design Patterns existem vários livros!
21. Regular - paperclip
class User < ActiveRecord::Base
has_attached_file :avatar,
:styles => {
:medium => "300x300>",
:thumb => "100x100>"
}
end
22. Bom - Carrierwave
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'public/my/upload/directory'
end
def extension_white_list
%w(jpg jpeg gif png)
end
end
class User
mount_uploader :avatar, AvatarUploader
end
23. POROs
• Plain Old Ruby Object
• Simplesmente um Objeto Limpo Ruby.
• Não tenha medo de escrever uma nova
classe!
• Culpa do famigerado lib/ ...
24. Exemplo: filters
class UsersController < ApplicationController
before_filter :my_complex_filter
def my_complex_filter
# Code, Code and code...
end
end
25. Problemas
• Como testar esse filtro de forma isolada?
• Ao invés de unitários, escrevemos testes de
integração!
• POROs para salvar o dia!
26. Filters
class UsersController < ApplicationController
before_filter ComplexFilter.new
end
class ComplexFilter
def filter(controller)
# Agora sim!
end
end
28. Código
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
end
<% if @user.active? %>
<%= @user.activated_at.strftime("%d/%m/%y") %>
<% else %>
<%= @user.created_at.strftime("%d/%m/%y") %>
<% end %>
29. Presenter
class UserPresenter
attr_accessor :user
def initialize(user)
@user = user
end
def activity_date
if @user.active?
@user.activated_at.strftime("%d/%m/%y")
else
@user.created_at.strftime("%d/%m/%y")
end
end
end
30. Refatorando
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@user_presentter = UserPresenter.new(@user)
end
end
<%= @user_presentter.activity_date %>
31. Mas, e os Helpers?
• Helpers são somente funções
• Presenters são “Helpers” orientados a
objeto!
• Responsabilidades extras: controle de
cache, etc.
32. Break Out Method
Object
• Extraído do Livro: Working Effectively With
Legacy Code
• A idéia é simples, transformar métodos
complexos em objetos.
• Crie um novo objeto, passando os
parametros necessários para o construtor,
e copiando o corpo do método
33. Exemplo..
class Order
def calculate_shipping
# blablabla, method with 50 lines.
end
end
35. Model
class Order
def calculate_shipping
ShippingCalculator.new(self).value
end
end
36. Pontos Positivos
• Removendo responsabilidades do model
• Método gigante pode ser quebrado em
métodos menores
• Testado de forma isolada
37. Dependency Injection
• Reduz acoplamente entre objetos
• Objeto devem focar em suas
responsabilidades, tendo suas dependências
bem definidas.
• Aplicado extensivamente em Java/C#
• Por que não usar em Ruby?!
38. Exemplo
class UserUpdaterService
attr_accessor :user
def initialize(user_id)
@user = User.find(user_id)
end
def update
# code for my update method.
end
end
UserUpdaterService.new(params[:id])
39. Problemas
• o UserUpdater depende do usuário
• para testar, teriamos que criar um novo
usuário no banco, e passar a id dele para o
UserUpdater
• Ruim e lento :(
40. Refatorando
class UserUpdaterService
attr_accessor :user
def initialize(user)
@user = user
end
def update
# code for my update method.
end
end
UserUpdaterService.new(User.find(params[:id]))
# ou para testar
UserUpdaterService.new(mock_model(User))
41. mas, mocks?!
• Eu ouvi falar que mocks são do mau!
• O problema não são os mocks
• São VOCÊ!
42. Opiniões
• To be a successful mockist, you must dislike
mocks.
• Isole os testes unitários, porém cubra eles
com integração!
44. #FicaADica
• Railers acabaram esquecendo de OO
• A beleza do Ruby está na sua OO
• Não seja bitolado, pense diferente :)
• Tente, experimente, pense.. e veja o que
funciona para você!
45. Leituras Recomendadas
• Blog do Steve Klabnik - http://
blog.steveklabnik.com/
• Blog do Cássio Marques - http://
cassiomarques.wordpress.com/
• Working Effectively with Legacy Code -
http://goo.gl/DKrF0
• James on Software - http://jamesgolick.com/
46. Extra #1
Três Regras para escrever callbacks:
1. Somente escreva um callback quando você
souber o que está fazendo
2. Você não sabe o que está fazendo
3. Veja regra 1.
47. Extra #2
class User < ActiveRecord::Base
def after_save
Log.create(:class => "User", :attrs => attributes)
end
end
@user = User.find(1)
@user.save
Será mesmo que está claro para o programador
que após salvar o usuário, o log será criado?