Autor: Carlos Júnior

Recommend Me

14Ago08 Novo no Rails Edge: Suporte simples a “Conditional-Get” (ETags)

O Carlos Brando vem fazendo um excelente trabalho em trazer para nós as novidades do Rails Edge (no caso, o 2.2). Mas ao me deparar com esta atualização não pude me conter em blogar sobre. Na verdade, apenas traduzir o artigo do Ryan.

———–

Conditional-gets são uma facilidade da especificação do HTTP que fornece um método de os servidores we comunicarem aos brosers que a resposta para a requisição GET não mudou desde a última requisição e que o cache do browser pode ser usado com segurança.

Eles funcionam usando os cabeçalhos HTTP_IF_NONE_MATCH e HTTP_IF_MODIFIED_SINCE para passar para frente e para trás um identificador único do conteúdo e o timestamp de quando o conteúdo foi modificado pela última vez. Se o browser fizer a requisição onde o identificador do conteúdo (etag) ou a data da última modificação bater com a versão do servidor então o servidor precisa apenas enviar de volta uma resposta vazia com um status de “não modificado” (304).

É do servidor (nós) a responsabilidade de olhar a data da última modificação e o cabeçalho if-none-match e determinar quando ou não enviar a resposta completa (com a renderização da página, por exemplo). Com este novo suporte a conditional-get no rails, isto se torna uma tarefa muito fácil:

class ArticlesController < ApplicationController
    def show
        @article = Article.find(params[:id])

        # Define o cabeçalho da resposta para refletir exatamente
        # o estado do objeto requisitado
        response.last_modified = @article.published_at.utc
        response.etag = @article

        # Se o estado da requisição é o mesmo do estado no servidor
        # então sabemos que não precisamos enviar todo o resultado
        if request.fresh?(response)
          head :not_modified
        else
          respond_to do |wants|
            # normal response processing
          end
        end
    end
end

O valor do etag é calculado para você com o metodo setter etag=. Tudo que você tem que fazer é prover um único objeto ou array de objetos que definem de forma única a identificação desta requisição. Neste exemplo o artigo por si mesmo contém toda a informação que identifica o estado desta requisição. De qualquer forma, você pode precisar de usar mais de uma chave em seu aplicativo. Como para uma requisição específica para cada usuário:

response.etag = [@article, current_user]

O método request.fresh?(response) é quem dirá a você se a requisição casa com o valor de last-modified-since ou if-none-match da resposta que está sendo enviada. Se sim, você pode evitar de responder todo o conteúdo e economizar alguma (ou muita!) banda.

Também é possível você evitar de acessar o banco de dados se seu aplicativo trata com páginas completamente estáticas armazenadas no banco de dados (isso é raro):

class ArticlesController < ApplicationController
    def show
        # Se o artigo não muda, o etag pode se basear apenas em
        # ítems que temos na requisição
        response.etag = [:article, params[:id]]

        # Se o estado da requisição é o mesmo do servidor
        # podemos evitar também de acessar o banco de dados
        if request.fresh?(response)
          head :not_modified
        else
          @article = Article.find(params[:id])
          respond_to do |wants|
            ...
          end
        end
    end
end

Então, seja um bom cidadão e faça suas requisições compatíveis com conditional-get. Isto é a coisa certa a ser feita - e melhoram também a performance de seus programas.

—-

Vale notar, que você pode também usar estes cabeçalhos para acessar webservices (principalmente os feitos em Rails agora :D) fazendo requisição de XML, se você, por exemplo, estiver guardando os dados que você acessou e guardar a data em que isto foi feito, assim você economisa ainda mais banda!

tags {, }

7Jun08 Rails DataBrowser no rubyforge!

Agora você também pode instalar o Rails DataBrowser via RubyGems!

update 25/06/08: o site do Rails DataBrowser mudou! Abandonamos o uso do Trac, e agora estamos com o Redmine. Então acesse redmine.milk-it.net/projects

Acabei de publicar a gem no RubyForge, e esta será atualizada constantemente assim como o plugin!

Bom, para installar:

gem install databrowser

Para usar:

require 'data_browser'
ActionController::Routing::Routes.draw do |map|
  # your routes
  map.databrowser
end

Ou, se você está usando Rails >= 2.1, ao invés de fazer o require no routes.rb, adicione esta linha em seu environment.rb

  config.gem "databrowser", :lib => "data_browser"

Espero que gostem!

tags {, }

26Mai08 Processamento de imagens com Qt - Parte II

Bom, apenas para deixar vocês a par do que foi feito após aquele post. Continuei meu belo trabalho e hoje resolvi colocá-lo no Github[1], quem sabe ele não cresce e mais pessoas adicionam novos recursos para ajudar outras a entrar na área de Processamento de Imagens Digitais (PID)?

