44
Desenvolvimento Web com Ruby on Rails João Lucas Pereira de Santana gtalk | linkedin | twitter: jlucasps

Desenvolvimento web com Ruby on Rails (parte 3)

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Desenvolvimento web com Ruby on Rails (parte 3)

Desenvolvimento Web com Ruby on

Rails

João Lucas Pereira de Santanagtalk | linkedin | twitter: jlucasps

Page 2: Desenvolvimento web com Ruby on Rails (parte 3)

Definindo Models

Vamos definir a entidade User para representar os usuários de um sistemaUm usuário possui nome, email e idade.

@jlucasps

UserActiveRecord

users

.......

aplicação rails banco de dados

Page 3: Desenvolvimento web com Ruby on Rails (parte 3)

Definindo Models

@jlucasps

users

name email age

usersid name email age created_at updated_at

Como criar a tabela no banco de dados utilizando o Rails ?

Page 4: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

Migrations disponibilizam uma forma conveniênte para alterar o seu banco de dados de uma maneira estruturada e organizadaActiveRecord armazena quais migrations foram executadas utilizando timestampsTudo o que precisamos fazer é atualizar o código e executar $ rake db:migrate

Vamos analizar a anatomia de uma migration

@jlucasps

Page 5: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

@jlucasps

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

release (up) e rollback (down)

Page 6: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

@jlucasps

release-rollback (change)

class AddSizeToProducts < ActiveRecord::Migration def change add_column :products, :size, :integer, :null => :false endend

Rails consegue identificar rollback a partir da release, bem como release a partir do rollback

release <-> rollback

Page 7: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

Migrations são subclasses de ActiveRecord::MigrationImplementam dois métodos: up e down (change)E uma série de métodos auxiliares

@jlucasps

Page 8: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

ActiveRecord fornece métodos que executam tarefas comuns de definição e manipulação de dado.Realizam esta tarefa de forma independente de um banco de dados específico

@jlucasps

Data Definition Language (DDL)

CREATE

ALTER

DROP

Data Manipulation Language (DML)

SELECT

UPDATE

DELETE

Page 9: Desenvolvimento web com Ruby on Rails (parte 3)

MigrationsMétodos para definição de dados

@jlucasps

create_table(:name, options)drop_table(:name)rename_table(:old_name, :new_name)add_column(:table_name, :column_name, :type, options)rename_column(:table_name, :column_name, :new_column_name)change_column(:table_name, :column_name, :type, options)remove_column(:table_name, :column_names)add_index(:table_name, :column_names, options)remove_index(:table_name, :column => :column_name)remove_index(:table_name, :name => :index_name)

Page 10: Desenvolvimento web com Ruby on Rails (parte 3)

MigrationsActiveRecord suporta os seguintes tipos de dados para colunas do banco:

@jlucasps

:binary:boolean:date:datetime:decimal:float:integer:primary_key:string:text:time:timestamp

Page 11: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

Vamos gerar a Migration, implementá-la e executá-la para criar a tabela users

@jlucasps

class CreateTableUsers < ActiveRecord::Migration def change create_table :users do |t| t.column :name, :string t.column :email, :string t.column :age, :integer

t.timestamps end endend

jlucasps@lotus:/media/first_app$ rails g migration CreateUsersTable invoke active_record create db/migrate/20130613063127_create_users_table.rb

Page 12: Desenvolvimento web com Ruby on Rails (parte 3)

Migrations

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:migrate== CreateTableUsers: migrating ===============================================-- create_table(:users) -> 0.0491s== CreateTableUsers: migrated (0.0492s) ======================================

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:rollback== CreateTableUsers: reverting ===============================================-- drop_table("users") -> 0.0008s== CreateTableUsers: reverted (0.0009s) ======================================

Page 13: Desenvolvimento web com Ruby on Rails (parte 3)

Criando Models

Vamos criar o model User para representar entidades mapeadas na tabela users

@jlucasps

class User < ActiveRecord::Base # Attrs accessible attr_accessible :name, :email, :age # Validations # Associations # Scopes # Públic methods end

Page 14: Desenvolvimento web com Ruby on Rails (parte 3)

Testes unitários nos models

Adicionar a gem shoulda e executar $ bundle installCriar o arquivo /spec/models/user_spec.rb

@jlucasps

# -*- coding: utf-8 -*-require 'spec_helper'

