Unix and Me

About unix, web programing and me

Ruby

Vez ou outra aparece alguém perguntando, ou melhor, pedindo "ensina rails?".

Bom a linguagem e framework da moda inspira a muitos e as pessoas creem que vão do dia para noite: fazer o próximo imenso programa que todo mundo vai usar e vai pagar por isso e que vai ficar milionário.

Vou logo avisando, antes de você pegar projeto para fazer com rails estude muito o framerowk e principalmente a linguagem Ruby.

Nos tópicos abaixo explico alguns detalhes, isso só faz parte do ponta pé inicial.

Introdução

Segundo a wikipedia Ruby é originada no Japão durante a década de 90, desenvolvida por Yukihiro "Matz" uma linguagem dinâmica, orientada a objetos com syntax inspirada por Perl com algumas características de Smalltalk.

O "padrão" 1.8.7 foi desenvolvido em C pelo Matz. Não há exatamente um padrão Ruby, além do 1.8.7 existe JRuby, MacRuby e outros padrões da linguagem.

Antes ainda de começar com código gostaria de falar um pouco sobre sua "filosofia": ainda segundo Matz Ruby é desenvolvido com foco na produtividade do programador e não na otimização da máquina ou na "velocidade do código". Pense nisso ao escrever código Ruby ;)

Script ou IRB?

Ruby é uma linguagem de script e desta forma para nosso primeiro script:

``` ruby

#!/usr/bin/env ruby

puts "Script em ruby"
puts "Soma #{1+2}"

```

Salve-os e execute-os assim:

% ruby script.rb

Para nossos teste nos vamos usar mais o IRB que é um shell (ou interpretador) de código, semelhante ao que acontece com shell bash, assim que teclarmos enter a linha escrita será executada:

ruby-1.8.7-p352 :004 > puts "Interpretador Ruby"
Interpretador Ruby
=> nil
ruby-1.8.7-p352 :005 > puts "soma: #{1+1}"
soma: 2
=> nil

Tipos em Ruby

String

Uma string é qualquer valor dentro de um " ou ', dentro de " podem existir variáveis, semelhante a última linha do nosso primeiro script.

ruby-1.8.7-p352 :002 > "string"
=> "string"
ruby-1.8.7-p352 :003 > puts "string"
string
=> nil
ruby-1.8.7-p352 :004 > puts "string#{1+10}"
string11
=> nil
ruby-1.8.7-p352 :005 > puts 'string#{1+10}'
string#{1+10}
=> nil

Integers (Inteiros)

Sobre inteiros:

ruby-1.8.7-p352 :005 > 1+1
=> 2
ruby-1.8.7-p352 :006 > 10/2
=> 5

Além de inteiros você pode tentar usar também os "Floats": 1.0.

Arrays

Arrays são criados usando [] ou %w(), exemplo:

ruby-1.8.7-p352 :007 > ["1", "2"]
=> ["1", "2"]
ruby-1.8.7-p352 :008 > %w(1 2 3 )
=> ["1", "2", "3"]

O mais interessante dos arrays em Ruby é que pode ser uma mistura de diversos tipos:

ruby-1.8.7-p352 :009 > [1, "2"]
=> [1, "2"]

Ao invés do que acontece com outras linguagem que se for um Array de inteiros você só pode colocar inteiros.

Hash

Hash é mais ou menos como Arrays indexados.

Você define um hash assim:

{ :idx => 'valor' }

E seguindo os arrays eles podem ser de diversos tipo, o índice ou o valor:

{ :idx => 'valor', 1 => 'um', 'dois' => 2 }

A nova syntax no ruby19 é definida da seguinte forma:

{ idx: => 'valor' }

E não foge a regra mistura de estilos:

{ um: 1, :dois => 2, 'tres' => 3, 4 => 'quatro' }

Mais um fato interessante dos Hashs é que o índice é na verdade "salvo internamente" como um hash em inteiro:

ruby-1.8.7-p352 :007 > a = { :a => :b, :c => :d }
=> {:c=>:d, :a=>:b}
ruby-1.8.7-p352 :008 > a.keys.map(&:hash)
=> [237308, 335388]
ruby-1.8.7-p352 :009 >

Ou seja, quando você quer recuperar a chave :a o Ruby transforma o :a em hash:

ruby-1.8.7-p352 :010 > :a.hash
=> 335388

Tornando assim a busca mais rápida.

Laços

if else end

if else end funciona da mesma forma como em outras linguagens:

ruby-1.8.7-p352 :011 > a = 1
=> 1
ruby-1.8.7-p352 :012 > if a == 1
ruby-1.8.7-p352 :013?>   puts 'numero um'
ruby-1.8.7-p352 :014?> else
ruby-1.8.7-p352 :015 >   puts 'outro numero'
ruby-1.8.7-p352 :016?> end
numero um
=> nil

