Mudança nos trilhos: criando e alterando projetos Rails em desenvolvimento

Douglas Xavier
8 min readAug 19, 2019

Quem já fez sua iniciação em Rails viu na prática um dos frameworks de Rapid Application Development (RAD) que faz jus ao conceito. Desenvolver com Ruby on Rails (RoR) é de fato focar no core da aplicação e reusar componentes a partir de pacotes à vontade. Das características mais fascinantes desse framework que debuta esse ano, eu escolhi falar aqui sobre o scaffold, que é um processo de automatização para construção de uma aplicação, em conjunto com um tópico complementar, que são as migrations.

O verbete da Wikipedia sobre o termo descreve scaffolding como uma atividade da engenharia civil que faz uso de estruturas de suporte, como andaimes, para auxiliar os operários no trabalho de desenvolvimento de uma construção predial. Na programação, a apropriação da palavra tem praticamente o mesmo sentido. Traduzindo para este contexto, fazer um scaffold é criar de forma automatizada uma estrutura Model-View-Controller (MVC) para o seu projeto.

No caso do Rails, de acordo com sua documentação, o scaffold é capaz de gerar todo o modelo de um projeto, com seus respectivos controllers para manipulá-los e também as respectivas views, além de migrações de banco de dados, performando o mapeamento objeto-relacional (ORM) por meio da API Active Records, e ainda módulo de testes.

Baseando-me na minha experiência, posso concluir que RoR oferece é uma ferramenta muito poderosa e eficiente comparada a outros frameworks do mercado, como o ASP.net da Microsoft, com qual trabalhei ano passado. Apesar de esse último também ser um framework relevante que conta com um scaffold, eu dou ao RoR uma estrela a mais pela flexibilidade e simplicidade (a um passo de uma linha de comando).

Para fazer a defesa desses aspectos que citei, vou descrever a seguir de forma básica a criação de um projeto Rails com MVC gerado via scaffold. Assim, constataremos o segundo item destacado, a simplicidade. Mas, dando sequência, quero falar mais especificamente de migrations e de como elas podem ser usadas num projeto em curso com alterações de requisitos em pleno desenvolvimento.

Criando um projeto rails

Este não é um tutorial de instalação do Rails. Então, se você ainda não tem o Ruby, o gem e o Rails gerenciado por um manager como RVM ou rodando em container do Docker em sua máquina, comece pelo getting started do fornecedor, mas também há muitos bons e detalhados tutoriais na web.

Considerado que você está com o ambiente todo configurado, vamos aos comandos e códigos. No diretório onde você deseja criar seu projeto, execute o comando a seguir e então acesse a pasta criada com o comando cd do seu terminal.

$ rails new myapp

Se você abrir o diretório do projeto e der uma fuçada, vai ver que foram gerados dezenas de itens de configuração e a estrutura “oca” do MVC que mais tarde abrigará os arquivos gerados pelo scaffold no seu projeto. É interessante perceber que dentro da pasta app, além das pastas Model, View e Controller, existem outras com componentes auxiliares, para statics (e.g., js scripts, arquivos css), por exemplo.

Esse é pacote com tudo que é padrão e essencial para iniciar uma aplicação Rails. Prova é que se você executar o comando rails server, você já tem seu app acessível no servidor (isso se estiver tudo ok com as configurações do ambiente, incluindo acesso à porta via Docker, por exemplo).

Construindo o MVC do seu app com scaffold

Chegamos ao ponto. Mas antes do scaffold para o MVC do modelo de negócio do seu projeto, você precisará de uma página inicial e, para gerenciá-la, um controlador dedicado. Para isso, o Rails tem o generator, que tem a prerrogativa de criar componentes como controllers (e automaticamente views vinculadas), ou models independentes. Ele executa inclusive o scaffold e as migrations.

Para criar o controller e a respectiva view de uma página Home, a porta de entrada da aplicação, basta executar o comando abaixo. O nome do controlador será HomeController, cujo nome “home” é passado como parâmetro junto com o nome da view que ele deverá controlar. Neste caso, o parâmetro index gerará o arquivo index.html.erb.

$ rails generate controller home index

Observação: o generate pode ser abreviado para apenas g

A rota para essa view deverá ser incluída no arquivo config/routes.rb:

root :to => “home#index”

Vamos agora para o core do projeto myapp, ou seja, toda a estrutura e desenvolvimento dessa nova aplicação com seus requisitos e lógica. Como exemplo, eu vou propor um esquema bem simples, com dois modelos: Film e Actor, cada um com dois campos, conforme o diagrama UML de classes abaixo. O nome do cabeçalho é o nome que será gerado para as respectivas tabelas no banco de dados, seguindo a convenção de nomes Active Records.

Usemos então o scaffold para nos ajudar nessa tarefa. Começando por films, execute o comando a seguir:

rails generate scaffold film id:integer title:string

Explicando: o generate invoca o scaffold e gera o MVC para Films incluindo os dois campos id (tipo integer) e title (tipo string). No log do seu terminal, você verá quais pastas e arquivos foram criados, bem como no diretório do projeto que agora consta de um modelo, seu controlador com métodos CRUD e uma série de arquivos de views.

Dessa vez, vamos criar o modelo Actor, seguindo a mesma sintaxe:

rails generate scaffold actor id:integer name:string

