coisas que aprendi e quero passar adiante - rubyconf brasil 2010
DESCRIPTION
Palestra apresentada na RubyConf Brasil 2010 mostrando dicas de bibliotecas úteis para aplicações web escritas em Rails e maneiras para conseguir código flexível, fácil de testar e modificar utilizando SOLID, defensive programming e outras técnicas.TRANSCRIPT
![Page 1: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/1.jpg)
Coisas que aprendi e quero passar adiante
Lucas Húngaro@lucashungaro
GoNow Tecnologia
![Page 2: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/2.jpg)
Lucas
Culinária
Tecnologia
Apple
Home Cinema
Futebol
Café
Drinks
Games MúsicaSoftware
Cinema
Ruby
![Page 3: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/3.jpg)
Duas partes
![Page 4: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/4.jpg)
Serviços e bibliotecasO que usei e recomendo
![Page 5: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/5.jpg)
Gems e Plugins
bullet
oink
query_reviewer
rails_indexes
kasket
} db performance & optimization
} caching
} profiling
cachy
![Page 6: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/6.jpg)
Oink
Aug 12 11:31:15 Iron-Man rails[9541]: Memory usage: 101938 | PID: 9541Aug 12 11:31:15 Iron-Man rails[9541]: Instantiation Breakdown: Total: 39 | User: 20 | FeedItem: 10 | Tag: 9
![Page 7: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/7.jpg)
Cachy
versionamento
evita o “dog pile effect”
caches interdependentes
várias outras features muito interessantes
http://github.com/grosser/cachy
![Page 8: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/8.jpg)
Master/SlavePara ActiveRecord, DBCharmer
![Page 9: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/9.jpg)
Filas: ResqueSimples, fácil e eficiente
![Page 10: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/10.jpg)
Resque
![Page 11: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/11.jpg)
Resque
monitoramento
plugins
cuidado: json e símbolos
cuidado: tarefas antes seriais passam a ser paralelas
cuidado: locale dos workers (sobem outro contexto)
O Resque adiciona um componente à sua infra-estrutura, mas é feito para ser facilmente monitorado, estendido e distribuído. Muitas vantagens a um preço muito baixo (e “improvisar” filas no banco de dados da sua app pode custar bem mais caro)
![Page 12: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/12.jpg)
Arquitetura
Web App == Database View
Utilize filas, caching, server push (Comet, Sockets etc)
Processamento síncrono mínimo
![Page 13: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/13.jpg)
Cloud ComputingMuito cuidado com o hype
Hype não é algo ruim por si só, pois leva a mais inovação mas, sem o devido cuidado, podemos “errar por empolgação”
![Page 14: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/14.jpg)
Amazon
Noisy neighbors
Latência e I/OSuperpopulação
Caso a caso Evite o hypeBenchmark
![Page 15: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/15.jpg)
Amazon
Staging/QA/Homologação
Processamento paralelo/distribuído
CDN } The Good
Banco de Dados
I/O intensiva{The Bad
Aumento instantâneo de throughput
![Page 17: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/17.jpg)
Código eficienteE como padrões ineficientes podem te atrapalhar
![Page 18: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/18.jpg)
Maturidade
O conhecimento, tanto individual, quanto o do grupo, evolui de forma lenta
![Page 19: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/19.jpg)
Excesso de observers/callbacks
Micro-gerenciamento
Ineficiente
Bug prone
Caching
![Page 20: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/20.jpg)
Smart Keys FTW!
aka Versionamento
![Page 21: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/21.jpg)
class User < ActiveRecord::Base has_many :search_terms, :dependent => :destroy, :order => "term ASC"
def save_search_term(term) self.update_attribute(:last_saved_search_at, Time.now.utc) search_terms.create(:term => term) end
def remove_search_term(term_id) self.update_attribute(:last_saved_search_at, Time.now.utc) self.search_terms.destroy(term_id) end
![Page 22: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/22.jpg)
def saved_search_terms Rails.cache.fetch(cache_key_for_saved_searches, :expires_in => 30.days) do self.search_terms end end
private
def cache_key_for_saved_searches timestamp = self[:last_saved_search_at].to_s.gsub(" ","_").gsub(":","-") "User/#{self[:id]}/search_terms-#{timestamp}" endend
![Page 23: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/23.jpg)
cache_proxy
http://github.com/lucashungaro/cache_proxy
![Page 24: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/24.jpg)
Programação defensiva
Não assuma coisas
Principalmente sobre entrada
![Page 25: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/25.jpg)
(robustness principle)
“Be conservative in what you send; be liberal in what you accept.”
Postel’s Law
![Page 26: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/26.jpg)
Programação defensiva
Gather input
Perform work
Deliver results
Handle errors
![Page 27: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/27.jpg)
Law of Demeter
“Each unit should only talk to its friends; don't talk to strangers.”
![Page 28: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/28.jpg)
class Company < ActiveRecord::Base has_many :addresses
def formatted_city self.addresses.first.city.name endend
def formatted_city main_address.city.name end
private def main_address self.addresses.first end
![Page 29: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/29.jpg)
SOLID
Jim WeirichAmanhã, 17h
![Page 30: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/30.jpg)
Single ResponsibilityPrincipleUma e apenas uma razão para mudar
![Page 31: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/31.jpg)
Negócios + Persistência
Esconde o domínio
Falta: flexibilidade, isolamento
Fat Models
![Page 32: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/32.jpg)
Camadas Tudo junto e misturado
![Page 33: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/33.jpg)
class ActivityService def initialize(actor) @actor = actor @activities = [] end
def register(type, action, options = {}) raise ActionrgumentError("Every action needs a valid object") unless options[:object]
(...) # complex treatment of the activity data end
def process(queue_manager = Resque) queue_manager.enqueue(ActivityProcessor, @activities, I18n.locale) end
(...)end
SOLID:SRP: a razão para mudar se torna única - apenas a formatação das opções. Quem cuida da busca é o wrapper.OCP: fechada para modificação da implementação, mas aberta para extensão via modificação do wrapper e injeção da dependênciaDIP: duck typing + injeção de dependência - a dependência é gerenciada pelo cliente, liberando o código e tornando-o mais flexível
![Page 34: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/34.jpg)
class SearchService def initialize(classes = []) @classes = classes end
def add_class(klass) @classes << klass end
def execute(query, options = {}, wrapper = ThinkingSphinx) (...) # options processing (defaults and so on) wrapper.search(query, options_for_engine) end
(...)end
SOLID:SRP: a razão para mudar se torna única - apenas a formatação das opções. Quem cuida da busca é o wrapper.OCP: fechada para modificação da implementação, mas aberta para extensão via modificação do wrapper e injeção da dependênciaDIP: duck typing + injeção de dependência - a dependência é gerenciada pelo cliente, liberando o código e tornando-o mais flexível
![Page 35: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/35.jpg)
let(:service) { SearchService.new([User]) }
it "should pass default options to the search wrapper" do default_options = { :page => 1, :per_page => 20, :match_mode => :extended } ThinkingSphinx.expects(:search)... # suppressed service.execute("teste")end
Sem utilizar a injeção de dependência, acabamos manipulando o comportamento da classe diretamente, revelando a falta de flexibilidade do código.
![Page 36: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/36.jpg)
let(:service) { SearchService.new([User]) }
it "should pass default options to the search wrapper" do default_options = { :page => 1, :per_page => 20, :match_mode => :extended } wrapper = mock('search_wrapper') wrapper.expects(:search)... # suppressed service.execute("teste", {}, wrapper)end
Com a injeção da dependência, podemos utilizar objetos fake para especificar o comportamento esperado sem setup adicional, graças ao duck typing.Também conseguimos o efeito descrito em “Mock roles, not object states” (http://www.infoq.com/news/2008/08/Mock-Roles-Pryce-and-Freeman)
![Page 37: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/37.jpg)
ConclusõesGuidelines, not laws
![Page 38: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/38.jpg)
Mantra
Se é difícil de testar em isolamento, está mal projetado*
* 99,99% de chance ;)
![Page 39: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/39.jpg)
ChecklistsUma boa forma de adquirir bons hábitos
![Page 40: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/40.jpg)
ser explícita
ter um nome
ter uma localização clara
Lógica importante deve:
![Page 41: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/41.jpg)
Ao finalizar spec e objeto:
aplica DRY?
tem apenas uma responsabilidade?
depende de componentes que mudam menos que ele?
![Page 42: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/42.jpg)
Zen of Python>>> import this
![Page 43: Coisas que aprendi e quero passar adiante - RubyConf Brasil 2010](https://reader033.vdocuments.mx/reader033/viewer/2022052619/555931e0d8b42a543d8b4960/html5/thumbnails/43.jpg)
Obrigado