O que eu acho mais interessante é que os parenteses é de uso opcional:

if(1 == 1) { puts "ok" }
if 1 == 1 { puts "ok" }

E seguindo os fatos extraordinários:

ruby-1.8.7-p352 :017 > puts 'numero um' if a == 1
numero um
=> nil

Case

Fugindo também o padrão o case aceita diversos tipos como comparação:

ruby-1.8.7-p352 :023 > a = 1
=> 1
ruby-1.8.7-p352 :024 > case a
ruby-1.8.7-p352 :025?>   when 1
ruby-1.8.7-p352 :026?>     puts 'numero 1'
ruby-1.8.7-p352 :027?>   when "1"
ruby-1.8.7-p352 :028?>     puts 'string 1'
ruby-1.8.7-p352 :029?>   else
ruby-1.8.7-p352 :030 >     puts 'nenhum'
ruby-1.8.7-p352 :031?>   end

e:

ruby-1.8.7-p352 :001 > a = '1'
=> "1"
ruby-1.8.7-p352 :002 > case a
ruby-1.8.7-p352 :003?>   when 1
ruby-1.8.7-p352 :004?>     puts 'numero 1'
ruby-1.8.7-p352 :005?>   when '1'
ruby-1.8.7-p352 :006?>     puts 'string 1'
ruby-1.8.7-p352 :007?>   else
ruby-1.8.7-p352 :008 >     puts 'nenhum'
ruby-1.8.7-p352 :009?>   end
string 1
=> nil

While

ruby-1.8.7-p352 :011 > a = 1; while a < 5 ; a += 1 ; puts a; end
2
3
4
5
=> nil

Until

É mais ou menos o "contrário do while":

ruby-1.8.7-p352 :013 > a = 1; until a < 5 ; a += 1 ; puts a; end
=> nil

ou

ruby-1.8.7-p352 :014 > a = 1; until a > 5 ; a += 1 ; puts a; end
2
3
4
5
6
=> nil

x.times?

O x.times é mais um loop "fácil" no rails, ele executa o mesmo que tiver no bloco por x vezes:

ruby-1.8.7-p352 :017 > 3.times { puts 'uma vez' }
uma vez
uma vez
uma vez
=> 3

Ao invés de usar um número fixo, pode-se facilmente usar uma variável:

ruby-1.8.7-p352 :018 > vezes = 5
=> 5
ruby-1.8.7-p352 :019 > vezes.times { puts 'mais vezes' }
mais vezes
mais vezes
mais vezes
mais vezes
mais vezes
=> 5
ruby-1.8.7-p352 :020 > vezes = 2
=> 2
ruby-1.8.7-p352 :021 > vezes.times { puts 'menos vezes' }
menos vezes
menos vezes
=> 2

Each?

O each é um laço que facilmente substitui o laço for (por falar em for eu nem lembro mais como se usa oO):

ruby-1.8.7-p352 :002 > %w(1 2 3).each { |string| puts string }
1
2
3
=> ["1", "2", "3"]

Importando Comentários com o disqus.com

Segundo o disqus.com sobre si mesmo:

Disqus is all about changing the way people think about discussion on the web. We're big believers in the conversations and communities that form on blogs and other sites.

ou

Disqus é como mudar a forma como as pessoas pensam sobre "discussão" na internet. Acreditamos em grandes conversas e nas comunidades que se formam em blogs e outros sites.

Como parte da mudança entre site estático, wordpress, app própria e por fim octopress tenho levado comigo além, claro, dos artigos, também todos os comentários.

Como ferramenta "hacker" o octopress não "permite" comentários, para isso, como é sugerido temos que usar o http://disqus.com/.

Primeiro faça seu cadastro em http://disqus.com/admin/register/.

O disqus permite importar comentárias das ferramentas mais comuns para blogs como wordpress e blogger.

Como eu usava uma app própria não tive outra saída a não ser criar um xml, porém não tive êxito ao fazê-lo, pesquisando mais um pouco achei um projeto para usar a API do disqus, https://github.com/norman/disqus.

Partindo deste projeto e de exemplos cheguei a seguinte tarefa, você só vai precisar adaptar aos seus models e obter sua api_key, neste link http://disqus.com/api/get_my_key/:

namespace :import do
  desc 'Import comments into disqus'
  task :comments => :environment do
    Disqus::defaults[:api_key] = '****** MINHA API KEY ******'
    blog_name = 'nome_do_blog_dentro_do_disqus'


    forums = Disqus::Api.get_forum_list
    forum = forums['message'].find do |forum| forum['shortname'] = blog_name end
    forum_key = (Disqus::Api.get_forum_api_key :forum_id => forum["id"])['message']
    Page.all.each do |page|
      thread_url = "http://exemplosite.com/#{page.slug}/"
      thread_id = Disqus::Api.thread_by_identifier(:forum_api_key => forum_key, :title => page.title, :identifier => thread_url)['message']['thread']['id']
      page.comments.each do |comment|
        post = Disqus::Api.create_post :forum_api_key => forum_key,
          :thread_id => thread_id,
          :message => comment.content,
          :author_name => comment.author,
          :author_email => comment.author_email,
          :parent_post => nil,
          :created_at => Time.at(comment.created_at).strftime('%Y-%m-%dT%H:%M'),
          :author_url => comment.author_url,
          :ip_address => comment.user_ip

        if post['succeeded']
          puts "added a post successfully: #{post['message']['id']}"
          comment.imported = true
          comment.save
        else
          puts "false: #{post.inspect}"
        end
      end
    end
  end
end

Como eu já usava o akismet, a migração foi muito fácil e o melhor: até a data que foi feito o comentário foi importado.

Usando Octopress

Definição

O Octopress, como o próprio site diz é:

Um framework de blog para hackers

É um framework feito usando Jekyll, que por sua vez é um gerador de sites estáticos.

Como para alguns pode ser muito trabalhoso usar o Jekyll, pois é preciso escrever seu próprio template html, css, javascripts e toda a configuração. Mas usando o Octopress bastante disso já é feito, claro que depois que se aprende a usar você faz suas próprias customizações.

Obtendo o Octopress

No meu caso não pensei que iria usar o Octopress então fiz um clone (e não um fork), mas caso você já tenha decidido usá-lo faça um fork. De qualquer forma obtenha o Octopress:

git clone git://github.com/imathis/octopress.git

Setup

Antes de qualquer ação execute o bundle install para instalar as dependências.

O arquivo de configuração é _config.yml. Basicamente só é necessário mudar url, title, subtitle e author e caso queira as configurações do github, twitter, Google Analytics e do disqus:

url: http://blog.dmitrynix.com
title: Unix and Me
subtitle: About unix, web programing and me
author: Dmitry Nix

Agora o rake install vai copiar o tema padrão:

octopress% rake install
## Copying classic theme into ./source and ./sass

E para levantar um preview:

octopress% rake preview

E ficará disponível na porta 4000 da sua máquina: http://localhost:4000.

Blogando

Os arquivos das postagens devem ser armazenadas seguindo este padrão: YYYY-MM-DD-post-title.markdown, dentro de source/_posts.

O próprio Octopress já tem uma tarefa rake para executar esta ação:

rake new_post["Título da Postagem"]

No meu caso ela não está funcionando e eu ainda tenho que fazer isso no "braço".

Abra o seu editor de texto favorito e comece a escrever:

As primeiras linhas são em yaml, para informar configurações e informações da postagem:

```

---
layout: post
title: "Titulo da Postagem"
date: 2011-09-08 21:33
comments: true
categories: [categoria1, categoria2]
---

Comece a escrever daqui...

```

É importante lembrar que como yaml as categorias precisam ser informadas seguindo o seu padrão, não apenas separadas por vírgula.

Leia mais em Blogging Basics.

Deploy

Caso queira usar o heroku para hospedagem só é preciso retirar o diretório public do .gitignore. Só alerto que isso pode acarretar em mais conteúdo para seu commit, exemplo: caso seja modificado o author do site, a maioria dos arquivos em public serão afetados.

Já na minha hospedagem Linode eu utilizo o capistrano para atualizar e tive alguns problemas, tudo foi resolvido com o config/deploy.rb semelhante a este:

set :keep_releases, 5
set :scm, :git
set :scm_verbose, false
set :repository, 'git@endereco.com:/octopress.git'
set :deploy_via, :remote_cache
set :user, 'dmitry'
set :deploy_to, '/home/dmitry/blog'
set :use_sudo, false
role :app, 'blog.dmitrynix.com', :primary => true
default_run_options[:pty] = true

namespace :octopress do
  task :generate, :roles => :app do
    run "cd #{release_path} && bundle exec jekyll"
  end
end

after 'bundle:install', 'octopress:generate'
after 'deploy:update_code', 'deploy:cleanup'

E com o nginx apontando para o diretório public dentro do "current" do capistrano.

Atualização

Após escrever este artigo uma atualização do Octopress (ou do Jekyll) passou a não ser mais compatível com ruby18 e como ainda não atualizei o servidor passei a usar o seguinte script para fazer a autalização:

#!/bin/bash

# Não use '/' ao final de endereço de pastas
ssh_user="dmitry@blog.dmitrynix.com"
remote_tmp_dir="/tmp/.site_update"
remote_final_dir="/home/dmitry/blog/current/public"

