#desenvolvimento, #projetos

No Comments – Extensão pro Chrome


Olá pessoas.

Após muito tempo sem posts resolvi escrever sobre uma extensão para o Chrome que fiz recentemente. Não irei me ater aos detalhes de como criar a extensão porque acredito que uma lida na documentação é suficiente para este fim.

Meu objetivo é discorrer sobre a motivação para criá-la e os recursos que utilizei.

Alguns sabem que a Web 2.0  trouxe a possibilidade dos usuários colaborarem com o conteúdo publicado através da inclusão de comentários, avaliações, personalização e compartilhamento. A ideia é linda mas o fato é que na maioria dos sites os comentários não adicionam nenhum valor ao conteúdo.

Hoje em uma lista de comentários é raro não ter pelo menos um comentário escrito com o intuito de ofender alguém. Nos grandes sites de notícias do nosso lindo país é comum ver centenas de comentários que nos fazem desacreditar na humanidade.  Às vezes a gente nem quer ler mas acaba lendo involuntariamente ao navegar pela página. E neste caso eu concordo plenamente com a frase: “A ignorância é um dom”. E não, eu não estou sendo contra a liberdade de expressão, só acho que não sou obrigada a ler esses lixos certas opiniões.

Partindo daí eu procurei por alguma extensão, pro Chrome, que bloqueasse a visualização dos comentários. Encontrei algumas, testei mas não funcionaram exatamente como eu queria e por isso eu resolvi desenvolvê-la :D.

O objetivo da extensão é bem simples: ocultar todos os comentários de todas as páginas assim que a página for carregada. Como um plus eu adicionei a possibilidade de reverter o processo porque apesar de tudo ainda tem alguns casos que queremos ver os comentários, mesmo que seja só para rir ou passar raiva.

Talk is cheap. Show me the code.

Torvalds, Linus

Então vamos ver como essa tal extensão foi feita, né.

Para que o seu código seja visto como uma extensão você precisa adicionar um arquivo chamado manifest.json que conterá as definições das propriedades.

{
  ...

  "background": {
    // Define qual é o JS principal da extensão.
    "scripts": ["background.js"]
  },

  "icons": {
    // Define o ícone exibido na lista de extensões (chrome://extensions).
    "128": "icon128.png";
  },

  "browser_action": {
    // Define o ícone exibido na omnibox.
    "default_icon": "icon.png"
  },

  "content_scripts": [ {
    // Define quais JS serão utilizados pela extensão.
    "js": [ "jquery-2.0.3.min.js", "functions.js" ],
    // Define para quais sites a extensão será carregada.
    "matches": [ "http://*/*", "https://*/*" ],
    // Define para quais sites a extensão NÃO será carregada.
    "exclude_matches": [ "https://*.github.com/*" ]
  }]

  ...
}

Como definimos o arquivo background.js como JS principal, ele será executado assim que a extensão for carregada.

// Adiciona o ouvinte para o clique no ícone da extensão.
chrome.browserAction.onClicked.addListener(function(tab) {
  // Define qual JS será executado quando receber o clique.
  chrome.tabs.executeScript(tab.id, {file: 'onclick_script.js'});
});

O arquivo onclick_script.js contém apenas uma verificação para identificar se os comentários serão exibidos ou ocultados no momento em que receber o clique.

if (show){
  show = false;
  showComments();
} else {
  show = true;
  hideComments();
}

E a ~~mágica~~ toda está acontecendo no arquivo functions.js. Aqui eu utilizei o jQuery para facilitar a vida juntamente com este sagaz código que seleciona os elementos utilizando expressão regular. A intenção era encontrar todos os elementos que tivessem nos atributos id e class o conteúdo coment(ario) ou comment. Por fim eu adicionei também um critério para buscar pelos comentários que utilizam o Disqus.

function showComments() {
  // Percorre os elementos encontrados exibindo-os.
  jQuery.each(elementsToShowById, function() {
    $(this).show();
  });
}

function hideComments() {

  var disqusExpression = '.*disqus_thread.*';
  var commentExpression = '.*[Cc]o(m|mm)ent.*';

  getVisibleElements('id', commentExpression);
  getVisibleElements('class', commentExpression);
  getVisibleElements('id', disqusExpression);

  // Percorre os elementos encontrados ocultando-os.
  jQuery.each(elementsToShowById, function() {
    $(this).hide();
  });
}

