difícil do que realmente é Conhecer seu princípio é a base da facilidade Aprender uma nova linguagem pode ser fácil Porém, sempre existirá uma curva de aprendizado David Black @david_a_black 2 quarta-feira, 23 de novembro de 11
ActiveRecord::Base validades_uniqueness_of :email end Rails não garante unicidade Marcos Toledo @mtoledo ⤷ Unicidade 4 quarta-feira, 23 de novembro de 11
ActiveRecord::Migration def self.up add_index :users, :email, :unique => true end def self.down remove_index :users, :email end end Marcos Toledo @mtoledo ⤷ Solução 5 quarta-feira, 23 de novembro de 11
Post < ActiveRecord::Base has_many :comments, :dependent => :destroy end Surpresas no uso de concorrência em Rails Rails não garante dependência entre eles Marcos Toledo @mtoledo ⤷ Dependência de registros 6 quarta-feira, 23 de novembro de 11
|table| table.integer :post_id, :null => false end ActiveRecord::Base.connection.execute <<-EOS ALTER TABLE ‘comments’ ADD CONSTRAINT FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE restrict ON UPDATE cascade EOS Marcos Toledo @mtoledo ⤷ Solução 7 quarta-feira, 23 de novembro de 11
end end class Post < ActiveRecord::Base after_update :update_comments def update_comments comments.each { |c| c.published = self.published; c.save } end end Surpresas no uso de concorrência em Rails Transações lock tabelas na atualização Marcos Toledo @mtoledo ⤷ Deadlock 8 quarta-feira, 23 de novembro de 11
⤷ Solução #1 => Atualizar dependentes antes class Comment < ActiveRecord::Base after_create :update_posts_count def update_posts_count self.posts.increment :comments_count end end class Post < ActiveRecord::Base before_update :update_comments def update_comments comments.each { |c| c.published = self.published; c.save } end end 10 quarta-feira, 23 de novembro de 11
⤷ Solução #1 => Atualizar dependentes antes class Comment < ActiveRecord::Base after_create :update_posts_count def update_posts_count self.posts.increment :comments_count end end class Post < ActiveRecord::Base before_update :update_comments def update_comments comments.each { |c| c.published = self.published; c.save } end end 11 quarta-feira, 23 de novembro de 11
⤷ Solução #2 => Evite propagação de estado class Comment < ActiveRecord::Base def visible? post.published? && self.published? end end 12 quarta-feira, 23 de novembro de 11
Transfer REST utiliza características do HTTP Usamos pouco do REST Roar => framework dele para trabalhar com REST Facilitador na troca de mensagens e estado Passagem de links personalizados via REST Nick Sutterer @apotonick 15 quarta-feira, 23 de novembro de 11
include Roar::Representer::JSON # should be XML, for example property :name # plain property property :id # plain property link :self do beer_url(id) # Hypermedia support end end Nick Sutterer @apotonick ⤷ Roar: exemplo 16 quarta-feira, 23 de novembro de 11
“Uncle Bob” Martin @unclebobmartin SOLID S: Single responsibility O: Open-closed L: Liskov substitution I: Interface segregation D: Dependency inversion Lucas Húngaro @lucashungaro S: deve ter apenas uma única responsabilidade O: entidades deveriam ser abertas por extensão, mas fechada por modificação L: objetos deveriam ser sobrescritos com instâncias de seus sub-tipos sem alterar o correto funcionamento do sistema I: muitas interfaces específicas de clientes são melhores que uma interface de propósito generalizado D: crie dependências por abstrações, não por classes concretas 20 quarta-feira, 23 de novembro de 11
codificação rápida Menos atenção a aspectos importantes Fat Models aka programação procedural Solução: OOP, BDD, SOLID SRP => Single Responsibility DIP => Dependency Inversion Coesão e acoplamento Lucas Húngaro @lucashungaro 21 quarta-feira, 23 de novembro de 11
baixa coesão Executa parte de uma ou mais responsabilidades Eliminar o alto acoplamento Um caso de uso e suas dependências Apps padrão (migrations, finders): Models gigantes (fat models) Classes sem importância Lucas Húngaro @lucashungaro 22 quarta-feira, 23 de novembro de 11
responsabilidades bem definidas Evitar ter testes que dependam de BDs Fortalecer a utilização dos Mocks Escrever testes isolados para AR é difícil ActiveRecord: validações, associações e finders SOMENTE! Quer mudar? Comece pelo comportamento! Lucas Húngaro @lucashungaro 23 quarta-feira, 23 de novembro de 11
=> :create has_secure_password def send_password_reset generate_token(:password_reset_token) self.password_reset_sent_at = Time.zone.now save! UserMailer.password_reset(self).deliver end def generate_token begin self[column] = SecureRandom.urlsafe_base64 end while User.exists? (column => self[column]) end end ⤷ Exemplo: Fat model Lucas Húngaro @lucashungaro SOLID através de BDD: Guia prático para Rubistas 24 quarta-feira, 23 de novembro de 11
=> :create has_secure_password def send_password_reset generate_token(:password_reset_token) self.password_reset_sent_at = Time.zone.now save! UserMailer.password_reset(self).deliver end def generate_token begin self[column] = SecureRandom.urlsafe_base64 end while User.exists? (column => self[column]) end end ⤷ Exemplo: Fat model Lucas Húngaro @lucashungaro SOLID através de BDD: Guia prático para Rubistas 25 quarta-feira, 23 de novembro de 11
Guia prático para Rubistas describe UserAuthentication do let(:user) { Factory(:user) } let(:password) { “123456” } context “with valid credentials” do subject{ UserAuthentication.new( user.username, password ) } it “allow access” do subject.authenticate.should be_true end end context “with invalid credentials” do subject{ UserAuthentication.new( user.username, “invalid” ) } it “denied access” do subject.authenticate.should be_true end end end 26 quarta-feira, 23 de novembro de 11
Guia prático para Rubistas class UserAuthentication def initialize(username, password) @username= username @password= password end def authenticate if user = User.find_by_username(@username) user.password_hash == BCrypt::Engine.hash_secret(@password, user.password_salt) else false end end end Acoplamento 27 quarta-feira, 23 de novembro de 11
BDD: Guia prático para Rubistas ⤷ Objetos dublês ⤷ Injeção de dependência Consigo garantir ⤷ Objetos desacoplados e coesos ⤷ Testes rápidos ⤷ Custo de manutenção reduzido 28 quarta-feira, 23 de novembro de 11
através de BDD: Guia prático para Rubistas class UserAuthentication def initialize(username, password) @username= username @password= password end def authenticate(user_repo = User, encryption_engine = BCrypt::Engine) if user = user_repo.find_by_username(@username) user.password_hash == encryption_engine.hash_secret(@password, user.password_salt) else false end end end 29 quarta-feira, 23 de novembro de 11
@lucashungaro SOLID através de BDD: Guia prático para Rubistas describe UserAuthentication do let(:user) { double(“an user”).as_null_object } let(:user_repo) { double(“an user repository”) } let(:encryption_engine) { double(“an encryption engine”) } before(:each) do user.stub(:password_hash).and_return “the hash” user_repo.stub(:find_by_username).and_return user end context “with valid credentials” do before(:each) { encryption_engine.stub( :hash_secret ).and_return user.password_hash } subject{ UserAuthentication.new( “username”, “123456” ) } it “allow access” do subject.authenticate(user_repo, encryption_engine).should be_true end end context “with invalid credentials” do before(:each) { encryption_engine.stub( :hash_secret ).and_return “another hash” } subject{ UserAuthentication.new( “username”, “invalid” ) } it “denied access” do subject.authenticate(user_repo, encryption_engine).should be_true end end end 30 quarta-feira, 23 de novembro de 11
Facebook, GitHub, Foursquare etc. Single Page Applications Spine.js - Backbone.js melhorado! MVC em JavaScript Fortemente baseado em requisições AJAX Aplicação simples, limpa e iterativa Roberto Rodriguez Artavia @rodriguezartav 32 quarta-feira, 23 de novembro de 11
ativas Antes: cada model criava um novo pool Gerenciamento de rotas Fazer o match de uma rota: if pra todo canto E se tivermos muitas rotas? Muito, muito lento! Fundamento: autômatos e expressão regular Muito mais rápido Aaron Patterson @tenderlove 40 quarta-feira, 23 de novembro de 11
= routes.length rg = regexp compare O( r * rg ) Isso leva muito tempo! Pode ser melhorado? Claro, autômatos Aaron Patterson @tenderlove 50 quarta-feira, 23 de novembro de 11
e de difícil manutenção Utilização de gems que fazem coisas triviais Dispatch JS; Simple Form; Simple Presenter Aprenda a usar partials Não utilizar CoffeeScript - difícil de depurar Usar o charset na aplicação <meta charset=”utf-8”/> Use locale: <html dir=”ltr” lang=”<%= I18n.locale %>”> Nando Vieira @fnando 55 quarta-feira, 23 de novembro de 11
Team Elixir “extends” Erlang Aprendeu ainda mais sobre Ruby E se não existissem blocos? E se existisse Arrays e Hashes comprehensions? E se existisse interfaces? E partial applications? Aprendam novas linguagens José Valim @josevalim 57 quarta-feira, 23 de novembro de 11
é importante Designer presente na construção do projeto Todas as etapas Programador entende / analisa requisitos Designer entende / analisa views Identifica melhorias na apresentação das informações Daniel Lopes @danielvlopes 60 quarta-feira, 23 de novembro de 11
garantir a integridade dos dados Use REST como realmente deve ser: simples! Use SOLID, você não morrerá por fazer da forma certa Se puder, evite refreshes em tela; Use Single Page Applications Crie desapego pelo seu código... Refactore ele! Lógica de programação não deve estar nas views Aprenda novas linguagens, você ganhará muito mais Um bom design é fundamental 77 quarta-feira, 23 de novembro de 11