Posts Tagged 'Programação'



Ferramenta em Rails para criação de blogs

Post movido para: http://blog.guilhermegarnier.com/2008/10/31/ferramenta-em-rails-para-criacao-de-blogs/

Muitos tutoriais de Rails para iniciantes, para demonstrarem na prática como é rápido e simples criar aplicações web com ele, explicam como criar um blog em 15 minutos, baseados no screencast do David Heinemeier Hansson, criador do Rails, para o FISL 6.0. Apesar de ser uma aplicação funcional de blog, com opção para criar posts e adicionar comentários, é bastante limitado.

O Typo é uma aplicação desenvolvida em Rails para criação de blogs bastante completa (inclusive encontrei este blog criado com ele). Ele lembra muito o WordPress, acredito que tenha sido inspirado nele. A interface de administração é muito semelhante à do WordPress, apresentando basicamente as mesmas opções: criar posts e páginas, ver/aprovar/rejeitar comentários, criar usuários, customizar a barra lateral, etc. Há também uma opção para seleção de temas (outros temas podem ser encontrados aqui) e plugins com diversas funcionalidades, como APIs para Delicious, Flickr, Twitter e outros. Só senti falta das opções de estatísticas de acesso que o WordPress oferece.

A instalação pode ser feita através do comando gem (gem install typo), porém, tentei e não consegui instalar desta forma. Instalei a ferramenta pela versão tgz. Neste caso, basta descompactar a aplicação, copiar o arquivo database.yml.example, no diretório config, para database.yml, editá-lo conforme a configuração do banco de dados, e em seguida executar rake db:create para criar a estrutura do banco de dados. Caso você queira ser avisado por email quando receber comentários nos posts, copie também o arquivo config/mail.yml.example para mail.yml no mesmo diretório, e edite as configurações de SMTP.

O Typo também possui um servidor de feeds RSS/Atom. Porém, na versão atual (5.1.3), recebi uma mensagem de erro ao tentar acessar os feeds. Encontrei o erro no arquivo app/models/article.rb e corrigi substituindo o método link_to_author? (linhas 384 a 386) pelo seguinte:

def link_to_author?
  begin
    !user.email.blank? && blog.link_to_author
  rescue NoMethodError
    return false
  end
end
Anúncios

Rails sem banco de dados

Post movido para: http://blog.guilhermegarnier.com/2008/06/17/rails-sem-banco-de-dados/

O Active Record é um dos principais componentes do Rails, pois é exatamente o model do MVC. Ele mapeia automaticamente uma classe numa tabela do banco de dados, criando métodos para acesso a cada atributo. Porém, e se quisermos desenvolver uma aplicação sem banco de dados?

Passei por esta situação recentemente: a aplicação acessa um servidor diretamente, via Atom, e, portanto, não precisa armazenar dados localmente. Porém, não basta comentar todas as linhas do arquivo config/database.yml, pois o Rails mostrará uma mensagem de erro informando que não encontrou o banco de dados correspondente.

Para resolver este problema, resolvi, inicialmente, tentar não utilizar o Active Record. Para isso, primeiramente precisei modificar o model que eu tinha na aplicação, retirando a herança de ActiveRecord::Base. Porém, ao tentar executar o servidor, recebi mensagens de erro informando que o banco de dados não foi encontrado. Isso ocorre porque, apesar de não haver qualquer classe herdando de ActiveRecord::Base no projeto, este módulo estava sendo carregado. Conseqüentemente, o Rails tentava ler o arquivo config/database.yml, que não estava configurado, resultando na mensagem de erro. Para evitar este problema, foi necessário evitar o carregamento do ActiveRecord explicitamente, acrescentando a linha abaixo ao arquivo config/environment.rb:

config.frameworks -= [ :active_record ]

