datherra / manual

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rails

Nova Aplicação

Usaremos como aplicação exemplo um cadastro de SRs atendidas de cada sistema que também relacione o analista que a atendeu.

Crie a nova aplicação:

$ rails new srmanager -T -d mysql

Ajuste o seu Gemfile:

source "https://rubygems.org"

gem "rails", "3.2.6"
gem "mysql2"

group :assets do
  gem "sass-rails",   "~> 3.2.3"
  gem "therubyracer", :platforms => :ruby
  gem "uglifier", ">= 1.0.3"
end

gem "jquery-rails"

group :development, :test do
  gem "rspec-rails"
  gem "capybara"
  gem "thin"
  gem "rspec-rails"
end

SEMPRE que atualizar seu Gemfile, execute:

$ bundle install

Primeiros Passos com RSpec

Integre o rspec com o Rails:

$ rails generate rspec:install

Crie esqueleto do teste de integração:

$ rails generate integration_test cadastra_sr

Edite o arquivo criado pelo comando anterior, deixando-o com o seguinte conteúdo:

# encoding: utf-8

require "spec_helper"

describe "Cadastra SRs" do
  context "quando enviando dados válidos" do
    
    before do
      visit "/"
      click_link "Cadastrar SR"
      
      fill_in "Número da SR", :with => "SR12001234"
      select "DSF", :from => "Sistema"
      select "Robert Plant", :from => "Analista"
      fill_in "Projeto", :with => "23o dígito"
      fill_in "Conclusão da SR", :with => "Tudo certo!"
      click_button "Adicionar SR"
    end
    
    it "redireciona para página de cadastro de SR"
    it "mostra mensagem de sucesso"
    it "mostra a SR cadastrada"
  end
  
  context "quando enviando dados inválidos" do
    
    before do
      visit "/"
      click_link "Cadastrar SR"
    end

    it "renderiza a página de cadastro de SR"
    it "mostra mensagem de erro"
  end    
end

Ainda não temos os testes implementados, mas neste arquivo já podemos verificar o quão simples é a DSL do RSpec. Ao ser executado, ele mostrará nossos testes com o status pending:

$ rspec  spec/requests/cadastra_srs_spec.rb 

*****

Pending:
  Cadastra SRs quando enviando dados válidos redireciona para página de cadastro de SR
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:20
  Cadastra SRs quando enviando dados válidos mostra mensagem de sucesso
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:21
  Cadastra SRs quando enviando dados válidos mostra a SR cadastrada
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:22
  Cadastra SRs quando enviando dados inválidos renderiza a página de cadastro de SR
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:32
  Cadastra SRs quando enviando dados inválidos mostra mensagem de erro
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:33

Finished in 0.00056 seconds
5 examples, 0 failures, 5 pending

Agora, vamos começar a implementar as verificações, também conhecidas como asserts. Deixe o mesmo arquivo com o seguinte conteúdo:

# encoding: utf-8

require "spec_helper"

describe "Cadastra SRs" do
  context "quando enviando dados válidos" do
    
    before do
      visit "/"
      click_link "Cadastrar SR"
      
      fill_in "Número da SR", :with => "SR12001234"
      select "DSF", :from => "Sistema"
      select "Robert Plant", :from => "Analista"
      fill_in "Projeto", :with => "23o dígito"
      fill_in "Conclusão da SR", :with => "Tudo certo!"
      click_button "Adicionar SR"
    end
    
    it "redireciona para página de cadastro de SR" do
      current_path.should be(sr_path)
    end
    it "mostra mensagem de sucesso" do
      page.should have_content("SR12001234 adicionada com sucesso!")
    end
    it "mostra a SR cadastrada" do
      current_path.should match(%r[/srs/\d+])
    end
  end
  
  context "quando enviando dados inválidos" do
    
    before do
      visit "/"
      click_link "Cadastrar SR"
    end

    it "renderiza a página de cadastro de SR"
    it "mostra mensagem de erro"
  end    
end

Execute o teste novamente e verifique a mensagem de erro:

$ rspec  spec/requests/cadastra_srs_spec.rb 
FFF**

Pending:
  Cadastra SRs quando enviando dados inválidos renderiza a página de cadastro de SR
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:38
  Cadastra SRs quando enviando dados inválidos mostra mensagem de erro
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:39

Failures:

  1) Cadastra SRs quando enviando dados válidos redireciona para página de cadastro de SR
     Failure/Error: Unable to find matching line from backtrace
     Mysql2::Error:
       Unknown database 'srmanager_test'
     # /Users/datherra/.rvm/gems/ruby-1.9.3-p194/gems/mysql2-0.3.11/lib/mysql2/client.rb:44:in `connect'

3 testes falharam. Como todos foram pelo mesmo motivo, removi algumas linhas do erro. A linha Mysql2::Error: Unknown database 'srmanager_test' é bem autoexplicativa e esperada, já que ainda não criamos a base de dados desta aplicação.

Vamos criá-la?

Criando a Base de Dados

Simples assim:

$ rake db:create

(aqui pode ser bom falar um pouco sobre o rake, pra que serve, etc)

Rode o teste novamente, desta vez, vamos rodar através do rake, que dá no mesmo:

$ rake spec:requests
/Users/datherra/Devel/Ruby/Rails/apps/srmanager/db/schema.rb doesn't exist yet. Run `rake db:migrate` to create it then try again. If you do not intend to use a database, you should instead alter /Users/datherra/Devel/Ruby/Rails/apps/srmanager/config/application.rb to limit the frameworks that will be loaded

