SlideShare uma empresa Scribd logo
1 de 43
Baixar para ler offline
UTILIZANDO FORM OBJECTS PARA SIMPLIFICAR SEU CÓDIGO

WHERE DOES THE FAT GOES?
Guilherme Cavalcanti
github.com/guiocavalcanti
APLICAÇÕES MONOLÍTICAS

O Que São?

• Dependências compartilhadas
• Difícil de modificar
• Difícil de evoluir
• Mas o assunto ainda são
aplicações monolíticas

NÃO VOU FALAR DE REST

• Outras estratégias para
decompor

• Form Object
ROTEIRO
Sobre O Que Vamos
Falar?

• O problema
• Sintomas
• Form objects
O Problema
MV "F*" C

• Separação de concerns
• Baldes
• Views: apresentação
• Controller: Telefonista
• Model
•

Persistência

•

Domain logic
M

V

C
Código Inicial
APLICAÇÃO
E-Commerce

• Criação de usuário
• Criação de loja
• Envio de emails
• Auditoria
FAT CONTROLLER

	
  	
  def	
  create	
  
	
  	
  	
  	
  @user	
  =	
  User.new(user_params)	
  
	
  	
  	
  	
  @store	
  =	
  @user.build_store(store_params)	
  

!

• Inicialização
• Validação (humano)
• Database stuff
• Auditoria (IP)
• Email
• Rendering/redirect

	
  	
  	
  	
  captcha	
  =	
  CaptchaQuestion.find(captcha_id)	
  
	
  	
  	
  	
  unless	
  captcha.valid?(captcha_answer)	
  
	
  	
  	
  	
  	
  	
  flash[:error]	
  =	
  'Captcha	
  answer	
  is	
  invalid'	
  
	
  	
  	
  	
  	
  	
  render	
  :new	
  and	
  return	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  ActiveRecord::Base.transaction	
  do	
  
	
  	
  	
  	
  	
  	
  @user.save!	
  
	
  	
  	
  	
  	
  	
  @store.save!	
  
	
  	
  	
  	
  	
  	
  @user.store	
  =	
  @store	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  IpLogger.log(request.remote_ip)	
  
	
  	
  	
  	
  SignupEmail.deliver(@user)	
  

!

	
  	
  	
  	
  redirect_to	
  accounts_path	
  

!

	
  	
  rescue	
  ActiveRecord::RecordInvalid	
  
	
  	
  	
  	
  render	
  :new	
  
	
  	
  end
SLIM MODEL
• Validação
• Relacionamentos

class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  has_one	
  :store	
  
	
  	
  validates	
  :name,	
  presence:	
  true	
  
!
	
  	
  accepts_nested_attributes_for	
  :store	
  
end

class	
  Store	
  <	
  ActiveRecord::Base	
  
	
  	
  belongs_to	
  :user	
  
	
  	
  validates	
  :url,	
  presence:	
  true	
  
end
PROBLEMAS
Mas O Que Isso
Significa?

• E se precisássemos de mais de
um controller para criar conta?

• Vários pontos de saída
• Acoplamento entre modelos
(user e store)
CODE SMELLS
Martin Fowler

Refactoring: Improving
The Design Of Existing
Code Ruby
CODE SMELLS

	
  	
  def	
  create	
  
	
  	
  	
  	
  @user	
  =	
  User.new(user_params)	
  
	
  	
  	
  	
  @store	
  =	
  @user.build_store(store_params)	
  

!

• Divergent change
• This smell refers to

making unrelated
changes in the same
location.

• Feature Envy
• a method that seems
more interested in a
class other than the
one it actually is in

	
  	
  	
  	
  captcha	
  =	
  CaptchaQuestion.find(captcha_id)	
  
	
  	
  	
  	
  unless	
  captcha.valid?(captcha_answer)	
  
	
  	
  	
  	
  	
  	
  flash[:error]	
  =	
  'Captcha	
  answer	
  is	
  invalid'	
  
	
  	
  	
  	
  	
  	
  render	
  :new	
  and	
  return	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  ActiveRecord::Base.transaction	
  do	
  
	
  	
  	
  	
  	
  	
  @user.save!	
  
	
  	
  	
  	
  	
  	
  @store.save!	
  
	
  	
  	
  	
  	
  	
  @user.store	
  =	
  @store	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  IpLogger.log(request.remote_ip)	
  
	
  	
  	
  	
  SignupEmail.deliver(@user)	
  

!

	
  	
  	
  	
  redirect_to	
  accounts_path	
  

!

	
  	
  rescue	
  ActiveRecord::RecordInvalid	
  
	
  	
  	
  	
  render	
  :new	
  
	
  	
  end
SANDI METZ'
RULES FOR
DEVELOPERS
Rubyrogues.Com

Poodr.Com
SANDI RULES

	
  	
  def	
  create	
  
	
  	
  	
  	
  @user	
  =	
  User.new(user_params)	
  
	
  	
  	
  	
  @store	
  =	
  @user.build_store(store_params)	
  

!

• Classes can be no longer

than one hundred lines of
code.

• Methods can be no longer
than five lines of code.

• Pass no more than four
parameters into a
method.

• Controllers can

instantiate only one
object.

	
  	
  	
  	
  captcha	
  =	
  CaptchaQuestion.find(captcha_id)	
  
	
  	
  	
  	
  unless	
  captcha.valid?(captcha_answer)	
  
	
  	
  	
  	
  	
  	
  flash[:error]	
  =	
  'Captcha	
  answer	
  is	
  invalid'	
  
	
  	
  	
  	
  	
  	
  render	
  :new	
  and	
  return	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  ActiveRecord::Base.transaction	
  do	
  
	
  	
  	
  	
  	
  	
  @user.save!	
  
	
  	
  	
  	
  	
  	
  @store.save!	
  
	
  	
  	
  	
  	
  	
  @user.store	
  =	
  @store	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  IpLogger.log(request.remote_ip)	
  
	
  	
  	
  	
  SignupEmail.deliver(@user)	
  