echo '## Generating Site with Jekyll'
bundle exec rake generate >/dev/null 2>/dev/null
echo '## Sending Site with rsync'
rsync -az --delete public/ ${ssh_user}:${remote_tmp_dir}
ssh ${ssh_user} "rsync -a --delete ${remote_tmp_dir}/ ${remote_final_dir}"

Rails e Jquery-ui Datepicker

Instalar o jquery e jquery-ui

Baixar o jquery e o jquery-ui para o diretório public, mas se vc quiser pode usar a gem jquery-rails:

source 'http://rubygems.org'

gem 'rails', '3.0.9'
gem 'jquery-rails'

gem 'sqlite3'

Instalar:

% bundle install

E adicionar o jquery:

% rails g jquery:install --ui
  remove  public/javascripts/prototype.js
  remove  public/javascripts/effects.js
  remove  public/javascripts/dragdrop.js
  remove  public/javascripts/controls.js
 copying  jQuery (1.6.1)
  create  public/javascripts/jquery.js
  create  public/javascripts/jquery.min.js
 copying  jQuery UI (1.8.12)
  create  public/javascripts/jquery-ui.js
  create  public/javascripts/jquery-ui.min.js
 copying  jQuery UJS adapter (0e7426)
  remove  public/javascripts/rails.js
  create  public/javascripts/jquery_ujs.js

O model

rails g scaffold Aniversario nome:string dia:date

Nosso projeto já recebe datas, pelo método date_select, mas nos queremos que ou o usuário digite a data (separada por /) ou selecione no datapicker do jquery-ui uma.

Um fato interessante desta forma de inserir datas é que permite deixar a data vazia, algo que no padrão rails não consegui encontrar.

Primeiro: Receber a data como string

Vamos primeiro preparar o model e o formulário para inserir a data manualmente usando o text_field.

O primeiro passo é mudar a formulário de data_select para text_field:

Se você for criar algum registro desta forma a data será reconhecida de forma errada.

Então vamos preparar o model para receber a data em formato usado no Brasil.

class Aniversario < ActiveRecord::Base
  def dia_br
    "#{dia.to_s[8..9]}/#{dia.to_s[5..6]}/#{dia.to_s[0..3]}"
  end

  def dia_br=(dia_en)
    self.dia = "#{dia_en[6..9]}-#{dia_en[3..4]}-#{dia_en[0..1]}"
  end
end

Como pode-se notar nos não mudamos a forma padrão, nos apenas criamos um método auxiliar.

Continuando no form use, ao invés de dia, dia_br:

<div class="field">
  <%= f.label :dia %><br />
  <%= f.text_field :dia_br %>
</div>

Implementando o datapicker

Antes de colocar o datapicker vamos primeiro adicionar o locale dele, para isso basta baixar o arquivo jquery.ui.datepicker-pt-BR.js, colocar em public e adicionar usando javascript_include_tag:

<%= javascript_include_tag :defaults, 'jquery.ui.datepicker-pt-BR' %>

Agora só falta usar o jquery-ui datapicker como manda a sua documentação:

<% javascript_tag do %>
  $(document).ready(function() {
    $('#aniversario_dia_br').datepicker();
  });
<% end %>

E para ficar mais bonito adicionar um tema.

Como de costume o projeto está no github: dmitrynix/rails-datepicker.

Hello Sinatra ;)

Depois de recomendações do @hakagura sobre o framework sinatra (o mesmo que me recomendou o Rails ;) resolvi voltar a testá-lo. Neste post vou mostrar como criar uma aplicação usando sinatra e rspec.

Preparando ambiente com Bundler

Crie um arquivo de Gemfile seguindo este exemplo:

source 'http://rubygems.org'

gem 'sinatra'

Agora um bundle install para instalar as dependêcias e criar o Gemfile.lock.

Agora vamos criar o config.ru e o demo_app.rb para carregar as dependências usando o Bundler:

config.ru:

require 'rubygems'
require 'bundler'

Bundler.require :default

require File.dirname(__FILE__)+'/demo_app'

run DemoApp::Application

demo_app.rb:

module DemoApp
  class Application < Sinatra::Base
  end
end

Nossa aplicação já está pronta e ela é considerada do "tipo modular" quando se usa sinatra.

Iniciando o ambiente de testes com RSpec

Primeiro adicionar o rspec e o rack-test para o Gemfile:

group :development, :test do
  gem 'rspec'
  gem 'rack-test', :require => 'rack/test'
end

Agora vamos criar um arquivo spec_helper.rb dentro do diretório spec:

require 'rubygems'
require 'bundler'

Bundler.require :default, :test

ENV['RACK_ENV'] = 'test'

require File.dirname(__FILE__)+'/../demo_app'

RSpec.configure do |config|
  include Rack::Test::Methods

  def app
    DemoApp::Application
  end
end

Nosso ambiente de teste também já está pronto.

Primeira spec