Feito isto, o Rails passa a funcionar sem banco de dados e sem ActiveRecord. Porém, surgiu um outro problema: os testes unitários do Rails pararam de funcionar, pois a classe ActiveSupport::TestCase, que é a classe base para os testes unitários, não funciona sem ActiveRecord. Para resolver, troquei a herança desta classe para Test::Unit::TestCase, que era utilizada como padrão antes da versão 2 do Rails. Não sei exatamente quais são as diferenças entre as duas, mas só consegui resolver este problema desta forma – se alguém descobrir alguma outra solução, me avise!

Também precisei carregar explicitamente o model no arquivo de teste, pois o Rails deixou de fazer a referência automática a esta classe. Feitas as alterações, o arquivo de teste (ex: test/unit/usuario_test.rb) ficou assim:

require 'test/test_helper'
require 'test/unit'
require 'app/models/usuario'

class UsuarioTest < Test::Unit::TestCase
  # Replace this with your real tests.
  def test_truth
    assert true
  end
end

IDEs para Ruby on Rails

Post movido para: http://blog.guilhermegarnier.com/2008/05/27/ides-para-ruby-on-rails/

O uso de IDEs para desenvolvimento em Rails é um assunto bastante controverso. Devido às diversas facilidades no desenvolvimento com este framework, muitas pessoas afirmam que uma IDE não é necessária, basta um editor de textos simples e um terminal. Outros acreditam que, apesar do alto grau de automação do Rails, ainda é vantajoso usar uma IDE. No meio desta discussão, acredito que, no caso do Ruby on Rails, a escolha torna-se um gosto pessoal.

Para os que estão no primeiro caso, a escolha do editor de texto também é uma questão de gosto pessoal. Alguns se sentem confortáveis com o vi, outros com o Emacs. Estes casos são mais comuns quando já há uma intimidade com estes editores – dificilmente alguém vai aprender a usar Emacs para desenvolver em Rails. Para quem prefere um editor mais amigável, há opções para todos os sistemas operacionais: no Linux, o Gedit, quando bem configurado com alguns dos diversos plugins disponíveis, torna-se uma poderosa ferramenta de desenvolvimento, com code completion, por exemplo. No Mac há o TextMate e no Windows o SciTe (também disponível para Linux). Para tarefas como rodar o servidor web, executar migrations e generates, debugar a aplicação ou trabalhar com uma ferramenta de controle de versão (CVS, SVN, GIT, etc), usa-se o terminal.

Para quem prefere usar uma IDE, a principal vantagem é não precisar recorrer ao terminal para executar as tarefas acima. Tudo é integrado, facilitando o trabalho. Realmente algumas dessas tarefas são extremamente simples, como executar migrations. Nestes casos, pouca diferença há entre usar a IDE ou o terminal. Porém, trabalhar com uma ferramenta de controle de versão e executar um debug por linha de comando pode ser trabalhoso. Acredito que estas sejam as principais vantagens de se usar uma IDE. Por outro lado, as IDEs requerem bastante memória do desktop, ao contrário dos editores de texto, e costumam ser mais instáveis.

As opções de IDE para Rails também são variadas, e as duas principais são o Aptana RadRails e o NetBeans. Nos últimos meses trabalhei com ambos alternadamente, e encontrei diversas vantagens e desvantagens em cada um.

Aptana RadRails

O Aptana RadRails pode ser instalado como um plugin do Eclipse ou isoladamente, caso você não o tenha instalado. Na primeira vez que o testei, há alguns meses, achei o plugin bastante instável, e com alguns bugs incômodos. Recentemente, ao verificar que havia uma nova versão disponível, testei novamente, e verifiquei que esta nova versão está bem melhor. Há alguns meses atrás, escrevi neste post que o RadRails não conseguia ler arquivos database.yml usando o formato descrito. Conforme este comentário do Chris Williams, um dos desenvolvedores do RadRails, a nova versão corrigiu este problema e outros que eu havia encontrado. A versão mais recente também possui um Ruby Shell bastante útil, inclusive com autocomplete de comandos e parâmetros, porém instável – já o vi travar algumas vezes. Outras vantagens do RadRails são:

  • Botões para acesso rápido ao Model, View, Controller, Helper e Test equivalentes ao arquivo ativo
  • Problema do DRY no database.yml corrigido
  • Suporte a testes mais completo (permite executar apenas um arquivo de testes de cada vez)

