Primeirona: segunda parte

Table of Contents

1 Primeirona (continuação)

1.1 Completando o CRUD

  1. Remoção

    Observando as rotas, vemos que delete está redirecionado para destroy.

    Vamos alterar a visão dos apelidos e incluir um link para remoção. Aproveitemos também para usar uma tabela ao invés de uma lista.

    O index.html.haml fica assim:

    %h1 Apelidos cadastrados
    %table{border: 3}
      %tr
         %th Nome
         %th Apelido
         %th 
      - @apelidos.each do |ap|
        %tr
           %td= ap.nome
           %td= ap.apelido
           %td
             = link_to 'Apagar', apelido_path(ap), method: :delete,
               data: {confirm: 'Certeza?'}
    = link_to 'Voltar', new_apelido_path
    

    A opção :data abre uma janela de confirmação.

    Falta ajustar o método destroy

    def destroy
        @apelido = Apelido.find(params[:id])
        @apelido.destroy
    
        redirect_to apelidos_path
    end
    
  2. Edição

    Como edição implica em alteração no banco, é importante restringir quais atributos podem ser modificados. Já fizemos isso na criação onde exigimos a presença e liberamos os campos permitidos. Vamos transferir o teste para uma única função privada.

    Junto com a edição, acertamos a atualização (update). As novas versões são estas:

    def edit
      @apelido = Apelido.find(params[:id])
    end
    
    def update
      @apelido = Apelido.find(params[:id])
      if @apelido.update(apelido_params)
         redirect_to @apeliddo
      else
         render 'edit'
      end
    end
    
    private
    def apelidos_params
       params.require(:apelido).permit(:nome, :apelido)
    end
    

    O index.html.haml precisa mostrar campos de edição também:

    %h1 Apelidos cadastrados
    %table{border: 3}
      %tr
         %th Nome
         %th Apelido
         %th 
      - @apelidos.each do |ap|
      %tr
        %td= ap.nome
        %td= ap.apelido
        -# link para edição
        %td
          = link_to 'Editar', edit_apelido_path(ap)
        %td
          = link_to 'Apagar', apelido_path(ap), method: :delete,
               data: {confirm: 'Certeza?'}
    = link_to 'Voltar', new_apelido_path
    

    No show.html.haml também

    %h1 Pronto!
    %p
      %i= @apelido.nome
      já pode ser chamado de
      %i= @apelido.apelido
    %p
      = link_to 'Editar', edit_apelido_path(@apelido)
      = link_to 'Novo',   new_apelido_path
      = link_to 'Lista',  apelidos_path
    

    A edição é bastante parecida com a criação. O princípio DRY sugere reaproveitar o código tanto quanto possível. Faremos com a visão.

    O formulário é o mesmo, usaremos uma página parcial, que nada mais é do que um arquivo que começa com _.

    Este é o _formulario.html.haml

    = form_with model: @apelido, local: true  do |f|
      %table
        %tr
          %td
            = f.label :nome
          %td
            = f.text_field :nome
        %tr
          %td
            = f.label :apelido
          %td
            = f.text_field :apelido
      = f.submit t(:save)
    

    Com ele, tanto new.html.haml como edit.html.haml ficam mais simples. Respectivamente:

    %h1 Novo apelido
    = render 'formulario'
    = link_to 'Voltar', apelidos_path
    

    e

    %h1 Editando um apelido
    = render 'formulario'
    = link_to 'Voltar', apelidos_path
    

1.2 Validação

Garantir que os dados são aceitáveis e tratar eventuais erros.

Isso é feito no model. Vamos colocar as seguintes restrições:

  • nome tem que ter pelo menos 3 letras
  • nome e apelido têm que estar presentes

O método validates cuida disso.

class Apelido < ApplicationRecord
   validates :nome, presence: true, length: {minimum: 3}
   validates :apelido, presence: true
end

Verifique que os registros inválidos não são salvos.

Mas agora precisamos agir melhor quando um erro ocorre. Vamos acertar algumas coisas no controlador, essencialmente o create e o new.

def create
  @apelido = Apelido.new(apelidos_params)
  # salva
  @apelido.save
  # Testamos se conseguiu salvar
  if @apelido.save
     redirect_to @apelido
  else
     # tenta de novo
     render 'new'
end

def new
  # como tem um render new no create precisamos de um @apelido definido
  @apelido = Apelido.new
end

Precisamos também avisar o usuário do que deu errado. Isso fica no view.

= form_with model: @apelido, local: true  do |f|
  - @apelido.errors.any?
    %ul
    - @apelido.errors.full_messages.each do |msg|
      %li= msg
  %table
    %tr
      %td
        = f.label :nome
      %td
        = f.text_field :nome
    %tr
      %td
        = f.label :apelido
      %td
        = f.text_field :apelido
  = f.submit 'Salva'

1.3 Internacionalização

As mensagens de erro estão em inglês. Podemos acertar com a classe I18n. Os ajustes ficam em config/application.rb. Primeiro precisamos habilitar os locales disponíveis, depois escolher o default.

Coloque essas linhas em config/application.rb:, depois dos require~s e do ~Bundler.

# Whitelist locales available for the application
I18n.available_locales = [:en, :pt]

# Set default locale to something other than :en
I18n.default_locale = :pt

