Design Patterns on Rails

  • 1,710 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,710
On Slideshare
0
From Embeds
0
Number of Embeds
6

Actions

Shares
Downloads
11
Comments
0
Likes
3

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • \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

Transcript

  • 1. Design Patterns on Rails Thiago Pradi
  • 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...
  • 5. Plugins• Padrão act_as_.....• Instalação simples• Convenção sobre configuração• Porém...
  • 6. Problemas..• Alta utilização de monkey patch• Nasce o alias_method_chain• OMG! SO EVIL!
  • 7. alias_method_chain
  • 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.
  • 12. User.rb
  • 13. Responsabilidades• Envio de e-mail• Processamento de CSV• Upload de Arquivos• Geração de HTML
  • 14. Responsabilidades II• Cálculos complexos• Geração de PDF• Processamento de Imagens• Liste a sua!
  • 15. Resultado disso..• Testes lentos• Altamente acoplados• Manutenção horrível!
  • 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!
  • 17. SOLID• Single Responsability• Open-closed• Liskov Substitution• Interface Segregation• Dependecy Inversion
  • 18. ActionController::Baseree-1.8.7-2011.03 :017 >ActionController::Base.instance_methods(false).count => 721
  • 19. Upload de Arquivos
  • 20. Péssimo - FileColumn class Entry < ActiveRecord::Base file_column :image end
  • 21. Regular - paperclipclass User < ActiveRecord::Base has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }end
  • 22. 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
  • 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: filtersclass UsersController < ApplicationController before_filter :my_complex_filter def my_complex_filter # Code, Code and code... endend
  • 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. Filtersclass UsersController < ApplicationController before_filter ComplexFilter.newendclass ComplexFilter def filter(controller) # Agora sim! endend
  • 27. Presenters• Extração de lógica da view.• Camada extra entre o Controller e a View.
  • 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. 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
  • 30. Refatorandoclass UsersController < ApplicationController def show @user = User.find(params[:id]) @user_presentter = UserPresenter.new(@user) endend <%= @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. endend
  • 34. Refatorandoclass ShippingCalculator attr_accessor :order def initialize(order) @order = order end def value # copy and paste endend
  • 35. Modelclass Order def calculate_shipping ShippingCalculator.new(self).value endend
  • 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. 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])
  • 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. 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))
  • 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!
  • 43. Vantagens• Facilita testabilidade• Feature testada de forma isolada• Reuso de forma inteligente• Testes voando!
  • 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á fazendo2. Você não sabe o que está fazendo3. 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.saveSerá mesmo que está claro para o programador que após salvar o usuário, o log será criado?
  • 48. Obrigado!• thiago.pradi@gmail.com• www.thiagopradi.net• http://www.github.com/tchandy• http://www.twitter.com/thiagopradi