Unix and Me

About unix, web programing and me

Blocos pending com Rspec

O Rspec tem diferentes formas de colocar um bloco de código como pending, minhas formas de uso são:

  • Como TODO;
  • Colocar teste que quebra sem ainda saber o motivo como pendente;
  • Marcar o teste que quebra como certo, porém a interface não "preparada";

Como TODO

Vamos supor que está criando uma lib de criptografia (bem simples), como gosto de fazer é desenvolver pelo teste, normalmente, eu coloco todos os casos de uso que lembro, depois implemento cada teste e por fim o código:

require './lib/crypt'

describe Crypt do
  it 'encrypt'

  it 'decrypt'
end

O seu TODO pode ser visto no próprio rspec:

% rspec spec/crypt_spec.rb
**

Pending:
  Crypt encrypt
    # Not yet implemented
    # ./spec/crypt_spec.rb:4
  Crypt decrypt
    # Not yet implemented
    # ./spec/crypt_spec.rb:6

Finished in 0.0004 seconds
2 examples, 0 failures, 2 pending

Quando o teste está quebrando

Há vários motivos para um teste quebrar: pode ter mudado algo na interface/código, uma gem que você usa está quebrando, …

Mas e se seu teste estiver correto? Uma gem de terceiros pode estar quebrando, mas sua implementação de uso dela pode estar correta.

Basta colocar um pending no começo do teste, opcionalmente com um motivo.

Imagine que temos esse teste:

require './lib/crypt'

describe Crypt do
  it 'encrypt' do
    subject = Crypt.new('abc')

    expect(subject.encrypt).to eq 'bcd'
  end

  it 'decrypt'
end

Rodar ele vai resultar em:

% rspec spec/crypt_spec.rb
F*

Pending:
  Crypt decrypt
    # Not yet implemented
    # ./spec/crypt_spec.rb:10

Failures:

  1) Crypt encrypt
     Failure/Error: expect(subject.encrypt).to eq 'bcd'

       expected: "bcd"
            got: "zzz"

       (compared using ==)
     # ./spec/crypt_spec.rb:7:in `block (2 levels) in '

Finished in 0.00104 seconds
2 examples, 1 failure, 1 pending

Failed examples:

rspec ./spec/crypt_spec.rb:4 # Crypt encrypt

Vai falhar, mas e se colocar o pending com um bloco (o bloco e o motivo são opcionais) ele vai ser marcado como pendente:

require './lib/crypt'

describe Crypt do
  it 'encrypt' do
    pending "waiting to work right" do
      subject = Crypt.new('abc')

      expect(subject.encrypt).to eq 'bcd'
    end
  end

  it 'decrypt'
end

E executando o teste:

% rspec spec/crypt_spec.rb
**

Pending:
  Crypt encrypt
    # waiting to work right
    # ./spec/crypt_spec.rb:4
  Crypt decrypt
    # Not yet implemented
    # ./spec/crypt_spec.rb:12

Finished in 0.00107 seconds
2 examples, 0 failures, 2 pending

Se não mudarmos nada no teste, mas corrigir o código ele vai exibir a seguinte saída:

% rspec spec/crypt_spec.rb
F*

Pending:
  Crypt decrypt
    # Not yet implemented
    # ./spec/crypt_spec.rb:12

Failures:

  1) Crypt encrypt FIXED
     Expected pending 'waiting to work right' to fail. No Error was raised.
     # ./spec/crypt_spec.rb:5:in `block (2 levels) in '

Finished in 0.00102 seconds
2 examples, 1 failure, 1 pending

Failed examples:

rspec ./spec/crypt_spec.rb:4 # Crypt encrypt

Note o FIXED.

Basicamente: "Você esperava que esse teste estivesse quebrando, MAS foi corrigido". Agora pode-se remover o pending.

Usando o xit

Outra forma de desativar o teste é usando o xit ao invés de it. Ele tem o mesmo efeito que usar o pending (sem bloco), mas sua saída é um pouco diferente.

No nosso exemplo:

require './lib/crypt'

describe Crypt do
  it 'encrypt' do
    pending
  end

  xit 'decrypt'
end

Executando:

% rspec spec/crypt_spec.rb
**