Opa, novo erro =(
Mas a solução já vem indicada no próprio erro. (Conseguiu achar?)
Rode:

$ rake db:migrate

Rode o teste novamente:

$ rake spec:requests
/Users/datherra/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec ./spec/requests/cadastra_srs_spec.rb
FFF**

Pending:
  Cadastra SRs quando enviando dados inválidos renderiza a página de cadastro de SR
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:38
  Cadastra SRs quando enviando dados inválidos mostra mensagem de erro
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:39

Failures:

  1) Cadastra SRs quando enviando dados válidos redireciona para página de cadastro de SR
     Failure/Error: click_link "Cadastrar SR"
     Capybara::ElementNotFound:
       no link with title, id or text 'Cadastrar SR' found
     # (eval):2:in `click_link'
     # ./spec/requests/cadastra_srs_spec.rb:10:in `block (3 levels) in <top (required)>'

  2) Cadastra SRs quando enviando dados válidos mostra mensagem de sucesso
     Failure/Error: click_link "Cadastrar SR"
     Capybara::ElementNotFound:
       no link with title, id or text 'Cadastrar SR' found
     # (eval):2:in `click_link'
     # ./spec/requests/cadastra_srs_spec.rb:10:in `block (3 levels) in <top (required)>'

  3) Cadastra SRs quando enviando dados válidos mostra a SR cadastrada
     Failure/Error: click_link "Cadastrar SR"
     Capybara::ElementNotFound:
       no link with title, id or text 'Cadastrar SR' found
     # (eval):2:in `click_link'
     # ./spec/requests/cadastra_srs_spec.rb:10:in `block (3 levels) in <top (required)>'

Finished in 0.3224 seconds
5 examples, 3 failures, 2 pending

Failed examples:

rspec ./spec/requests/cadastra_srs_spec.rb:20 # Cadastra SRs quando enviando dados válidos redireciona para página de cadastro de SR
rspec ./spec/requests/cadastra_srs_spec.rb:23 # Cadastra SRs quando enviando dados válidos mostra mensagem de sucesso
rspec ./spec/requests/cadastra_srs_spec.rb:26 # Cadastra SRs quando enviando dados válidos mostra a SR cadastrada
rake aborted!
/Users/datherra/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec ./spec/requests/cadastra_srs_spec.rb failed

Tasks: TOP => spec:requests
(See full trace by running task with --trace)

Ok, agora o erro mudou. Temos uma exception lançada pelo Capybara, que é a lib usada pelo RSpec para simular a navegação do usuário em um site. A exception diz:

Failure/Error: click_link "Cadastrar SR"
Capybara::ElementNotFound: no link with title, id or text 'Cadastrar SR' found
# (eval):2:in `click_link'
# ./spec/requests/cadastra_srs_spec.rb:10:in `block (3 levels) in <top (required)>'

Faz sentido, já que pedimos para o teste clicar no link "Cadastrar SR", mas ele ainda não existe.

Qual o próximo passo?
Repare nestas linhas de nosso teste:

  visit "/"
  click_link "Cadastrar SR"

Quando o usuário visita a raiz da aplicação, o que é exibido no browser?
Vamos verificar?

Server

Para subir a aplicação, basta executar o comando abaixo na raiz do projeto, neste caso, na pasta srmanager:

$ rails server

Ou então, para economizar ponta de dedo:

$ rails s
=> Booting Thin
=> Rails 3.2.6 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
>> Thin web server (v1.3.1 codename Triple Espresso)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:3000, CTRL+C to stop

Detalhando o output do comando:

  • => Booting Thin
    Thin é o application server que estamos usando, graças esta linha em nosso Gemfile:
gem "thin"

O application server padrão do Rails é o WEBrick, mas bisbilhotando e conversando com alguns desenvolvedores Rails mais experientes, fiquei sabendo que o Thin é mais rápido. Se no seu ambiente você usar o WEBrick, não tem problema, pois nada muda no código da aplicação. As diferenças poderiam começar a aparecer apenas no caso de você precisar ajustar alguma configuração muito específica deste middleware.

  • => Rails 3.2.6 application starting in development on http://0.0.0.0:3000
    Versão do Rails e a URL onde ele está ouvindo. Copie esta URL e cole no seu navegador.

Esta página deverá aparecer:

Esta página é mostrada por padrão no Rails e ela fica em srmanager/public/index.html
Bem, ela não tem o link "Cadastrar SR" como esperado pelo nosso teste, não é mesmo? Já que não vamos utilizá-la, apague o arquivo:

$ rm public/index.html

Sem mesmo reiniciar o servidor Rails, recarregue a página no seu browser. Isto é o que temos agora:

Algumas dicas aparecem neste passo:

  1. Repare que na janela que você deixou rodando seu rails server apareceram algumas mensagens. Lá é exibido o output do servidor, que muitas vezes possui mensagens úteis para um troubleshooting. Vale dizer que estas mesmas mensagens podem ser encontradas no arquivo log/development.log

  2. O erro no browser acompanha uma sugestão: Try running rake routes for more information on available routes.

Ok então. Execute:

$ rake routes

Nada!?

Pois é, ainda não definimos nenhuma rota em nossa aplicação. Mas...
...que diabos são ROTAS!?

Routes

Citando o Rails Routing from the Outside In :

The Rails router recognizes URLs and dispatches them to a controller’s action. It can also generate paths and URLs, avoiding the need to hardcode strings in your views.

Pois bem, se é ele quem decide para onde mandar o usuário quando os requests do browser chegam ao Rails, precisamos configurar o que fazer quando o usuário solicitar o "/" (barra).

O arquivo config/routes.rb é criado junto com aplicação e possui vários exemplos comentados. Para simplificar, irei apagar todas as linhas comentadas e ir adicionando somente as úteis para a aplicação.

Edite o arquivo config/routes.rb e deixe-o assim:

Srmanager::Application.routes.draw do
  root :to => "pages#index"
end

Em aplicações web é comum possuirmos as páginas que lidam com conteúdo dinâmico, que são aquelas por onde se manipula informações que são extraídas e salvas de algum repositório de dados, normalmente um banco de dados.

Mas há também as páginas estáticas, como uma home page, página institucional com informações da empresa, about page com informações básicas da própria aplicação, etc. Para estes casos, é muito comum na comunidade Rails o uso de um controller chamado pages ou site que servirá apenas para servir suas páginas estáticas.

No trecho de código que colocamos no routes.rb estamos indicando que, toda vez que alguém visitar a raiz da aplicação, ela deverá ser redirecionada a ACTION index do CONTROLLER pages.

Salve o routes.rb e verifique suas rotas novamente:

$ rake routes
root  / pages#index

Vimos que a rota relacionada ao "/" foi adicionada.

Agora, rode o teste:

$ rspec spec/requests/cadastra_srs_spec.rb 
FFF**

Pending:
  Cadastra SRs quando enviando dados inválidos renderiza a página de cadastro de SR
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:38
  Cadastra SRs quando enviando dados inválidos mostra mensagem de erro
    # Not yet implemented
    # ./spec/requests/cadastra_srs_spec.rb:39

