Associação has_many em Rails com múltiplos checkboxes

Nesse meu começo de estudos de Ruby on Rails, cai na questão de como modelar e implementar uma associação assim: "uma camiseta pode ter várias cores".
Primeiro pensei num Enum clássico do Java (e outras linguagens) mas não serviria, pois as cores teriam que ser cadastradas pelo usuário. Logo teria que ser uma associação many-to-many entre dois modelos. Depois de muito ler, preferi usar a :has_many com :through e um terceiro model fazendo a conexão do que a has_and_belongs_to (HABTM), Deixo para esse railscasts uma explicação mais detalhada das duas implementações, de qual o conceito por trás do :mas_many, e quando usar uma ou outra.

O exemplo abaixo foi testado no Ubuntu 11.04, RoR 3.0.9 e Ruby 1.8.7

Crie o projeto
rails new Camisetas

Scaffold para model Shirt
rails generate scaffold Shirt name:string description:text

Scaffold para model Color
rails generate scaffold Color name:string hexa:string

Gere o model Colorization (já vai criar a migration com os campos necessários), que fará a associação de Shirt com uma Color
rails generate model Colorization shirt_id:integer color_id:integer

Altere o model Colorization (app/model/colorization.rb)
class Colorization < ActiveRecord::Base
  belongs_to :shirt
  belongs_to :color
end

E altere o model Color (app/model/color.rb) dizendo que ele tem várias colorizações (colorizations) e que possue muitas shirts através das colorizations
class Color < ActiveRecord::Base
  has_many :colorizations
  has_many :shirts, :through => :colorizations
end

e o model Shirt (app/model/shirt.rb), nesse caso também dizendo que possui vários colorizações e que através delas, possui muitas cores
class Shirt < ActiveRecord::Base
  has_many :colorizations
  has_many :colors, :through => :colorizations
end

Criar o banco e rodar as migrações
rake db:create
rake db:migrate

Rode o servidor
rails server

Crie algumas shirts e colors
http://localhost:3000/shirts/
http://localhost:3000/colors/

Agora, precisamos relacionar na edição de uma shirt as colors possíveis a serem escolhidas, através de checkboxes.
Em app/views/shirt/_form.html.erb, dentro do form de edição


  <% Color.all.each do |color| -%>
    <%= check_box_tag :color_ids, color.id, @shirt.colors.include?(color), :name => 'shirt[color_ids][]' -%>
    <%= label_tag :color_ids, color.name -%>
  <% end -%>

Para exibir as cores, em app/views/shirt/show.html.erb


  Colors:
  <% @shirt.colors.each do |color| %>
    <%= color.name %>
  <% end %>

Pronto! Edição e visualização ok!

 

Obs.: vale a pena ver o link acima do railscast sobre as associações e esse post sobre o uso de múltiplos checkboxes com :has_many.

Utilizando memcache no CodeIgniter

Recentemente passei o And After da versão 1.7 para a versão 2.0.2 do CodeIgniter (CI),  e fiquei feliz com as facilidades do driver de cache na nova versão, recomendo a atualização do framework!

Nas versões anteriores só existia (nativamente) o cache de "output" no CI, isso significa que eu só podia cachear uma "saída" para aquela url (por método de um controller). A performance fica voando, porém isso engessava um pouco o cache, não podia fazer verificações de usuário logado, personalização, executar scripts, etc. Se já existia um cache para determinado controller ele era usado e ponto, não podia contar views, verificar data, exibir informações de acordo com o usuário, etc.

Com o novo driver de cache ficou muito mais fácil, você pode optar entre os seguintes tipos de cache:

  • Dummy – Utilizado apenas para não dar erro em ambiente de desenvolvimento, ele não cacheia nada.
  • File – Cache de arquivo, fica armazenado em /application/cache
  • Memcache – cache de memória utilizando um servidor memcached
  • APC  – Cache APC do PHP

Utilizando o memcache no CodeIgniter

O memcache é um cache de memória, e precisa de um servidor de cache. O primeiro passo é instalar o memcache.

Com o servidor instalado e configurado, você precisa configurar sua aplicação no CodeIgniter para o uso do cache, vamos lá!

Carregando o Driver de cache

A primeira coisa que você precisa é o Driver de Cache carregado:

$this->load->driver('cache');

Com isso você já pode usar os métodos de cache que o driver suporta.

Salvando um objeto de cache

O driver de cache funciona com chave->valor, para salvar um objeto é simples, o método save é chamado com 3 parâmetros nesta ordem:

  1. Chave do objeto
  2. Valor do objeto
  3. "Time to live", tempo em segundos que o cache deve permanecer salvo.

Para salvar o objeto "site" com valor "O Desenvolvedor" por 2 minutos temos o seguinte:

$this->cache->memcached->save('site', 'O Desenvolvedor', 120);

Recuperando um objeto de cache

O método get retorna o valor do objeto se ele existir e false se não existir o objeto em cache:

$site = $this->cache->get('site');

O retorno "false" permite verificação se um objeto existe, por exemplo:

if($this->cache->get('site')){
   //faz alguma coisa pois o cache existe
}else{
   //Faz uma consulta no banco e salva ela no cache
   $this->cache->memcached->save('site', 'O Desenvolvedor', 120);
}

Apagando um objeto do cache

Função delete, bastante intuitiva, só passa como parâmetro a chave do objeto a ser excluído:

$this->cache->delete('site');

 

Com essas informações tudo o que você precisa para melhorar a performance da sua aplicação é um pouco de criatividade e testes, testes e testes. No And After fiz diversos testes, optei pelo uso de file cache para os posts.