describe User do it { should allow_mass_assignment_of :name } it { should allow_mass_assignment_of :email } it "creates an user" do user = User.new(:name => "João Lucas", :email => "[email protected]", :age => 24) user.save.should be_true endend

Page 15: Desenvolvimento web com Ruby on Rails (parte 3)

Testes unitários nos models

Executar $ rspec

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec....FFF Failures: 1) User 2) User 3) User creates an user Finished in 0.35138 seconds7 examples, 3 failures Failed examples: rspec ./spec/models/user_spec.rb:7 # Userrspec ./spec/models/user_spec.rb:6 # Userrspec ./spec/models/user_spec.rb:9 # User creates an user Randomized with seed 10444

Page 16: Desenvolvimento web com Ruby on Rails (parte 3)

Testes unitários nos models

Executar $ rake db:test:load e $ rspec

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:test:loadjlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec.......Finished in 0.41314 seconds7 examples, 0 failures

$ git status$ git add .$ git commit -m "Testes unitários no modelo User"

Page 17: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

E se quisermos que os campos nome e email sejam obrigatórios?

@jlucasps

Page 18: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócioVamos implementar testes para nossa nova regra

@jlucasps

# -*- coding: utf-8 -*-require 'spec_helper'describe User do it { should allow_mass_assignment_of :name } it { should allow_mass_assignment_of :email } it "creates an user" do user = User.new(:name => "João Lucas", :email => "[email protected]", :age => 24) user.save.should be_true end it "fail to create a user when name is blank" do user = User.new(:email => "[email protected]", :age => 24) user.save.should be_false end it "fail to create a user when email is blank" do user = User.new(:name => "João Lucas", :age => 24) user.save.should be_false endend

Page 19: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec.......FF Failures: 1) User fail to create a user when name is blank Failure/Error: user.save.should be_false expected: false value got: true # ./spec/models/user_spec.rb:16:in `block (2 levels) in <top (required)>' 2) User fail to create a user when email is blank Failure/Error: user.save.should be_false expected: false value got: true # ./spec/models/user_spec.rb:21:in `block (2 levels) in <top (required)>'Finished in 0.37949 seconds9 examples, 2 failuresFailed examples:rspec ./spec/models/user_spec.rb:14 # User fail to create a user when name is blankrspec ./spec/models/user_spec.rb:19 # User fail to create a user when email is blankRandomized with seed 46469

Page 20: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

O que devemos implementar para o teste passar?Implementar alterações no banco (not null)Implementar alterações no model (validations)

@jlucasps

jlucasps@lotus:/first_app$ rails g migration NotNullToNameAndEmailToUsers invoke active_record create db/migrate/20130613074955_not_null_to_name_and_email_to_users.rb

Page 21: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

class NotNullToNameAndEmailToUsers < ActiveRecord::Migration def up change_column :users, :name, :string, :null => false change_column :users, :email, :string, :null => false end def down change_column :users, :name, :string, :null => true change_column :users, :email, :string, :null => true endendjlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:migrate== NotNullToNameAndEmailToUsers: migrating ===================================-- change_column(:users, :name, :string, {:null=>false}) -> 0.0106s-- change_column(:users, :email, :string, {:null=>false}) -> 0.0038s== NotNullToNameAndEmailToUsers: migrated (0.0146s) ==========================

Page 22: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

class User < ActiveRecord::Base # Attrs accessible attr_accessible :name, :email, :age # Validations validates :name, :presence => true, :allow_blank => false validates :email, :presence => true, :allow_blank => false # Associations # Scopes # Públic methods end

Page 23: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/first_app$ rake db:test:loadjlucasps@lotus:/media/truecrypt1/first_app$ rspec......... Finished in 0.50004 seconds9 examples, 0 failures Randomized with seed 2195

$ git status$ git add .$ git commit -m "Não permitir que sejam criados usuários com nome ou email em branco."

Page 24: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

Outra vez as regras mudaram, agora precisamos adicionar um campo para opção sexual do usuário.*Não há necessidade do campo ser obrigatórioQuais os passos?

@jlucasps

Testes Código Refactoring

Page 25: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

# -*- coding: utf-8 -*-require 'spec_helper' describe User do ................... ...................

it "creates a user with gender value MALE" do user = User.new(:name => "Bob Dylan", :email => "[email protected]", :age => 72, :gender => User::MALE) user.save.should be_true end it "creates a user with gender value FEMALE" do user = User.new(:name => "Candice Swanepoel", :email => "[email protected]", :age => 24, :gender => User::FEMALE) user.save.should be_true endend