function getVisibleElements(attribute, expression) {
  // Seleciona os elementos pela expressão regular.
  $(':regex(' + attribute + ', ' + expression + ')').filter(function() {
    // Armazena apenas os elementos que estão visíveis.
    if ($(this).is(':visible')){
      elementsToShowById.push($(this));
    }
  });
}

Claro que esta é somente uma das inúmeras maneiras de fazer isto e que pretendo continuar evoluindo a extensão. De pronto já informo que a primeira melhoria a ser implementada é fazer a extensão ocultar os comentários do Youtube que não utiliza a palavra comments para definir seus elementos relacionados aos comentários.

É isso! Fiquem à vontade para instalar, testar e colaborar com a extensão.

Até breve.

PS.: Como eu disse tem casos que convém ler os comentários por isso mesmo que pareça contraditório eu leio e libero todos os comentários do blog, exceto os de spam ;).

Anúncios
Padrão
#desenvolvimento, #projetos

Criando testes unitários com Ruby on Rails


Olá pessoal.

Após ter criado os models precisamos criar testes de unidade para validar as operações que iremos realizar.

Para isso acesse a pasta do seu projeto:

cd projetorails

Execute o comando abaixo para criar todos os bancos incluindo o banco de teste:

rake db:create:all

A saída do comando deve ser algo igual à:

db/production.sqlite3 already exists
db/development.sqlite3 already exists
db/test.sqlite3 already exists

Altere o ambiente de desenvolvimento para ambiente de teste:

rake db:migrate RAILS_ENV=test

Remova todo o conteúdo do arquivo test/fixtures/contatos.yml e adicione o conteúdo que será utilizado para gerar os testes:

contato:
   telefone: 11 2222 3333
   celular:  11 9999 8888
   twitter:  monteirobrena
   facebook: monteirobrena
   msn:      brenamonteiro@hotmail.com

Observação: é necessário manter a identação neste arquivo para diferenciar as propriedades do Contato.

Crie o teste unitário que verificará  as operações que você pode fazer com o model Contato:

require 'test/test_helper'
   class ContatoTest < ActiveSupport::TestCase
      fixtures :contatos
      def test_create_contato
         # Cria o contato com o conteúdo especificado no arquivo test/fixtures/contatos.yml.
         contato_brena = Contato.new :telefone => contatos(:contato).telefone,
                                     :celular  => contatos(:contato).celular,
                                     :twitter  => contatos(:contato).twitter,
                                     :facebook => contatos(:contato).facebook,
                                     :msn      => contatos(:contato).msn
   end
   def test_save_contato contato_brena
      # Verifica se consegue salvar o contato.
      assert contato_brena.save
   end
   def test_copy_contato
      # Cria o contato.
      contato_brena = test_create_contato
     # Salva o contato.
      test_save_contato contato_brena
      # Copia o contato pelo ID.
      contato_brena_copia = Contato.find(contato_brena.id)
      # Verifica se os contatos são iguais.
      assert_equal contato_brena, contato_brena_copia
   end
   def test_update_contato
      # Cria o contato.
      contato_brena = test_create_contato
      # Modifica o facebook do contato.
      contato_brena.facebook = ""
      # Salva o contato.
      test_save_contato contato_brena
   end
   def test_destroy_contato
      # Cria o contato.
      contato_brena = test_create_contato
      # Salva o contato.
      test_save_contato contato_brena
      # Destrói o contato.
      contato_brena.destroy
   end
end

Para executar o teste rode o comando:

ruby test/unit/contato_test.rb

É importante lembrar que as alterações feitas nos testes não serão visualizadas no banco de dados. O arquivo YAML é utilizado apenas para popular o banco de teste, mas as operações de save e destroy não irão alterar as informações contidas no banco.

Saber isso é fundamental, perdi um tempo tentando entender porque as alterações não eram realizadas no banco e só após a ajuda do Rafael Ponte é que descobri o motivo pelo qual não via alterações no banco. Compartilho o link do gist que esclareceu minhas dúvidas sobre teste unitário.

Lembre-se que esta é apenas uma simples forma de começar a fazer testes nos projetos RoR.

Até breve 😉

Padrão
#desenvolvimento, #projetos

Criando models com Ruby on Rails


Voltando ao Rails gostaria de compartilhar a criação dos models do meu novo projeto pessoal.

O primeiro passo é criar um projeto Rails como o que foi criado aqui.