Em alguns casos de posts com centenas de comentários o ganho de performance foi muito grande, no post de cartão de visita por exemplo a performance de back-end ficou mais de 10 vezes mais rápida para entrega dos objetos. Agora estou trabalhando no cache do O Desenvolvedor e do Eu Compraria! Shop.

A melhoria de performance e redução de gasto com servidor valem qualquer tempo investido no estudo e aplicação do cache!

Sugestões, críticas e melhorias nos comentários! 🙂

Referências

Anexando imagens a um model no Ruby on Rails

Primeiro post sobre Ruby on Rails: tenho estudado bastante tanto o framework como a linguagem (Ruby), gostado bastante deles. A linguagem é totalmente orientada a objetos e com uma leitura muito simples, e o framework com seu conceito Convention over Configuration faz aumentar muito a produtuvidade.

Vamos lá, para um exemplo simples de como possibilitar que uma entidade da sua aplicação tenha uma imagem atrelada. Para isso, vamos usar a gem Paperclip (em https://github.com/thoughtbot/paperclip tem a documentação completa, inclusive como instalar).
O exemplo abaixo foi testado no Ubuntu 11.04, RoR 3.0.9 e Ruby 1.8.7

Criar a aplicação
rails new MinhaApp

Editar Gemfile
gem 'paperclip'
*Se necessário, rode bundle install

Como é para exemplificar, vamos usar scaffold para gerar uma entidade Image
rails generate scaffold Image name:string description:text

Gerar migration que adiciona em Image um file (obs: asset pode ter qualquer outro nome)
rails generate paperclip Image asset

Alterar model de Image (image.rm)
class Image < ActiveRecord::Base
  has_attached_file :asset,
                    :styles => { :medium => "400×400>",
                                 :thumb => "100×100>" }
end

Alterar o form de criação de uma Image, deixando o form com multipart
<%= form_for(@image, {:multipart => true}) do |f| %>

Inserir no form campo de imagem
<%= f.file_field :asset %>

Alterar o show.html.erb inserindo as tags que exibe a imagem e suas variações
<%= image_tag @image.asset.url %>
<%= image_tag @image.asset.url(:medium) %>
<%= image_tag @image.asset.url(:thumb) %>

Crie o banco e rode as migrations
rake db:create
rake db:migrate

Pronto, só subir a aplicação Rails
rails server

E acessar http://localhost:3000

Listando o tamanho de pastas no Ubuntu

Estava fazendo monitoramento do servidor do nosso servidor (EC2 da Amazon) e como ainda não configurei o logrotate para todos logs é necessário tomar cuidado para evitar logs monstros, a melhor forma de fazer isso é monitorar os tamanhos de diretórios.

Ver o tamanho de diretórios por terminal no Ubuntu

O primeiro passo é navegar até a pasta "pai" dos diretórios que você quer verificar o tamanho, chegando neste diretório digite o seguinte comando:

du -kh --max-depth=1

O resultado do comando vai listar todas as subpastas com o tamanho formatado em K, M, ou G, por exemplo:

0 ./lock
17M ./log
48K ./run
4.0K ./opt
860K ./backups
199M ./cache
206M ./lib
28K ./spool
4.0K ./crash
2.2G ./www
4.0K ./mail
4.0K ./tmp
4.0K ./local
2.6G .

Com essas informações consigo monitorar quando algum log está crescendo muito rapidamente.

Ps: não deixem de utilizar o logrotate ou algum outro sistema automatizado para limpeza / manutenção de logs!

Alterando a Timezone do Ubuntu pelo terminal

Este final de semana migrei o And After, O Desenvolvedor, Eu Compraria e Eu Compraria! Shop do meu servidor "cloud" da Locaweb para o EC2 da Amazon, um cloud de verdade (está saindo um post com dicas para uma migração bem sucedida).

Nossa loja de produtos geeks tem um sistema que depende do horário do servidor para exibição específica de produtos, e ontem a @biab notou um comportamento estranho:, eu havia esquecido de configurar a data e hora do servido para GMT -3 (horário de Brasília).

A troca do timezone é bem simples, seguindo minhas experiências com configuração de servidor no Ubuntu (instalando o memcache e instalando o nodeJs), resolvi criar um post com essa informação:

 

Configurando a data e hora do Ubuntu pelo terminal

A informação de qual timezone deve ser usado pelo sistema fica em /etc/timezone, então para editar essa informação digite no terminal:

sudo vi /etc/timezone

É um arquivo de texto que contém a chave de qual 

timezone será utilizada pelo Ubuntu, apague o que estiver lá e digite o 

timezone que você quiser. No meu caso (São Paulo) o 

timezone é "America/Sao_Paulo" (sem as aspas). Feita a alteração salve o arquivo de texto.

sudo dpkg-reconfigure --frontend noninteractive tzdata

Feito isso seu sistema já estará utilizando o novo timestamp – no meu caso foi necessário reiniciar o Apache para que ele também utilizasse o novo timestamp configurasse, se você tem serviços que dependem da configuração de hora verifique se não é necessário reiniciar para eles atualizarem a hora do sistema.

 

Lista de timestamps para Ubuntu

Para descobrir qual a "string" do seu timestamp digite o comando abaixo:

 ls /usr/share/zoneinfo/

O comando irá listar todas as "areas" de timezone. Um dos itens é America por exemplo, para listar os timestamps da America, incremente o comando acima com:

ls /usr/share/zoneinfo/America

E para buscar por um timestamp específico (por exemplo, "Paulo") digite:

 

ls /usr/share/zoneinfo/America | grep Paulo

Com essas informações você consegue buscar o caminho de qualquer timezone do mundo para inserir no /etc/timezone

 

 

Referência

Ubuntu Time