RSS Feed
  1. [ Quick Post ] – Callbacks no Rails 3 – before_save

    July 13, 2011 by urieljuliatti

    Os callbacks são conhecidos por funcionarem como um “gancho” para o ciclo de vida de um objeto Active Record. Eles permitem acionar lógica antes ou depois de uma alteração do estado de um objeto. Basicamente, você lida com acionamento de triggers que podem alterar o objeto para um determinado estado, como se fosse uma chave de “liga e desliga”. Imagine uma lâmpada apagada em um quarto em que o interruptor fique do lado de fora. Para entrar no quarto e ter o estado lâmpada como “acesa” você precisa apertar o interruptor antes de entrar e, caso queira mudar o estado para “apagada” , você precisa sair do quarto e acionar o interruptor novamente. Repararam? Para os dois diferentes estados (apagada e acesa) do objeto Lâmpada, precisei executar uma tarefa antes (before) e outra depois (after).

    Para ilustrar a aplicação dessa funcionalidade, ontem tive que encriptar uma senha antes dela ser salva como um registro, mudando o estado para salvo. Logo abaixo, o método encrypt_new_password é executado e aplica a todas as operações (create e update)  antes de mudar o estado do objeto através do callback save.

    Em suma, toda vez que um objeto for criado ou atualizado, ele acionará esse método de encriptação antes mudar seu estado para salvo e gravar o registro.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    before_save :encrypt_new_password
     
    protected
     
    def encrypt_new_password
    return if password.blank?
    self.hashed_password = encrypt(password)
    end
       def encrypt(string)
    Digest::SHA1.hexdigest(string)
    end
     
    end
    before_save :encrypt_new_password
    
    protected
    
    def encrypt_new_password
    return if password.blank?
    self.hashed_password = encrypt(password)
    end
       def encrypt(string)
    Digest::SHA1.hexdigest(string)
    end
    
    end

    Existem outros callbacks que o Rails oferece por padrão, você pode estudá-los através da própria API .

    Abraços e até o próximo quick post!

    Créditos ao amigo @m3nd3s pelas correções :)


  2. Tipos de dados e Objetos em Ruby – Texto

    July 10, 2011 by urieljuliatti

    Fala galera, beleza? Estamos novamente de volta aos estudos de Ruby que dessa vez englobará o tipo Texto.

    Os Textos em Ruby são representados através de objetos mutáveis da classe String, que possui vários métodos e operadores para inserir, deletar, repor, etc. Geralmente, as patterns respectivas a classe Text são representadas por expressões regulares , sendo uma característica forte no Ruby, que as usa quase que  literalmente. Porém, não mais usadas do que outros tipos de dados como os números, strings e array são.

    String Literais com aspas simples

    São marcadas através do apóstrofo ‘Isso é uma string literal’ . Caso deseje acrescentar uma aspas simples dentro de uma String, basta utilizar um contrabarra:

    1
    
     'Ruby é 'simples'  né? ' 
     'Ruby é 'simples'  né? ' 

    E caso necessite de utilizar a contrabarra em meio a uma string literal, faça:

    1
    
     ' Isso é uma contra barra \' 
     ' Isso é uma contra barra \' 

    ou

    1
    
    'ab' == 'a\b'
    'ab' == 'a\b'

    String Literais com aspas duplas

    Ao contrário das com aspas simples, essas Strings são muito mais flexíveis, pois permitem mais seqüencias de interação como o “n”, por exemplo, que permite a quebra de linha , e para aqueles que já estiverem familiarizados com a linguagem C vai ficar bem mais fácil de entender.

    O funcionamento das strings com aspas duplas no Ruby é bem simples: Ela é criada, a expressão é averiguada, convertida para uma String e então colocada em seu devido lugar como uma expressão textual. Isso é conhecido como “interpolação de strings”, onde uma string de aspas duplas pode ter uma outra expressão que inicie com o caracter # e as obtenha as chaves { }. Só para visualizar essa interpolação, vejamos o seguinte exemplo onde

    1
    
     $salutation = 'hello' "#$salutation world" # retorna "hello world" 
     $salutation = 'hello' "#$salutation world" # retorna "hello world" 

    Estamos passando uma variável global como expressão para uma string e ela interpreta normalmente. Se você está habituado ao C, não terá problemas em entender o conceito.

    Engraçado foi que ao ver em alguns códigos, perebi que o Ruby permite a utilização do printf ou sprintd, o que ajuda ainda mais na interpolação , por exemplo:

    1
    
     sprintf("o valor de PI esta em torno de: %.4f", Math::PI) # "O valor de Pi esta em torno de: 3.1416" 
     sprintf("o valor de PI esta em torno de: %.4f", Math::PI) # "O valor de Pi esta em torno de: 3.1416" 

    Bacana, certo?

    Here Documents

    O Here Document é uma maneira de expressar uma string literal em linhas de comando shell. Ele preserva as quebras de linha e espaços em branco (até mesmo a indentação) no texto. Existem algumas linguagens que permitem até mesmo a substituição de variável e substituição de comando dentro da string.

    Eles incia geralmente com << ou <<- . O texto de uma string literal começa na linha seguinte, pelo texto a ser citado, e depois fechada pelo mesmo identificador em sua própria linha. Exemplo:

    1
    2
    3
    4
    5
    6
    
    ruby-1.9.2-p136 :007 > heredoc = <<TEXTO
    ruby-1.9.2-p136 :008"> texto pro blog
    ruby-1.9.2-p136 :009"> here documents
    ruby-1.9.2-p136 :010"> :) 
    ruby-1.9.2-p136 :011"> TEXTO
    => "texto pro blognhere documentsn:)n"
    ruby-1.9.2-p136 :007 > heredoc = <<TEXTO
    ruby-1.9.2-p136 :008"> texto pro blog
    ruby-1.9.2-p136 :009"> here documents
    ruby-1.9.2-p136 :010"> :)
    ruby-1.9.2-p136 :011"> TEXTO
    => "texto pro blognhere documentsn:)n"

    O interpretador do Ruby capta o conteúdo de uma string literal lendo linha por linha de acordo com sua inserção. Na realidade, após entender que o conteúdo é um here document, o interpretador volta na última linha que foi inserida e continua com o parse.

    Só para adiantar, você usará bastante no Rails.. Uma vez que você consegue fazer isso:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    htmlDocument = <<-"# # #"
     
    <html><head><title>#{title}</title></head>
     
    <body>
     
    <h1>#{title}</h1>
     
    #{body}
     
    </body>
     
    </html>
     
    # # #
    htmlDocument = <<-"# # #"
    
    <html><head><title>#{title}</title></head>
    
    <body>
    
    <h1>#{title}</h1>
    
    #{body}
    
    </body>
    
    </html>
    
    # # #

    Mutabilidade e as Strings literais

    No Ruby, o interpretador  não consegue usar um mesmo objeto para representar duas strings literais idênticas, pois cada vez que o interpretador encontra uma string literal, ele cria um novo objeto.  Só para ilustrar o que falo, vejamos esse laço de repetição:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    ruby-1.9.2-p136 :044 > 10.times { puts "abc".object_id }
    2177866260
    2177866220
    2177866180
    2177866080
    2177865980
    2177865940
    2177865900
    2177865860
    2177865820
    2177865780
    => 10
    ruby-1.9.2-p136 :044 > 10.times { puts "abc".object_id }
    2177866260
    2177866220
    2177866180
    2177866080
    2177865980
    2177865940
    2177865900
    2177865860
    2177865820
    2177865780
    => 10

    Observa que cada iteração de “abc” possui uma id diferente? Então, se você incluir uma string literal em um laço de repetição, o Ruby vai criar um objeto para cada iteração. Portanto, é bom evitar o uso excessivo de string literais.

    Caracteres literais

    Os caracteres podem ser expressos em Ruby inserindo um ponto de interrogação antes, por exemplo:

    1
    2
    
    ruby-1.9.2-p136 :050 > ?A
     => "A"
    ruby-1.9.2-p136 :050 > ?A
     => "A"

    Apesar do Ruby possuir uma sintaxe para caracteres literais ele não possui uma classe especifica para representar caracteres. Ao invés disso, os caracteres literais são tidos como uma string de tamanho 1.

    Operações com Strings

    A classe String do Ruby fornece uma série de operadores para tratar as strings. Para fazer uma operação simples de concatenação, basta adicionar um “+”:

    1
    2
    3
    4
    
    ruby-1.9.2-p136 :051 > nome = "Uriel"
    => "Uriel"
    ruby-1.9.2-p136 :052 > nome + " Juliatti"
    => "Uriel Juliatti"
    ruby-1.9.2-p136 :051 > nome = "Uriel"
    => "Uriel"
    ruby-1.9.2-p136 :052 > nome + " Juliatti"
    => "Uriel Juliatti"

    Muito simples. Assim como o “+”, o operador << também trabalha bastante no Ruby, porém deve-se tomar cuidado, pois se você der um append com um número, o Ruby entende que é um caracter (ASCII).

    1
    2
    3
    4
    
    ruby-1.9.2-p136 :055 > alfabeto = "A"
    => "A"
    ruby-1.9.2-p136 :056 > alfabeto << 67
    => "AC"
    ruby-1.9.2-p136 :055 > alfabeto = "A"
    => "A"
    ruby-1.9.2-p136 :056 > alfabeto << 67
    => "AC"

    O operador * espera um inteiro após , portanto se você fizer:

    1
    2
    
    ruby-1.9.2-p136 :057 > reticente  = '.'*3
    => "..."
    ruby-1.9.2-p136 :057 > reticente  = '.'*3
    => "..."

    É isso ai, tive que deixar o post bem direto e enxuto, portanto vou abordar um tema futuramente, que é o de Acessar caracteres e substrings. Mas o próximo post será sobre Arrays :)

    Contudo, até a próxima e grande abraço a vocês!


  3. [ Quick Post ] – Escopos

    July 8, 2011 by urieljuliatti

    A idéia do escopo é básica: Bem, sabemos que a medida que escrevemos nossas aplicações acabamos que por notar que repetimos várias vezes certas condições. No meu caso, estou desenvolvendo uma aplicação que necessita sempre de mostrar os alunos em ordem alfabética, pois geralmente é assim que esperamos vindo de uma lista de chamada.

    O ActiveRecord para ser mais exato, fornece uma técnica de escopo que encapsula os métodos de pesquisa mais comuns. O Rails não impõe uma regra padrão, o que permite o banco de dados cuidar da ordenação dos resultados, que na maioria dos casos é feito na id (primary_key).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    class Student < ActiveRecord::Base
     
    has_and_belongs_to :teachers
     
    default_scope order('students.name')
     
    end
     
    Student.all
     
    => [#<Student id: 2, name: "Hackin", ...>, #<Student id: 4, name: "m3nd3s", ...>, #<Student id: 1, name: "Petros", ...>, #<Student id: 5, name: "Tagliati", ...>, #<Student id: 3, name: "Uriel", ...>]
    class Student < ActiveRecord::Base
    
    has_and_belongs_to :teachers
    
    default_scope order('students.name')
    
    end
    
    Student.all
    
    => [#<Student id: 2, name: "Hackin", ...>, #<Student id: 4, name: "m3nd3s", ...>, #<Student id: 1, name: "Petros", ...>, #<Student id: 5, name: "Tagliati", ...>, #<Student id: 3, name: "Uriel", ...>]

    O que fizemos? Anteriormente, se você fizesse um Student.all ele traria todos os resultados ordenados pelo registro da chave primária. Usando o escopo padrão, eu digo ao rails que, nesse caso, essa regra deve ordenar por ordem alfabética.

    Existem outros tipos de escopos e abordagens, mas para seguir a regra do Quick Post por hoje fica só essa mesmo :)

    É isso aí e até o próximo Quick Post sobre Rails!

    See ya.


  4. [ Quick Post ] Routes: Diferenças entre url e path

    July 5, 2011 by urieljuliatti

    Uma grande dúvida para aqueles que estão no processo embrionário de aprendizado com o Ruby on Rails é entender o conceito motor que está por trás das rotas, ou Routes disponibilizado pelo framework. Como ainda estou esmiuçando cada parte do Rails, pretendo reportar um tópico longo e específico sobre as Routes, mas enquando esse post não sai, vamos em fragmentos mesmo.

    Hoje, trabalhando em um projeto específico, tive dúvidas sobre quando usar um user_path ou user_url para redirecionar para uma rota. A solução foi simples, mas fiquei na dúvida sobre a diferença entre um em outro e portanto, decidi compartilhar o que aprendi.

    Ao chamar um users_url, o próprio helper do Rails gera uma URL que inclui um protocolo e o nome do host. Já o users_path gera apenas o caminho específico daquela rota.

    Temos para o users_url:

    1
    
    users_url # http://localhost:3000/users
    users_url # http://localhost:3000/users

    E ao chamar o users_path, o rails gera para você uma porção do caminho específico:

    1
    
    users_path # /users
    users_path # /users

    Em prática, você pode usar o users_path geralmente nas views para direcionar forms ou links simples e diretos, como por exemplo: link_to “Usuario”, user_path(user). Quando queremos redirecionar mapeando através de toda uma aplicação, utilizamos o _url. Por exemplo, utilizado um redirect_to no UsersController dessa forma:

    1
    
     .. redirect_to site_url, :notice => 'Usuario inexistente' .. 
     .. redirect_to site_url, :notice => 'Usuario inexistente' .. 

    mapeamos o protocolo e o dominio e enviamos (http://locahost:3000/, por exemplo), redirecionando o usuario à página principal caso ele tenha acessado uma view que não tenha correspondência com a sua ação desejada.

    Por enquanto é só.

    É isso aí e até o próximo quick post.

    Abraços!


  5. [ Quick Post ] – Lidando com erros no Rails 3

    July 4, 2011 by urieljuliatti

    Fala pessoal, beleza? Enquanto meu outro post de Ruby não sai, resolvi postar alguma coisinha sobre Rails.

    Hoje, ao dar continuidade ao meu projeto de Rails, tive uma dúvida de como cuidar de alguns erros nos Controllers. Eu me deparei com um erro besta: Passei uma id para a action show de um determinado controller e obtive retorno de um erro padrão, sem tratamento.

    A solução foi majestosa, pois o rails disponibiliza um logger em que você pode armazenar  erros e para cada controller da sua aplicação há um logger disponível. Além disso, utilizei um recurso específico da classe ActiveRecord.

    1
    Eis a solução:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    def show
     
    begin
     
     @user = User.find(params[:id])
     
     rescue ActiveRecord::RecordNotFound
     
     logger.error "Tentando acessar um usuário com id inexistente ou inválida"
     
     redirect_to  site_url, :notice => 'Usuário inválido'
     
     else
     
      respond_to do |format|
     
        format.html # show.html.erb
     
        format.xml { render : xml => @user }
     
      end
     
     end
     
    end
    def show
    
    begin
    
     @user = User.find(params[:id])
    
     rescue ActiveRecord::RecordNotFound
    
     logger.error "Tentando acessar um usuário com id inexistente ou inválida"
    
     redirect_to  site_url, :notice => 'Usuário inválido'
    
     else
    
      respond_to do |format|
    
        format.html # show.html.erb
    
        format.xml { render : xml => @user }
    
      end
    
     end
    
    end

    Além dos métodos que lidam com os erros (begin, rescue), reparem bem que frente ao rescue, utilizei o RecordNotFound, que é uma exceção específica do ActiveRecord.

    Rode a aplicação e force um erro na requisição do browser (passe qualquer valor inexistente, tal como: http://localhost:3000/users/valor-inexistente, veja o erro renderizado na view e vá a esse diretório (log/development.log). Você encontrará algo do tipo:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    Processing UsersController#show to */*
    sql (0.0ms) SELECT * FROM "users" WHERE (("users"."id" = valor-inexistente))
    ...
     
    <strong>ActiveRecord::RecordNotFound (Couldn't find Cart with ID = valor-inexistente):</strong>
    app/controllers/users_controller.rb:23:in 'show'
     
    <strong>Attempt to access invalid user valor-inexistente</strong>
    sql (0.0ms) SELECT * FROM "users"WHERE (("users"."id" = 0))
    Redirected to http://127.0.0.1:3000/site
     
    ...
    Processing UsersController#show to */*
    sql (0.0ms) SELECT * FROM "users" WHERE (("users"."id" = valor-inexistente))
    ...
    
    <strong>ActiveRecord::RecordNotFound (Couldn't find Cart with ID = valor-inexistente):</strong>
    app/controllers/users_controller.rb:23:in 'show'
    
    <strong>Attempt to access invalid user valor-inexistente</strong>
    sql (0.0ms) SELECT * FROM "users"WHERE (("users"."id" = 0))
    Redirected to http://127.0.0.1:3000/site
    
    ...

    Está aí :)

    Prometo postar mais uns quick posts sobre Rails, com dicas rápidas baseadas nas minhas experiências diárias.