!

	
  	
  	
  	
  redirect_to	
  accounts_path	
  

!

	
  	
  rescue	
  ActiveRecord::RecordInvalid	
  
	
  	
  	
  	
  render	
  :new	
  
	
  	
  end
Refactor I
Fat Model, Slim Controller
SLIM CONTROLLER

• Inicialização
• Rendering/redirect

	
  	
  def	
  create	
  
	
  	
  	
  	
  @user	
  =	
  User.new(params)	
  
	
  	
  	
  	
  @user.remote_ip	
  =	
  request.remote_ip	
  
	
  	
  	
  	
  @user.save	
  

!

	
  	
  	
  	
  respond_with(@user,	
  location:	
  accounts_path)	
  
	
  	
  end
• Classes can be no longer
than one hundred lines
of code.

• Methods can be no

longer than five lines of
code.

• Pass no more than four
parameters into a
method.

• Controllers can

instantiate only one
object.
FAT MODEL

	
  	
  class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  	
  attr_accessor	
  :remote_ip,	
  :captcha_id,	
  :captcha_answer	
  

!

	
  	
  	
  	
  has_one	
  :store	
  

!

• Criação de Store
• Validação (humano)
• Database stuff
• Auditoria (IP)
• Email

	
  	
  	
  	
  validates	
  :name,	
  presence:	
  true	
  
	
  	
  	
  	
  validate	
  :ensure_captcha_answered,	
  on:	
  :create	
  
	
  	
  	
  	
  accepts_nested_attributes_for	
  :store	
  

!

	
  	
  	
  	
  after_create	
  :deliver_email	
  
	
  	
  	
  	
  after_create	
  :log_ip	
  

!

	
  	
  	
  	
  protected	
  

!

	
  	
  	
  	
  def	
  deliver_email	
  
	
  	
  	
  	
  	
  	
  SignupEmail.deliver(@user)	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  def	
  log_ip	
  
	
  	
  	
  	
  	
  	
  IpLogger.log(self.remote_ip)	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  def	
  ensure_captcha_answered	
  
	
  	
  	
  	
  	
  	
  captcha	
  =	
  CaptchaQuestion.find(self.captcha_id)	
  

!

	
  	
  	
  	
  	
  	
  unless	
  captcha.valid?(self.captcha_answer)	
  
	
  	
  	
  	
  	
  	
  	
  	
  errors.add(:captcha_answer,	
  :invalid)	
  
	
  	
  	
  	
  	
  	
  end	
  
	
  	
  	
  	
  end	
  
	
  	
  end
CODE SMELLS
•

	
  	
  class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  	
  attr_accessor	
  :remote_ip,	
  :captcha_id,	
  :captcha_answer	
  

!

	
  	
  	
  	
  has_one	
  :store	
  

!

Divergent change

• This smell refers to making
unrelated changes in the
same location.

• Feature Envy
• a method that seems more

interested in a class other
than the one it actually is in

• Inappropriate Intimacy
•

too much intimate
knowledge of another
class or method's inner
workings, inner data, etc.

	
  	
  	
  	
  validates	
  :name,	
  presence:	
  true	
  
	
  	
  	
  	
  validate	
  :ensure_captcha_answered,	
  on:	
  :create	
  
	
  	
  	
  	
  accepts_nested_attributes_for	
  :store	
  

!

	
  	
  	
  	
  after_create	
  :deliver_email	
  
	
  	
  	
  	
  after_create	
  :log_ip	
  

!

	
  	
  	
  	
  protected	
  

!

	
  	
  	
  	
  def	
  deliver_email	
  
	
  	
  	
  	
  	
  	
  SignupEmail.deliver(@user)	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  def	
  log_ip	
  
	
  	
  	
  	
  	
  	
  IpLogger.log(self.remote_ip)	
  
	
  	
  	
  	
  end	
  

!

	
  	
  	
  	
  def	
  ensure_captcha_answered	
  
	
  	
  	
  	
  	
  	
  captcha	
  =	
  CaptchaQuestion.find(self.captcha_id)	
  

!

	
  	
  	
  	
  	
  	
  unless	
  captcha.valid?(self.captcha_answer)	
  
	
  	
  	
  	
  	
  	
  	
  	
  errors.add(:captcha_answer,	
  :invalid)	
  
	
  	
  	
  	
  	
  	
  end	
  
	
  	
  	
  	
  end	
  
	
  	
  end
ACTIVE RECORD
Regras De Negócio No
Active Record?

• Precisa do ActiveRecord (specs)
• Acesso a métodos de baixo
nível

• update_attributes

• A instância valida a sí mesma
• Difícil de testar
Refactor II
Um Passo A Frente
Form Objects
NOVOS BALDES
• Novas camadas
• Melhor separação
de concerns

• Por muito tempo o
Rails não
estimulava isso
FORM OBJECTS
•
• Realiza validações
• Dispara Callbacks
• app/forms

Delega persistência

module	
  Form	
  
	
  	
  extend	
  ActiveSupport::Concern	
  
	
  	
  include	
  ActiveModel::Model	
  
	
  	
  include	
  DelegateAccessors	
  

!
	
  	
  included	
  do	
  
	
  	
  	
  	
  define_model_callbacks	
  :persist	
  
	
  	
  end	
  

!
	
  	
  def	
  submit	
  
	
  	
  	
  	
  return	
  false	
  unless	
  valid?	
  
	
  	
  	
  	
  run_callbacks(:persist)	
  {	
  persist!	
  }	
  
	
  	
  	
  	
  true	
  
	
  	
  end	
  

!
	
  	
  def	
  transaction(&block)	
  
	
  	
  	
  	
  ActiveRecord::Base.transaction(&block)	
  
	
  	
  end	
  
end
FORM: O BÁSICO

• Provê accessors
• Delega

responsabilidades

• Infra de callbacks
• Realiza validações
• Inclusive
customizadas

