Leia o post em português.


It’s almost two and a half year of development. 2845 lines of code. After a lot of effort and collaboration between me and my parter and friend Guilherme Miksza, we’re out of “Beta”. The result you can see in http://www.tudopreço.com.br.

Tudo Preço Logo

Tudo Preço is the brand new Brazilian price comparison tool, 100% done in Ruby on Rails. We tried to make Tudo Preço straight to the point, with a minimalist design and without meaningless distractions. We basically take what we understand is is wrong on the other tools, and we fixed.

We are a bootstrapped Startup, with no investment. Yet, I believe we are in the right path to compete with the big companies that act in the same field, which spend thousands of dollars only in marketing.

If you like our product and want to bring it to your country, send an email to me, or comment this post. I’ll be glad to chat with you.

Read the post in english.


São quase dois anos e meio de desenvolvimento. 2845 linhas de código. Depois de muito esforço e colaboração entre eu e meu sócio e amigo Guilherme Miksza, saímos do “Beta”. O resultado disso você pode conferir em http://www.tudopreço.com.br.

Tudo Preço Logo

O Tudo Preço é o mais novo comparador de preços no Brasil, 100% feito em Ruby on Rails. Tentamos fazer o Tudo Preço o mais direto possível, com design minimalista e sem distrações sem sentido. Basicamente pegamos o que no nosso entendimento é feito errado nos outros comparadores, e consertamos.

Nós somos uma Startup começada do zero, sem investimento. Ainda assim, acredito que estamos no caminho certo para com concorrer com as grandes empresas que também atuam nesse ramo, que gastam milhares de reais somente em marketing.

Se você compra produtos pela internet, use o Tudo Preço para achar os preços mais baratos. Indique para seus amigos. Desenvolve e-commerces? Indique para para eles nosso site: http://www.tudopreco.com.br/lojista

Se tiver uma sugestão ou crítica a fazer, faça. Toda ajuda é bem vinda. Escreva um comentário, me mande e-mail, ou diretamente pelo contato do site: http://www.tudopreco.com.br/lojista/contato

Mini-palestra feita para alunos calouros e veteranos do curso de Sistemas para Internet da Faculdade Dom Bosco, Cascavel - PR.

If you have a site where users can send content, and they often send emails on it, it’s good to protect them from spam bots.

Here follows a simple helper method that uses a javascript technique to do that.

def hide_emails_from_bots(text)
    match = text.match(/\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6}/).to_s
    if match.blank?
      text
    else
      size = match.size
      splitter = size / 3
      string1 = match[0...splitter]
      string2 = match[splitter...2*splitter]
      string3 = match[2*splitter..size]
      js = "
        <script type="\&quot;text/javascript\&quot;"><!--mce:0--></script>
      "
      text.gsub(/#{match}/, js)
    end
  end

So, you want to know how to get emails from GMail to manipulate in your Rails app right?

First let’s create our app and generate our main class:

$ rails app
$ cd app
$ script/generate scaffold task name:string description:text
$ rake db:migrate
$ rm -rf public/index.html

Map the tasks index action to be the root of the app:

#config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.resources :tasks
 
  map.root :tasks
end

At this point we have a working very simple app to manage our tasks list. Let’s start our server:

$ script/server

You can go to http://localhost:3000 and see our app running.
Now, create the mailer that will check gmail for us:

$ script/generate mailer MailReader

Edit the generated mailer to look like the above:

# app/models/mail_reader.rb
require 'net/pop'
 
class MailReader < ActionMailer::Base
  def receive(email)
    task = Task.new :name => email.subject, :description => email.body
    if email.has_attachments?
      email.attachments.each do |attachment|
        task.assets.create :data => attachment
      end
    end
  end
 
  def self.check_mail
    logger = RAILS_DEFAULT_LOGGER
 
    logger.info "Checking for emails..."
    Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE) #This line raises error if ruby version &lt; 1.8.7
    Net::POP3.start("pop.gmail.com", 995, "your-email@gmail.com", "your-password") do |pop|
      if pop.mails.empty?
        logger.info "No emails found."
      else
        pop.mails.each do |email|
          begin
            logger.info "Retrieving mail..."
            MailReader.receive(email.pop)
            email.delete
          rescue Exception => e
            logger.error "[" + Time.now.to_s + "] " + e.message
          end
        end
      end
    end
    logger.info "Done."
  end
end

Note that your ruby version need to be 1.8.7 or higher.
Now we need to create our model that will keep the attachments and install the paperclip plugin:

$ script/plugin install git://github.com/thoughtbot/paperclip.git
$ script/generate model asset

Put this in the migration file:

# db/migrate/20090303181609_create_assets.rb
class CreateAssets < ActiveRecord::Migration
  def self.up
    create_table :assets do |t|
      t.string      :data_file_name
      t.string      :data_content_type
      t.integer     :data_file_size
      t.datetime    :data_updated_at
      t.references  :task
      t.timestamps
    end
  end
 
  def self.down
    drop_table :assets
  end