Pending:
  Crypt encrypt
    # No reason given
    # ./spec/todo_spec.rb:4
  Crypt decrypt
    # Temporarily disabled with xit
    # ./spec/todo_spec.rb:8

Finished in 0.00049 seconds
2 examples, 0 failures, 2 pending

No reason given é para o pending (sem bloco e sem motivo) e o Temporarily disabled with xit é para o uso do xit.

Existem muitas formas de desativar blocos de teste, poderiamos até mesmo comentar! Mas quando você entrega seu código para outro desenvolvedor é bom manter o mais claro possível o motivo de um teste estar pendente.

tmux - Introdução

O Tmux é um "multiplexador de terminal", um software que nos permite de um ambiente único iniciar vários outros terminais, cada um rodando seu próprio processo.

Além de abrir programas simultaneamente o tmux também permite que a janela seja dividida na horizontal ou vertical, i. e., pode rodar dois programas simultamente na mesma janela, lado a lado.

Mas se você já faz isso no iTerm2, por exemplo, por que fazer usando tmux?

A resposta, para mim, seria: padronização. Imagine-se configurando um servidor remoto, e no "meio" disso você tenta fazer o "split" usando o que já está acostumado? Certamente funcionaria, mas teria que ser feito outra conexão ssh.

E uma terceira característica do tmux é a capacidade de anexar ou sair de uma sessão sem que ela seja fechada.

Este post é introdutório, então vamos ver o básico de criar "janelas" e sessões.

Instalando

Certifique-se de que tem o tmux instalado:

% tmux -V
tmux 1.6

Caso não tenha use no ubuntu o apt-get para instalar:

% sudo apt-get install tmux

E no mac, o homebrew:

% brew install tmux

Para começar vamos abrir o tmux:

% tmux

Caso queira sair basta apenas encerrar o terminal, i. e., usando o comando exit do terminal.

Janelas

Com o tmux aberto execute algum comando e crie uma nova janela com as combinações control + b, c, ou seja, tecle simultameamente control e b, solte-os e tecle c.

Com a nova janela aberta é possível mudar de uma para outra com o control + b, n ou control + b, p.

Para sair nesse caso é preciso sair de todas as janelas ou "matar" o tmux.

Sessões

Cada vez que se executa o comando tmux ele abre uma nova sessão.

Para ver as sessões abertas use o tmux ls:

% tmux ls
0: 3 windows (created Wed Dec 19 08:53:28 2012) [97x28]
1: 1 windows (created Wed Dec 19 11:15:37 2012) [80x22] (attached)

Para entrar numa sessão basta usar o comando attach e opcionalmente o parâmetro -t para escolher qual sessão deseja "se anexar" (nesse caso temos a sessão 0 e 1):

% tmux attach

Exemplo prático:

Abra uma nova seção no tmux e abra o vim (ou execute qualquer outro comando):

% vim ~/editando-no-tmux.txt

Insira algum conteúdo ou simplesmente deixe-o aberto, agora vamos sair do tmux e deixar tudo aberto usando control + b, d, se quiser pode até fechar o terminal e/ou fazer logout.

Usando o tmux ls você verá que tem uma sessão aberta, vamos entrar nela novamente e constatar que o conteúdo é o mesmo que deixamos.

Agora para entender melhor isso abra outro terminal deixando lado a lado com o primeiro, e digite novamente o tmux attach, começe a fazer mudanças em qualquer um para ver o outro "se atualizando".

pry no Terminal

Pry Logo

Primeiro instale a gem:

% gem install pry

Se você usa o rvm com bundler pode colocar no gemset global.