Page 26: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec.......FF.. Failures: 1) User fail to create a user with gender value MALE Failure/Error: user = User.new(:name => "Bob Dylan", :email => "[email protected]", :age => 72, :gender => User::MALE) NameError: uninitialized constant User::MALE # ./spec/models/user_spec.rb:25:in `block (2 levels) in <top (required)>' 2) User fail to create a user with gender value FEMALE Failure/Error: user = User.new(:name => "Candice Swanepoel", :email => "[email protected]", :age => 24, :gender => User::FEMALE) NameError: uninitialized constant User::FEMALE # ./spec/models/user_spec.rb:30:in `block (2 levels) in <top (required)>'

Finished in 0.38009 seconds11 examples, 2 failuresFailed examples:rspec ./spec/models/user_spec.rb:24 # User fail to create a user with gender value MALErspec ./spec/models/user_spec.rb:29 # User fail to create a user with gender value FEMALERandomized with seed 23755

Page 27: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

class User < ActiveRecord::Base # Attrs accessible attr_accessible :name, :email, :age, :gender # Constants MALE = 1 FEMALE = 2 OTHER = 3 # Validations validates :name, :presence => true, :allow_blank => false validates :email, :presence => true, :allow_blank => false # Associations # Scopes # Públic methods end

Page 28: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/first_app$ rails g migration AddGenderToUsers gender:integer invoke active_record create db/migrate/20130613081853_add_gender_to_users.rb

class AddGenderToUsers < ActiveRecord::Migration def change add_column :users, :gender, :integer endend

Page 29: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:migrate== AddGenderToUsers: migrating ===============================================-- add_column(:users, :gender, :integer) -> 0.0009s== AddGenderToUsers: migrated (0.0010s) ====================================== jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:test:load...........

Finished in 0.38494 seconds11 examples, 0 failuresRandomized with seed 52935

$ git status$ git add .$ git commit -m "Adicionando campo gender no modelo User."

Page 30: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócioE se for necessário informar o sexo caso a idade seja maior ou igual a 18?

@jlucasps

context "when age >= 18" do it "creates an user with gender value" do user = User.new(:name => "João Lucas", :email => "[email protected]", :age => 18, :gender => User::MALE) user.save.should be_true end it "does not create an user without gender value" do user = User.new(:name => "João Lucas", :email => "[email protected]", :age => 18) user.save.should be_false end endcontext "when age < 18" do it "creates an user with gender value" do user = User.new(:name => "João Lucas", :email => "[email protected]", :age => 17, :gender => User::MALE) user.save.should be_true end it "does not create an user without gender value" do user = User.new(:name => "João Lucas", :email => "[email protected]", :age => 17) user.save.should be_true end end

Page 31: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec...........F....Failures: 1) User when age >= 18 does not create an user without gender value Failure/Error: user.save.should be_false expected: false value got: true # ./spec/models/user_spec.rb:48:in `block (3 levels) in <top (required)>' Finished in 0.40493 seconds16 examples, 1 failureFailed examples:rspec ./spec/models/user_spec.rb:46 # User when age >= 18 does not create an user without gender value

Page 32: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

class User < ActiveRecord::Base # Attrs accessible attr_accessible :name, :email, :age, :gender # Constants MALE = 1 FEMALE = 2 OTHER = 3 # Validations validates :name, :presence => true, :allow_blank => false validates :email, :presence => true, :allow_blank => false validates :gender, :presence => true, :if => :adulthood # Associations # Scopes # Públic methods def adulthood age >= 18 end end

Page 33: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec................

Finished in 0.40208 seconds16 examples, 0 failures

Randomized with seed 46088

$ git status$ git add .$ git commit -m "Necessário informar o sexo caso a idade seja maior ou igual a 18."

Page 34: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

Vasmo modificar nosso código para que não seja possível o cadastro de dois usuários com o mesmo email.

Vale lembrar que, além da validação no modelo, podemos também implementar uma validação no banco de dados para garantir unicidade de uma coluna.

No entanto, primeiro, vamos aos testes.

Page 35: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