end

Run the migration:

$ rake db:migrate

Create the associations in the models:

# app/models/asset.rb
class Asset < ActiveRecord::Base
  belongs_to :task
 
  has_attached_file :data
end
# app/models/task.rb
class Task < ActiveRecord::Base
  has_many :assets
end

…and finally, modify the task show template to show the attachments:

  <!-- app/views/tasks/show.html.erb -->
 
  <b>Name:</b>
  <%=h @task.name %>
 
  <b>Description:</b>
  <%=h @task.description %>
 
  <% unless @task.assets.blank? -%>
 
  <b>Attachments:</b>
  <% @task.assets.each do |asset| -%>
    <%= link_to "Download \"#{asset.data_file_name}\"", asset.data.url %>
  <% end -%>
 
<% end -%>
 
<%= link_to 'Edit', edit_task_path(@task) %> |
<%= link_to 'Back', tasks_path %>

You can set up a cron job to check if there are new emails every two minutes by putting this your crontab file:

##__email_checker__
*/2 * * * * RAILS_ENV=production /usr/local/bin/ruby /home/mauricio/pdn-posts/how-to-pull-emails-from-gmail-to-rachments/app/script/runner MailReader.check_mail

And last but not least, the source code used for this post is hosted on github.

While I’m working, I always have a minimum of 3 or 4 open terminals: one for watching the logs, one for ssh and so on.
It’s not good to have a lot of windows open, and tab switching can be a pain, specially when you want to see what’s going on in one window while you type in another one.
So I’ve found recently Terminator.
Taken from terminator’s home page:

The goal of this project is to produce a useful tool for arranging terminals.
It is inspired by programs such as gnome-multi-term, quadkonsole, etc. in that the main focus is arranging terminals in grids (tabs is the most common default method, which Terminator also supports).

Much of the behaviour of Terminator is based on GNOME Terminal, and we are adding more features from that as time goes by, but we also want to extend out in different directions with useful features for sysadmins and other users. If you have any suggestions, please file wishlist bugs! (see below for the address)

Features:

* Arrange terminals in a grid
* Tabs
* Drag and drop re-ordering of terminals
* Lots of keyboard shortcuts
* Config file to override gnome-terminal settings
* Simultaneous typing to arbitrary groups of terminals

My screen.

My screen.

You can put your default configurations in the file ~/.config/terminator/config
Mine configurations are this:

#/home/mauricio/.config/terminator/config
enable_real_transparency=True
force_no_bell=True
scrollbar_position=disabled
titlebars=False
handle_size=1

This increased a lot my productivity. Unfortunately I haven’t found anything like Terminator for Mac.

Update (26/01): I’m using IDs on the feeds now, as I was getting duplicated feed entries. I followed this post written by Carlos Brando.

I had some trouble recently with atom feeds. I made it work with almost all the readers around there, but it took some time until I found why it wasn’t working with Google Reader.
So, I’ll try to save some people time here.

First, you need a action for creating the feeds (could be the index action too, you just need to add the format.atom line):

# app/controllers/stories_controller.rb
def feed
    @project = Project.find(params[:project_id])
    @type = @project.types.find(params[:type_id])
    @stories = @type.stories(:order =&gt; 'updated_at desc')
 
    respond_to do |format|
      format.atom
    end
  end

Now, you need a builder for that action:

# app/views/stories/feed.atom.builder
atom_feed do |feed|
  feed.title("#{@project.name}-&gt;#{@type.name}")
  feed.updated(@stories.first.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
 
  for story in @stories
    next if story.updated_at.blank?
    feed.entry([@project, @type, story]) do |entry|
      entry.title(story.headline)
      entry.content(story.text, :type =&gt; 'html')
      entry.updated(story.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ")) # needed to work with Google Reader.
      entry.author do |author|
        author.name(story.author)
      end
    end
  end
end

And finally, a route to the feed:

map.connect '/projects/:project_id/types/:type_id/feed', :controller =&gt; 'stories', :action =&gt; 'feed', :format =&gt; 'atom'

Now you can check if it’s valid with the Feed Validator.

The problem why it wasn’t working with Google Reader was that I forgot the entry.updated line. Also, you need to format the time as it is above, so it’ll follow the rules for dates in the atom specification and will work with Google Reader.

Estava procurando uma forma de escrever no twitter de forma rápida e sem precisar abrir nenhum cliente, nem mesmo o site. Foi quando uma pergunta rápida ao guru me trouxe esse artigo.
Fiz algumas modificações (não conseguia postar mensagems com !) e acabei com esse script:

#!/usr/bin/ruby
`curl --basic --user "<myusername>:<mysecretpassword>" --data-ascii "status=#{ARGV.join(" ")}" "http://twitter.com/statuses/update.json"`

Substitua e com seu usuário e sua senha, salve o arquivo com o nome de twit em /usr/bin ou qualquer pasta no seu path, de permissão de execução (chmod +x /usr/bin/twit) e execute:

$ twit Estou postando a partir da linha de comando!

Se essa dica foi útil à você, poste nos comentários. :)

Fiquei um longo período sem postar nada. A coisa está corrida por aqui. Estou fazendo o deployment da minha aplicação, refatorando pedaços de código, corrigindo bugs, enfim, aparando as últimas arestas antes de colocar no ar.
Assim sendo, vamos com uma dica rápida para facilitar o acesso aos recursos de internacionalização inseridos no Rails 2.2.

Até o Rails 2.1 eu usava o Brazilian Rails para a formatação de datas, moeda, etc. Resolvi que era hora de tentar o I18n do Rails 2.2.

Primeiro passo: criar o locale ‘pt-BR.yml’ dentro de config/locales/:

time:
    formats:
      default: "%A, %d de %B de %Y, %H:%M"
      short: "%d/%m, %H:%M"
      long: "%A, %d de %B de %Y, %H:%M"
      hour: "%H:%M"
    am: ''
    pm: ''

no config/environment.rb:

I18n.default_locale = "pt-BR"

e vualá:

>> I18n.localize(Time.now)
=> "Segunda, 08 de Dezembro de 2008, 17:00"
>> I18n.localize(Time.now, :format => :short)
=> "08/12, 17:00"

Muito bem. Tudo funcionando. Mas é muito chato ter que chamar a classe I18n todo o tempo. Então vamos usar o poder de DSLs que Ruby nos dá.
Crie um arquivo em config/initalizers com o nome que preferir e adicione:

class Time
  def localize(options={})
    I18n.localize(self, options)
  end
end

Agora ficou muito mais fácil e intuitivo:

>> Time.now.localize
=> "Segunda, 08 de Dezembro de 2008, 17:04"
>> Time.now.localize :format => :short
=> "08/12, 17:04"

Isso pode ser feito similarmente com outros métodos, de outras classes também. Fica para o próximo post.

Continuando a série de benchmarks começada ao comparar o Rails 2.1 com o Rails 2.2 em cima do Ruby 1.8, agora é a vez de comparar o JRuby e Ruby.
Se você não leu a explicação das diferenças do modelo de threading entre Ruby 1.8, Ruby 1.9 e JRuby, leia antes de prosseguir.
Estou rodando os testes no meu computador pessoal, que é um Pentium 4 3.0 HT, com 1Gb de memória rodando Ubuntu Linux. Já é um PC um tanto quanto ultrapassado, portanto o tempo de resposta acaba sendo um pouco alto, além de que ao mesmo tempo em que rodam os testes rodam também outros programas, como o Firefox por exemplo.
Vamos ao que interessa:

Ruby 1.8

$ ruby script/server -p 3001 -e production
$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   3.903964 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      573554 bytes
HTML transferred:       547350 bytes
Requests per second:    12.81 [#/sec] (mean)
Time per request:       390.396 [ms] (mean)
Time per request:       78.079 [ms] (mean, across all concurrent requests)
Transfer rate:          143.44 [Kbytes/sec] received

JRuby 1.1.4

$ jruby -J-Djruby.objectspace.enabled=false -J-Xmn128m -J-Xms512m -J-Xmx512m -J-server -J-Djruby.thread.pooling=true script/server -e production
$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   7.976448 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      573600 bytes
HTML transferred:       547350 bytes
Requests per second:    6.27 [#/sec] (mean)
Time per request:       797.645 [ms] (mean)
Time per request:       159.529 [ms] (mean, across all concurrent requests)
Transfer rate:          70.21 [Kbytes/sec] received

Mesmo utilizando todas as flags de performance tunning da wiki do JRuby, ele ainda se mostrou mais lento. Isso significa que o JRuby não vai tirar proveito do threadsafeting do Rails 2.2? Não!
Pelo que eu entendi pesquisando brevemente, ainda é preciso fazer alterações no código do JRuby pra que ele ofereça a melhor performance possível. Mesmo a última barreira para que o Rails possa ser executado em modo multi-threaded tenha sido removida, ainda é preciso implementar essa função no core do JRuby. Me corrijam se eu estiver errado.

Tempo de carga de uma única requisição no JRuby:

Processing IndexController#index (for 127.0.0.1 at 2008-10-30 18:44:58) [GET]
Completed in 186ms (View: 116, DB: 7) | 200 OK [http://localhost/]

…e no Ruby:

Processing IndexController#index (for 127.0.0.1 at 2008-10-30 18:45:13) [GET]
Completed in 131ms (View: 60, DB: 20) | 200 OK [http://localhost/]

O Ruby é 2x mais rápido que o JRuby ao renderizar a view, mas quase 3x mais lento em consultas ao banco.
Alguém já brincou com o JRuby e Rails 2.2? Obteve resultados melhores? Comentem.