class	
  AccountForm	
  
	
  	
  include	
  Form	
  

!
	
  	
  attr_accessor	
  :captcha_id,	
  :captcha_answer	
  

!
	
  	
  delegate_accessors	
  :name,	
  	
  
	
  	
  	
  :password,	
  :email,	
  to:	
  :user	
  

!
	
  	
  delegate_accessors	
  :name,	
  :url,	
  	
  
	
  	
  	
  	
  to:	
  :store,	
  prefix:	
  true	
  

!
	
  	
  validates	
  :captcha_answer,	
  captcha:	
  true	
  
	
  	
  validates	
  :name,	
  :store_url,	
  	
  
	
  	
  	
  	
  presence:	
  true	
  
end
FORM: ATRIBUTOS

• Alguns são da class
• Alguns são

	
  	
  
attr_accessor	
  :captcha_id,	
  :captcha_answer	
  

delegados

•

!

delegate_accessors

!

delegate_accessors	
  :name,	
  	
  
	
  	
  	
  :password,	
  :email,	
  to:	
  :user	
  
delegate_accessors	
  :name,	
  :url,	
  	
  
	
  	
  	
  	
  to:	
  :store,	
  prefix:	
  true
FORM: VALIDAÇÃO

• Fácil de compor em

outros FormObjects

• Não modifica a
lógica do Form
Object

• Pode ser testada em
isolamento

#	
  account_form.rb	
  
validates	
  :captcha_answer,	
  captcha:	
  true


!
#	
  captcha_validator.rb

class	
  CaptchaValidator	
  
	
  	
  def	
  validate_each(r,	
  attr,	
  val)	
  
	
  	
  	
  	
  captcha	
  =	
  CaptchaQuestion.find(r)	
  
!
	
  	
  	
  	
  unless	
  captcha.valid?(val)	
  
	
  	
  	
  	
  	
  	
  r.errors.add(attr,	
  :invalid)	
  
	
  	
  	
  	
  end	
  
	
  	
  end	
  
end	
  
FORM: CALLBACKS

• Dispara callbacks
• Callbacks

implementados em
classe a parte

• Reutilizáveis
• Pode ser testado em
isolamento

#	
  account_form.rb	
  
after_persist	
  SendSignupEmail,	
  LogIp	
  
!
!
!
class	
  SendSignupEmail	
  
	
  	
  class	
  <<	
  self	
  
	
  	
  	
  	
  def	
  after_persist(form)	
  
	
  	
  	
  	
  	
  	
  SignupEmail.deliver(form.user)	
  
	
  	
  	
  	
  end	
  
	
  	
  end	
  
end	
  
!
class	
  LogIp	
  
	
  	
  class	
  <<	
  self	
  
	
  	
  	
  	
  def	
  after_persist(form)	
  
	
  	
  	
  	
  	
  	
  IpLogger.log(form.remote_ip)	
  
	
  	
  	
  	
  end	
  
	
  	
  end	
  
end
#	
  account_form.rb	
  

FORM: PERSISTÊNCIA

!

	
  	
  protected	
  
!

• Delega para os
models

• Precisa do