Failures:

  1) Cadastra SRs quando enviando dados válidos redireciona para página de cadastro de SR
     Failure/Error: visit "/"
     ActionController::RoutingError:
       uninitialized constant PagesController
     # ./spec/requests/cadastra_srs_spec.rb:9:in `block (3 levels) in <top (required)>'

Antes de analizarmos o erro, vamos mudar uma configuração do RSpec para que o output dele fique mais bacana. Edite o arquivo .rspec na raiz do projeto e atualize-o para este conteúdo:

--colour --format documentation

Rode o teste novamente e repare na diferença do output:

Cadastra SRs
  quando enviando dados válidos
    redireciona para página de cadastro de SR (FAILED - 1)
    mostra mensagem de sucesso (FAILED - 2)
    mostra a SR cadastrada (FAILED - 3)
  quando enviando dados inválidos
    renderiza a página de cadastro de SR (PENDING: Not yet implemented)
    mostra mensagem de erro (PENDING: Not yet implemented)

Este sumário do resultado dos testes é muito útil por já deixar claro quais funcionalidades falharam e também nos ajuda a construir descritivos coerentes para os testes, já que eles ao serem lidos desta forma, lhe direcionarão mais rapidamente ao problema (quando houver um).

Voltando ao erro:

Failure/Error: visit "/"
ActionController::RoutingError:
  uninitialized constant PagesController
# ./spec/requests/cadastra_srs_spec.rb:9:in `block (3 levels) in <top (required)>'

Bem, como instruímos o Rails a redirecionar os usuários que procuram a raiz da aplicação para "pages#index", vamos ter que criar este controller citado no erro, o PagesController.

Criando entidades da aplicação

Lembra deste desenho?

mvc

A grande maioria das entidades que usarmos nas aplicações, passarão por estes 3 componentes: o Controller, o Model e a View.

Vamos começar criando o Controller que nosso teste está pedindo.
Conforme a imagem a abaixo, crie o arquivo pages_controller.rb na pasta app/controllers/ :

Para criarmos um controller no Rails, apenas extenda a classe ApplicationController:

class PagesController < ApplicationController
  
end

Salve o arquivo e, adivinhe, rode o teste again!

$ rake spec:requests

Para economizar espaço, irei colar aqui apenas o trecho relevante do output do RSpec:

Failure/Error: visit "/"
AbstractController::ActionNotFound:
  The action 'index' could not be found for PagesController

Bom, resolvemos o erro que reclamava do PagesController.
Agora precisamos resolver o próximo erro, o da action index.

Em Rails, actions são simples métodos dos controllers, portanto, no arquivo pages_controller.rb, dentro da classe PagesController defina o método index:

class PagesController < ApplicationController
  def index
    
  end
end

Teste.

$ rake spec:requests

Novo erro:

Failure/Error: visit "/"
 ActionView::MissingTemplate:
   Missing template pages/index, application/index with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder]}. Searched in:
     * "/Users/datherra/Devel/Ruby/Rails/apps/srmanager/app/views"
 # ./spec/requests/cadastra_srs_spec.rb:9:in `block (3 levels) in <top (required)>'

ActionView::MissingTemplate!
Pois bem, já passamos do controller e chegamos agora no outro componente, a view.

Vamos criá-la?

View

A view é o que utilizamos para solicitar e mostrar dados aos usuários. E ela que traz ou leva as informações devidamente tratadas em nossos controladores.
Até aqui ainda não colocamos nenhuma inteligência no controller, mas isso irá mudar mais adiante.

Vamos detalhar esta linha do erro:

Missing template pages/index, application/index with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder]}
  • Missing template pages/index
    indica qual a página que está faltando, neste caso, http://servidor/pages/index
  • :locale=>[:en]
    ajuda a definir qual template renderizar baseado no idioma detectado no navegador (fora do escopo deste tutorial)
  • :formats=>[:html]
    qual formato está sendo solicitado e portanto o que deverá ser comtemplado pelo template. Neste caso mostra ":html" porque utilizamos o browser (o RSpec simula o request de um browser). Poderia ser XML ou JSON, por exemplo.
  • :handlers=>[:erb, :builder]
    qual parser/handler/template engine (chame como preferir) que será usado para renderizar este template

Sendo mais prático, vamos corrigir o erro.
Na pasta app/view crie a subpasta pages (repare no plural), e dentro dela o arquivo index.html.erb:

Deixe o arquivo index.html vazio mesmo.

Teste (preciso colocar o comando aqui? Acho que não mais né?)

Novo erro:

Failure/Error: click_link "Cadastrar SR"
Capybara::ElementNotFound:
  no link with title, id or text 'Cadastrar SR' found
# (eval):2:in `click_link'
# ./spec/requests/cadastra_srs_spec.rb:10:in `block (3 levels) in <top (required)>'

Faz sentido não é?
Recarregue a página no browser, e você verá apenas uma página vazia, sem erros, o que já é bom sinal.

O teste está reclamando que não encontra o link "Cadastrar SR".

Vamos criá-lo?

ERB e mais Routes

Citando a apostila gratuíta do curso de Rails da Caelum:

ERb
ERb é uma implementação de eRuby que já acompanha a linguagem Ruby. Seu funcionamento é similar ao dos arquivos JSP/ASP: arquivos html com injeções de código. A idéia é que o HTML serve como um template, e outros elementos são dinâmicamente inseridos em tempo de renderização. Para uma página aceitar código Ruby, ela deve estar entre "<%" e "%>". Há uma variação deste operador, o "<%=", que não só executa códigos Ruby, mas também imprime o resultado na página HTML. É importante notar que todos os atributos de instância (@variavel) de um controlador estão disponíveis em sua view.

De volta ao arquivo app/views/pages/index.html, aplique este conteúdo para criarmos o link "Cadastrar SR":

<%= link_to "Cadastrar SR", new_sr_path %>

Rode o teste.
Erro:

Failure/Error: visit "/"
ActionView::Template::Error:
  undefined local variable or method `new_sr_path' for #<#<Class:0x00000128de3388>:0x000001012b41a0>

Que diabos é este new_sr_path afinal? E o link_to?

O link_to é o que no Rails são chamados de helpers methods. Este em específico tem a óbvia função de transformar os argumentos que passaremos à ele em uma tag html como:

<a href="sr/new">Cadastrar SR</a>

Ok, a parte do "Cadastrar SR" ficou clara, mas e o outro argumento? Como transformar new_sr_path em sr/new?

Conseguimos fazer isso declarando resources no arquivo config/routes.rb. Deixe o seu arquivo com este conteúdo:

Srmanager::Application.routes.draw do
  root :to => "pages#index"
  resources :srs
end

Esta simples linha

resources :srs

produz um série de rotas e helpers. Após salvar o arquivo routes.rb, rode:

$ rake routes
   root        /                       pages#index
    srs GET    /srs(.:format)          srs#index
        POST   /srs(.:format)          srs#create
 new_sr GET    /srs/new(.:format)      srs#new
edit_sr GET    /srs/:id/edit(.:format) srs#edit
     sr GET    /srs/:id(.:format)      srs#show
        PUT    /srs/:id(.:format)      srs#update
        DELETE /srs/:id(.:format)      srs#destroy

Repare na linha:

 new_sr GET    /srs/new(.:format)  srs#new
  • new_sr - nome do helper criado pelo rails para se referir a esta rota via código (justamente a que usamos no arquivo index.html após o link_to)
  • GET - método HTTP usado
  • /srs/new(.:format) - é a URL para a qual o helper indicado na primeira coluna é "traduzido" (e também é justamente o link que queríamos)
  • srs#new - significa que esta rota tem como destino o controller srs com a action new. Vamos precisar disso já já.

Para compreender melhor esta parte, vale usar esta tabela como referência. Ela seria o resultado de resources :users:


fonte: Rails Tutorial

Outra excelente fonte de informação sobre este tópico está nos guias do próprio Rails:
http://guides.rubyonrails.org/routing.html

Rode o teste.

Failure/Error: click_link "Cadastrar SR"
 ActionController::RoutingError:
   uninitialized constant SrsController

Reclamou do SrsController, mas não reclamou mais da falta do link "Cadastar SR". Abra http://localhost:3000 no seu navegador e verifique que o link está lá:

Vamos criar o novo controller?

Um novo Controller

Crie o arquivo srs_controller.rb no diretório indicado abaixo:

O arquivo deve ficar com este conteúdo:

class SrsController < ApplicationController
  
end

Rode o teste.

Failure/Error: click_link "Cadastrar SR"
AbstractController::ActionNotFound:
  The action 'new' could not be found for SrsController

Agora ele diz não encontrar a action new. Crie:

class SrsController < ApplicationController
  def new
    
  end
end

Rode o teste. Erro:

Failure/Error: click_link "Cadastrar SR"
ActionView::MissingTemplate:
  Missing template srs/new, application/new with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder]}. Searched in:
    * "/Users/datherra/Devel/Ruby/Rails/apps/srmanager/app/views"

Missing Template? Já passamos por isso, lembra?
Vamos criar o template da action new que ele está esperando criando a pasta e o arquivo demonstrado abaixo:

Salve o arquivo em branco mesmo e...

Rode o teste. Novo erro:

Failure/Error: fill_in "Número da SR", :with => "SR12001234"
 Capybara::ElementNotFound:
   cannot fill in, no text field, text area or password field with id, name, or label 'Número da SR' found

Não achou o campo "Número da SR", que de acordo com nosso teste, seria o primeiro campo do formuládio de cadastro da SR a ser preenchido. Lembra do bloco before do nosso teste?

before do
  visit "/"
  click_link "Cadastrar SR"
  
  fill_in "Número da SR", :with => "SR12001234"
  select "DSF", :from => "Sistema"
  select "Robert Plant", :from => "Analista"
  fill_in "Projeto", :with => "23o dígito"
  fill_in "Conclusão da SR", :with => "Tudo certo!"
  click_button "Adicionar SR"
end

Vamos agora conhecer as facilidades, ou na linguagem Rails, os helpers para criação de formulários Web. Preencha o arquivo app/views/srs/new.html.erb com o seguinte conteúdo:

<%= form_for @sr do |f| %>
  <p>
    <%= f.label :numero_sr, "Número da SR" %>
    <%= f.text_field :numero_sr %>
  </p>
  
  <p>
    <%= f.label :sistema_id, "Sistema" %>
    <%= f.collection_select :sistema_id,
          Sistema.all,
          :id,
          :nome,
          :include_blank => true
    %>
  </p>

  <p>
    <%= f.label :analista_id, "Analista" %>
    <%= f.collection_select :analista_id,
          Analista.all,
          :id,
          :nome,
          :include_blank => true
    %>
  </p>
  
  <p>
    <%= f.label :projeto, "Projeto" %>
    <%= f.text_field :projeto %>
  </p>
  
  <p>
    <%= f.label :conclusao, "Conclusão da SR" %>
    <%= f.text_area :conclusao, :rows => 3 %>
  </p>
  
  <p>
    <%= f.submit "Envia SR", :class => "btn btn-primary" %>
  </p>
  
<% end %>

Rode o teste. Erro:

Failure/Error: click_link "Cadastrar SR"
ActionView::Template::Error:
  undefined method `model_name' for NilClass:Class

###Aqui vou descrever um trobleshooting e porque ele é necessário.

O Rails é um framework que adere ao Convention over Configuration, que resumidamente significa que ele espera que você cumpra (e decore) algumas convenções para não passar horas ajustando e debugando XMLs de configuração, algo muito comum em ambiente JavaEE.

Uma destas convenções tem relação com o plural ou singular para nomear classes, variáveis e arquivos encontrados nas views, models e controllers.

Comecemos por esta lista:

  • Database table names: plural

Database table names are expected to be pluralized. For example, a table containing employee records should be named Employees.

  • Model class names: singular

Model class names are the singular form of the database table that they are modeling. For example, an Employee model is created based on a table named employees.

  • Controller class names: plural

Controller class names are pluralized, such as EmployeesController or AccountsController.

Uma maneira de percebermos como funciona a pluralização nos nomes das pastas e arquivos do Rails, é solicitando o scaffolding de um objeto. Execute o comando abaixo, que por causa do parâmetro -p irá somente fingir (pretend) que executa o comando:

$ rails generate scaffold User -p
      invoke  active_record
      create    db/migrate/20120808212428_create_users.rb
      create    app/models/user.rb
      invoke    rspec
      create      spec/models/user_spec.rb
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    rspec
      create      spec/controllers/users_controller_spec.rb
      create      spec/views/users/edit.html.erb_spec.rb
      create      spec/views/users/index.html.erb_spec.rb
      create      spec/views/users/new.html.erb_spec.rb
      create      spec/views/users/show.html.erb_spec.rb
      invoke      helper
      create        spec/helpers/users_helper_spec.rb
      create      spec/routing/users_routing_spec.rb
      invoke      rspec
      create        spec/requests/users_spec.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      rspec
      invoke  assets
      invoke    js
      create      app/assets/javascripts/users.js
      invoke    scss
      create      app/assets/stylesheets/users.css.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.css.scss
Model Route Controller View
app/models/user.rb resources :users app/controllers/users_controller.rb app/views/users/*.html.erb

Lembre-se, como usou o -p nada foi criado na sua app.

Existe uma classe no Rails (ActiveSupport::Inflector::Inflections) responsável por dar o correto plural a cada palavra. Mas ele trata de fazer isto com o inglês, por isso é sempre mais fácil nomear tudo em sua app em inglês.

Mas se não for possível, não tem problema, existem alguns meios de lidar com isso.

Por exemplo nesta app não tivemos problema até agora com o objeto Sr, mas o erro que apareceu por último aconteceu após termos adicionado o nome de duas novas Classes (que ainda serão criadas): Sistema e Analista.

Existe uma ferramenta MUITO útil para o desenvolvedor Rails que é o Console. Execute:

$ rails console
Loading development environment (Rails 3.2.6)
1.9.3p194 :001 > 

Você terá carregado um prompt interativo de Ruby, o famoso irb, mas no contexto de sua aplicação. Teste:

 > "Sr".pluralize
 => "Srs" 
 > "Sistema".pluralize
 => "Sistemas" 
 > "Analista".pluralize
 => "Analista" 

Veja como a palavra Analista não teve seu plural aplicado corretamente. Lembrando que as outras duas terem dado certo foi sorte, já que nem palavras do inglês elas são.

Este foi o motivo do último erro que encontramos.

Vamos então "ensinar" para o Rails o plural de Analista.
Edite o arquivo config/initializers/inflections.rb e adicione o conteúdo abaixo:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.plural /^(analista)$/i, '\1s'
  inflect.singular /^(analista)s$/i, '\1'
end

Abra um rails console e faça os testes de pluralização novamente. Se sua sessão de console já estava aberta, tente atualizar as configurações com o comando abaixo, se não funcionar, reinicie o console:

001 > reload!
Reloading...
=> true 
 
002 > "Sr".pluralize
=> "Srs" 
003 > "Sistema".pluralize
=> "Sistemas" 
004 > "Analista".pluralize
=> "Analistas" 

Agora a palavra "Analista" teve seu plural aplicado corretamente.
Além de ajustar estas correções através de Inflections, também existe um projeto chamado brazilian-rails que pode ser útil.

Voltando ao projeto, rode os testes novamente. Novo erro:

Failure/Error: click_link "Cadastrar SR"
ActionView::Template::Error:
  undefined method `model_name' for NilClass:Class

Agora o teste reclama da falta do MODEL (model_name). É justo, já que estamos tentando salvar um Sr mas ainda não definimos como salvá-la no banco de dados.

Como o Rails foi criado pensando na forma ÁGIL de se desenvolver software web, as modificações no banco de dados são feitas aos poucos, conforme a necessidade aparece, assim como todo o resto que tratamos até aqui.

Estas alterações são aplicadas através de migrations, que são pequenas classes Ruby, filhas da classe Rails ActiveRecord::Migration, que possuem instruções de como FAZER e como DESFAZER as modificações que você precisa.

Ensinar a desfazer ajuda no rollback de alterações na base.

O Rails aplica estas migrações na sequência em que elas foram criadas, tomando como referência o timestamp existente no nome dos arquivos de migrações.

Isso significa duas coisas:

  1. usaremos um script do Rails para facilitar a criação do arquivo de migrações já com o timestamp definido

  2. se precisar fazer ajustes a uma migration já criada, não altere o arquivo existente, crie uma outra migration somente com os ajustes que precisa, fazendo com que suas alterações sejam sempre incrementais e sequenciais.

Vamos criar o primeiro MODEL?

Model

Use o comando abaixo para criar o esqueleto da sua migration:

$ rails generate migration cria_sr_model
      invoke  active_record
      create    db/migrate/20120810170240_cria_sr_model.rb

Edite o arquivo criado e deixe-o com o seguinte conteúdo:

class CriaSrModel < ActiveRecord::Migration
  def up
    create_table :srs do |t|
      t.string :numero_sr, :null => false
      t.string :projeto
      t.string :conclusao
      
      t.references :analista, :sistema, :null => false
      
      t.timestamps
    end
  
    add_index :srs, :analista_id
    add_index :srs, :sistema_id
    add_index :srs, [:analista_id, :sistema_id], :name => :analista_sistema_index
  end

  def down
    drop_table :srs
  end
end

Aqui chamo atenção especial para dois pontos:

  1. nomes de tabelas são no plural => srs
  2. ao invés de criarmos as relações entre SRs, Sistemas e Analistas "na mão", criando os campos de suas respectivas PKs como FKs em SR, podemos apenas usar o método references como no exemplo acima. Ele corresponde a estes comandos:
 t.integer :analista_id, :null => false
 t.integer :sistema_id, :null => false

Para aplicar a migração, use:

$ rake db:migrate
==  CriaSrModel: migrating ====================================================
-- create_table(:srs)
   -> 0.1661s
-- add_index(:srs, :analista_id)
   -> 0.2253s
-- add_index(:srs, :sistema_id)
   -> 0.1439s
-- add_index(:srs, [:analista_id, :sistema_id], {:name=>:analista_sistema_index})
   -> 0.1853s
==  CriaSrModel: migrated (0.7210s) ===========================================

Conecte ao banco de dados para verificar o aconteceu lá. Uma das formas de se fazer isto é usando o rails dbconsole:

$ rails dbconsole
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 285
Server version: 5.5.16 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
+---------------------------------+
| Tables_in_srmanager_development |
+---------------------------------+
| schema_migrations               |
| srs                             |
+---------------------------------+
2 rows in set (0.00 sec)

mysql> desc srs;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| numero_sr   | varchar(255) | NO   |     | NULL    |                |
| projeto     | varchar(255) | YES  |     | NULL    |                |
| conclusao   | varchar(255) | YES  |     | NULL    |                |
| analista_id | int(11)      | NO   | MUL | NULL    |                |
| sistema_id  | int(11)      | NO   | MUL | NULL    |                |
| created_at  | datetime     | NO   |     | NULL    |                |
| updated_at  | datetime     | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
8 rows in set (0.01 sec)

mysql> 

Saia do shell do MySQL e rode o teste.

Failure/Error: click_link "Cadastrar SR"
ActionView::Template::Error:
  undefined method `model_name' for NilClass:Class

Continuamos com o mesmo erro.
Isso acontece porque o Rails detecta, através da variável de ambiente RAILS_ENV, em qual ambiente estamos trabalhando. Na saída do comando acima, verifique que o título da coluna mostrada pelo comando show tables é Tables_in_srmanager_development:

  • test
  • development
  • production

O ambiente padrão é o development, mas os testes, claro, rodam no ambiente test.

Podemos verificar como está a base de teste da seguinte maneira:

$ export RAILS_ENV=test;rails dbconsole
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 287
Server version: 5.5.16 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
Empty set (0.00 sec)

Não esquece de voltar ao ambiente development. Uma maneira de fazer isso pode ser:

$ unset RAILS_ENV

Para preparar a base de testes com suas migrações, execute:

$ rake db:test:prepare

Rode o teste novamente.
Ainda o mesmo erro:

Failure/Error: click_link "Cadastrar SR"
ActionView::Template::Error:
  undefined method `model_name' for NilClass:Class

Quando abrimos a app no browser e clicamos no link "Cadastrar SR", o seguinte erro é mostrado:

Toda esta explicação sobre as migrations e os diferentes ambientes do Rails fazem sentido aqui. Porém, temos que voltar ao assunto MVC para entender como esta variável @sr flui pela aplicação.

O desenho acima é do Rails Tutorial e usa como exemplo um objeto User. Estes são os passos:

  1. O browser manda um request para /users
  2. Rails roteia /users para a action index no User controller
  3. A action index pede ao User model para trazer todos o usuários (User.all)
  4. O User model busca todos os usuários na base
  5. O User model retorna a lista de todos os usuários ao controller
  6. O controller coloca os usuários na varíavel @users, que é passada para a index view
  7. A view usa ERB para renderizar a página HTML
  8. O controller passa o HTML de volta ao browser.

Vamos então começar a colocar código dentro do nosso arquivo app/controller/srs_controller:

class SrsController < ApplicationController
  def new
   @sr = Sr.new 
  end
end

Antes mesmo de rodar o teste já é possível prever que teremos problemas com esta nova linha:

@sr = Sr.new

Não criamos o objeto Sr ainda. Até aqui criamos apenas a estrutura dele na base de dados através das migrações. Vamos então criar o model Sr. Crie o arquivo abaixo:

class Sr < ActiveRecord::Base
  attr_accessible :numero_sr, :sistema_id, :analista_id, 
                  :projeto, :conclusao
                  
  belongs_to :sistema
  belongs_to :analista
end

Para maiores detalhes consulte a seção Models dos Rails Guides

Salve o arquivo e rodo o teste:

Failure/Error: click_link "Cadastrar SR"
ActionView::Template::Error:
 uninitialized constant ActionView::CompiledTemplates::Sistema

Reclamou da "constante" Sistema que usamos. Faz sentido, pois tanto Sistema quanto Analista são models de nossa app que ainda não foram criadas.

Vamos criar a migration para SISTEMA:

$ rails generate migration cria_sistema_model
      invoke  active_record
      create    db/migrate/20120810182318_cria_sistema_model.rb

Preencha o arquivo criado com este conteúdo:

class CriaSistemaModel < ActiveRecord::Migration
  def up
    create_table :sistemas do |t|
      t.string :nome, :null => false
      
      t.timestamps
    end
  end

  def down
    drop_table :sistemas
  end
end

Crie o model app/models/sistema.rb

class Sistema < ActiveRecord::Base
  attr_accessible :nome
  
  has_many :sr
end

Crie a migration para ANALISTA:

$ rails generate migration cria_analista_model
      invoke  active_record
      create    db/migrate/20120810182758_cria_analista_model.rb

Preencha o arquivo criado com este conteúdo:

class CriaAnalistaModel < ActiveRecord::Migration
  def up
    create_table :analistas do |t|
      t.string :nome, :null => false
      
      t.timestamps
    end
  end

  def down
    drop_table :analistas
  end
end

Crie o model app/models/analista.rb

class Analista < ActiveRecord::Base
  attr_accessible :nome
  
  has_many :sr
end

Rode as migrations:

$ rake db:migrate
==  CriaSistemaModel: migrating ===============================================
-- create_table(:sistemas)
   -> 0.1412s
==  CriaSistemaModel: migrated (0.1413s) ======================================

==  CriaAnalistaModel: migrating ==============================================
-- create_table(:analistas)
   -> 0.2123s
==  CriaAnalistaModel: migrated (0.2124s) =====================================

Rode o teste com este comando em específico, ou seja, a partir do rake, pois esta é outra forma de conseguirmos aplicar as migrações pendentes à base de testes:

$ rake spec:requests

Novo erro:

Failure/Error: select "DSF", :from => "Sistema"
Capybara::ElementNotFound:
 cannot select option, no option with text 'DSF' in select box 'Sistema'

De acordo com nosso teste, deveria haver uma opçõa DSF no combo box Sistema. Abra a página no browser e verifique o temos até aqui:

Os combo boxes Sistema e Analisa estão vazios.

Para ajudar nossos testes, vamos alimentar a base de dados com alguns dados sobre sistema e analistas usando o mecanismo de seed do Rails.
Abra o arquivo db/seeds.rb e preencha com o conteúdo abaixo:

%w( DSF OPSC1 Portal\ Web BSCS ).each do |sistema|
  Sistema.create :nome => sistema
end

%w( Jimmy\ Page John\ Paul\ Jones 
    Robert\ Plant John\ Bonham ).each do |analista|
      Analista.create :nome => analista
end

Esta é uma das formas de gerar dados para a base de dados, com simples código Ruby. Porém, conforme suas necessidades vão aumentando e seus testes ficam mais sofisticados, alguns frameworks ajudam neste trabalho. Um muito usado pela comunidade é o projeto Factory Gilr. Para usar com Rails, utilize a gem Factory Girl Rails.

Para fazer o Rails carregar a base com este dados, rode:

$ rake db:seed
$ export RAILS_ENV=test;rake db:seed
$ unset RAILS_ENV

Novo erro:

Failure/Error: click_button "Adicionar SR"
Capybara::ElementNotFound:
 no button with value or id or text 'Adicionar SR' found

Aqui é só diferença na string. Em nosso arquivo de teste escrevemos:

  click_button "Adicionar SR"

E na view new.html.erb escrevemos:

<p>
  <%= f.submit "Envia SR" %>
</p>

Escolha qual dos dois alterar, o importante é que estejam identicas as strings entre o teste e a realidade. Eu irei alterar o teste para:

  click_button "Envia SR"

Salve. Rode o teste. Novo erro:

Failure/Error: click_button "Envia SR"
AbstractController::ActionNotFound:
  The action 'create' could not be found for SrsController

Ao clicar no botão do nosso formulário, o browser dispara uma requisição POST HTTP na URL de nosso resource. Traduzindo:
Lembra da nossa tabela de rotas padrão do Rails:


fonte: Rails Tutorial

Veja como a action que será chamada no controller é a action create. Vamos ao nosso SrsController e adicione o método (action) abaixo:

def create
  @sr = Sr.new(params[:sr])
  
  if @sr.save
    redirect_to sr_path(@sr)
  else
    render :new
  end
end

O Rails "empacota" os dados que irá mandar do browser para a app em um hash chamado params. Neste caso estamos colhendo o objeto :sr de dentro dele e jogando dentro da variável global @sr. Lembrando que as variáveis globais declaradas nos controllers ficam disponívels para uso nas views.

Em seguida tentamos salvar os dados recebidos e, em caso positivo, redirecionamos o usuário para a página que mostra a SRs cadastrada. Em caso de erro renderizamos o formulário novamente.

Rode o teste. Novo erro:

Failure/Error: click_button "Envia SR"
AbstractController::ActionNotFound:
  The action 'show' could not be found for SrsController

A action show é a action que lista um objeto em específico. Vamos adicioná-la ao SrsController:

def show
  @sr = Sr.find(params[:id])
end

Teste:

Failure/Error: click_button "Envia SR"
ActionView::MissingTemplate:
  Missing template srs/show, application/show with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder]}. Searched in:
    * "/Users/datherra/Devel/Ruby/Rails/apps/srmanager/app/views"

Falta a view show. Vamos criá-la:

<h1>SR<%= @sr.numero_sr %></h1>

Projeto:
<%= @sr.projeto %>

Analista:
<%= @sr.analista.nome %>

Sistema:
<%= @sr.sistema.nome %>

Conclusão:
<%= @sr.conclusao %>

<%= link_to "Editar", edit_sr_path(@sr) %>
<%= link_to "Voltar", srs_path %>

Teste. Novo erro:

Failure/Error: current_path.should be(new_sr_path)
   
   expected #<String:2177851000> => "/srs/new"
        got #<String:2177854840> => "/srs/20"

Este não é um erro na aplicação, mas sim um erro meu quando criei o teste.
O teste diz:

describe "Cadastra SRs" do
  context "quando enviando dados válidos" do
    
    before do
      visit "/"
      click_link "Cadastrar SR"
      
      fill_in "Número da SR", :with => "SR12001234"
      select "DSF", :from => "Sistema"
      select "Robert Plant", :from => "Analista"
      fill_in "Projeto", :with => "23o dígito"
      fill_in "Conclusão da SR", :with => "Tudo certo!"
      click_button "Envia SR"
    end
    
    it "redireciona para página de cadastro de SR" do
      current_path.should be(new_sr_path)
    end
    it "mostra mensagem de sucesso" do
      page.should have_content("SR12001234 adicionada com sucesso!")
    end
    it "mostra a SR cadastrada" do
      current_path.should match(%r[/srs/\d+])
    end
  end

Ou seja, antes de executar os 3 asserts (it "alguma coisa") ele executa o que está no bloco before. Mas o nosso bloco before clica no link, já preenche o formulário e em seguida clica no link/botão "Envia SR". Como resultado, a primeira asserção nunca será verdadeira, já que o "redireciona para página de cadastro de SR" acontece logo após o click_link "Cadastrar SR", ou seja, no meio do bloco before e não ao fim dele.
Deste modo, irei apenas apagar este teste.

Devido ao escopo reduzido deste manual, irei remover também os testes que testam o envio de dados inválidos, que são muito úteis para testar validações nos modelos (como por exemplo se aceitará o campo de conclusão vazio ou não) dentre outras coisas.

Deixe o arquivo de teste spec/requests/cadastra_srs_spec.rb com o conteúdo abaixo:

# encoding: utf-8

require "spec_helper"

describe "Cadastra SRs" do
  context "quando enviando dados válidos" do
    
    before do
      visit "/"
      click_link "Cadastrar SR"
      
      fill_in "Número da SR", :with => "SR12001234"
      select "DSF", :from => "Sistema"
      select "Robert Plant", :from => "Analista"
      fill_in "Projeto", :with => "23o dígito"
      fill_in "Conclusão da SR", :with => "Tudo certo!"
      click_button "Envia SR"
    end
    
    it "mostra a SR cadastrada" do
      current_path.should match(%r[/srs/\d+])
    end
  end
end

Rode o teste.

$ rspec spec/requests/cadastra_srs_spec.rb

$ rspec spec/requests/cadastra_srs_spec.rb 

Cadastra SRs
  quando enviando dados válidos
    mostra a SR cadastrada

Finished in 0.45126 seconds
1 example, 0 failures

Nenhum erro.
Claro, os que estavam dando erro eu removi, mas que fique claro que apenas esta asserção já é o suficiente para passarmos por todos os passos que demos até este ponto. Até mesmo porque até aqui os erros nas 3 asserções eram os mesmos.

Ciclo

O importante aqui é verificar como o ciclo se repete:

  1. Crie um teste de integração que falhe
  2. Crie o que os erros do teste (ou browser) indica e execute o teste novamente. Aqui, um outro ciclo costuma ser o padrão:
  • crie o controller
  • crie a action
  • crie a view
  • crie as migrations e os models
  • repita o loop executando criando os itens que faltam de acordo como os erros dos testes
  1. Faça o teste passar
  2. Crie uma nova funcionalidade começando por um novo teste falho.

Desta maneira sua aplicação estará sempre coberta por testes de integração pelo menos, já que aqui não tratamos do teste mais detalhado, conhecido como teste unitário.

Embelezando

Se neste ponto navegar na aplicação, verá que já possível cadastrar uma SR e vê-la ao final.
Porém a página está muito simples.
Além disso, faltam as funcionalidades de visualização de todas as SRs cadastradas, edição e delete, para as quais eu irei colocar aqui o código que completa estas funcionalidades.

Primeiro, vou falar um pouco do Twitter Bootstrap.

O Twitter Bootstrap é basicamente um monte de estilo e snippets de javascript, criados pelo time do Twitter, que ao serem instalados na sua aplicação ajudam a ter uma página mais bacana, precisando apenas da adição das classes CSS previstas na documentação dele.

Para não precisar executar a instalação do Twitter Bootstrap na mão, existem algumas gems que ajudam com isso. Vou indicar a twitter-bootstrap-rails.

Maiores detalhes sobre o Twitter Bootstrap e esta gem podem ser encontradas nestes videos:

RailsCasts Twitter Bootstrap Basics - free

More on Twitter Bootstrap - requer assinatura (recomendo)

Edite seu Gemfile* e deixe o group assets assim:

group :assets do
  gem "sass-rails",   "~> 3.2.3"
  gem "therubyracer", :platforms => :ruby
  gem "uglifier", ">= 1.0.3"
  gem 'coffee-rails', '~> 3.2.1'
  gem "twitter-bootstrap-rails"
end

Execute:

$ bundle install

Após a instalação da gem, execute:

$ rails generate bootstrap:install
      insert  app/assets/javascripts/application.js
      create  app/assets/javascripts/bootstrap.js.coffee
      create  app/assets/stylesheets/bootstrap_and_overrides.css.less
        gsub  app/assets/stylesheets/application.css
        gsub  app/assets/stylesheets/application.css

Em seguida, instale um tema:

$ rails g bootstrap:layout application fluid
    conflict  app/views/layouts/application.html.erb
Overwrite /Users/datherra/Devel/Ruby/Rails/apps/srmanager/app/views/layouts/application.html.erb? (enter "h" for help) [Ynaqdh] y
       force  app/views/layouts/application.html.erb

Responda "Y" (yes) para a pergunta sobre conflito. Irá sobrescrever um arquivo que não modificamos.

Abra o browser e recarregue a página. Veja que o bootstrap já implementa o conceito de responsive design. Se sua janela estiver estreita, irá ver a aplição assim:

Se estiver mais larga, ficará assim:

Redimensione sua janela e veja a diferença.

Agora irei passar o resto do código necessário e em quais arquivos eles vão para deixar a aplicação com as outras funcionalidades que citei acima e também com as classes do Twitter Bootstrap já na view:

Deixe o conteúdo do app/controllers/srs_controller.rb assim:

class SrsController < ApplicationController
  def new
    @sr = Sr.new
  end
  
  def create
    @sr = Sr.new(params[:sr])
    
    if @sr.save
      redirect_to sr_path(@sr)
    else
      render :new
    end
  end
  
  def show
    @sr = Sr.find(params[:id])
  end
  
  def edit
    @sr = Sr.find(params[:id])
  end
  
  def update
    @sr = Sr.find(params[:id])
    
    if @sr.update_attributes(params[:sr])
      redirect_to srs_path
    else
      render :edit
    end
  end
  
  def index
    @srs = Sr.all
  end
  
  def destroy
    @sr = Sr.find(params[:id])
    @sr.destroy
    redirect_to srs_path
  end
end

Crie os arquivos:

De baixo para cima, começe substituindo o conteúdo do arquivo app/views/srs/show.html.erb:

<div class="page-header">
  <h1>SR<%= @sr.numero_sr %></h1>
</div>

<dl class="dl-horizontal">
    <dt><strong>Projeto:</strong></dt>
    <dd><%= @sr.projeto %></dd>

    <dt><strong>Analista:</strong></dt>
    <dd><%= @sr.analista.nome %></dd>

    <dt><strong>Sistema:</strong></dt>
    <dd><%= @sr.sistema.nome %></dd>

    <dt><strong>Conclusão:</strong></dt>
    <dd><%= @sr.conclusao %></dd>
</dl>

<div class="form-actions">
  <%= link_to "Editar", edit_sr_path(@sr), :class => "btn" %>
  <%= link_to "Voltar", srs_path, :class => "btn btn-primary" %>
</div>

Como o formulário da view new e edit são idênticos, coloquei o código em uma partial (_form.html.erb) e a chamei nas respectivas views:

Substitua o conteúdo do arquivo app/views/srs/new.html.erb:

<%= render "form" %>

Substitua o conteúdo do arquivo app/views/srs/edit.html.erb:

<%= render "form" %>

Substitua o conteúdo do arquivo app/views/srs/_form.html.erb:

<%= form_for @sr do |f| %>
  <p>
    <%= f.label :numero_sr, "Número da SR" %>
    <%= f.text_field :numero_sr %>
  </p>
  
  <p>
    <%= f.label :sistema_id, "Sistema" %>
    <%= f.collection_select :sistema_id,
          Sistema.all,
          :id,
          :nome,
          :include_blank => true
    %>
  </p>

  <p>
    <%= f.label :analista_id, "Analista" %>
    <%= f.collection_select :analista_id,
          Analista.all,
          :id,
          :nome,
          :include_blank => true
    %>
  </p>
  
  <p>
    <%= f.label :projeto, "Projeto" %>
    <%= f.text_field :projeto %>
  </p>
  
  <p>
    <%= f.label :conclusao, "Conclusão da SR" %>
    <%= f.text_area :conclusao, :rows => 3 %>
  </p>
  
  <p>
    <%= f.submit "Envia SR", :class => "btn btn-primary" %>
  </p>
  
<% end %>

Salve a abra a aplicação no browser.

About