Ainda dentro do diretório spec vamos criar o arquivo demo_app_spec.rb:

require 'spec_helper'

describe DemoApp::Application do
  context 'Get /' do
    it 'should be ok' do
      get '/'
      last_response.status.should be(200)
    end
  end
end

Nosso primeiro spec irá, obviamente, falhar:

sinatra-demo-app % rspec spec/demo_app_spec.rb
F

Failures:

  1) DemoApp::Application Get / should be ok
     Failure/Error: last_response.status.should be(200)

       expected #<Fixnum:401> => 200
            got #<Fixnum:809> => 404

       Compared using equal?, which compares object identity,
       but expected and actual are not the same object. Use
       'actual.should == expected' if you don't care about
       object identity in this example.
     # ./spec/demo_app_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.02014 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/demo_app_spec.rb:5 # DemoApp::Application Get / should be ok

Se você tentar inicializar o servidor para testar no navegador também verá uma mensagem de erro:

sinatra-demo-app % rackup config.ru -p 4000
[2011-08-09 08:43:37] INFO  WEBrick 1.3.1
[2011-08-09 08:43:37] INFO  ruby 1.9.3 (2011-07-31) [x86_64-darwin11.0.0]
[2011-08-09 08:43:37] INFO  WEBrick::HTTPServer#start: pid=61772 port=4000
127.0.0.1 - - [09/Aug/2011 08:43:40] "GET / HTTP/1.1" 404 429 0.0085

Escrevendo nosso código:

module DemoApp
  class Application < Sinatra::Base
    get '/' do
      ""
    end
  end
end

Executando o rspec novamente:

sinatra-demo-app % rspec spec/demo_app_spec.rb
.

Finished in 0.02425 seconds
1 example, 0 failures

Mais um exemplo de spec

Agora vamos testar o texto que retorna da requisição:

require 'spec_helper'

describe DemoApp::Application do
  context 'Get /' do
    it 'should be ok' do
      get '/'
      last_response.status.should be(200)
    end

    it 'should have the correct text' do
      get '/'
      last_response.body.should == 'DemoApp::Application request'
    end
  end
end

Quando executado este teste irá obviamente falhar, mas ao escrever corretamente a aplicação:

module DemoApp
  class Application < Sinatra::Base
    get '/' do
      "DemoApp::Application request"
    end
  end
end

Ele irá passar.

Links recomendados

Migração

Atualmente estou num projeto de migração de dados, ao invés de fazer dump de banco nos resolvemos "esqueçer" o banco e iniciar um novo projeto.

Após a conclusão deste projeto nos vamos "importar" o banco… Não mudaremos uma coluna ou tabela no banco de dados "antigo".

Criando o Projeto para Migrar

Vamos começar pelo Gemfile:

source "http://rubygems.org"

gem 'rake'
gem 'activerecord', '3.0.0', :require => 'active_record'
gem 'mysql', '2.8.1'

Nos vamos usar o Rake para migrar a base de dados, o activerecord será usado juntamente com o mysql (ou sqlite, caso vc queira somente testar) para pegar os dados e passar para o outro banco.

Apenas para migração de dados é somente isso (por enquanto) que precisamos.

Inicializando o DB e os "Models"

No meu caso eu criei o arquivo db.rb na raiz do projeto para inicializar o banco de dados e carregar os models:

require 'active_record'

class MigrateDb < ActiveRecord::Base
end

MigrateDb.establish_connection :adapter => 'mysql',
  :database => 'portal',
  :username => 'root',
  :password => '',
  :timeout => 5000

Dir['./models/*.rb'].each { |f| require f }

Veja a criação da classe MigrateDb, ela será usada somente neste projeto, para não dar conflito de conexão com o ActiveRecord do projeto novo, caso a migração ocorra para outro tipo de banco como o mongodb ou couchdb, não seria necessario isto.

Ao invés de criar este arquivo vc poderia me questionar de o pq não adicionei logo ao Rakefile, já que usarei tarefa rake para migrar. São duas as necessidade de não adicionar direto no Rakefile:

  • Pode-se adicionar a linha de require dentro do Rakefile;
  • Para abrir um "rails console" você usa: bundle exec -r ./db.rb e pronto o irb já vai carregar com o "banco";

Rakefile

require 'bundler/setup'
require 'rake'
require File.dirname(__FILE__)+'/db'
Dir[ [File.dirname(__FILE__), 'lib', 'tasks'].join('/') + '**/*'].each { |task| load task }

O Rakefile vai fazer um require no arquivo de inicialização do banco de dados (db.rb) e nas tarefas em lib/tasks/.

Construindo os models

Ao invés de descender do ActiveRecord::Base, nossos models desenderá do MigrateDb:

class Secao < MigrateDb
end