ActiveRecord :(

	
  	
  def	
  store	
  
	
  	
  	
  	
  @store	
  ||=	
  Store.new	
  
	
  	
  end	
  
!

	
  	
  def	
  user	
  
	
  	
  	
  	
  @user	
  ||=	
  User.new	
  
	
  	
  end	
  
!

	
  	
  def	
  persist!	
  
	
  	
  	
  	
  transaction	
  do	
  
	
  	
  	
  	
  	
  	
  user.save	
  
	
  	
  	
  	
  	
  	
  store.save	
  
	
  	
  	
  	
  	
  	
  user.store	
  =	
  store	
  
	
  	
  	
  	
  end	
  
	
  	
  end
SLIM CONTROLLER

• Inicialização
• Rendering/redirect

	
  	
  def	
  create	
  
	
  	
  	
  	
  @form	
  =	
  AccountForm.new(accout_params)	
  
	
  	
  	
  	
  @form.remote_ip	
  =	
  request.remote_ip	
  
	
  	
  	
  	
  @form.submit	
  

!

	
  	
  	
  	
  respond_with(@form,	
  location:	
  accounts_path)	
  
	
  	
  end
SLIM MODEL
• Apenas

relacionamentos

• Sem validações
• Sem callbacks

	
  	
  class	
  Store	
  <	
  ActiveRecord::Base	
  
	
  	
  	
  	
  belongs_to	
  :user	
  
	
  	
  end	
  
!
	
  	
  class	
  User	
  <	
  ActiveRecord::Base	
  
	
  	
  	
  	
  has_one	
  :store	
  
	
  	
  end
CODE SMELL
• Divergent change
• This smell refers to

making unrelated
changes in the same
location.

	
  	
  def	
  persist!	
  
	
  	
  	
  	
  transaction	
  do	
  
	
  	
  	
  	
  	
  	
  user.save	
  
	
  	
  	
  	
  	
  	
  store.save	
  
	
  	
  	
  	
  	
  	
  user.store	
  =	
  store	
  
	
  	
  	
  	
  end	
  
	
  	
  end
Perpetuity
Implementação
do DataMapper
Pattern
PERPETUITY
•

Desacopla
persistência de
lógica de domínio

• Funciona com

qualquer PORO

form	
  =	
  AccountForm.new	
  
form.name	
  =	
  ‘Guilherme'	
  
form.store_url	
  =	
  ‘http://...’	
  
!
Perpetuity[Account].insert	
  account
Reform

Infraestrutura
para form objects
REFORM
• Desacopla

persistência de
lógica de domínio

• Nesting
• Relacionamentos
• Coerção (usando o
Virtus)

@form.save	
  do	
  |data,	
  nested|	
  
	
   u	
  =	
  User.create(nested[:user])	
  
	
   s	
  =	
  Store.create(nested[:store])	
  
	
   u.stores	
  =	
  s	
  
end
OBRIGADO!
guilherme@geteloquent.com
• http://pivotallabs.com/form-backing-objects-for-fun-and-profit/
• http://robots.thoughtbot.com/activemodel-form-objects
• http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
• http://www.reddit.com/r/ruby/comments/1qbiwr/
any_form_object_fans_out_there_who_might_want_to/

• http://panthersoftware.com/blog/2013/05/13/user-registration-using-form-objects-in-rails/
• http://reinteractive.net/posts/158-form-objects-in-rails
• https://docs.djangoproject.com/en/dev/topics/forms/#form-objects
• http://engineering.nulogy.com/posts/building-rich-domain-models-in-rails-separating-persistence/
• http://robots.thoughtbot.com/sandi-metz-rules-for-developers
• https://github.com/brycesenz/freeform
• http://nicksda.apotomo.de/2013/05/reform-decouple-your-forms-from-your-models/
• http://joncairns.com/2013/04/fat-model-skinny-controller-is-a-load-of-rubbish/
• http://engineering.nulogy.com/posts/building-rich-domain-models-in-rails-separating-persistence/
• https://www.youtube.com/watch?v=jk8FEssfc90

Mais conteúdo relacionado

Mais procurados

Office add ins community call - april 2019
Office add ins community call - april 2019Office add ins community call - april 2019
Office add ins community call - april 2019Microsoft 365 Developer
 
Java script Session No 1
Java script Session No 1Java script Session No 1
Java script Session No 1Saif Ullah Dar
 
Old code doesn't stink - Detroit
Old code doesn't stink - DetroitOld code doesn't stink - Detroit
Old code doesn't stink - DetroitMartin Gutenbrunner
 
Customizing the Presentation Model and Physical Renderer in Siebel Open UI
Customizing the Presentation Model and Physical Renderer in Siebel Open UICustomizing the Presentation Model and Physical Renderer in Siebel Open UI
Customizing the Presentation Model and Physical Renderer in Siebel Open UITech OneStop
 
Efficient Rails Test-Driven Development - Week 6
Efficient Rails Test-Driven Development - Week 6Efficient Rails Test-Driven Development - Week 6
Efficient Rails Test-Driven Development - Week 6Marakana Inc.
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDeclan Whelan
 
Moving to the client - HTML5 is here
Moving to the client - HTML5 is here Moving to the client - HTML5 is here
Moving to the client - HTML5 is here Christian Heilmann
 
Siebel Open UI Debugging (Siebel Open UI Training, Part 7)
Siebel Open UI Debugging (Siebel Open UI Training, Part 7)Siebel Open UI Debugging (Siebel Open UI Training, Part 7)
Siebel Open UI Debugging (Siebel Open UI Training, Part 7)Tech OneStop
 
Fixture Replacement Plugins
Fixture Replacement PluginsFixture Replacement Plugins
Fixture Replacement PluginsPaul Klipp
 
SharePoint Saturday St. Louis - SharePoint & jQuery
SharePoint Saturday St. Louis - SharePoint & jQuerySharePoint Saturday St. Louis - SharePoint & jQuery
SharePoint Saturday St. Louis - SharePoint & jQueryMark Rackley
 
[Practical] Functional Programming in Rails
[Practical] Functional Programming in Rails[Practical] Functional Programming in Rails
[Practical] Functional Programming in RailsGilbert B Garza
 
Prairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API ResponsesPrairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API Responsesdarrelmiller71
 
Don't Ignore Your Errors!
Don't Ignore Your Errors!Don't Ignore Your Errors!
Don't Ignore Your Errors!Mary Jo Sminkey
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsMike Subelsky
 
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)James Titcumb
 
Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)James Titcumb
 

Mais procurados (20)

Office add ins community call - april 2019
Office add ins community call - april 2019Office add ins community call - april 2019
Office add ins community call - april 2019
 
Java script Session No 1
Java script Session No 1Java script Session No 1
Java script Session No 1
 
Old code doesn't stink - Detroit
Old code doesn't stink - DetroitOld code doesn't stink - Detroit
Old code doesn't stink - Detroit
 
Windows Store: Top Learnings from the first Belgian Windows 8 apps
Windows Store: Top Learnings from the first Belgian Windows 8 appsWindows Store: Top Learnings from the first Belgian Windows 8 apps
Windows Store: Top Learnings from the first Belgian Windows 8 apps
 
Customizing the Presentation Model and Physical Renderer in Siebel Open UI
Customizing the Presentation Model and Physical Renderer in Siebel Open UICustomizing the Presentation Model and Physical Renderer in Siebel Open UI
Customizing the Presentation Model and Physical Renderer in Siebel Open UI
 
Efficient Rails Test-Driven Development - Week 6
Efficient Rails Test-Driven Development - Week 6Efficient Rails Test-Driven Development - Week 6
Efficient Rails Test-Driven Development - Week 6
 
Domain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with RailsDomain Driven Design and Hexagonal Architecture with Rails
Domain Driven Design and Hexagonal Architecture with Rails
 
Moving to the client - HTML5 is here
Moving to the client - HTML5 is here Moving to the client - HTML5 is here
Moving to the client - HTML5 is here
 
Siebel Open UI Debugging (Siebel Open UI Training, Part 7)
Siebel Open UI Debugging (Siebel Open UI Training, Part 7)Siebel Open UI Debugging (Siebel Open UI Training, Part 7)
Siebel Open UI Debugging (Siebel Open UI Training, Part 7)
 
Fixture Replacement Plugins
Fixture Replacement PluginsFixture Replacement Plugins
Fixture Replacement Plugins
 
SharePoint Saturday St. Louis - SharePoint & jQuery
SharePoint Saturday St. Louis - SharePoint & jQuerySharePoint Saturday St. Louis - SharePoint & jQuery
SharePoint Saturday St. Louis - SharePoint & jQuery
 