Quando comecei a fazer o trabalho eu tinha muita informação sobre processamento de imagens, mas pouca coisa em C++ (na verdade a linguagem fui eu quem escolhi, causei minha própria coceira). O pouco que encontrei nesta linguagem não era lá realmente funcional (apenas classes isoladas e etc.).

Quem quiser adicionar filtros, mesmo que simples (média, mediana, etc), é só fazer o fork e me comunicar sobre as mudanças ou então gerar um patch e me enviar.

O que realmente está precisando ser feito:

  • Fazer o espectro de Fourier funcionar direito (a transformada está correta, a inversa ídem);
  • Temporada de caça aos memory leaks! Achei vários e já tratei de corrigir, porém após alguns filtros ele consome muita memória e precisamos fechar e abrir novamente se quisermos nossos recursos de volta.
  • Adicionar recursos para trabalhar com imagens coloridas (como o trabalho era para trabalhar com imagens em escala de cinza, apenas isso foi feito) - quando a imagem é colorida, nós a convertemos para escala de cinza;

Abraços!

[1] http://github.com/chjunior/blind-camaleon/tree/master

tags {, , }

21Abr08 Histórico da linha de comando

Seguindo o post do Luke Franci:

$ history 1000 | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head
110 vi
65 rake
43 gcc
40 ls
40 cd
24 rm
24 ./mc
18 thin
18 svn
15 su

Faça o teste e poste nos comentários ou em seu blog!

tags {, }

13Abr08 Suporte a TimeZone no Rails 2.1: uma visão geral

Esta é uma tradução do artigo escrito por Geoff Buesing.

Este será o primeiro de muitos posts que farei sobre os novos recursos de time zone (fuso horário) na próxima versão (2.1) do Rails. Neste post, eu darei uma visão geral destes novos recuros, na criação de um novo aplicativo.

Começarei com um aplicativo Rails 2.1 criado através do comando “rails”. No 2.1, suporte a fuso horário estará ligado por padrão no environment.rb, através da opção config.time_zone:

# config/environment.rb
config.time_zone = 'UTC'

– Este estará definido para UTC como padrão, mas você irá normalmente modificar isto para um fuso horário apropriado para sua localidade. As novas tarefas do rake time:zones:all, time:zones:us, e time:zones:local foram adicionadas para ajudá-lo a encontrar os nomes adequados de fuso horário. time:zones:local farão uma tentativa educada baseada no sistema local de horário, então este é um bom lugar para se começar:

$ rake time:zones:local
* UTC -06:00 *
Central America
Central Time (US & Canada)
Guadalajara
Mexico City
Monterrey
Saskatchewan

Carlos: este é o exemplo usado no artigo original, não vou modificar pois os exemplos se baseam nestes resultados…. no meu sistema acontece isso:

$ rake time:zones:local
* UTC -02:00 *
Mid-Atlantic

Para este exemplo, eu usarei config.time_zone para US Central Time:

# config/environment.rb
config.time_zone = 'Central Time (US & Canada)'

A seguir, criarei um simples scaffold para um modelo Task, com um atributo datetime alert_at:

$ script/generate scaffold Task name:string alert_at:datetime
$ rake db:migrate
$ script/server

Irei ao formulário para nova tarefa, e criarei uma:

new_task1.png

A action show exibe a data e hora que eu digitei, seguido pela correção (deslocamento) UTC:

show_task1.png

… para este exemplo, o prefixo UTC é -0500, que é a correção para US Central Time durante o “horário de verão”:

Para mostrar como este horário é guardado no banco de dados, eu vou ao script/console, e usar o método #alert_at_before_type_cast:

>> t = Task.find_by_name('foo')
=> #< Task … >
>> t.alert_at
=> Sun, 06 Apr 2008 10:30:00 CDT -05:00
>> t.alert_at_before_type_cast
=> "2008-04-06 15:30:00"

O banco de dados está guardando a representação UTC de nosso horário: 15:30 UTC é simultâneamente 10:30 CDT (Central Daylight Time - daylight é o horário de verão). A diferença entre os dois horários é a correção UTC (-5 horas, neste caso).

Depois, eu vou editar a tarefa e alterar o mês para Janeiro (fora do horário de verão, lá):

edit_task.png

Note que a correção UTC agora é -0600 - por que o alert_at não está mais no horário de verão.

show_task_updated.png

script/console confirmará que o banco de dados recebeu a representação correta UTC:

>> t = Task.find_by_name('foo')
=> #< Task … >
>> t.alert_at
=> Sun, 06 Jan 2008 10:30:00 CST -06:00
>> t.alert_at_before_type_cast
=> "2008-01-06 16:30:00"

A hora do banco de dados é agora 16:30 ao invés de 15:30, por que a correção UTC é -6 horas agora.

Fusos por usuário

O que eu defini anteriormente funcionará bem para um aplicativo onde todos os usuários estão no mesmo fuso. Se o aplicativo eventualmente necessitar suportar usuários em fusos diferentes, é bastante fácil de se fazer isso:

Primeiramente, eu criarei um scaffold de usuário, com um atributo string para guardar o fuso do usuário:

$ script/generate scaffold User name:string time_zone:string
$ rake db:migrate

Mudarei formulário do usuário para usar o combo de fusos ao invés de um campo de texto:

# views/users/new.html.erb
<%= f.time_zone_select :time_zone, TimeZone.us_zones %>

O novo formulário de usuário ficará assim — eu o usarei para criar alguns usuários com fusos diferentes:

new_user.png

Para efeitos de demonstração, eu adicionarei um before_filter simples chamado “login_from_querystring” ao ApplicationController:

# controllers/application.rb
before_filter :login_from_querystring
def login_from_querystring
  @current_user = User.find_by_name(params[:user])
end

Então adicionarei um outro before_filter “set_time_zone”, que definirá o Time.zone para o fuso do usuário atual:

# controllers/application.rb
before_filter :set_time_zone
def set_time_zone
  Time.zone = @current_user.time_zone if @current_user
end

Adicionarei um cabeçalho ao layout para mostrar quem está logado, seu fuso horário e o hora atual em seu fuso horário:

# views/layouts/tasks.html.erbCurrent user: <%= @current_user.name if @current_user %>
Current time zone: <%= Time.zone.name %>
Current time: <%= Time.zone.now.inspect %>
<hr />

Finalmente, modificarei a view da index das tarefas para usar a representação #inspect do alert_at, para nos revelar alguns detalhes adicionais:

# views/tasks/index.html.erb
<%=h task.alert_at.inspect %>

Agora, se eu logar com um dos usuários que eu criei, eu verei a tarefa que criei anteriormente, com o alert_at ajustado para o fuso do usuário atual:

index_usuario1.png

… note que o horário exibido para a tarefa é 11:30 EST - que é o mesmo que 10:30 CST.

Para o usuário em US Mountain Time, a tarefa será mostrada como 9:30 MST:

index_usuario2.png

Sem um usuário logado, o fuso usado é o definido em config.time_zone:

index_usuario21.png

Métodos para criar horários no Time.zone atual

Anteriormente, nós estávamos nos apoiando no ActiveRecord para automaticamente converter atributos do model para o horário local do usuário. Para casos onde você precisa criar novas instâncias de Time no fuso local do usuário, os métodos Time.zone.local(), Time.zone.parse() e Time.zone.now() estão disponíveis, assim como Time.zone.now():

>> Time.zone = 'Hawaii'
=> "Hawaii"
>> Time.zone.now
=> Wed, 09 Apr 2008 15:48:18 HST -10:00
>> Time.zone.local(2008, 4, 9, 15, 48, 18)
=> Wed, 09 Apr 2008 15:48:18 HST -10:00
>> Time.zone.parse('2008-04-09 15:48:18')
=> Wed, 09 Apr 2008 15:48:18 HST -10:00
>> Time.zone.at(1207792098)
=> Wed, 09 Apr 2008 15:48:18 HST -10:00

Time e DateTime #in_time_zone converterá qualquer instância para o fuso em Time.zone:

>> Time.zone = 'Alaska'
=> "Alaska"
>> t = Time.utc(2000)
=> Sat Jan 01 00:00:00 UTC 2000
>> t.in_time_zone
=> Fri, 31 Dec 1999 15:00:00 AKST -09:00

… ou, para qualquer fuso ou identificador de fuso (ex.: nome, inteiro ou Duration):

>> t.in_time_zone('Hawaii')
=> Fri, 31 Dec 1999 14:00:00 HST -10:00
>> t.in_time_zone(-6.hours)
=> Fri, 31 Dec 1999 18:00:00 CST -06:00

Dica: Atualizando seu aplicativo

  1. o novo recurso de fuso horário assume que seu banco de dados está guardando horários em UTC, então se você está atualmente guardando horários em um fuso diferente de UTC, você precisará migrar os dados existentes para UTC.
  2. se você instalou o plugin tzinfo_timezone, você precisará removê-lo, dado que este sobrescreve a classe TimeZone no ActiveSupport.
  3. a gem TZInfo não é mais necessárioa, dado que esta está agora dentro do ActiveSupport. De toda forma, se você tem uma versão recente desta gem instalada, Rails favorecerá a gem sobre a versão incluída no ActiveSupport.
  4. A versão do TZInfo incluída no ActiveSupport é uma versão compacta da gem, então se você está interagindo com a API do TZInfo diretamente, você precisará da gem instalada.
  5. Se você não deseja usar o novo recurso de fuso horário - este novo recurso não deve interferir em seu código existente, desde que você não declare o config.time_zone em seu environment.rb.

No próximo capítulo…

Nos próximos posts, tentarei cobrir mais coisas “escondidas”, mas esperamos que este post ajude vocês

Se você acha fuso horário, correções UTC, e horário de verão confuso, você talvez queira ver estas validações de fuso horário, que talvez te deixem ainda mais confuso…

tags {, , }