ruby on rails i - modelos
DESCRIPTION
Uma breve apresentação explicando as funcionalidades básicas dos modelos com o ActiveRecord como ORM.TRANSCRIPT
Ruby on Rails I
Tiago Ferreira Lima - fltiago
Roteiro
• Começando com Rails• Migrações• ActiveRecord
• Validações
• Callbacks
• Associações
• Query Interface
• Mãos à obra
Começando com Rails
O que é Rails?
• Framework para desenvolvimento de aplicações Web
• Filosofia Rails• DRY - “Don’t Repeat Yourself”
• Convenção sobre configuração
• REST
MVC
• Arquitetura de software baseada em Modelos, Views e Controladores
• Benefícios• Isolamento da lógica de negócios
• Clara separação de responsabilidades, facilitando a manutenção
Os componentes do Rails
• Action Pack• Action Controller
• Action Dispatch
• Action View
• Action Mailer
Os componentes do Rails
• Active Model• Active Record• Active Resource• Active Support• Railties
Instalando Rails
• Linux e Mac (se já houver o rubygems)
• Windows• Rails Installer FTW! http://
railsinstaller.org/
$ gem install rails$ rails -‐-‐version
Criando uma aplicação Rails
• Um único comando$ rails new nome_do_app
Estrutura de uma aplicação Rails
Migrações
Migrações
• Um jeito bonito e organizado de alterar o banco de dados
• Vantagens• Não há necessidade de comunicar outros
desenvolvedores que houve mudança no BD
• Não há necessidade de conhecer a linguagem específica do SQL
• É independente de banco de dados, suporte a diversos SGBDs
Migrações
• Estrutura das migrações:class CreateProducts < ActiveRecord::Migration
def up create_table :products do |t| t.string :name t.text :description t.timestamps end end def down drop_table :products endend
Migrações
• Ou:class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end endend
Migrações
• Olhando mais de perto as migraçõesclass CreateProducts < ActiveRecord::Migration
def up create_table :products do |t| t.string :name t.text :description t.timestamps end end def down drop_table :products endend
Migrações
• Métodos:add_columnadd_indexadd_referenceadd_timestampscreate_tablecreate_join_tabledrop_table (must supply a block)drop_join_table (must supply a block)remove_timestampsrename_columnrename_indexremove_referencerename_table
Migrações
• Types::binary:boolean:date:datetime:decimal:float:integer:primary_key:string:text:time:timestamp
E finalmente, criando uma migração
• Com o model$ rails generate model Product name:string description:text
# Geraclass CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end endend
E finalmente, criando uma migração
• Uma migração por si só
• Convenções facilitam a criação
$ rails generate migration AddPartNumberToProducts
# Geraclass AddPartNumberToProducts < ActiveRecord::Migration def change endend
$ rails generate migration AddPartNumberToProducts part_number:string
class AddPartNumberToProducts < ActiveRecord::Migration def change add_column :products, :part_number, :string endend
O mais importante
• Executando migrações
• Voltando uma migração
$ rake db:migrate
$ rake db:migrate VERSION=20080906120000
$ rake db:rollback
$ rake db:rollback STEP=3
ActiveRecord
ActiveRecord
• É a implementação de um padrão de mapeamento objeto-relacional (ORM)
• Conecta classes e atributos a tabelas e colunas
ActiveRecord
• Permite:• Migrações
• Validações
• Associações
• Callbacks
• Transações
• Entre outras coisas...
Validações
Visão geral
• Garante que apenas dados válidos serão inseridos no banco de dados
• Existem diversos meios de garantir a validação de um dado: restrições dos banco de dados, validações em client-side, validações em controladores e validações em modelo
• Vamos nos concentrar em modelos, é um jeito Rails de fazer validações
Quando uma validação acontece?
• Antes que os objetos sejam salvos no banco de dados
• Métodos que disparam validaçõescreatecreate!savesave!updateupdate_attributesupdate_attributes!
Exemplo de uso
• valid? e invalid?class Person < ActiveRecord::Base validates :name, :presence: trueend >> p = Person.new=> #<Person id: nil, name: nil>>> p.errors=> {} >> p.valid?=> false>> p.errors=> {name:["can't be blank"]} >> p = Person.create=> #<Person id: nil, name: nil>>> p.errors=> {name:["can't be blank"]} >> p.save=> false >> p.save!=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
Validations Helpers
• Aceitaçãoclass Person < ActiveRecord::Base validates :terms_of_service, acceptance: trueend
class Library < ActiveRecord::Base has_many :books validates_associated :booksend
class Person < ActiveRecord::Base validates :email, confirmation: true validates :email_confirmation, presence: trueend
• Valida modelos associados
• Confirmação e Presença
Validations Helpers
• Exclusãoclass Account < ActiveRecord::Base validates :subdomain, exclusion: { in: %w(www us ca jp), message: "Subdomain %{value} is reserved." }end
class Product < ActiveRecord::Base validates :legacy_code, format: { with: /\A[a-‐zA-‐Z]+\z/, message: "Only letters allowed" }end
class Coffee < ActiveRecord::Base validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }end
• Formatação
• Inclusão
Validations Helpers
• Tamanhoclass Person < ActiveRecord::Base validates :name, length: { minimum: 2, maximum: 500 } validates :password, length: { in: 6..20 } validates :registration_number, length: { is: 6 }end
class Player < ActiveRecord::Base validates :points, numericality: true validates :games_played, numericality: { only_integer: true }end
class Account < ActiveRecord::Base validates :email, uniqueness: trueend
• Numeração
• Unicidade
Callbacks
Visão Geral
• Callbacks são uma forma de associar comportamento a determinados momentos do ciclo de vida de um modelo
• É possível escrever códigos que executem sempre que um objeto do ActiveRecord é criado, salvado, atualizado, deletado, validado ou carregado do banco de dados.
Exemplos de uso
class User < ActiveRecord::Base validates :login, :email, presence: true before_validation :ensure_login_has_a_value protected def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? end endend
class User < ActiveRecord::Base validates :login, :email, presence: true before_create do |user| user.name = user.login.capitalize if user.name.blank? endend
Callbacks disponíveis
• Criando um objeto before_validationafter_validationbefore_savearound_savebefore_createaround_createafter_createafter_save
before_validationafter_validationbefore_savearound_savebefore_updatearound_updateafter_updateafter_save
• Atualizando um objeto
Callbacks disponíveis
• Destruindo um objetobefore_destroyaround_destroyafter_destroy
Associações
Visão Geral
• Relacionamentos/associações são parte central dos bancos de dados relacionais
• ActiveRecord facilita as interações entre modelos
Exemplo de uso
class Customer < ActiveRecord::Base has_many :orders, dependent: :destroyend class Order < ActiveRecord::Base belongs_to :customerend
# rails console$ @orders = @customer.orders$ @costumer = @order.customer
Tipos de associações
• Tipos:belongs_tohas_onehas_manyhas_many :throughhas_one :throughhas_and_belongs_to_many
Query Interface
Visão Geral
• Internface que define acesso ao banco e instancia os objetos
• ActiveRecord facilita a interação entre o objeto e banco de dados
Retornando um simples objeto
• Usando a primary key (find)# Encontra um client com id = 10client = Client.find(10)# => #<Client id: 10, first_name: "Ryan">
# O SQL gerado por RailsSELECT * FROM clients WHERE (clients.id = 10) LIMIT 1
• Usando métodos já prontosclient = Client.last# => #<Client id: 221, first_name: "Russel">
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
client = Client.first# => #<Client id: 1, first_name: "Lifo">
SELECT * FROM clients LIMIT 1
Retornando muitos objetos
• Usando as primary keys (find)client = Client.find([1, 10]) # Or even Client.find(1, 10)# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]
SELECT * FROM clients WHERE (clients.id IN (1,10))
• Retornando todos os elementos de um objeto
# Só existem dois elementos guardados no bancoclients = Client.all# => [#<Client id: 221, first_name: "Russel">, #<Client id: 10, first_name: "Ryan">]
Condições
• String puraclient = Client.where("orders_count = '2'") # => [#<Client id: 1, first_name: "Lifo", orders_count: 2>, #<Client id: 10, first_name: "Ryan", orders_count: 2>]
SELECT * FROM clients WHERE (clients.orders_count = '2')
• Passando Array como parâmetroClient.where("orders_count = ?", params[:orders])Client.where("orders_count = ? AND locked = ?", params[:orders], false)Client.where("created_at >= :start_date AND created_at <= :end_date", {start_date: params[:start_date], end_date: params[:end_date]})
Client.where(created_at: (params[:start_date].to_date)..(params[:end_date].to_date))
SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-‐09-‐29' AND '2010-‐11-‐30')
Ordem e Seleção
• OrdemClient.order("created_at DESC")# ouClient.order("created_at ASC")
Client.order("orders_count ASC, created_at DESC")
• Seleção de colunaClient.select("viewable_by, locked")
SELECT viewable_by, locked FROM clients
Limite e Offset
• Limite e OffsetClient.limit(5).offset(30)
SELECT * FROM clients LIMIT 5 OFFSET 30
Mãos à obra
Caso de uso
• O usuário, possui nome, e-mail, sobrenome, idade
• O usuário não existe sem nome e e-mail
• O e-mail deve ser formatado de maneira correta
• Um usuário possui vários livros
Caso de uso
• O livro deve ter nome e pode ter edição
• Os livros não existem sem os usuários
• Deve ser possível consultar os 5 livros mais recentes de um usuário
• Deve ser possível procurar usuários por nome e sobrenome
Caso de uso
• Deverá ser possível procurar por usuário entre um determinado intervalo de idade
Referências
• Rails Guides - http://guides.rubyonrails.org/
• Ruby on Rails 3 Tutorial - http://ruby.railstutorial.org/
• Design Patterns in Ruby - Russ Olsen
Obrigado!