EVOLVE'14 | Enhance | Gabriel Walt | Sightly Component Development
EVOLVE'14 | Enhance | Gabriel Walt | Sightly Component DevelopmentEVOLVE'14 | Enhance | Gabriel Walt | Sightly Component Development
EVOLVE'14 | Enhance | Gabriel Walt | Sightly Component Development
 
[Practical] Functional Programming in Rails
[Practical] Functional Programming in Rails[Practical] Functional Programming in Rails
[Practical] Functional Programming in Rails
 
Reusable Apps
Reusable AppsReusable Apps
Reusable Apps
 
Prairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API ResponsesPrairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API Responses
 
Don't Ignore Your Errors!
Don't Ignore Your Errors!Don't Ignore Your Errors!
Don't Ignore Your Errors!
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web Apps
 
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
 
Java script
Java scriptJava script
Java script
 
Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)Crafting Quality PHP Applications (PHPkonf 2018)
Crafting Quality PHP Applications (PHPkonf 2018)
 

Semelhante a Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código

Grails patterns and practices
Grails patterns and practicesGrails patterns and practices
Grails patterns and practicespaulbowler
 
Nsc 2013 06-17 - random rants on 2013
Nsc 2013 06-17 - random rants on 2013Nsc 2013 06-17 - random rants on 2013
Nsc 2013 06-17 - random rants on 2013Mikael Svenson
 
Controller Testing: You're Doing It Wrong
Controller Testing: You're Doing It WrongController Testing: You're Doing It Wrong
Controller Testing: You're Doing It Wrongjohnnygroundwork
 
Apex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong FoundationsApex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong FoundationsSalesforce Developers
 
Refactoring @ Mindvalley: Smells, Techniques and Patterns
Refactoring @ Mindvalley: Smells, Techniques and PatternsRefactoring @ Mindvalley: Smells, Techniques and Patterns
Refactoring @ Mindvalley: Smells, Techniques and PatternsTristan Gomez
 
Frozen rails 2012 - Fighting Code Smells
Frozen rails 2012 - Fighting Code SmellsFrozen rails 2012 - Fighting Code Smells
Frozen rails 2012 - Fighting Code SmellsDennis Ushakov
 
Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiPivorak MeetUp
 
Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing SoftwareSteven Smith
 
RESTFul Tools For Lazy Experts - CFSummit 2016
RESTFul Tools For Lazy Experts - CFSummit 2016RESTFul Tools For Lazy Experts - CFSummit 2016
RESTFul Tools For Lazy Experts - CFSummit 2016Ortus Solutions, Corp
 
Random Ruby Tips - Ruby Meetup 27 Jun 2018
Random Ruby Tips - Ruby Meetup 27 Jun 2018Random Ruby Tips - Ruby Meetup 27 Jun 2018
Random Ruby Tips - Ruby Meetup 27 Jun 2018Kenneth Teh
 
Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016Steven Smith
 
HTML5 Form Validation
HTML5 Form ValidationHTML5 Form Validation
HTML5 Form ValidationIan Oxley
 
API SOAP e Cron: integrare Magento con servizi esterni
API SOAP e Cron: integrare Magento con servizi esterniAPI SOAP e Cron: integrare Magento con servizi esterni
API SOAP e Cron: integrare Magento con servizi esterniAndrea De Pirro
 
Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing SoftwareSteven Smith
 
Becoming a better WordPress Developer
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress DeveloperJoey Kudish
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
Power Automate Techniques that "Save
Power Automate Techniques that "SavePower Automate Techniques that "Save
Power Automate Techniques that "SaveThomas Duff
 
Saleforce For Domino Dogs
Saleforce For Domino DogsSaleforce For Domino Dogs
Saleforce For Domino DogsMark Myers
 

Semelhante a Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código (20)

Refactoring
RefactoringRefactoring
Refactoring
 
Grails patterns and practices
Grails patterns and practicesGrails patterns and practices
Grails patterns and practices
 
Nsc 2013 06-17 - random rants on 2013
Nsc 2013 06-17 - random rants on 2013Nsc 2013 06-17 - random rants on 2013
Nsc 2013 06-17 - random rants on 2013
 
Controller Testing: You're Doing It Wrong
Controller Testing: You're Doing It WrongController Testing: You're Doing It Wrong
Controller Testing: You're Doing It Wrong
 
Apex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong FoundationsApex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong Foundations
 
Refactoring @ Mindvalley: Smells, Techniques and Patterns
Refactoring @ Mindvalley: Smells, Techniques and PatternsRefactoring @ Mindvalley: Smells, Techniques and Patterns
Refactoring @ Mindvalley: Smells, Techniques and Patterns
 
Frozen rails 2012 - Fighting Code Smells
Frozen rails 2012 - Fighting Code SmellsFrozen rails 2012 - Fighting Code Smells
Frozen rails 2012 - Fighting Code Smells
 
Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy Koshovyi
 
Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing Software
 
Rest ful tools for lazy experts
Rest ful tools for lazy expertsRest ful tools for lazy experts
Rest ful tools for lazy experts
 
RESTFul Tools For Lazy Experts - CFSummit 2016
RESTFul Tools For Lazy Experts - CFSummit 2016RESTFul Tools For Lazy Experts - CFSummit 2016
RESTFul Tools For Lazy Experts - CFSummit 2016
 
Random Ruby Tips - Ruby Meetup 27 Jun 2018
Random Ruby Tips - Ruby Meetup 27 Jun 2018Random Ruby Tips - Ruby Meetup 27 Jun 2018
Random Ruby Tips - Ruby Meetup 27 Jun 2018
 
Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016
 
HTML5 Form Validation
HTML5 Form ValidationHTML5 Form Validation
HTML5 Form Validation
 
API SOAP e Cron: integrare Magento con servizi esterni
API SOAP e Cron: integrare Magento con servizi esterniAPI SOAP e Cron: integrare Magento con servizi esterni
API SOAP e Cron: integrare Magento con servizi esterni
 
Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing Software
 