A wiki do pry tem vários exemplos de como usar com rails, eu particularmente gostei mais da forma "sem colocar no Gemfile" (https://github.com/pry/pry/wiki/Setting-up-Rails-or-Heroku-to-use-Pry).

Como a wiki descreve: ele vai iniciar o pry fazendo require do config/environment.rb da sua aplicação, se o arquivo existir, caso contrário vai iniciar normalmente.

Também é sugerido que instale a gem pry-rails

No meu caso eu usei ele um pouco diferente:

config_environment = File.join Dir.getwd, 'config', 'environment.rb'

if File.exist?(config_environment) && ENV['SKIP_RAILS'].nil?
  require config_environment

  if Rails.version[0..0] == '3'
    require 'rails/console/app'
    require 'rails/console/helpers'

    extend Rails::ConsoleMethods
  else
    warn "[WARN] cannot load Rails console commands (Not on Rails 3?)"
  end
end

require "/home/dmitry/Developer/awesome_print/init"
  • Tirei suporte a doc;
  • Renomeei a var rails para config_environment (que, para mim, faz mais sentido);
  • Tirei o suporte a rails 2 (faz muito tempo que não uso, então seria apenas "deixar teias de aranha" no arquivo :P);
  • Para carregar a gem awesome_print sem usar o Gemfile tive que fazer um clone local e fazer require direto no init;

Links:

Compressor para o Jekyll

A um bom tempo estou usando Jekyll como "engine" deste blog. Tenho certeza que esta é a melhor engine de blog que já usei, mas o html gerado me incomodava um pouco.

O html gerado continha muitas linhas em branco, algumas vezes são linhas de checagem (if, else, case) usados pelos arquivos em _layouts ou _include, semelhante ao que já vi em código gerado pelo erb.

E como vez por outra gosto de "rever meus conceitos" olhando novamente a lista de plugins do jekyll eis que encontro um "compressor".

Para usá-lo é bastante simples: salvar o arquivo deste gist https://gist.github.com/2758691 para _plugins/compressor.rb:

% wget --no-check-certificate -O _plugins/compressor.rb https://gist.github.com/raw/2758691/5f49511b09887878103d827660ae3d7fce3d1773/compressor.rb

Agora apenas executar o comando para gerar novamente o blog.

Observações

No meu caso precisei desativar o uso do pack, pois não tenho interesse em comprimir os javascripts.

E uma "boa prática" quanto ao conteúdo do _layouts/_include.

Ao invés de fazer algo como:

{% for category in post.categories %}
  {{ category }}
{% endfor %}

Tive que mudar para:

{{ post.categories | category_links }}

Na primeira forma as tags a geradas eram uma por linha, porém o compressor unia na mesma linha, o que por sua vez fazia com que o navegador exibisse todos os links juntos.

Reinstalar VirtualBox Guest Additions Usando Vagrant

Usando uma máquina virtual ubuntu percebi que ao fazer uma atualização completa do sistema e reiniciar a máquina o seguinte erro passa a surgir na tela:

test-vm% vagrant up
[default] VM already created. Booting if it's not already running...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- v-root: /vagrant
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` v-root /vagrant

Para corrigir isso basta reinstalar VirtualBox Additions. O jeito mais fácil, como o @esmerinocosta comentou é entrar na máquina virtual e executar:

vagrant@precise64:~$ sudo /etc/init.d/vboxadd setup

Mais se tiver atualizado o VirtualBox ou tiver pego uma box antiga a gem vagrant-vbguest automatizar total ou parcialmente o processo.

Usando vagrant-vbguest:

Caso você tenha uma conexão ruim ou queira baixar a iso do VBoxGuestAdditions apenas uma vez:

Primeiro adicione essa linha no Vagrantfile:

Vagrant::Config.run do |config|
  config.vbguest.auto_update = false
end

Isso vai impedir que o tudo seja feito de forma automatizada.

Baixe a iso, no meu caso eu baixei assim:

test-vm% wget -c http://download.virtualbox.org/virtualbox/4.1.14/VBoxGuestAdditions_4.1.14.iso

Instalar a gem vagrant-vbguest:

test-vm% gem install vagrant-vbguest

Com a ISO baixada e a gem instalada basta rodar este commando:

test-vm% vagrant vbguest -f --iso ./VBoxGuestAdditions_4.1.14.iso
[default] Detected Virtualbox Guest Additions 4.1.14 --- OK.
[default] Forcing installation of Virtualbox Guest Additions 4.1.14 - guest's version is 4.1.14
[default] Copy iso file ./VBoxGuestAdditions_4.1.14.iso into the box /tmp/VBoxGuestAdditions.iso
[default] Copy installer file setup_debian.sh into the box /tmp/install_vbguest.sh
stdin: is not a tty
Reading package lists...
Building dependency tree...
Reading state information...
linux-headers-3.2.0-24-generic is already the newest version.
linux-headers-3.2.0-24-generic set to manually installed.
The following extra packages will be installed:
  fakeroot make patch
Suggested packages:
  make-doc diffutils-doc
The following NEW packages will be installed:
  dkms fakeroot make patch
dpkg-preconfigure: unable to re-open stdin: No such file or directory
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/359 kB of archives.
After this operation, 1,219 kB of additional disk space will be used.
Selecting previously unselected package make.
(Reading database ... 55209 files and directories currently installed.)
Unpacking make (from .../make_3.81-8.1ubuntu1_amd64.deb) ...
Selecting previously unselected package patch.
Unpacking patch (from .../patch_2.6.1-3_amd64.deb) ...
Selecting previously unselected package dkms.
Unpacking dkms (from .../dkms_2.2.0.3-1ubuntu3_all.deb) ...
Selecting previously unselected package fakeroot.
Unpacking fakeroot (from .../fakeroot_1.18.2-1_amd64.deb) ...
Processing triggers for man-db ...
Setting up make (3.81-8.1ubuntu1) ...
Setting up patch (2.6.1-3) ...
Setting up dkms (2.2.0.3-1ubuntu3) ...
Setting up fakeroot (1.18.2-1) ...
update-alternatives: using /usr/bin/fakeroot-sysv to provide /usr/bin/fakeroot (fakeroot) in auto mode.
mount: warning: /mnt seems to be mounted read-only.
Verifying archive integrity... All good.
Uncompressing VirtualBox 4.1.14 Guest Additions for Linux.........
VirtualBox Guest Additions installer
Removing installed version 4.1.14 of VirtualBox Guest Additions...
Removing existing VirtualBox DKMS kernel modules ...done.
Removing existing VirtualBox non-DKMS kernel modules ...done.
Building the VirtualBox Guest Additions kernel modules ...done.
Doing non-kernel setup of the Guest Additions ...done.
Starting the VirtualBox Guest Additions ...done.
Installing the Window System drivers ...fail!
(Could not find the X.Org or XFree86 Window System.)

E quando a máquina for iniciar novamente:

test-vm% vagrant up
[default] VM already created. Booting if it's not already running...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Detected Virtualbox Guest Additions 4.1.14 --- OK.
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- v-root: /vagrant

O erro não surge mais e ele monta o diretório em /vagrant.

A saída abaixo foi capturada usando a forma totalmente automatizada (sem o config.vbguest.auto_update = false no Vagrantfile):

% vagrant up
[default] Importing base box 'precise64'...
[default] The guest additions on this VM do not match the install version of
VirtualBox! This may cause things such as forwarded ports, shared
folders, and more to not work properly. If any of those things fail on
this machine, please update the guest additions and repackage the
box.

Guest Additions Version: 4.1.14
VirtualBox Version: 4.2.4
[default] Matching MAC address for NAT networking...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Installing Virtualbox Guest Additions 4.2.4 - guest's version is 4.1.14
[default] Copy iso file /usr/share/virtualbox/VBoxGuestAdditions.iso into the box /tmp/VBoxGuestAdditions.iso
[default] Copy installer file setup_debian.sh into the box /tmp/install_vbguest.sh
stdin: is not a tty
stdin: is not a tty
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  fakeroot linux-headers-3.2.0-23 make patch
Suggested packages:
  make-doc diffutils-doc
The following NEW packages will be installed:
  dkms fakeroot linux-headers-3.2.0-23 linux-headers-3.2.0-23-generic make
  patch
0 upgraded, 6 newly installed, 0 to remove and 10 not upgraded.
Need to get 12.7 MB of archives.
After this operation, 68.5 MB of additional disk space will be used.
Get:1 http://us.archive.ubuntu.com/ubuntu/ precise/main make amd64 3.81-8.1ubuntu1 [118 kB]
Get:2 http://us.archive.ubuntu.com/ubuntu/ precise/main patch amd64 2.6.1-3 [80.2 kB]
Get:3 http://us.archive.ubuntu.com/ubuntu/ precise/main dkms all 2.2.0.3-1ubuntu3 [73.1 kB]
Get:4 http://us.archive.ubuntu.com/ubuntu/ precise/main fakeroot amd64 1.18.2-1 [87.2 kB]
Get:5 http://us.archive.ubuntu.com/ubuntu/ precise/main linux-headers-3.2.0-23 all 3.2.0-23.36 [11.4 MB]
Get:6 http://us.archive.ubuntu.com/ubuntu/ precise/main linux-headers-3.2.0-23-generic amd64 3.2.0-23.36 [947 kB]
dpkg-preconfigure: unable to re-open stdin: No such file or directory
Fetched 12.7 MB in 2min 58s (71.3 kB/s)
Selecting previously unselected package make.
(Reading database ... 51083 files and directories currently installed.)
Unpacking make (from .../make_3.81-8.1ubuntu1_amd64.deb) ...
Selecting previously unselected package patch.
Unpacking patch (from .../patch_2.6.1-3_amd64.deb) ...
Selecting previously unselected package dkms.
Unpacking dkms (from .../dkms_2.2.0.3-1ubuntu3_all.deb) ...
Selecting previously unselected package fakeroot.
Unpacking fakeroot (from .../fakeroot_1.18.2-1_amd64.deb) ...
Selecting previously unselected package linux-headers-3.2.0-23.
Unpacking linux-headers-3.2.0-23 (from .../linux-headers-3.2.0-23_3.2.0-23.36_all.deb) ...
Selecting previously unselected package linux-headers-3.2.0-23-generic.
Unpacking linux-headers-3.2.0-23-generic (from .../linux-headers-3.2.0-23-generic_3.2.0-23.36_amd64.deb) ...
Processing triggers for man-db ...
Setting up make (3.81-8.1ubuntu1) ...
Setting up patch (2.6.1-3) ...
Setting up dkms (2.2.0.3-1ubuntu3) ...
Setting up fakeroot (1.18.2-1) ...
update-alternatives: using /usr/bin/fakeroot-sysv to provide /usr/bin/fakeroot (fakeroot) in auto mode.
Setting up linux-headers-3.2.0-23 (3.2.0-23.36) ...
Setting up linux-headers-3.2.0-23-generic (3.2.0-23.36) ...
Examining /etc/kernel/header_postinst.d.
run-parts: executing /etc/kernel/header_postinst.d/dkms 3.2.0-23-generic /boot/vmlinuz-3.2.0-23-generic
mount: warning: /mnt seems to be mounted read-only.
Verifying archive integrity... All good.
Uncompressing VirtualBox 4.2.4 Guest Additions for Linux..........
VirtualBox Guest Additions installer
Removing installed version 4.1.14 of VirtualBox Guest Additions...
Removing existing VirtualBox DKMS kernel modules ...done.
Removing existing VirtualBox non-DKMS kernel modules ...done.
Building the VirtualBox Guest Additions kernel modules ...done.
Doing non-kernel setup of the Guest Additions ...done.
You should restart your guest to make sure the new modules are actually used

Installing the Window System drivers ...fail!
(Could not find the X.Org or XFree86 Window System.)
[default] Restarting VM to apply changes...
[default] Attempting graceful shutdown of VM...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Detected Virtualbox Guest Additions 4.2.4 --- OK.
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- v-root: /vagrant
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- v-root: /vagrant

Bot IRC para Salvar Mensagens

Usando dos dois últimos posts nós vamos fazer agora com que nosso bot de irc armazene as mensagens.

Primeiro um Gemfile:

source 'http://rubygems.org'

gem 'cinch'
gem 'sqlite3'
gem 'activerecord', :require => 'active_record'

Seguindo o post sobre ActiveRecord sem Rails nos vamos criar os models, as migrations e o nosso arquivo boot.rb.

boot.rb:

require 'rubygems'
require 'bundler'

Bundler.require :default

ActiveRecord::Base.establish_connection :adapter => 'sqlite3',
  :database => 'development.sqlite3'

ActiveRecord::Migrator.migrate('./migrations') require './quote'

quote.rb

class Quote < ActiveRecord::Base
end

migrations/01_create_quotes.rb

class CreateQuote < ActiveRecord::Migration
  def change
    create_table :quotes do |t|
      t.string :added_nick
      t.string :quote
      t.timestamps
    end
  end
end

Agora seguindo o post sobre Bot IRC usando Ruby vamos criar o bot.rb:

require './boot'
require 'cinch'

DEFAULT_CHANNEL = '#gurupi'

bot = Cinch::Bot.new do
  configure do |c|
    c.server = 'irc.freenode.net'
    c.channels = [DEFAULT_CHANNEL]
  end

  on :message, /^!add (.+)/ do |m, msg|
    Quote.create :added_nick => m.user.nick, :quote => msg
    m.reply 'Quote adicionado com sucesso.'
  end

  on :message, /^!quote (.+)/ do |m, nick|
    ch = Channel DEFAULT_CHANNEL
    quotes = Quote.where('quote like ?', "%#{nick}%").to_a
    ch.send quotes[rand(quotes.count)].quote
  end

  on :message, '!count' do |m|
    m.reply "Quote: #{Quote.count}"
  end
end

bot.start

Agora é só iniciar o bot.rb para que ele entre no canal.

Como de costume o código do post está em https://github.com/dmitrynix/bot-irc-and-active-record.

ActiveRecord sem Rails

Uma boa ferramenta do Rails é o ActiveRecord, sem dúvida ela pode ajudar e muito, seja para o rails, para sinatra ou apenas para algum script simples.

Neste post nós vamos tentar usar o ActiveRecord apenas com um script bem simples de Ruby.

Como de costume um Gemfile:

source 'http://rubygems.org'

gem 'sqlite3'
gem 'activerecord', :require => 'active_record'

Vamos criar nosso model de mensagem no arquivo message.rb:

class Message < ActiveRecord::Base
end

Nosso model precisa da base de dados, então vamos escrever uma migration, para isso coloque o seguinte conteúdo no arquivo migrations/01_create_messages.rb:

class CreateMessages < ActiveRecord::Migration
  def change
    create_table :messages do |t|
      t.string :text
      t.timestamps
    end
  end
end

Agora vamos criar um arquivo boot.rb que vai se encarregar de criar a conexão com a base de dados, de fazer todas as migrações e também de carregar todos os models:

require 'rubygems'
require 'bundler'

Bundler.require :default

ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :database => 'development.sqlite3'
)

ActiveRecord::Migrator.migrate('./migrations')

require './message'

Nos podemos testar isso tudo de várias formas, uma delas é usando o irb como "console":

% irb -r ./boot.rb
==  CreateMessages: migrating
=================================================
-- create_table(:messages)
   -> 0.0014s
==  CreateMessages: migrated (0.0016s)
========================================

1.9.3p0 :001 > Message.create :text => 'Teste 01'
 => #
1.9.3p0 :002 > Message.create :text => 'Outro Teste'
 => #
1.9.3p0 :003 > Message.count
 => 2
1.9.3p0 :004 > Message.find(1).text
 => "Teste 01"
1.9.3p0 :005 >

Note acima que logo que o arquivo boot.rb foi carregado foi feita uma migração da base de dados, isso é exatamente o que esperamos que aconteça.

Agora vamos remover alguma coluna para testar novamente as migrations, crie o arquivo migrations/02_remove_messages_updated_at.rb:

class RemoveMessagesUpdatedAt < ActiveRecord::Migration
  def change
    remove_column :messages, :updated_at
  end
end

Mais uma vez no console:

% irb -r ./boot.rb
==  RemoveMessagesUpdatedAt: migrating
========================================
-- remove_column(:messages, :updated_at)
   -> 0.0224s
==  RemoveMessagesUpdatedAt: migrated (0.0225s)
===============================

1.9.3p0 :001 > Message.first
 => #
1.9.3p0 :002 > Message.first.attributes
 => {"id"=>1, "text"=>"Teste 01", "created_at"=>2012-02-19 23:09:33
-0300}

O código está disponível no github em: https://github.com/dmitrynix/ruby-active-record.

Bot IRC usando Ruby

Voltei a usar IRC depois de um tempo e como antes eu já ouvi falar muito de bots em irc resolvi testar por mim mesmo.

Após pesquisar um pouco achei o cinch:

The IRC Bot Building Framework

Ou seja: "Um framework de criação de bot IRC".

Mãos a obra

Primeiro instale a gem 'cinch' ou como eu prefiro cria um arquivo Gemfile:

source 'http://rubygems.org'

gem 'cinch'

No nosso primeiro exemplo, vamo fazer o bot responder bom dia para todos que derem bom dia:

require 'rubygems'
require 'bundler/setup'

bot = Cinch::Bot.new do
  configure do |c|
    c.server = 'irc.freenode.net'
    c.channels = ['#gurupi']
  end

  on :message, /bom dia/i do |m|
    m.reply "Bom dia, #{m.user.nick}"
  end
end

bot.start

Pronto nosso bot já está ativo, basta usar.

Note que estou usando expressão regular, ou seja, bom dia, Bom dia e Bom dia!, vão funcionar sem problemas ;).

Estou tendo mais ideias para posts sobre IRC, fiquem ligados ;)

E por falar em ligados entrem na irc.freenode.net na sala ##gurupi (com dois #).

Ordenar Array em Javascript

Recentemente tive um problema ao ordenar array em javascript, na verdade o array era ordenado, porém não da "forma padrão" desta forma quando eu rodava no firefox (entenda: teste de integração com capybara) e com o webkit (entenda: teste de integração usando o capybara-webkit) retornava valores diferentes.

Vou usar o nodejs como console.

Vamos ao primeiro exemplo:

> var nomes = ['Bob', 'Jonh', 'Alice'];
undefined
> nomes.sort();
[ 'Alice', 'Bob', 'Jonh' ]

Ordenação normal, mas agora com números:

> var numeros = [ 20, 2010, 300 ];
undefined
> numeros.sort();
[ 20, 2010, 300 ]

Ops… erro. Tenta ordenar como string ao invés de número.

Para "remediar" podemos passar uma função para a função sort. Essa função espera um retorno do tipo -1, 0 ou 1:

  • -1 (ou menor que zero) se o primeiro argumento for menor que o segundo;
  • 0 se os dois forem iguais;
  • 1 (ou maior que zero) se o segundo argumento for maior que o primeiro;

Uma solução bem verbosa é esta:

> numeros.sort(function(x, y){ return((x > y) ? -1 : ( x > y ? 1 : 0) ) });
[ 20, 300, 2010 ]

Uma solução "menor" é esta:

> numeros.sort(function(x, y) { return( x - y); });

Basicamente na subtração nos podemos ter os seguintes valores:

  • quando o x é menor que o y retorna um número negativo;
  • quando x é igual a y retorna zero;
  • e quando x é maior que y retorna um número positivo;

Meu caso era um pouco mais complicado, pois eu queria ordenar um array semelhante a este:

> var arr = [[, 'ponto'] , [2, 'ponto e virgua'], [1, 'barra']];

Ou seja: um array de array para ordenar pelo primeiro elemento (podendo ainda este ser vazio).

A solução:

> arr.sort(function(x, y){ if(!x[0]) { return(1) } else { return (x[0] - y[0]) } });
[ [ 1, 'barra' ],
  [ 2, 'ponto e virgua' ],
  [ , 'ponto' ] ]

Sim o "vazio" fica por último, essa é a regra.

Fonte: Sorting a JavaScript array using array.sort()

Eu Sou Um Fracasso

Eu devia este post a uns 2-3 meses, mas como aconteceu muito neste tempo e deixei outras jogadas ao relento esse é um bom momento para voltar a escrever isto.

Comecei a ser um fracasso na empresa que queria "ver crescer". Até criei um post com a lista do que se evitar o fracasso, mas não deu, o que posso citar como erro é:

Supus que TODOS os "homens de tecnologia" pensassem semelhante a mim.

Só isto, sem apontar o dedo para ninguém. Quem me conhece sabe que não conseguiria listar defeitos, não assim citando alguém para todos.

Meu segundo fracasso neste período foi tentando andar sozinho.

Trabalhei, não terminei o projeto (ainda está em andamento, mas depois atualizo aqui) e ainda por cima sem receber o pagamento dos meus trabalhos (para os curiosos é cerca de 1-2 meses de atraso).

Não sei me impor (estou aprendendo x), não sei negociar, não sei dizer não.

O que resta? Resta um imenso #EpicFail para mim e só.

Estava procurando um motivo de este post não ser um fracasso, e encontrei, o que me alegra:

Saia da teoria e pratique mais.

É extremamente fácil abrir uma empresa, muito fácil ser freela, mas eu falhei nestas duas áreas, o motivo é simples: sei muito bem a teoria, mas na prática.

Para não dizer que eu sou um completo fracasso eu estou tentando uma outra forma de "viver".