Ok, temos dois modelos prontos com o V e o C também automaticamente gerados. Verifique os arquivos na pasta db/migrate do seu projeto. Você lembra que é o scaffold quem prepara as migrações para o banco de dados? Sendo assim, os modelos que criamos não parecem certos, ou pelo menos não são o ideal, seja pensando em nível de programação, no paradigma orientado a objetos, seja no aspecto dos dados relacionais. O fato é que os modelos não estão associados. Está faltando uma chave estrangeira ali em algum lugar. Erramos a mão na construção. Great Scott! E agora, o que fazer?

Fazendo reformas com migrations

Eis que temos duas soluções quase milagrosas. A primeira é mais aplicada ao caso, já que só criamos o MVC, mas não começamos a desenvolver nada do projeto. A estratégia seria destruir o scaffold que acabamos de fazer. Para isso, basta executar a linha a seguir:

rails destroy scaffold film

Rapidamente, o Rails desfaz todos os arquivos que foram gerados em massa. Algo que supera em custo de tempo e eficácia um trabalho manual para consertar o erro. Dá pra fazer “na mão”? Dá! Mas a gente não quer ter dor de cabeça! Então, guarde essa dica na cabeça, no coração e no Google Keep.

Aprendida a lição, vamos fazer tudo de novo, mas do jeito “certo”. Na verdade, ainda não é o ideal, mas iremos evoluir, prometo! Dessa vez, vamos criar um relacionamento um tanto quanto engessado, mas pelo menos é um relacionamento (rs), conforme o diagrama abaixo.

Note que agora temos um novo campo no modelo Actor, uma chave estrangeira (film_id) que referencia o modelo Film / tabela Films. Assim, cada filme pode ter vários atores a ele relacionados, mas cada ator ou atriz só poderá estar vinculado a um filme.

Execute, na sequência:

rails generate scaffold film id:integer title:string

rails generate scaffold actor id:integer name:string film:references

A segunda linha, que gera novamente o modelo Actor, tem um acréscimo que é justamente a chave estrangeira que referencia Films. O campo film_id é o que será gerado no banco de dados. Ao executar o scaffold, é necessário apenas incluir o nome do modelo, no singular, dois pontos e a cláusula references. Pronto, o scaffold entendeu que é para incluir uma terceira coluna no modelo/tabela que será uma chave estrangeira para outro modelo/tabela.

Com os modelos prontos, você já pode criar o banco, caso já não o tenha criado. Veja instruções sobre configuração do arquivo database.yml na documentação do Rails. Depois de criado o banco, é hora das migrations mostrarem a que vieram. Outro comando simples. Novamente, use uma das opções.

rails db:migrate

rake db:migrate

Entretanto, às vezes o problema que aparece batendo à porta é de necessidade de mudanças no projeto, inclusão de novos requisitos que podem demandar novos modelos ao esquema. Como se sabe, projetos ágeis devem ter a capacidade de gerir e atender a mudanças de forma eficiente e em menor risco e tempo. Fato é que nossa lógica está nada realista para o mercado de cinema, pois sabemos que um(a) ator/atriz participa de vários filmes ao longo da vida. Então, teremos mesmo de fazer as mudanças apontadas.

Suponhamos que já tenhamos um banco com tabelas criadas, um app em pleno desenvolvimento, que ainda não está em produção (com acesso pelo usuário final.) Essa mudança requerida pode ser feita sem grandes efeitos colaterais. Mas você deverá fazer um drop no banco. Se for o caso de ele já estar populado e você precisar manter os dados, faça um backup de tudo antes. É possível, porém, que não seja necessário fazer o drop, se não houver dependências e os campos apagados não sejam mais necessários.

O drop do banco pode ser feito com um dos comandos abaixo:

rails db:drop

rake db:drop

Temos um novo diagrama na nossa mesa para reformular o projeto. O que precisamos é de um modelo/tabela que represente e armazene, respectivamente, as relações muitos-para-muitos entre Film e Actor. As coisas agora parecem fazer mais sentido.

É uma espécie de reforma que vamos fazer. A solução é criar migrations para o que pretendemos. Elas podem ser usadas para adicionar e remover campos também. Como elas são executadas na ordem de criação, as alterações mais recentes sobrescrevem as anteriores.

São dois passos que devemos executar:

1) Remover do modelo Actor a chave estrangeira que havia referenciado Film:

rails g migration RemoveFilmFromActors film:references

(Atenção para o uso de plural no nome do modelo)

2) Criar o novo modelo com as referências a ambos os modelos já criados

rails generate scaffold cast id:integer film:references actor:references

Serviço feito. Mas não terminamos sem antes aparecer mais uma demanda: adicionar colunas em dois modelos. É só seguir a receita:

rails g migration AddYearToFilm year:integer

rails g migration AddGenderToActor gender:string

rails db:migrate

Enfim, nosso diagrama final de classes do projeto. Espero que tenham chegado até aqui.

Conclusão

O scaffold é mesmo uma solução muito boa. Uma mão na roda, como se diz. Por outro lado, engessa um pouco em virtude de algumas características, entre elas, a convenção de nomes que o framework utiliza. Como deu pra perceber, os nomes das tabelas são geradas automaticamente a partir dos nomes dos modelos, sendo estes no singular e os das tabelas do banco no plural. Isso muitas vezes acaba em uma semântica ruim para a aplicação e o modelo de negócio. Quer um exemplo? Tente gerar um scaffold para o modelo Resposta de uma enquete ou quizz. Veja o que acontece. Surge um problema para ser resolvido, em relação a refatorações de nomes de modelos e rotas. Fica para um próximo post.

--

--