O ActiveRecord funciona com menos esforço quando você segue o padrão dele, mas caso vc tenha uma tabela com outro nome use o set_table_name:

class Secao < MigrateDb
  set_table_name 'portal_secao'
end

Uma outra dica rápida seria criar comandos ao invés de decorrar a base de dados:

class Postagem < MigrateDb
  set_table_name 'portal_postagens'

  def cidade
    Cidade.find(cidade_id)
  end

  def cidade_id
    self.cod_cidade
  end
end

Se você está acostumado com o "padrão ActiveRecord" seria melhor vc criar estes dois métodos, uma vez que nossa base de dados deve se conservar o mais somente leitura quanto possível, não seria necessário métodos como cidade=(cidade).

Por fim eu recomendo que seja criado um método migrate_attrs (ou qualquer outro nome) que retorna um hash somente com os dados necessários para a nova base de dados:

class Usuario < MigrateDb
  set_table_name 'portal_usuario'

  def migrate_attrs
  # Dados como :endereco, :cep, não será necessário
    {
      :email => email,
      :name => nome,
      :created_at => data_cadastro,
      :cell_phone => celular,
      :login => codinome,
      :phone => telefone
    }
  end
end

E o "Banco Novo"?

Bom… eu faço um require do projeto novo "inteiro" para poder usar o banco, esta é a melhor forma para não ter que ficar duplicando os projetos (ou somente os models).

Há um problema no require: o Gemfile do projeto novo, vc ainda vai precisar copiar e colar ele para dentro do Gemfile do projeto de migração:

source "http://rubygems.org"

gem 'rake'
gem 'activerecord', '3.0.0', :require => 'active_record'
gem 'mysql', '2.8.1'

# Gemfile do novo projeto
gem 'rails', '3.0.0'
gem 'devise', :git => 'git://github.com/plataformatec/devise.git'
gem 'sqlite3' # Ainda estou somente testando a migração dos dados

Para fazer este require eu adiciono no arquivo rake (por exemplo: lib/tasks/db_import.rake):

require File.expand_path("../../../../novo_projeto_rails/config/environment", __FILE__)

Logo abaixo desta linha eu coloco a conexão do banco de dados do projeto novo:

ActiveRecord::Base.establish_connection :adapter => 'sqlite3',
  :database => 'db/development.sqlite3',
  :pool => 5,
  :timeout => 5000

Tarefa rake

agora basta completar a(s) tarefa(s) rake:

# encoding utf-8
require File.expand_path("../../../../novo_projeto_rails/config/environment", __FILE__)

require File.dirname(__FILE__)+'/../../db.rb'

ActiveRecord::Base.establish_connection :database => 'portal', :username => 'root', :password => '', :adapter => 'mysql'

namespace :db do
  task :import do
    Usuario.all.each do |usuario|
      User.create usuario.migrate_attrs
    end
  end
end

Rodando a Importação

Vá no projeto novo e execute a migração da base de dados.

bundle exec rake db:migrate

E no projeto de migração execute:

bundle exec rake db:import

Git Básico

Iniciando um Projeto

Dentro da pasta que você quer começar a usar git execute o git init, pode já ter conteúdo ou não:

teste % git init
Initialized empty Git repository in /Users/dmitry/teste/.git/
teste %

Se você tem um projeto no github (fork ou algo semelhante)

Basta executar um git clone

dmitry % git clone git@github.com:dmitrynix/teste.git

O que eu mudei?

Se você já fez as mudanças dentro do seu projeto veja os arquivos que foram modificados com o comando git status

teste % git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   novo_arquivo
nothing added to commit but untracked files present (use "git add" to track)

Adicionando arquivos

Se um arquivo nunca existiu no git o git add vai adicionar um novo arquivo, mas caso o arquivo já exista no git o git add só vai adicionar as mudanças feitas.

 teste % git add novo_arquivo

Após adicionar com o git add tudo que você pretende por no git, use o comando git commit para escrever uma mensagem das mudação.

 teste % git commit -m 'Iniciando monitoramento ou adicionando modificacoes'
 [master (root-commit) 46b0893] Iniciando monitoramento ou adicionando modificacoes
  0 files changed, 0 insertions(+), 0 deletions(-)
  create mode 100644 novo_arquivo

Pronto seus arquivos já estão no git.

Enviando e Recebendo Arquivos do Servidor

Crie uma conta no github.com e experimente os comandos abaixo

Antes de enviar os arquivos você precisa configurar qual é o endereço do servidor, no meu caso vou usar: git@github.com:dmitrynix/teste.git.

Enviando arquivos:

teste % git push
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 244 bytes, done.
Total 2 (delta 0), reused 0 (delta 0)
To git@github.com:dmitrynix/teste.git
   46b0893..266ad1b  master -> master
teste %

Pegando arquivos:

teste % git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:dmitrynix/teste
   266ad1b..7e0527e  master     -> origin/master
Updating 266ad1b..7e0527e
Fast-forward
 novo_arquivo |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
teste %

Não publique conteúdo digital

NÃO GERE CONTEÚDO DIGITAL!!!

Recentemente cogitei publicamente (leia: twitter) em obter um e-book pago de forma gratuita, o educado que pensava eu ser mais um amigo de twitter logo anunciou o meu pedido e de mais alguém para o dono do e-book.

Isso não tem desculpa, sei disso, e é por isso que venho aqui pedir desculpas pelo incidente, não citarei nomes nem linkarei nada, apenas desculpa.

Bom, você que aceitou meu pedido de desculpas pode parar de ler, mas continue caso queira saber minha atual situação.

É cediço que estou tentando "abrir" (não diria abrir realmente) uma empresa com mais 2 amigos, a grana está extremamente curta, para ter uma ideia eu estou a cerca de 4 meses pagando entre o mínimo e abaixo do mínimo no cartão (2 cartões) enquanto a grana não entra…

Além disse eu também costumo a almoçar no mercado-velho no qual a comida é pela metade do preço, me orgulho disso, sério, a comida é boa, e quando não dá para ir eu vou comprar pelo preço normal (apenas um) para dividir entre mim e meu atual companheiro de trabalho.

Conteúdo digital eu já "crackeei", foi através disso que eu, por exemplo, conheci a pragprog, após, conhecer e passar do período de "vacas magras" eu comprei e em outra ocasião vou comprar mais, só para citar estes são os livros que eu comprei:

  • agile-web-development-with-rails_b11_0;
  • the-rspec-book_p1_0 (pdf e papper book, o livro impresso me ajudou na semana passada com uma dúvida);
  • using-memcached_p1_0 (ruim);

Já crackeei muito, programas, peguei conteúdo pago: ebook, livro scaneado, músicas…;

Da mesma forma já comprei muito conteúdo digital: pragprog, insistimento, diversos programas para mac (e para linux também), doações para entidades carentes…

Depois do pedido de desculpas vem o aviso: "NÃO PUBLIQUE CONTEÚDO DIGITAL", sim isso mesmo, se vc não quer que pelo menos "um cara passe para o seu amigo, que por exemplo, tem mais de 5 anos de amizade (que já pagou muitos lanches)", não publique.

Por questões financeiras ou mesmo pq é mais fácil pedir um ebook do que procurar aquele seu cartão de crédito para comprar (não sou obrigado a ter conta em TODOS os gateways de pagamento).

O aviso não é para imprimir e vender, ou qualquer outro: se vc tiver um programa, e-book, música, qualquer coisa, e quiser cobrar por ele simplesmente não faça, fique com ele para si mofando!

Só para constar:

  • Sim, estou revoltado.
  • Não, não estou estressado: conteúdo digital sempre terá a possibilidade de ser "crackeado".
  • Sim, pode comentar/criticar/dizer TUDO que pensa nos comentários (vou tentar não censurar ;)
  • e por fim:

Se você usa windows e não quer pegar vírus, não ligue nunca seu computador.

Moedas, Vale-transporte e Agradecimento

Hoje (quinta, 24 de março de 2011), quando estavamos saindo Sandro, Álvaro e eu da loja, uma pessoa "chegou junto" e, mostrando um papel, pediu uma ajuda, nessas horas eu sempre lembro: "devo ajudar, pois um dia eu posso ficar nesta situação".

Logo depois de ser deixado na Frei Serafim, ao por a mão no bolso senti falta do meu cartão do ônibus.

O desespero não bateu, pois como se diz de teresina: "cidadezinha pequena que praticamente todo mundo se conhece". Fiquei próximo da avenida, o local de maior movimento, esperei alguém passar...

... e passou, primeiro foi Francisco Smile, um amigo de escolha (ensino médio que fiz no CEFET-PI), falei minha situação para ele, expliquei pq não poderia voltar para a loja (longe), deu-me 1.65 em moedas, adradeci muito, muito mesmo.

Logo passou a Cristiane Menezes, amiga da época que eu trabalhava somente com manutenção de computadores, ela não tinha os 0.2 centavos para completar a passagem, mas com seu sorriso no rosto deu-me um vale-transporte, disse que dava os 1.65 para ela, mas ela praticamente me bateu por falar isso :-D

O fato é que: eu me sinto eternamente grato a todos os meus amigos, pelo simples fato de que eu os tenho como amigos.

Apaixone-se

Já tenho a experiencia 4 empresas (2 com carteira assinada e empreendimento da família), em todas eu entrei com o mesmo sentimento: "Apaixonado, vou dar meu sangue pela empresa, afinal sou parte dela agora."*

* Quem sabe onde eu trabalhei vai entender essa piada pronta.