Becoming a better WordPress Developer
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress Developer
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
Power Automate Techniques that "Save
Power Automate Techniques that "SavePower Automate Techniques that "Save
Power Automate Techniques that "Save
 
Saleforce For Domino Dogs
Saleforce For Domino DogsSaleforce For Domino Dogs
Saleforce For Domino Dogs
 

Mais de Guilherme

Descobrindo APIs REST
Descobrindo APIs RESTDescobrindo APIs REST
Descobrindo APIs RESTGuilherme
 
Redu walled garden
Redu walled gardenRedu walled garden
Redu walled gardenGuilherme
 
Desafio de crescer
Desafio de crescerDesafio de crescer
Desafio de crescerGuilherme
 
Consumindo APIs REST com Ruby
Consumindo APIs REST com RubyConsumindo APIs REST com Ruby
Consumindo APIs REST com RubyGuilherme
 
Aplicações tipo Canvas no Redu com Rails
Aplicações tipo Canvas no Redu com RailsAplicações tipo Canvas no Redu com Rails
Aplicações tipo Canvas no Redu com RailsGuilherme
 
Aplicações sociais usando a plataforma Redu
Aplicações sociais usando a plataforma ReduAplicações sociais usando a plataforma Redu
Aplicações sociais usando a plataforma ReduGuilherme
 
Introdução aos aplicativos tipo canvas
Introdução aos aplicativos tipo canvasIntrodução aos aplicativos tipo canvas
Introdução aos aplicativos tipo canvasGuilherme
 
Introdução a plataforma de aplicativos Redu
Introdução a plataforma de aplicativos ReduIntrodução a plataforma de aplicativos Redu
Introdução a plataforma de aplicativos ReduGuilherme
 
FLOSS and Startups
FLOSS and StartupsFLOSS and Startups
FLOSS and StartupsGuilherme
 
Ruby 101 && Coding Dojo
Ruby 101 && Coding DojoRuby 101 && Coding Dojo
Ruby 101 && Coding DojoGuilherme
 
Dojo: Sass - Syntactically Awesome Stylesheets
Dojo: Sass - Syntactically Awesome StylesheetsDojo: Sass - Syntactically Awesome Stylesheets
Dojo: Sass - Syntactically Awesome StylesheetsGuilherme
 
CSS first steps
CSS first stepsCSS first steps
CSS first stepsGuilherme
 
How does the Web work?
How does the Web work?How does the Web work?
How does the Web work?Guilherme
 
0 introducao padroes_web
0 introducao padroes_web0 introducao padroes_web
0 introducao padroes_webGuilherme
 
Plano de Pesquisa - Redu Respostas
Plano de Pesquisa - Redu RespostasPlano de Pesquisa - Redu Respostas
Plano de Pesquisa - Redu RespostasGuilherme
 
Desevolvimento Web Client-side - AJAX
Desevolvimento Web Client-side - AJAX Desevolvimento Web Client-side - AJAX
Desevolvimento Web Client-side - AJAX Guilherme
 
Desevolvimento Web Client-side - jQuery
Desevolvimento Web Client-side - jQueryDesevolvimento Web Client-side - jQuery
Desevolvimento Web Client-side - jQueryGuilherme
 
3 padroes-web-intro-javascript
3 padroes-web-intro-javascript3 padroes-web-intro-javascript
3 padroes-web-intro-javascriptGuilherme
 
Desevolvimento Web Client-side - CSS
Desevolvimento Web Client-side - CSSDesevolvimento Web Client-side - CSS
Desevolvimento Web Client-side - CSSGuilherme
 

Mais de Guilherme (20)

Descobrindo APIs REST
Descobrindo APIs RESTDescobrindo APIs REST
Descobrindo APIs REST
 
Redu walled garden
Redu walled gardenRedu walled garden
Redu walled garden
 
Desafio de crescer
Desafio de crescerDesafio de crescer
Desafio de crescer
 
Consumindo APIs REST com Ruby
Consumindo APIs REST com RubyConsumindo APIs REST com Ruby
Consumindo APIs REST com Ruby
 
Aplicações tipo Canvas no Redu com Rails
Aplicações tipo Canvas no Redu com RailsAplicações tipo Canvas no Redu com Rails
Aplicações tipo Canvas no Redu com Rails
 
Aplicações sociais usando a plataforma Redu
Aplicações sociais usando a plataforma ReduAplicações sociais usando a plataforma Redu
Aplicações sociais usando a plataforma Redu
 
Introdução aos aplicativos tipo canvas
Introdução aos aplicativos tipo canvasIntrodução aos aplicativos tipo canvas
Introdução aos aplicativos tipo canvas
 
Introdução a plataforma de aplicativos Redu
Introdução a plataforma de aplicativos ReduIntrodução a plataforma de aplicativos Redu
Introdução a plataforma de aplicativos Redu
 
FLOSS and Startups
FLOSS and StartupsFLOSS and Startups
FLOSS and Startups
 
Ruby 101 && Coding Dojo
Ruby 101 && Coding DojoRuby 101 && Coding Dojo
Ruby 101 && Coding Dojo
 
Dojo: Sass - Syntactically Awesome Stylesheets
Dojo: Sass - Syntactically Awesome StylesheetsDojo: Sass - Syntactically Awesome Stylesheets
Dojo: Sass - Syntactically Awesome Stylesheets
 
Sass
SassSass
Sass
 
CSS first steps
CSS first stepsCSS first steps
CSS first steps
 
How does the Web work?
How does the Web work?How does the Web work?
How does the Web work?
 
0 introducao padroes_web
0 introducao padroes_web0 introducao padroes_web
0 introducao padroes_web
 
Plano de Pesquisa - Redu Respostas
Plano de Pesquisa - Redu RespostasPlano de Pesquisa - Redu Respostas
Plano de Pesquisa - Redu Respostas
 
Desevolvimento Web Client-side - AJAX
Desevolvimento Web Client-side - AJAX Desevolvimento Web Client-side - AJAX
Desevolvimento Web Client-side - AJAX
 