O contexto utilizado será referente a uma publicação de um anúncio bem simplificado. E para isso será necessária a criação de três models: Usuário, Contato e Anúncio com as associações apresentadas na figura abaixo.

Aluguel Particular - Diagrama de classe

Aluguel Particular – Diagrama de classe

Entre na pasta do seu projeto Rails:

cd projetorails

Crie o model do Usuário:

rails g model usuario

O Rails criou uma migração inicial para o nosso model. Esta migração encontra-se na pasta db/migrate/<horário_da_criação>_create_usuarios.rb. Abra a migração criada para o model e adicione as colunas:

class CreateUsuarios < ActiveRecord::Migration
   def self.up
      create_table :usuarios do |t|
         t.string  :email, :limit => 50
         t.string  :senha, :limit => 20
         t.string  :nome,  :limit => 50
         t.string  :CPF,   :limit => 14
         t.timestamps
      end
   end

   def self.down
      drop_table :usuarios
   end
end

No terminal execute o comando abaixo para rodar a migração que criará a tabela de usuários:

rake db:migrate

Crie o model de Contato:

rails g model contato

Altere a migração db/migrate<horário_da_criação>_create_contatos.rb:

class CreateContatos < ActiveRecord::Migration
   def self.up
      create_table :contatos do |t|
        t.string :telefone, :limit => 20
        t.string :celular,  :limit => 20
        t.string :twitter,  :limit => 50
        t.string :facebook, :limit => 50
        t.string :msn,      :limit => 50
        t.timestamps
      end
   end

   def self.down
      drop_table :contatos
   end
end

No terminal execute o comando abaixo para rodar a migração que criará a tabela de contatos:

rake db:migrate

Crie o model para Anúncio:

rails g model anuncio

Altere a migração db/migrate<horário_da_criação>_create_anuncios.rb:

class CreateAnuncios < ActiveRecord::Migration
   def self.up
      create_table :anuncios do |t|
         t.text :descricao, :limit => 2048
         t.timestamps
      end
   end

   def self.down
      drop_table :anuncios
   end
end

No terminal execute o comando abaixo para rodar a migração que criará a tabela de anúncios:

rake db:migrate

Quase pronto! Os models estão criados e você pode visualizar as tabelas através do plugin SQLite Manager para o Firefox. Mas ainda falta acrescentar as associações entre os models e entre as tabelas.

Vamos então associar o contato ao usuário. Edite o model Usuário que se encontra no diretório app/models/usuario.rb:

class Usuario < ActiveRecord::Base
   has_one :contato
end

Agora edite o model Contato (app/models/contato.rb):

class Contato < ActiveRecord::Base
   belongs_to :usuario
end

Esta associação nos diz que um usuário possui um contato. Sendo assim vamos criar uma migração para adicionar este relacionamento na tabela.

rails g migration add_column_contato_usuario

E edite a migração criada em db/migrate/<horário_da_criação>_add_column_contato_usuario.rb:

class AddColumnContatoUsuario < ActiveRecord::Migration
   def self.up
      add_column :usuarios, :contato_id, :integer
   end

   def self.down
      remove_column :usuarios, :contato_id
   end
end

No terminal execute o comando para gerar a migração de atualização da tabela de usuários:

rake db:migrate

E para finalizar vamos associar o anúncio ao usuário.
Edite o model Usuário(app/models/usuario.rb) para indicar que um usuário possui vários anúncios:

class Usuario < ActiveRecord::Base
   has_one  :contato
   has_many :anuncios
end

Edite também o model Anúncio(app/models/anuncio.rb) para afirmar que ele pertence a um usuário:

class Anuncio < ActiveRecord::Base
   belongs_to :usuario
end

Crie a migração que irá adicionar o id do usuário ao anúncio:

rails g migration add_column_usuario_anuncio

Edite a migração db/migrate/<horário_da_criação>_add_column_usuario_anuncio.rb:

class AddColumnUsuarioAnuncio < ActiveRecord::Migration
   def self.up
      add_column :anuncios, :usuario_id, :integer
   end

   def self.down
      remove_column :anuncios, :usuario_id
   end
end

No terminal execute o comando para gerar a migração de atualização da tabela de anúncios:

rake db:migrate

Agora sim os models estão prontos para criarmos os testes e as validações, mas isto será assunto para o próximo post.

Para entender melhor o funcionamento dos models e suas associações não deixe de ler a apostila RR-71 da Caelum e a documentação da API ActiveRecord.

Até breve 😉

Padrão