As principais desvantagens do RadRails são:

  • Problemas no autocomplete (não mostra todos os métodos)
  • O console não aceita a tecla para cima para acessar os últimos comandos, como no shell
  • Ruby shell instável (trava com freqüência)
  • Server não permite selecionar um environment diferente dos 3 defaults (development, test e production), mesmo que você tenha algum outro environment definido no arquivo database.yml

NetBeans

O suporte do NetBeans ao Rails tornou-se estável há mais tempo que o RadRails, com um editor para código Ruby bem completo. Atualmente, ambos diferenciam-se nos detalhes. Principais vantagens do NetBeans:

  • Ao selecionar um texto e digitar (, o editor envolve o texto selecionado com parênteses
  • Ao selecionar um texto e digitar #, o editor envolve o texto selecionado com #{ e }
  • Ao colocar o cursor sobre um end, o editor destaca o inicio desse bloco/método/classe
  • O find é mais fácil de usar (estilo Firefox, com highlight automático dos termos conforme você digita)

Principais desvantagens do NetBeans:

  • Problemas no autocomplete (mostra os métodos que não deveriam aparecer)
  • O console não aceita a tecla para cima para acessar os últimos comandos, como no shell
  • Suporte a SVN bastante limitado
  • A execução do server não permite selecionar o environment (sempre utiliza o development)
  • A opção Test executa todos os testes (não há opção para executar apenas um arquivo, ou apenas os testes unitários, por exemplo)

Concluindo, o editor do NetBeans me parece mais completo para tratamento de código Ruby. Porém, o ambiente do RadRails é mais completo para execução de testes, integração com SVN e outras tarefas. Um ponto onde ambos apresentam problemas é o autocomplete – às vezes aparecem métodos de mais, outras vezes de menos. Porém, devemos reconhecer que, por se tratar de uma linguagem dinâmica, onde uma variável não tem tipo fixo, é bastante complicado termos um autocomplete realmente preciso.

O poder do Ruby

Post movido para: http://blog.guilhermegarnier.com/2008/05/08/o-poder-do-ruby/

Muito se fala das vantagens do Ruby sobre muitas das linguagens atuais, por ser uma linguagem de altíssimo nível. Mas muitas vezes não percebemos grandes diferenças entre as linguagens, além das usuais diferenças de sintaxe – se as linhas de comando precisam de ponto-e-vírgula no final, se as variáveis precisam de $ no início do nome, se estas são tipadas ou não, se precisam ser declaradas ou não, etc. Porém, em algumas situações específicas, enxergamos o verdadeiro poder do Ruby.

Hoje precisei implementar uma paginação de resultados de busca. Eu sei que existem plugins para Rails que simplificam esta tarefa, porém como esta busca não é feita no banco de dados, e sim através de um indexador que já retorna resultados paginados, optamos por fazer manualmente a lista de links para as páginas.

Por exemplo: ao realizar uma busca, são exibidos os 10 primeiros resultados, com um link para a próxima página e a lista de links para cada página. Como o número de páginas, teoricamente, não tem limite, fiz o seguinte:

  • caso o resultado tenha até 10 páginas, todas são exibidas
  • caso o resultado tenha mais de 10 páginas, são exibidas apenas 10, sendo que:
    • caso a página atual seja uma das 6 primeiras, exibe os links para as páginas 1 a 10
    • caso a página atual seja maior que 6, exibe da página atual – 5 até a página atual + 4
    • caso a página atual seja uma das 10 últimas, exibe as 10 últimas

Pela descrição acima, percebemos que é uma lógica bem simples, porém meio chata para ser implementada – na maioria das linguagens atuais, isto exigiria um grande número de if’s aninhados, para verificarmos se as condições descritas acima são atendidas. Porém, em Ruby o código ficou muito simples e enxuto:

if (num_pages > 1)
  page_start = [1, page-5].max
  page_end = [num_pages, page+4].min
  if num_pages > 10
    page_start = [page_start, num_pages-9].min
    page_end = [page_end, 10].max
  end
  page_start.upto(page_end) {|p|
    # Exibe os links
  }
end

Definição de nomes de atributos “humanizados”

Post movido para: http://blog.guilhermegarnier.com/2008/04/17/definicao-de-nomes-de-atributos-humanizados/

A classe ActiveRecord::ConnectionAdapters::Column tem um método human_name que cria uma versão “humanizada” para os nomes das colunas de tabelas (atributos de um model). Porém, nem sempre o nome criado é o que desejamos. Por exemplo, se temos uma coluna num_usuarios, o método human_name retornará “Num Usuarios”, que, provavelmente, não é o que queremos. Para configurar o human_name manualmente, há duas soluções:

1 – criar um hash e redefinir o método human_attribute_name:

class Model < ActiveRecord::Base
  HUMANIZED_ATTRIBUTES = {
    num_usuarios => 'Número de usuários'
  }

  def self.human_attribute_name(attr)
    HUMANIZED_ATTRIBUTES[attr.to_sym] || super
  end
end

2 – usar o plugin human_attribute_override. Esta solução é mais simples e elegante:

class Model < ActiveRecord::Base
  attr_human_name :num_usuarios => 'Número de usuários'
end

Configurações fora do padrão em Rails, parte 2 – Relacionamentos HABTM

Post movido para: http://blog.guilhermegarnier.com/2008/04/10/configuracoes-fora-do-padrao-em-rails-parte-2-relacionamentos-habtm/

Há um mês escrevi um post sobre configurações fora do padrão em Rails, onde descrevi como executar testes com models cujas tabelas não existem no banco de dados local, e sim em uma base externa. Porém, depois de postar, verifiquei que há um outro problema não resolvido com a configuração que descrevi nesse post: relacionamentos HABTM (has and belongs to many).

Nos relacionamentos HABTM, normalmente, há dois models, um correspondente a cada tabela do banco de dados. Como a relação entre eles é de muitos para muitos, há uma terceira tabela no banco de dados, que é responsável pela associação das demais tabelas. Como essa tabela só costuma ter dois campos, que são FK’s correspondentes às PK’s dessas tabelas, ela não precisa ter um model; basta criar o relacionamento dos dois models como has_and_belongs_to_many, passando como parâmetro join_table essa tabela intermediária.

A configuração descrita no post anterior carrega manualmente os fixtures de cada model, porém não carrega fixtures correspondentes à tabela intermediária. Para isso, precisei implementar um novo método na classe Test::Unit::TestCase (arquivo test/test_helper.rb):

def set_habtm_fixtures(class1, class2)
  return unless (class1.reflections && class1.reflections.values)
  id1 = nil
  id2 = nil
  table = nil

  # Verifica qual dos relacionamentos do model class1 está associado à tabela class2
  class1.reflections.values.each do |r|
    # Se a classe associada for class2 e for uma relação HABTM, le os FK's e o nome da tabela
    if (r.klass == class2 && !r.instance_values['options'][:join_table].nil?)
      id1 = r.primary_key_name
      id2 = r.association_foreign_key
      table = r.instance_values['options'][:join_table]
      break
    end
  end
  return if table.nil?
  connection = class1.connection

  data = File.open(File.join(RAILS_ROOT, 'test', 'fixtures', "#{table}.yml")).readlines.join
  result = ERB.new(data).result
  parsed = YAML.load(result)

  # Exclui todos os registros da tabela
  connection.execute "DELETE FROM #{table}"

  parsed.values.each do |value|
    value1 = value[id1] || 'NULL'
    value2 = value[id2] || 'NULL'
    connection.execute "INSERT INTO #{table} (#{id1}, #{id2}) values (#{value1}, #{value2})"
  end
end

Este método ficou bem “feio”, pois, como não existe um model correspondente a esta tabela, precisei criar a query manualmente. O método recebe dois nomes de classes (ActiveRecord) como parâmetro. Primeiramente é verificado qual dos relacionamentos do model class1 está associado a class2, para descobrir quais são as FK’s e o nome da tabela. Em seguida, os registros desta tabela são excluídos, e cada linha do arquivo de fixtures é carregada (usando a conexão de um dos ActiveRecords).

Além disso, modifiquei o método set_fixtures desta mesma classe, criado no post anterior, pois percebi que não era necessário passar o nome da tabela como parâmetro, basta usar o método table_name:

def set_fixtures (class_name)
  table = class_name.table_name
  return unless class_name.kind_of?(ActiveRecord::Base)

  # Define a conexao usada pela classe
  ActiveRecord::Base.connection = base.connection
  Fixtures.create_fixtures(File.join(RAILS_ROOT, 'test', 'fixtures'), table) { base.connection }
end

Para exemplificar como usar estes métodos, imagine um cadastro de usuários com grupos, onde um usuário pode fazer parte de mais de um grupo. Neste exemplo, teríamos um model Usuario (tabela usuarios), um model Grupo (tabela grupos) e uma tabela usuarios_grupos, sem um model correspondente. Na classe de teste do model Usuario, teríamos o seguinte:

class UsuarioTest < ActiveSupport::TestCase
  def setup
    set_fixtures(Usuario)
    set_fixtures(Grupo)
    set_habtm_fixtures(Usuario, Grupo)
  end

  # Testes
end

No método setup, que é executado automaticamente quando os testes são executados, as duas chamadas a set_fixtures carregam as fixtures das tabelas de usuários e grupos, respectivamente; a chamada a set_habtm_fixtures atualiza a tabela usuarios_grupos.

Como criar accessors para atributos de classe

Post movido para: http://blog.guilhermegarnier.com/2008/04/10/como-criar-accessors-para-atributos-de-classe/

Os métodos attr_reader, attr_writer e attr_accessor do Ruby servem para simplificar a criação de setters e getters para atributos de instância. Ex:

class Teste
  @valor = 1
  attr_accessor :valor
end

No código acima, o método attr_accessor já cria o getter e o setter para o atributo valor:

t = Teste.new
t.valor = 10
puts t.valor #=> 10

Porém, como fazer o mesmo para atributos de classe? Eu fiz essa pergunta no forum RubyOnBr. O Shairon Toledo me respondeu com o código do método attr_static_accessor, que é, na prática, o equivalente ao attr_accessor, só que para atributos de classe. Eu complementei o código dele com os métodos attr_static_reader e attr_static_writer:

class Module
  def attr_static_reader(*args)
    args.each do |meth|
      init_var(meth)
      set_reader(meth)
    end
  end

  def attr_static_writer(*args)
    args.each do |meth|
      init_var(meth)
      set_writer(meth)
    end
  end

  def attr_static_accessor(*args)
    args.each do |meth|
      init_var(meth)
      set_reader(meth)
      set_writer(meth)
    end
  end

  private
  def init_var(var_name)
    var = "@@#{var_name}".to_sym
    self.send(:class_variable_set, var, nil) unless self.send(:class_variable_defined?, var)
  end

  def set_reader(var_name)
    self.class.send(:define_method, var_name) {
      self.send(:class_variable_get, "@@#{var_name}".to_sym)
    }
  end

  def set_writer(var_name)
    self.class.class_eval %Q{
      def #{var_name}=(value)
        self.send(:class_variable_set, "@@#{var_name}".to_sym,value)
      end
    }
  end
end

Agora é possível fazer o seguinte:

class Teste
  @@valor = 1
  attr_static_accessor :valor
end

puts Teste.valor #=> 1
Teste.valor = 10
puts Teste.valor #=> 10

@guilhermgarnier

Erro: o Twitter não respondeu. Por favor, aguarde alguns minutos e atualize esta página.

Estatísticas

  • 58.723 hits
Linux Counter
Anúncios