Desde o 15 minutes blog o rails tem evoluido bastante e novos conceitos foram introduzidos com o passar do tempo, um mantra que foi introduzido na comunidade foi o "Skinny Controller, Fat Model" que pode gerar uma confusão aos desenvolvedores novatos e alguns mais experientes também.
Quando lemos o mantra pela primeira vez podemos ficar tentados a fazer algo que seria mover nossas lógicas do controller para o model.
Primeiro temos o código de enfileirar o envio de email no controller.
1 class PostsController < ApplicationController
2
3 def create
4 @post = Post.new(params[:post])
5
6 if @post.save
7 NotifyMailer.delay.notify(@post)
8 redirect_to(@post, :notice => 'Post was successfully created.') }
9 else
10 render :action => "new"
11 end
12 end
13 end
Refatorariamos para algo assim em nosso model Post.
1 class Post < ActiveRecord::Base
2 after_save :notify_users
3
4 private
5
6 def notify_users
7 NotifyMailer.delay.notify(self)
8 end
9 end
Com certeza melhor que a solução anterior, mas agora o nosso model está sabendo demais. Além de fazer o seu papel em tratar as informações do banco de dados, agora também enfileira emails toda vez que é salvo. Ou seja, temos um problema de alto acoplamento, a operação de salvar sempre ativa o enfileiramento de envio de emails e com isso violamos o SRP.
É aí que chegamos no ponto que fat models não necessariamente são classes que herdam de ActiveRecord::Base
, pois como vimos isso nos gera os seguintes problemas:
Para deixar as coisas mais claras e com as responsabilidade melhores definidas podemos criar um decorator.
1 class PostNotifyUsers
2
3 def initialize(post)
4 @post = post
5 end
6
7 def save
8 @post.save && notify_users
9 end
10
11 private
12
13 def notify_users
14 NotifyMailer.delay.notify(@post)
15 end
16 end
No código acima criamos uma classe em Ruby pura, conhecida também como PORO (Plain Old Ruby Objects). E o papel desta classe é notificar os usuários de um post novo enfileirando o email. Com isso conseguimos:
Post
lida com o banco de dados e PostNotifyUsers
notifica os usuários. Cada um com sua responsabilidade.Post
em seus testes ele não envia mais emailPost
, ele apenas é salvo.Veja agora como ficaria o nosso controller usando o decorator que acabamos de criar:
1 class PostsController < ApplicationController
2
3 def create
4 @post = Post.new(params[:post])
5
6 if PostNotifyUsers.new(@post).save
7 redirect_to(@post, :notice => 'Post was successfully created.') }
8 else
9 render :action => "new"
10 end
11 end
12 end
Como pode ver ficou mais claro pois agora sei que nesta action eu salvo o post e notifico os usuários. Outra coisa legal é que o a nossa classe PostNotifyUsers
quacks como User
sendo assim podemos continuar usando o #save
só que agora do PostNotifyUsers
em nosso controller.
O Anézio falou recentemente sobre a extração de responsábilidades também no post Cuidados com Observers e callbacks.
O que é comum são as pessoas iniciarem no rails sem noção nenhuma de Ruby e às vezes até orientação a objetos. O que levam a seguir sempre a abordagem do conhecido Rails Way esquecendo(às vezes nem sabendo) que Ruby é uma linguagem orientada a objetos, e que assim pode resolver muito dos seus problemas.
Comentários
Included file post/disqus_thread.html not found in _includes directory