Vamos adicionar o seguinte contexto à nossa suite de testes e executar $ rspec context "when tries to create two users with same email" do it "create two users with differente emails" do user_1 = User.create(:name => "Primeiro usuário", :email => "[email protected]") user_2 = User.new(:name => "Segundo usuário", :email => "[email protected]") user_2.save.should be_true end it "does no create two users with same emails" do user_1 = User.create(:name => "Primeiro usuário", :email => "[email protected]") user_2 = User.new(:name => "Segundo usuário", :email => "[email protected]") user_2.save.should be_false end end

Page 36: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

Verificamos que foram reportados dois erros que não esperávamos 1) User when tries to create two users with same email create two users with differente emails Failure/Error: user_1 = User.create(:name => "Primeiro usuário", :email => "[email protected]") NoMethodError: undefined method `>=' for nil:NilClass # ./app/models/user.rb:21:in `adulthood' # ./spec/models/user_spec.rb:68:in `block (3 levels) in <top (required)>' 2) User when tries to create two users with same email does no create two users with same emails Failure/Error: user_1 = User.create(:name => "Primeiro usuário", :email => "[email protected]") NoMethodError: undefined method `>=' for nil:NilClass # ./app/models/user.rb:21:in `adulthood' # ./spec/models/user_spec.rb:74:in `block (3 levels) in <top (required)>'

Page 37: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

Qual o motivo dos erros inesperados?Vamos implementar alguns testes para o método adulthood, verificar os erros e atualizar o código

describe "#adulthood" do it "is adult when age == 18" do user = User.new(:name => "Nome", :email => "[email protected]", :age => 18) user.adulthood.should be_true end it "is adult when age > 18" do user = User.new(:name => "Nome", :email => "[email protected]", :age => 30) user.adulthood.should be_true end it "is not adult when age < 18" do user = User.new(:name => "Nome", :email => "[email protected]", :age => 17) user.adulthood.should be_false end it "is not adult when age is blank" do user = User.new(:name => "Nome", :email => "[email protected]") user.adulthood.should be_false end end

Page 38: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

Atualizando o model Userclass User < ActiveRecord::Base # Attrs accessible attr_accessible :name, :email, :age, :gender # Constants MALE = 1 FEMALE = 2 OTHER = 3 # Validations validates :name, :presence => true, :allow_blank => false validates :email, :presence => true, :allow_blank => false validates :gender, :presence => true, :if => :adulthood # Associations # Scopes # Públic methods def adulthood self.age.present? and age >= 18 endend

Page 39: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec............F.........Failures: 1) User when tries to create two users with same email does no create two users with same emails Failure/Error: user_2.save.should be_false expected: false value got: true # ./spec/models/user_spec.rb:76:in `block (3 levels) in <top (required)>'Finished in 0.40533 seconds22 examples, 1 failure Failed examples: rspec ./spec/models/user_spec.rb:73 # User when tries to create two users with same email does no create two users with same emailsRandomized with seed 45305

Agora sim, encontramos o erro esperado na criação de dois usuários com mesmo email.

Page 40: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

first_app$ rails g migration AddUniqueToEmail

class AddUniqueToEmail < ActiveRecord::Migration def change add_index :users, :email, :unique => true endendjlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:migrate== AddUniqueToEmail: migrating ===============================================-- add_index(:users, :email, {:unique=>true}) -> 0.0009s== AddUniqueToEmail: migrated (0.0010s) ========================================

Page 41: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

class User < ActiveRecord::Base # Attrs accessible attr_accessible :name, :email, :age, :gender # Constants MALE = 1 FEMALE = 2 OTHER = 3 # Validations validates :name, :presence => true, :allow_blank => false validates :email, :presence => true, :allow_blank => false validates_uniqueness_of :email validates :gender, :presence => true, :if => :adulthood # Associations # Scopes # Públic methods def adulthood self.age.present? and age >= 18 end end

Page 42: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rspec...................... Finished in 0.46128 seconds22 examples, 0 failures Randomized with seed 64622

$ git status$ git add .$ git commit -m "Validação de unicidade de email."

Page 43: Desenvolvimento web com Ruby on Rails (parte 3)

Mudanças no negócio

@jlucasps

Outras possíveis validações:● formatos● inclusão em uma lista, range● exclusão● formato● presença● números (inteiros, decimais)● condicionais● custom validations

Page 44: Desenvolvimento web com Ruby on Rails (parte 3)

Desenvolvimento Web com Ruby on

Rails

João Lucas Pereira de Santanagtalk | linkedin | twitter: jlucasps

Obrigado!