• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Design Patterns on Rails
 

Design Patterns on Rails

on

  • 1,935 views

 

Statistics

Views

Total Views
1,935
Views on SlideShare
1,557
Embed Views
378

Actions

Likes
3
Downloads
11
Comments
0

14 Embeds 378

http://gabrielsobrinho.com 202
http://gabrielsobrinho.tumblr.com 68
http://assets.txmblr.com 60
http://localhost:4567 20
http://localhost 12
http://paper.li 3
https://twitter.com 2
http://sobrinho.ghost.io 2
http://www.hanrss.com 2
http://sobrinho.herokuapp.com 2
http://www.hanrss.com 2
http://example.com 1
http://localhost:8080 1
http://www.gabrielsobrinho.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Design Patterns on Rails Design Patterns on Rails Presentation Transcript

  • Design Patterns on Rails Thiago Pradi
  • Thiago Pradi• Desenvolvedor Web na ZInfinit• Ruby/Rails a 3 anos• Participante do Ruby Summer of Code• Sócio-Fundador da THP Informática
  • Padrões no Rails• No inicio, era só trevas...• Rails, nascido com o MVC• Simplicidade ao máximo
  • Primórdios• Muitos desenvolvedores vindo de .net / java• Atraídos por simplicidade e produtividade• Nascem aplicações cada vez mais complexas• Surgindo os plugins...
  • Plugins• Padrão act_as_.....• Instalação simples• Convenção sobre configuração• Porém...
  • Problemas..• Alta utilização de monkey patch• Nasce o alias_method_chain• OMG! SO EVIL!
  • alias_method_chain
  • Caso Octopus• Abusa de Monkey patch.• Parecia legal, no ínicio• as coisas foram mudando e...• terá que ser totalmente refatorado para Rails 3.1 =/
  • Discussões sobre código• Vários blogs criados• Discussões sobre códigos• Padrões surgindo...
  • Fat Model, Skinny Controller• Introduzido pelo blog Rails best practices• Mover código do controller para o model• Favorecendo testabilidade
  • 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.
  • User.rb
  • Responsabilidades• Envio de e-mail• Processamento de CSV• Upload de Arquivos• Geração de HTML
  • Responsabilidades II• Cálculos complexos• Geração de PDF• Processamento de Imagens• Liste a sua!
  • Resultado disso..• Testes lentos• Altamente acoplados• Manutenção horrível!
  • 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!
  • SOLID• Single Responsability• Open-closed• Liskov Substitution• Interface Segregation• Dependecy Inversion
  • ActionController::Baseree-1.8.7-2011.03 :017 >ActionController::Base.instance_methods(false).count => 721
  • Upload de Arquivos
  • Péssimo - FileColumn class Entry < ActiveRecord::Base file_column :image end
  • Regular - paperclipclass User < ActiveRecord::Base has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }end
  • Bom - Carrierwaveclass AvatarUploader < CarrierWave::Uploader::Base storage :file def store_dir public/my/upload/directory end def extension_white_list %w(jpg jpeg gif png) endendclass User mount_uploader :avatar, AvatarUploaderend
  • POROs• Plain Old Ruby Object• Simplesmente um Objeto Limpo Ruby.• Não tenha medo de escrever uma nova classe!• Culpa do famigerado lib/ ...
  • Exemplo: filtersclass UsersController < ApplicationController before_filter :my_complex_filter def my_complex_filter # Code, Code and code... endend
  • Problemas• Como testar esse filtro de forma isolada?• Ao invés de unitários, escrevemos testes de integração!• POROs para salvar o dia!
  • Filtersclass UsersController < ApplicationController before_filter ComplexFilter.newendclass ComplexFilter def filter(controller) # Agora sim! endend
  • Presenters• Extração de lógica da view.• Camada extra entre o Controller e a View.
  • 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 %>
  • Presenterclass 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 endend
  • Refatorandoclass UsersController < ApplicationController def show @user = User.find(params[:id]) @user_presentter = UserPresenter.new(@user) endend <%= @user_presentter.activity_date %>
  • Mas, e os Helpers?• Helpers são somente funções• Presenters são “Helpers” orientados a objeto!• Responsabilidades extras: controle de cache, etc.
  • 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
  • Exemplo..class Order def calculate_shipping # blablabla, method with 50 lines. endend
  • Refatorandoclass ShippingCalculator attr_accessor :order def initialize(order) @order = order end def value # copy and paste endend
  • Modelclass Order def calculate_shipping ShippingCalculator.new(self).value endend
  • Pontos Positivos• Removendo responsabilidades do model• Método gigante pode ser quebrado em métodos menores• Testado de forma isolada
  • 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?!
  • Exemploclass UserUpdaterService attr_accessor :user def initialize(user_id) @user = User.find(user_id) end def update # code for my update method. endendUserUpdaterService.new(params[:id])
  • 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 :(
  • Refatorandoclass UserUpdaterService attr_accessor :user def initialize(user) @user = user end def update # code for my update method. endendUserUpdaterService.new(User.find(params[:id]))# ou para testarUserUpdaterService.new(mock_model(User))
  • mas, mocks?!• Eu ouvi falar que mocks são do mau!• O problema não são os mocks• São VOCÊ!
  • Opiniões• To be a successful mockist, you must dislike mocks.• Isole os testes unitários, porém cubra eles com integração!
  • Vantagens• Facilita testabilidade• Feature testada de forma isolada• Reuso de forma inteligente• Testes voando!
  • #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ê!
  • 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/
  • Extra #1 Três Regras para escrever callbacks:1. Somente escreva um callback quando você souber o que está fazendo2. Você não sabe o que está fazendo3. Veja regra 1.
  • Extra #2 class User < ActiveRecord::Base def after_save Log.create(:class => "User", :attrs => attributes) end end @user = User.find(1) @user.saveSerá mesmo que está claro para o programador que após salvar o usuário, o log será criado?
  • Obrigado!• thiago.pradi@gmail.com• www.thiagopradi.net• http://www.github.com/tchandy• http://www.twitter.com/thiagopradi