SlideShare uma empresa Scribd logo
1 de 48
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::Base


ree-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 - paperclip
class User < ActiveRecord::Base
  has_attached_file :avatar,
                    :styles => {
                      :medium => "300x300>",
                      :thumb => "100x100>"
                    }
end
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
POROs

• Plain Old Ruby Object
• Simplesmente um Objeto Limpo Ruby.
• Não tenha medo de escrever uma nova
  classe!
• Culpa do famigerado lib/ ...
Exemplo: filters
class UsersController < ApplicationController
  before_filter :my_complex_filter

  def my_complex_filter
    # Code, Code and code...
  end
end
Problemas

• Como testar esse filtro de forma isolada?
• Ao invés de unitários, escrevemos testes de
  integração!
• POROs para salvar o dia!
Filters
class UsersController < ApplicationController
  before_filter ComplexFilter.new
end

class ComplexFilter
  def filter(controller)
    # Agora sim!
  end
end
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 %>
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
Refatorando
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    @user_presentter = UserPresenter.new(@user)
  end
end

     <%= @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.
  end
end
Refatorando
class ShippingCalculator
  attr_accessor :order

  def initialize(order)
    @order = order
  end

  def value
    # copy and paste
  end
end
Model

class Order
  def calculate_shipping
    ShippingCalculator.new(self).value
  end
end
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?!
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])
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 :(
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))
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á fazendo
2. Você não sabe o que está fazendo
3. 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.save



Será 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

Mais conteúdo relacionado

Semelhante a Design Patterns on Rails

Bdd rails 3
Bdd rails 3Bdd rails 3
Bdd rails 3tchandy
 
Palestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVAPalestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVAThiago Cifani
 
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeça
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeçaTrês anos de Scala em Produção: desafios, aprendizados e dores de cabeça
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeçaFelipe Hummel
 
Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...
Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...
Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...Daniel Makiyama
 
Redu walled garden
Redu walled gardenRedu walled garden
Redu walled gardenGuilherme
 
Como desenvolver com um sistema com um front-end colossal?
Como desenvolver com um sistema com um front-end colossal?Como desenvolver com um sistema com um front-end colossal?
Como desenvolver com um sistema com um front-end colossal?Mozart Diniz
 
Desenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmDesenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmGuilherme Blanco
 
Django - Desenvolvimento web ágil com Python
Django - Desenvolvimento web ágil com PythonDjango - Desenvolvimento web ágil com Python
Django - Desenvolvimento web ágil com PythonIgor Sobreira
 
TDD com Código Legado - "Atualizado"
TDD com Código Legado - "Atualizado"TDD com Código Legado - "Atualizado"
TDD com Código Legado - "Atualizado"Cesar Romero
 
qualidade de código: boas práticas, princípios e padrões
qualidade de código: boas práticas, princípios e padrõesqualidade de código: boas práticas, princípios e padrões
qualidade de código: boas práticas, princípios e padrõesedgarddavidson.com
 
Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)
Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)
Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)Douglas Fischer
 
JHipster - Produtividade e Maturidade em suas mãos
JHipster - Produtividade e Maturidade em suas mãosJHipster - Produtividade e Maturidade em suas mãos
JHipster - Produtividade e Maturidade em suas mãosThiago Soares
 
Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010
Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010
Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010lucashungaro
 
Rails - EXATEC2009
Rails - EXATEC2009Rails - EXATEC2009
Rails - EXATEC2009Caue Guerra
 

Semelhante a Design Patterns on Rails (20)

Curso AngularJS - Parte 1
Curso AngularJS - Parte 1Curso AngularJS - Parte 1
Curso AngularJS - Parte 1
 
Bdd rails 3
Bdd rails 3Bdd rails 3
Bdd rails 3
 
Palestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVAPalestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVA
 
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeça
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeçaTrês anos de Scala em Produção: desafios, aprendizados e dores de cabeça
Três anos de Scala em Produção: desafios, aprendizados e dores de cabeça
 
Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...
Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...
Aprendendo Na Prática: Aplicativos Web Com Asp.Net MVC em C# e Entity Framewo...
 
Redu walled garden
Redu walled gardenRedu walled garden
Redu walled garden
 
Como desenvolver com um sistema com um front-end colossal?
Como desenvolver com um sistema com um front-end colossal?Como desenvolver com um sistema com um front-end colossal?
Como desenvolver com um sistema com um front-end colossal?
 
Princípios SOLID
Princípios SOLIDPrincípios SOLID
Princípios SOLID
 
Django Básico
Django BásicoDjango Básico
Django Básico
 
Rails na pratica
Rails na praticaRails na pratica
Rails na pratica
 
Desenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmDesenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine Orm
 
Django - Desenvolvimento web ágil com Python
Django - Desenvolvimento web ágil com PythonDjango - Desenvolvimento web ágil com Python
Django - Desenvolvimento web ágil com Python
 
TDD com Código Legado - "Atualizado"
TDD com Código Legado - "Atualizado"TDD com Código Legado - "Atualizado"
TDD com Código Legado - "Atualizado"
 
qualidade de código: boas práticas, princípios e padrões
qualidade de código: boas práticas, princípios e padrõesqualidade de código: boas práticas, princípios e padrões
qualidade de código: boas práticas, princípios e padrões
 
Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)
Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)
Cocoaheads RJ - Como você faz? (Douglas Fischer - 26/03/2015)
 
JHipster - Produtividade e Maturidade em suas mãos
JHipster - Produtividade e Maturidade em suas mãosJHipster - Produtividade e Maturidade em suas mãos
JHipster - Produtividade e Maturidade em suas mãos
 
Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010
Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010
Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010
 
Ruby & Rails
Ruby & RailsRuby & Rails
Ruby & Rails
 
Rails - EXATEC2009
Rails - EXATEC2009Rails - EXATEC2009
Rails - EXATEC2009
 
Programando php com excelência
Programando php com excelênciaProgramando php com excelência
Programando php com excelência
 

Design Patterns on Rails

  • 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!
  • 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.
  • 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
  • 20. Péssimo - FileColumn class Entry < ActiveRecord::Base file_column :image end
  • 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
  • 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. 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
  • 34. Refatorando class ShippingCalculator attr_accessor :order def initialize(order) @order = order end def value # copy and paste 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!
  • 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á 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?
  • 48. Obrigado! • thiago.pradi@gmail.com • www.thiagopradi.net • http://www.github.com/tchandy • http://www.twitter.com/thiagopradi

Notas do Editor

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n