Nos Trilhos com RAILS Sylvestre Mergulhão Necessidades Desenvolver para a web Desenvolver para a web de forma organizada Desenvolver para a web de forma fácil de manter Desenvolver para a web de forma fácil Desenvolver para a web de forma rápida Desenvolver para a web de forma (mais formas???) Motivação n° 1/3 Java Muito trabalho para pouco resultado Java+Struts Muita(mesmo!) configuração e muito(mesmo!) trabalho para pouco resultado PHP Muita liberdade resulta em pouca organização Motivação n° 2/3 Comparativos encontrados na Internet de um tal de framework Ruby on Rails (RoR ou apenas Rails) com o Struts. Alguns chegavam a dizer num total de 10:1 em relação a quantidade de linhas de código entre um e outro, mas sem muitos detalhes. Por que não pagar pra ver? Motivação n° 3/3 Na verdade não paguei, recebi... pois estava no horário de expediente. Download e instalação de linguagem, bibliotecas, conector para o banco de dados Postgresql e o framework RoR: 1h. Desenvolvimento passo-a-passo de uma aplicação CRUD seguindo um tutorial: 1h. Resultado Duas aplicações... Uma em Java+Struts+Hibernate Outra em Ruby on Rails O mesmo banco de dados... O mesmo cérebro... Introdução ao Ruby “Uma linguagem que não afeta seu jeito de pensar sobre programação não vale a pena aprender” Alan Jay Perlis Cientista da computação Conceitos utilizados pelo Ruby Ruby? Oh, quanta preciosidade... DRY. Poxa, mas por que tão seco? KISS. Tomara que não seja o Last. POLS: A lógica da menor surpresa. Algumas características Orientação a objetos sempre! Tipificação forte, mas dinâmica Atributo de classe? sempre privado... acesso só por métodos... Orientação a objetos sempre! Tudo(mesmo!) é um objeto. puts "alguma coisa".upcase # imprimirá "ALGUMA COISA" Tipificação forte, mas dinâmica num = 81 6.times do puts "#{num.class}: #{num}" num *= num end Fixnum: Fixnum: Fixnum: Bignum: Bignum: Bignum: 81 6561 43046721 1853020188851841 3433683820292512484657849089281 11790184577738583171520872861412518665678211592275841109096961 Outros tipos Fixnum Bignum Float Range – representa intervalo de valores como 1..10 ou b..e; Expressão regular – representa uma expressão regular /a/ ou /^\s*[a-z]/ Um Array... meu_array = [3,6,7,10] Um Hash... meu_hash = {:nome => 'Sylva', :telefone => '(21)8106-9960'} Getter e setter? Simples... class Carro attr_accessor :modelo, :fabricante end Herança e métodos/operadores class Numero < Fixnum def +(numero) 71 end end Blocos de código 3.times { puts "Ruby rulez!" } # Irá imprimir 3 vezes # "Ruby rulez!" 3.times do # Irá imprimir 6 vezes "Ruby rulez!" puts "Ruby rulez!" puts "Ruby rulez!" end Muitas outras funcionalidades Módulos, Mixins, tratamento de exceção, etc... O framework Rails O framework Rails Framework MVC 3 módulos principais: . Active Record – ORM . Action Controller . Action View Utiliza os mesmos conceitos do Ruby e mais um, chamado Convention over Configuration. Configuração só é necessária quando não existe padrão. Estrutura de diretórios 1/2 minha_aplicação/ /app - arquivos dos modelos, visões e controles /components - componentes reutilizáveis /config - arquivos de configuração, como de banco de dados, por exemplo /db /doc /lib /log /public - arquivos do schema do banco de dados - Documentação gerada automaticamente - código compartilhado - arquivos de log gerados pela aplicação - o diretório que é acessível pela web. Da visão do navegador parece que toda a aplicação roda daqui /scripts /test /vendor - conjunto de scripts utilitários - local para os testes de unidade, funcionais, mock objects e fixtures - códigos de terceiros, como plugins Estrutura de diretórios 2/2 app/ /controllers - onde são colocados os controladores /helpers - onde são colocados os helpers /views - onde são colocados arquivos de visão /models - onde são colocados os modelos e templates Active Record, o salvador! A classe que representa o modelo class Client < ActiveRecord::Base validates_uniqueness_of :login, :name validates_presence_of :login, :name, :password validates_confirmation_of :password def self.login(login, password) find(:first, :conditions => ["login = ? and password = ?",login,password]) end def try_to_login Client.login(self.login, self.password) end end Active Record Requisição ao Banco de Dados Quero buscar o cliente com login “sylva”! oh quem poderá me ajudar? Client.find(:first, :conditions => ["login = ?","sylva"]) Client.find_by_login("sylva") Active Record Requisição ao Banco de Dados De onde surgiram os métodos 'find' e 'find_by_login'?? Eles não estão definidos na classe Client! Simple! It's a kind of magic! \o/ Active Record Requisição ao Banco de Dados Na verdade, nem tanto... é simplesmente herança... class Client ... ... ... end < ActiveRecord::Base Active Record Requisição ao Banco de Dados Então, nenhum código precisou ser escrito para buscar um cliente no banco. Bastou invocar o método que é herdado do ActiveRecord and it's done! Em Hibernate.... Após escrever o XML de mapeamento, gerar a fábrica de requisições, resgatar a sessão e outras coisas mais... package org.gula.persistence.dao; import java.util.ArrayList; import java.util.Collection; import org.gula.entity.Cliente; import org.hibernate.*; import org.hibernate.criterion.Expression; import org.gula.persistence.dao.DAOException; import org.gula.persistence.HibernateUtil; public class ClienteDAO { ... public Cliente retrieve(Cliente clientePk) throws DAOException { Cliente cliente; Session session = null; try { session = HibernateUtil.currentSession(); cliente = (Cliente) session.createCriteria(Cliente.class).add( Expression.eq( "login", clientePk.getLogin())) .uniqueResult(); HibernateUtil.closeSession (); } catch (Exception e) { throw new DAOException(e); } return cliente; } } Como pode-se perceber, a diferença é grande.... Active Record Mapeamento dos relacionamentos Usando-se a convenção do Rails fica simples e, SEM CONFIGURAÇÃO! class Sale < ActiveRecord::Base has_many :sale_items (...) end class SaleItem < ActiveRecord::Base belongs_to :product belongs_to :sale (...) end Beans? Pra que feijões? Struts faz validação dos FORMULÁRIOS ActionForm ou DynaActionForm Diretamente na Action, via método validate() no ActionForm ou via Validator Framework (mais xml! ¬¬) Beans? Pra que feijões? Validação do modelo, não do formulário... Validação fica junta com o modelo. class Product < ActiveRecord::Base belongs_to :category belongs_to :manufacturer validates_uniqueness_of :nome validates_presence_of :nome validates_presence_of :category_id validates_presence_of :manufacturer_id validates_numericality_of :preco validates_numericality_of :estoque ... end Beans? Pra que feijões? Se as validações default não forem suficientes basta definir o método validate() dentro do modelo E mais! Pode ser sobrecarregado também o método validate_on_create() e validate_on_update() Os nomes seguem o POLS ;) class Product < ActiveRecord::Base belongs_to :category belongs_to :manufacturer (...) protected def validate errors.add(:preco, 'Preço negativo') unless preco.nil? || preco > 0.0 end def validate_on_create errors.add(:picture, l(:error_jpeg)) if self.imagem.empty? || validate_content end def validate_on_update if self.imagem.empty? @product = Product.find(self.id) self.imagem = @product.imagem return end errors.add(:picture, l(:error_jpeg)) if validate_content end (...) end Sessões em Rails... just KISS! Atributo session sempre disponível dentro dos controladores permite uso fácil da sessão... Como num Hash. def authorize unless session[:client_id] flash[:notice] = l(:do_login) session[:jumpto] = request.parameters redirect_to(:controller => "client", :action => "login") end end O que foi desenvolvido? Foram desenvolvidas duas aplicações muito semelhantes na interface e que utilizam a mesma base de dados. As duas possuem as mesmas funcionalidades e operam da mesma forma do ponto de vista do usuário. Uma foi desenvolvida em Java+Struts+Hibernate. A outra em Ruby on Rails. Comparativo numérico SLOCCount - http://www.dwheeler.com/sloccount/ CLOC - http://cloc.sourceforge.net/ Contadores de linhas de código. Ignoram linhas em branco e comentários. Número de linhas de código 4000 3750 3500 3250 3000 2750 2500 2250 2000 1750 1500 1250 1000 750 500 250 0 Linhas Ruby 681 Java 3854 Java Ruby Relação de 1:5,66 Número de arquivos de código 45 42,5 40 37,5 35 32,5 30 27,5 25 22,5 20 17,5 15 12,5 10 7,5 5 2,5 0 Arquivos Ruby 24 Java 44 Relação de 1:1,833 Java Ruby Número de linhas de templates 900 Linhas RHTML 500 JSP 875 800 700 600 500 JSP RHTML 400 300 200 100 0 Relação de 1:1,75 Número de arquivos de template 27,5 25 Arquivos RHTML 25 JSP 26 22,5 20 17,5 15 12,5 10 7,5 5 2,5 0 JSP RHTML Quase a mesma relação Número de linhas em arquivos de configuração YAML XML Linhas 18 273 275 Vale a pena calcular 250 essa relação? 225 200 175 YAML XML Linhas 18 273 Apenas para manter a apresentação em 150 125 XML YAML conformidade: 1:15. 100 75 50 25 0 PS: Nas 18 linhas YAML estão configurados 3 bancos: devel, test e prod. O trabalho buscou mais que ser um simples comparativo de linhas de código. Na pequena aplicação desenvolvida, o Rails mostrou como a lógica de sua implementação deixa as coisas mais simplificadas para os desenvolvedores. Claro que usar apenas um caso como verdade universal não é correto. Mas ele se mostrou preparado para os desafios de grandes sistemas web. “Ruby on Rails: porque programação não precisa ser entediante.” Fabio Akita Programador Java O futuro é livre. Open your minds. Obrigado! Contato: Sylvestre Mergulhão [email protected] O futuro é livre. Open your minds.