Desevolvimento Web Client-side - jQuery
Desevolvimento Web Client-side - jQueryDesevolvimento Web Client-side - jQuery
Desevolvimento Web Client-side - jQuery
 
3 padroes-web-intro-javascript
3 padroes-web-intro-javascript3 padroes-web-intro-javascript
3 padroes-web-intro-javascript
 
Desevolvimento Web Client-side - CSS
Desevolvimento Web Client-side - CSSDesevolvimento Web Client-side - CSS
Desevolvimento Web Client-side - CSS
 

Último

TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 

Último (20)

TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 

Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código

  • 1. UTILIZANDO FORM OBJECTS PARA SIMPLIFICAR SEU CÓDIGO WHERE DOES THE FAT GOES?
  • 3.
  • 4. APLICAÇÕES MONOLÍTICAS O Que São? • Dependências compartilhadas • Difícil de modificar • Difícil de evoluir
  • 5. • Mas o assunto ainda são aplicações monolíticas NÃO VOU FALAR DE REST • Outras estratégias para decompor • Form Object
  • 6. ROTEIRO Sobre O Que Vamos Falar? • O problema • Sintomas • Form objects
  • 8. MV "F*" C • Separação de concerns • Baldes • Views: apresentação • Controller: Telefonista • Model • Persistência • Domain logic
  • 11. APLICAÇÃO E-Commerce • Criação de usuário • Criação de loja • Envio de emails • Auditoria
  • 12. FAT CONTROLLER    def  create          @user  =  User.new(user_params)          @store  =  @user.build_store(store_params)   ! • Inicialização • Validação (humano) • Database stuff • Auditoria (IP) • Email • Rendering/redirect        captcha  =  CaptchaQuestion.find(captcha_id)          unless  captcha.valid?(captcha_answer)              flash[:error]  =  'Captcha  answer  is  invalid'              render  :new  and  return          end   !        ActiveRecord::Base.transaction  do              @user.save!              @store.save!              @user.store  =  @store          end   !        IpLogger.log(request.remote_ip)          SignupEmail.deliver(@user)   !        redirect_to  accounts_path   !    rescue  ActiveRecord::RecordInvalid          render  :new      end
  • 13. SLIM MODEL • Validação • Relacionamentos class  User  <  ActiveRecord::Base      has_one  :store      validates  :name,  presence:  true   !    accepts_nested_attributes_for  :store   end class  Store  <  ActiveRecord::Base      belongs_to  :user      validates  :url,  presence:  true   end
  • 14. PROBLEMAS Mas O Que Isso Significa? • E se precisássemos de mais de um controller para criar conta? • Vários pontos de saída • Acoplamento entre modelos (user e store)
  • 15. CODE SMELLS Martin Fowler
 Refactoring: Improving The Design Of Existing Code Ruby
  • 16. CODE SMELLS    def  create          @user  =  User.new(user_params)          @store  =  @user.build_store(store_params)   ! • Divergent change • This smell refers to making unrelated changes in the same location. • Feature Envy • a method that seems more interested in a class other than the one it actually is in        captcha  =  CaptchaQuestion.find(captcha_id)          unless  captcha.valid?(captcha_answer)              flash[:error]  =  'Captcha  answer  is  invalid'              render  :new  and  return          end   !        ActiveRecord::Base.transaction  do              @user.save!              @store.save!              @user.store  =  @store          end   !        IpLogger.log(request.remote_ip)          SignupEmail.deliver(@user)   !        redirect_to  accounts_path   !    rescue  ActiveRecord::RecordInvalid          render  :new      end
  • 18. SANDI RULES    def  create          @user  =  User.new(user_params)          @store  =  @user.build_store(store_params)   ! • Classes can be no longer than one hundred lines of code. • Methods can be no longer than five lines of code. • Pass no more than four parameters into a method. • Controllers can instantiate only one object.        captcha  =  CaptchaQuestion.find(captcha_id)          unless  captcha.valid?(captcha_answer)              flash[:error]  =  'Captcha  answer  is  invalid'              render  :new  and  return          end   !        ActiveRecord::Base.transaction  do              @user.save!              @store.save!              @user.store  =  @store          end   !        IpLogger.log(request.remote_ip)          SignupEmail.deliver(@user)   !        redirect_to  accounts_path   !    rescue  ActiveRecord::RecordInvalid          render  :new      end
  • 20. Fat Model, Slim Controller
  • 21. SLIM CONTROLLER • Inicialização • Rendering/redirect    def  create          @user  =  User.new(params)          @user.remote_ip  =  request.remote_ip          @user.save   !        respond_with(@user,  location:  accounts_path)      end
  • 22. • Classes can be no longer than one hundred lines of code. • Methods can be no longer than five lines of code. • Pass no more than four parameters into a method. • Controllers can instantiate only one object.
  • 23. FAT MODEL    class  User  <  ActiveRecord::Base        attr_accessor  :remote_ip,  :captcha_id,  :captcha_answer   !        has_one  :store   ! • Criação de Store • Validação (humano) • Database stuff • Auditoria (IP) • Email        validates  :name,  presence:  true          validate  :ensure_captcha_answered,  on:  :create          accepts_nested_attributes_for  :store   !        after_create  :deliver_email          after_create  :log_ip   !        protected   !        def  deliver_email              SignupEmail.deliver(@user)          end   !        def  log_ip              IpLogger.log(self.remote_ip)          end   !        def  ensure_captcha_answered              captcha  =  CaptchaQuestion.find(self.captcha_id)   !            unless  captcha.valid?(self.captcha_answer)                  errors.add(:captcha_answer,  :invalid)              end          end      end
  • 24. CODE SMELLS •    class  User  <  ActiveRecord::Base        attr_accessor  :remote_ip,  :captcha_id,  :captcha_answer   !        has_one  :store   ! Divergent change • This smell refers to making unrelated changes in the same location. • Feature Envy • a method that seems more interested in a class other than the one it actually is in • Inappropriate Intimacy • too much intimate knowledge of another class or method's inner workings, inner data, etc.        validates  :name,  presence:  true          validate  :ensure_captcha_answered,  on:  :create          accepts_nested_attributes_for  :store   !        after_create  :deliver_email          after_create  :log_ip   !        protected   !        def  deliver_email              SignupEmail.deliver(@user)          end   !        def  log_ip              IpLogger.log(self.remote_ip)          end   !        def  ensure_captcha_answered              captcha  =  CaptchaQuestion.find(self.captcha_id)   !            unless  captcha.valid?(self.captcha_answer)                  errors.add(:captcha_answer,  :invalid)              end          end      end
  • 25. ACTIVE RECORD Regras De Negócio No Active Record? • Precisa do ActiveRecord (specs) • Acesso a métodos de baixo nível • update_attributes • A instância valida a sí mesma • Difícil de testar
  • 27. Um Passo A Frente Form Objects
  • 28. NOVOS BALDES • Novas camadas • Melhor separação de concerns • Por muito tempo o Rails não estimulava isso
  • 29. FORM OBJECTS • • Realiza validações • Dispara Callbacks • app/forms Delega persistência module  Form      extend  ActiveSupport::Concern      include  ActiveModel::Model      include  DelegateAccessors   !    included  do          define_model_callbacks  :persist      end   !    def  submit          return  false  unless  valid?          run_callbacks(:persist)  {  persist!  }          true      end   !    def  transaction(&block)          ActiveRecord::Base.transaction(&block)      end   end
  • 30. FORM: O BÁSICO • Provê accessors • Delega responsabilidades • Infra de callbacks • Realiza validações • Inclusive customizadas class  AccountForm      include  Form   !    attr_accessor  :captcha_id,  :captcha_answer   !    delegate_accessors  :name,          :password,  :email,  to:  :user   !    delegate_accessors  :name,  :url,            to:  :store,  prefix:  true   !    validates  :captcha_answer,  captcha:  true      validates  :name,  :store_url,            presence:  true   end
  • 31. FORM: ATRIBUTOS • Alguns são da class • Alguns são     attr_accessor  :captcha_id,  :captcha_answer   delegados • ! delegate_accessors ! delegate_accessors  :name,          :password,  :email,  to:  :user   delegate_accessors  :name,  :url,            to:  :store,  prefix:  true
  • 32. FORM: VALIDAÇÃO • Fácil de compor em outros FormObjects • Não modifica a lógica do Form Object • Pode ser testada em isolamento #  account_form.rb   validates  :captcha_answer,  captcha:  true
 ! #  captcha_validator.rb
 class  CaptchaValidator      def  validate_each(r,  attr,  val)          captcha  =  CaptchaQuestion.find(r)   !        unless  captcha.valid?(val)              r.errors.add(attr,  :invalid)          end      end   end  
  • 33. FORM: CALLBACKS • Dispara callbacks • Callbacks implementados em classe a parte • Reutilizáveis • Pode ser testado em isolamento #  account_form.rb   after_persist  SendSignupEmail,  LogIp   ! ! ! class  SendSignupEmail      class  <<  self          def  after_persist(form)              SignupEmail.deliver(form.user)          end      end   end   ! class  LogIp      class  <<  self          def  after_persist(form)              IpLogger.log(form.remote_ip)          end      end   end
  • 34. #  account_form.rb   FORM: PERSISTÊNCIA !    protected   ! • Delega para os models • Precisa do ActiveRecord :(    def  store          @store  ||=  Store.new      end   !    def  user          @user  ||=  User.new      end   !    def  persist!          transaction  do              user.save              store.save              user.store  =  store          end      end
  • 35. SLIM CONTROLLER • Inicialização • Rendering/redirect    def  create          @form  =  AccountForm.new(accout_params)          @form.remote_ip  =  request.remote_ip          @form.submit   !        respond_with(@form,  location:  accounts_path)      end
  • 36. SLIM MODEL • Apenas relacionamentos • Sem validações • Sem callbacks    class  Store  <  ActiveRecord::Base          belongs_to  :user      end   !    class  User  <  ActiveRecord::Base          has_one  :store      end
  • 37. CODE SMELL • Divergent change • This smell refers to making unrelated changes in the same location.    def  persist!          transaction  do              user.save              store.save              user.store  =  store          end      end
  • 39. PERPETUITY • Desacopla persistência de lógica de domínio • Funciona com qualquer PORO form  =  AccountForm.new   form.name  =  ‘Guilherme'   form.store_url  =  ‘http://...’   ! Perpetuity[Account].insert  account
  • 41. REFORM • Desacopla persistência de lógica de domínio • Nesting • Relacionamentos • Coerção (usando o Virtus) @form.save  do  |data,  nested|     u  =  User.create(nested[:user])     s  =  Store.create(nested[:store])     u.stores  =  s   end
  • 43. • http://pivotallabs.com/form-backing-objects-for-fun-and-profit/ • http://robots.thoughtbot.com/activemodel-form-objects • http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ • http://www.reddit.com/r/ruby/comments/1qbiwr/ any_form_object_fans_out_there_who_might_want_to/ • http://panthersoftware.com/blog/2013/05/13/user-registration-using-form-objects-in-rails/ • http://reinteractive.net/posts/158-form-objects-in-rails • https://docs.djangoproject.com/en/dev/topics/forms/#form-objects • http://engineering.nulogy.com/posts/building-rich-domain-models-in-rails-separating-persistence/ • http://robots.thoughtbot.com/sandi-metz-rules-for-developers • https://github.com/brycesenz/freeform • http://nicksda.apotomo.de/2013/05/reform-decouple-your-forms-from-your-models/ • http://joncairns.com/2013/04/fat-model-skinny-controller-is-a-load-of-rubbish/ • http://engineering.nulogy.com/posts/building-rich-domain-models-in-rails-separating-persistence/ • https://www.youtube.com/watch?v=jk8FEssfc90