Mas em contrapartida há um sério problema: paixão é algo que, como todos dizem, passa rápido.

Para solucionar este problema eu crei a seguinte lista:

EVITE: Hierarquia, quem manda sou eu e pronto

Não deixe ter uma hierarquia FORTE, no empreendimento da família quem manda é minha mãe e acabou, ninguém tem opnião em NADA mais.

Isso me deixa furioso, pq se ela um dia passar isso para mim eu já vou começar mal, e já está tão mal que se ela me desse tudo agora eu recusaria veementemente. Ora: a empresa sempre foi dela e não vai ser agora que vai deixar de ser.

EVITE: Deixar as dívidas afundar a empresa

Em casa o empreendimento da família tem suas contas misturadas com as contas minha, da minha irmã, ou seja, as contas da empresa com as contas de casa. Em todos os cursos e materiais relacionado ao assunto dizem claramente para não misturar.

No meu primeiro emprego, o problema das contas era o não pagamento, como a empresa pegava em quantidades grandes de dinheiro, eu suponho, a DONA fariava com a grana e depois para pagar as dívidas? Não tinha mais dinheiro para nada, e eu ficava de saco cheio pq a todo instante tinha cobrador e eu tinha que ficar dizendo: "ela não está aqui e sim eu já avisei que você veio".

EVITE: Ser A empresa

Sabe quando alguém diz: "vou para a empresa da Dona Maria" e não "vou para a empresa Costura...", veja no exemplo o uso do DONO e não da instituição. Isso é um erro, quando se tem isso em mente, só existe duas coisas:

  • ou a empresa tem a mesma hierarquia acima e/ou;
  • ela só tem um funcionário e ninguém mais para me ajudar.

Neste último caso se o "único funcionário que eu penso que existe" ficar doente quem vai me ajudar?

Do mesmo jeito que apesar de ter 10 funcionários na empresa, na maioria das vezes somente 1 pode lhe ajudar, pq ele é o chefe ou pq só ele sabe como fazer.

EVITE: Rotina, rotina, rotina, rotina, rotina

Não caia no fardo de todo dia, um erro das empresas que trabalhei era o fardo de todo dia, ou seja, eu vou para o mesmo lugar fazer o mesmo para ganhar o mesmo salário. Ninguém quer fazer o mesmo por 1 ano, imagine se alguém ficaria assim por 5 anos! As pessoas (eu principalmente) gostam de mudar E ajudar a mudar.

Veja o meu exemplo: sou programador, durante o primeiro ano eu fiz o MESMO PROGRAMA, no segundo ano, tudo mudou (para a pior) pq surgiu mais um programa para fazer, mas a pessoa responsável fez tudo praticamente sozinha (analise, um monte de scaffold, designer, ...) e eu!? Não sirvo para NADA? Tudo bem, vc pode dizer que eu poderia opinar depois ou pq assim seria mais rápido entender o funcionamento, mas quer saber f*d@-se com esse pensamento e pegue o meu: EU QUERO FAZER PARTE.

EVITE: Deixar o ambiente ficar irrespirável

Em todos os aspectos a comunicação é algo fundamental para a empresa, não deixe de conversar com e nem tenha medo de ninguém, seja na empresa, na hora de pegar o cafezinho, quando for beber água, quando for ao banheiro ou chamando alguém para sair.

Em todos estes casos acima a pessoa está fazendo algo que "o natural" dela exige, ou seja, está descontraída e em 75% das vezes nem querem sequer lembra que trabalham em algum lugar.

E quando chamar alguém para sair lembre-se: nem todo mundo é alcolatra ou viciado em pizza como vc, pergunte para onde ele quer ir.

Toda vez que você chama um amigo (ou subordinado) para sair ele recusa pq vc gosta de ir para um bar, mas ele não. Eu, por exemplo, não vejo vantagem em ficar num porcaria de mesa de bar vendo uma porra de futebol, que a propósito eu odeio. Pense bem antes de chamar alguém para "relaxar". Vá para um lugar comum: veja se o lugar que você quer ir ele iria e veja se o lugar que ele quer ir você iria.

Não esqueça que na empresa não existe só você e que você não é o chefe que manda e desmanda.

SEJA: Parte da empresa

Precisa ficar um final de semana inteiro programando ou precisa ir no final de semana para "limpar" a empresa? Sem problema eu vou sim! Sou parte da empresa (não confunda como sou A empresa), se a empresa vai mal eu também ficarei mal, do mesmo jeito que se ele ficar rica eu fico rico ;)

Tenha isso em mente.

Finalizando

Isto é o que eu poderia dizer que aprendi na marra, mas não esqueça que você as vezes tem que cometer os mesmos erros. Não leve nada disso ao pé da letra, depende muito da pessoa que está na sua frente.