Agora, precisamos definir as traduções. Veja um exemplo e a documentação em config/locales/en.yml. Um arquivo YAML (extensão yml) é uma espécie simplificada de JSON e representa uma árvore com os contextos de cada palavra.

Crie o arquivo pt.yml no diretório config/locales/ con o conteúdo abaixo e complete para todos os casos:

pt:
  save:   'Salva'
  edit:   'Editar'
  back:   'Voltar'
  delete: 'Apagar'
  new:   'Novo'
  ready: 'Pronto'
  may_now: 'já pode ser chamado de'
  activerecord:
    errors:
      messages:
        blank: "falta alguma coisa aqui"
      models:
        apelido:
          attributes:
            nome:
              too_short: "curto demais"

Veja que as folhas são as chaves para tradução, assim 'save' será traduzida para 'Salva'. Onde uma tradução for necessária use t( chave ).

Os casos mais específicos têm precedência sobre os mais gerais. Para saber quais chaves ainda precisam ser incluídas, veja as mensagens de erro quando o Rails não conseguir traduzir.

Prepare o en.yml também

en:
  may_call: 'may already be called'

Finalemente, ajuste todas as mensagens com traduções. Veja este exemplo no index.html.haml (aproveitei para colocar uma borda na tabela, a título de ilustração).

%table{border: 3}
  %tr
    %th Nome
    %th Apelido
    %th 
  - @apelidos.each do |ap|
    %tr
      %td= ap.nome
      %td= ap.apelido
      -# link para edição
      %td
        = link_to t(:edit), edit_apelido_path(ap)
      %td
        = link_to t(:delete), apelido_path(ap), method: :delete,
        data: {confirm: t('sure')}
= link_to t(:back), new_apelido_path

Atenção, para ativar novos locales, é preciso reiniciar o servidor.

1.4 CSS (Cascade Style Sheets)

Para deixar a página mais apresentável, adicionamos CSS ao HTML. No nosse caso, usaremos SASS (Syntatically Awesome Style Sheets), que é uma extensão do CSS com diversas vantagems e, claro, continuaremos a usar o HAML.

SASS permite o uso de variáveis, aninhamento e mixins entre outras coisas. A sintaxe básica é a mesma da CSS.

Para detalhes de CSS veja, por exemplo, https://www.w3schools.com/css/default.asp.

Para SASS: https://sass-lang.com/documentation/file.SASS_REFERENCE.html

Os arquivos de formatação ficam em app/assets/stylesheets.

Vamos melhorar (ok, brincar com) nossa página principal. Primeiro centralizamos a tabela. Basta colocar %center no início do arquivo e identar todas as linhas.

Aproveitamos para por um título na página. Os títulos pertencerão à classe titulo.

%center
  %h1.titulo=t(:list)
  %table{border: 3}
    %tr
      %th Nome
      %th Apelido
    - @apelidos.each do |ap|
      %tr
        %td= ap.nome
        %td= ap.apelido
        -# link para edição
        %td
          = link_to t(:edit), edit_apelido_path(ap)
        %td
          = link_to t(:delete), apelido_path(ap), method: :delete,
          data: {confirm: t('sure')}
  = link_to t(:back), new_apelido_path

Em app/assets/stylesheets acertamos o estilo. Brinque à vontade.

body {
  background: gold;
}

table {
  color: blue;
}

h1.titulo {
  text-shadow: 2px 3px grey;
}

Uma variação interessante é usar um serviço de CSS como o bootstrap.

Inclua no final do Gemfile:

gem 'bootstrap-sass', '~> 3.4.1'
gem 'sassc-rails', '>= 2.1.0'
gem 'jquery-rails'

Não se esqueça do bundle install.

Atualize o application.scss, passando todos os estilos para lá.

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 * 
 * retirei o '=' das próximas duas linhas
 * require_tree .
 * require_self
 */

@import "bootstrap-sprockets";
@import "bootstrap";

@import "apelidos"

Para ativar javascript, que usaremos mais tarde, altere app/assets/applications.js colocando //require bootstrap-sprockets na penúltima linha (require_tree):

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require bootstrap-sprockets
//= require_tree .

Agora experimente novas versões

application.html.haml

!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %title Primeirona
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
  %body
    %nav.navbar.navbar-default.container-fluid
      .collapse.navbar-collapse
        .navbar.navbar-nav.navbar-right
          .btn= link_to t(:list), apelidos_path
          .btn= link_to t(:new), new_apelido_path
    = yield

index.html.haml

%center
  %h1.titulo=t(:list)
  %table.table-hover.table-bordered
    %thead
      %tr
        %th Nome
        %th Apelido
    - @apelidos.each do |ap|
      %tr
        %td= ap.nome
        %td= ap.apelido
        -# link para edição
        %td
          = link_to edit_apelido_path(ap) do
            %span.glyphicon.glyphicon-pencil
        %td
          = link_to  apelido_path(ap), method: :delete,
               data: {confirm: t('sure')} do
            %span.glyphicon.glyphicon-remove
  = link_to t(:back), new_apelido_path

Arrume os outros

Author: Marco Gubitoso

Created: 2019-04-13 sáb 11:52

Emacs 25.2.2 (Org mode 8.2.10)

Validate