Fazendo thumbnails de tamanho fixo com attachment_fu

O plugin attachment_fu é quase que o plugin padrão para tratar do upload de arquivos em aplicações Rails. Ele suporta também o resize de imagens mantendo as proporções, mas não suporta crop. Sempre precisamos criar avatar ou coisas semelhantes. E em geral o avatar tem um tamanho fixo, como 80x80. Para isso é necessário fazer o crop da imagem. Adicione o método resize_image, como abaixo, no seu modelo:

class Photo < ActiveRecord::Base
  belongs_to :owner, :class_name => "Person"
  has_attachment :content_type => :image,
    :processor => :rmagick,
    :storage => :file_system,
    :resize_to => '600>',
    :thumbnails => { 
      :album        => 'crop: 150x150',
      :icon         => '72>' }

  protected
  def resize_image(img, size)
    # resize_image take size in a number of formats, we just want
    # Strings in the form of "crop: WxH"
    reg = /^crop: (\d*)x(\d*)/i
    if (size.is_a?(String) && size =~ reg) ||
        (size.is_a?(Array) && size.first.is_a?(String) &&
          size.first =~ reg)
      img.crop_resized!($1.to_i, $2.to_i)
      # We need to save the resized image in the same way the
      # orignal does.
      self.temp_path = write_to_temp_file(img.to_blob)
    else
      super # Otherwise let attachment_fu handle it
    end
  end
end

Assim você sobrescreve o método original do attachment_fu para suportar uma sintaxe especial. Então quando você quiser um thumbnail ou até mesmo a imagem principal cortada de um tamanho específico, basta usar a string ‘crop: WxH’. Bom lembrar que isso só irá funcionar com o Rmagick. Eu dei uma olhada sobre como fazer usando o image_science, mas era um pouco mais chato. É bom setar: :processor => :rmagick na definição do has_attachment.

Eu também fiz um teste para garantir que os meus thumbnails estão sendo gerados no tamanho correto:

describe "photo resize" do
  it "should create thumbnails with correct size" do
    photo = new_photo
    photo.save!

    full, album, icon = *Magick::ImageList.new(
      photo.full_filename,
      photo.full_filename('album'),
      photo.full_filename('icon')
    )

    full.columns.should == 50
    full.rows.should == 64

    album.columns.should == 150
    album.rows.should == 150

    icon.columns.should == 50
    icon.rows.should == 64
  end
end

PS1: Sim, eu sou paranóico com testes e você também deveria ser!
PS2: Essa dica foi adaptada daqui.

3 comentários : 17.03.2009 07:04 PM

acts_as_taggable_on_steroids e will_paginate

Num dos projetos que trabalho utilizo o plugin acts_as_taggable_on_steroids. Pela primeira vez fui fazer paginação de elementos com tags utilizando o will_paginate. O acts_as_taggable_on_steroids fornece o método de classe find_tagged_with que serve para buscar por items com uma determinada tag:

Post.find_tagged_with('rails')

E é possível fazer paginação utilizando o método paginate_tagged_with, mas o problema é que ele não funciona como deveria. Apesar de fazer a paginação funcionar corretamente, a sql que ele gera para fazer o count dos elementos não é correta. Se usado sem conditions, ele gera o número de páginas considerando todos os elementos da base. Eu escrevi o seguinte monkey patch, que coloquei no /lib do projeto:

module ActiveRecord
  module Acts
    module Taggable
      module SingletonMethods
        def paginate_tagged_with(tags, args = {})
          options = find_options_for_find_tagged_with(tags, :match_all => true)
          options.merge!(args)
          paginate(options.merge(:count => { :select => options[:select].gsub("#{table_name}.*", "#{table_name}.id") }))
        end
      end
    end
  end
end

Para usar basta colocar no seu controller algo como:

@posts = Post.paginate_tagged_with(params[:tag_id], :page => params[:page] || 1)

Também escrevi uma spec para testar o funcionamento. Ela só faz sentido dentro do contexto do meu projeto, mas basta você modificar as fixtures, os modelos, etc, de acordo com seu projeto.

describe Post do
  it "should return the correct total_pages to will_paginate" do
    post = posts(:blog_post)
    5.times do
      new_post = BlogPost.new(post.attributes)
      new_post.tag_list = 'global'
      new_post.save
    end
    paginator = BlogPost.paginate_tagged_with('global', :page => 1, :per_page => 2)
    paginator.total_entries.should == 6
    paginator.total_pages.should == 3
  end
end

2 comentários : 21.01.2009 12:09 AM

Syntax Highlight Mephisto plugin

UPDATE: Atualizado localização do repositório svn!

Já havia um tempo que eu queria adicionar syntax highlight nos códigos que escrevo aqui no blog. Resolvi pesquisar se já hávia algum plugin para Mephisto, mas não encontrei nenhum que utilizasse o dp.SyntaxHighlighter.

Utilizei como base o plugin do Dan Webb e fiz as modificações necessárias para utilizar o dp.SyntaxHighlighter. Para instalar:

script/plugin install http://mergulhao.info/svn/mephisto_plugins/filtered_column_better_code_highlighter/trunk/

Agora é só seguir as instruções do README e já poderá fazer isso:

class CreateContas < ActiveRecord::Migration 
  def self.up 
    create_table :contas do |t| 
      t.string :codigo, :limit => 8, :null => false 
      t.integer :parent_id 
      t.integer :lft 
      t.integer :rgt 
      t.timestamps 
    end 
  end 

  def self.down 
    drop_table :contas 
  end 
end

A vantagem de utilizar um syntax highlighter client-side como o dp.SyntaxHighlighter é que o seu código original fica limpo, fácil de editar novamente. Uma possível desvantagem é que pode ficar lento caso tenha muito código para fazer a sintaxe, já que ele usa javascript.

1 comentário : 24.02.2008 12:41 PM