Como colocar uma aplicação no ar?

Já terminou a sua aplicação lindona e agora precisa colocá-la no ar? Existem inúmeras questões que precisam ser resolvidas para que tudo funcione corretamente e é sempre necessário dar uma atenção especial a segurança.

Ao longo dos anos lidando com aplicações online eu desenvolvi várias receitas para facilitar o deploy das minhas aplicações. E agora eu compartilho tudo no curso on-line Imersão Sys Deploy! São 4 manhãs de sábado recheadas de mão na massa!

Cada aluno tem acesso a um servidor exclusivo, onde poderá realizar todos os testes sem se preocupar e sem comprometer nenhum ambiente de produção. Um verdadeiro ambiente safe-to-fail.

Você vai aprender sobre Apache, otimização, DNS, Crontab, SSH, Firewall, Port Knocking, Capistrano e muito mais. O curso é focado no deploy de aplicações Ruby on Rails, mas pode ser adaptado facilmente para deploy de outras aplicações como PHP, Django, Sinatra etc.

A próxima turma começa no sábado, dia 16 de julho! Então não perca tempo, pois as vagas são limitadas. Inscreva-se agora!

0 comentários : 10.07.2011 11:13 AM

Processando emails recebidos no Rails com MailMan

Processar e-mails recebidos em um aplicativo Rails parece uma tarefa complicada. A maioria das soluções disponíveis atualmente envolve a configuração de um servidor de e-mail dedicado só para isso. Não parece ser uma boa idéia.

Quem também acha isso é o Jonathan Rudenberg que junto com mais uma galera, desenvolveu no Ruby Summer of Code do ano passado, o Mailman. Um microframework para processamento de e-mails recebidos.

A funcionalidade é bem simples. Resumindo, você configura uma caixa postal que o Mailman vai varrer e uma callback para ser executada para cada e-mail que corresponder ao critério que você estabelecer.

Um exemplo simples:

require 'mailman'
Mailman::Application.run do
  to 'post-%id%@blog.com' do

    Post.find(params[:id]).comments << Comment.create(:body => message)
  end
end

Mas vamos devagar. Começando pelo começo.

Como sempre, no seu Gemfile inclua a gem do Mailman:

gem 'mailman', '0.4.0'

Execute o bundler:

bundle install

No seu Mailer, defina a callback a ser chamada para os e-mails recebidos:

class Mailer < ActionMailer::Base
  def receive(email)
    # o seu código entra aqui
  end
end

Depois disso é necessário configurar o Mailman. Recomendamos a criação de um script (em script/mailman). Como abaixo:

#!/usr/bin/env ruby
require File.expand_path('../../config/application', __FILE__)
require "mailman"

Mailman.config.poll_interval = 0
Mailman.config.ignore_stdin = true
Mailman.config.logger = Logger.new('log/mailman.log')
Mailman.config.pop3 = {
  :username => 'incoming@example.com',
  :password => '************',
  :server   => 'pop.gmail.com',
  :port     => 995,
  :ssl      => true
}

Mailman::Application.run do
  to 'dropbox+%domain%+%type%+%type_id%@%host%' do
    UserMailer.receive(message)
  end
end

Vamos analisar as configurações mais importantes:

Mailman.config.poll_interval: define de quanto em quanto tempo o Mailman deve executar. Nós vamos configurar o cron para executar o script/mailman periodicamente, então devemos setar essa configuração para zero. Assim ele vai executar apenas uma vez e sair.

Mailman.config.ignore_stdin: essa configuração serve para ignorar e-mails passados diretamente para o Mailman (como no comando: cat plainmessage.eml | ruby script/mailman).

Mailman.config.logger: seta o arquivo onde você quer que o log seja salvo.

Mailman.config.pop3: configura o mailman para usar pop3 como receiver. O Mailman pode ser configurado para trabalhar com três receivers: pop3, standard input ou maildir (onde ele varre uma pasta local a espera de novas mensagens).

No nosso caso estamos usando o Gmail através do Google Apps.

Logo abaixo vem o método Mailman::Application.run, que é onde a mágica acontece.

Nele nós podemos configurar o que, no linguajar do Mailman, são chamados de ‘rotas’. Uma rota consiste em um método ( to, from, cc, subject, body), uma string ou regex e um bloco.

O método diz onde o Mailman vai procurar pela string ou regex, no nosso caso estamos procurando no campo “to” do e-mail. Todo e-mail cujo “to” bater com a string vai ser passada para o bloco. As partes do “to” que forem capturadas pelo parte %domain%, por exemplo, estará disponível no bloco como params[:domain]. A mensagem então é passada para o método receive do Mailer.

Agora, com o script pronto, precisamos nos assegurar que ele rode regularmente no servidor. Para isso existe o Cron, e nós usaremos o Whenever para agendar a execução do script/mailman.

Adicione essas linhas no seu schedule.rb:

job_type :command, "cd :path && :task :output" # para executar o comando de dentro do aplicativo
every 1.minute do
  command "script/mailman"
end

Configure a periodicidade de acordo com sua necessidade e pronto. Não foi tão difícil!

2 comentários : 11.01.2011 12:21 PM

Whenever - Cron Jobs do jeito Ruby

Uma das ferramentas mais úteis (até mesmo essenciais, eu diria) no arsenal de um desenvolvedor web são as tarefas do cron. Porém sua sintaxe um tanto quanto estranha intimida muitos desenvolvedores, que acabam subutilizando a ferramenta.

É ai que entra o Whenever, uma DSL ruby para escrever cron job que deixa essa tarefa bem mais fácil. Para instalar o whenever é necessario um:

gem install whenever

ou inclua a seguinte linha em seu Gemfile:

gem 'whenever'

e dê um:

bundle install

depois disso você precisa navegar até o diretorio do seu projeto e executar:

wheneverize .

Isso irá criar um arquivo chamado schedule.rb na pasta config.

É nesse arquivo que vão entrar suas tarefas, e é ai que começa a diversão.

Dê uma olhada no seguinte snippet:

every 2.hours do
 # seu comando entra aqui
end

É fácil assim.

Você pode ainda passar um segundo parametro para o metodo every especificando o horario que você quer que a tarefa seja executada.

every 2.days, :at => ‘2am’ do …

O whenever oferece também um metodo ‘rake’ que permite executar rake tasks com facilidade. Por exemplo:

every :hour do
  rake minha:rake:task
end

Você pode ainda executar comandos diretamente:

every 15.minutes do
 command "/usr/bin/comando_qualquer”
end

Lindo.

Só fique esperto para caso você receba um erro parecido com esse no seu /var/log/syslog:

Dec 28 02:00:01 toad CRON[23436]: (CRON) error (grandchild #23438 failed with exit status 127)
Dec 28 02:00:01 toad CRON[23436]: (CRON) info (No MTA installed, discarding output)

Isso significa que o cron não conseguiu encontrar o comando rake no path. Se isso acontecer é só você especificar o path do comando. como abaixo:

job_type :rake, "cd :path && RAILS_ENV=:environment /usr/local/bin/rake :task :output"

Mais algumas dicas:

Você pode redirecionar o log do whenever para outro arquivo incluindo a linha abaixo em seu schedule:

set :output, "/path/para/o/seu.log"

Pare integrar o whenerver com o capistrano inclua essas linhas no seu deploy.rb. Assim o cron será atualizado a cada deploy:

set :whenever_command, "bundle exec whenever"
require 'whenever/capistrano'

1 comentário : 03.01.2011 11:29 AM

Cursos presenciais de Ruby on Rails

Aproveitando o imenso sucesso que está sendo o curso Imersão Sys Deploy em parceria com e-Genial resolvi pesquisar sobre o interesse das pessoas em relação a cursos presenciais.

Estou em negociação com um centro de treinamento aqui no Rio de Janeiro e é possivel que fechemos uma parceria para cursos presenciais ligados a Ruby on Rails.

Além do Rio de Janeiro também tenho interesse em atingir outros estados. Por isso elaborei um pequeno questionário que será útil para verificar a viabilidade dos cursos presenciais no Rio e em outras cidades do Brasil. Vocês me ajudam a divulgar? Muito obrigado.

Link direto: https://spreadsheets.google.com/viewform?formkey=dGtKY0hyMjhqUEgxNnpHempISzlsbEE6MQ

1 comentário : 29.04.2010 10:35 PM

Curso de administração Linux e deploy de Apps Rails

logo e-genial

Hoje eu e o Carlos Eduardo da e-Genial fechamos os últimos detalhes de um novo curso: Imersão Sys Deploy! Os alunos vão aprender tudo sobre como configurar adequadamente um VPS para rodar aplicações Rails usando Apache com Passenger, Mysql e deploy com Capistrano.

E de quebra ainda vão ter capítulos específicos para tratar de serviço smtp com postfix, backup, monitoramento e segurança. É um curso completo sobre administração de servidores por um preço imperdível!

E ainda tem mais! Cada aluno do curso terá acesso a um VPS exclusivo onde ele executará as tarefas junto comigo. As aulas serão aos sábados pela manhã pelo TreinaTom.

Para saber mais detalhes acesse já o site do curso e faça a sua inscrição, as vagas são limitadíssimas.

1 comentário : 23.02.2010 10:34 PM

Empreender, essa é a hora! - Parte 1

is this good for the company?

Há pouco mais de um ano atrás eu fiz uma palestra no Encontro de TI sobre Empreendedorismo on Rails. A apresentação bombou, fez o maior sucesso, mas até hoje eu não tinha dado nenhum feedback sobre o que eu mesmo estava fazendo em relação ao que preguei na palestra. O fato é que 2009 foi um ano de muito trabalho, onde eu comecei a preparar os alicerces do que vou apresentar hoje. É sempre bom lembrar que tudo foi e continuará sendo feito durante os tempos vagos.

Em 2009 me juntei com mais 3 empreendedores para criar a idéia do nosso produto. Ele tem um nome: Clientella. Trata-se de um CRM para pequenas empresas e profissionais liberais. Alguns pensarão: “Ah fala sério, vocês vão copiar o Highrise da 37signals”. Bom a idéia não é essa. Para nós de TI que estamos adaptados aos sistemas web e com o idioma inglês é simples usar um sistema gringo. Com o Clientella estamos pensando nas empresas que tem especificidades brasileiras, que buscam um serviço nacional, com suporte na própria língua e não necessariamente entendem algo de web.

“Da onde saiu essa idéia maluca de fazer um CRM? Esse nome é batido pra caramba e existem diversos players no mercado!”, vocês pensariam. Bom, eu trabalhei customizando o SugarCRM - um bloated CRM opensource - para uma empresa há uns 3 anos atrás. O que eu aprendi lá é o quanto as empresas precisam de um CRM e o quanto as soluções existentes não resolvem os problemas. Lembrando que estamos falando de pequenas empresas, logo qualquer coisa como Oracle, SAP, SalesForce e Microsoft está fora de cogitação apenas pelo custo de aquisição das licenças ou de necessidade de infraestrutura própria. Daí pode-se ver a quanto tempo estou maturando essa idéia de um CRM na cabeça ;-)

E o que você pode fazer para ajudar? Se você tem uma empresa ou é um profissional liberal e precisa de um CRM, acesse o nosso site, leia a carta convite e preencha o formulário. Assim você nos ajudará com o Clientella e ainda poderá se tornar parte do programa de beta testers.

Foto de magnetbox(cc)

5 comentários : 04.02.2010 12:57 AM

Fazendo thumbnails de tamanho fixo com attachment_fu

O plugin attachment_fu é quase que o plugin padrão para tratar do upload de arquivos em aplicações Rails. Ele suporta também o resize de imagens mantendo as proporções, mas não suporta crop. Sempre precisamos criar avatar ou coisas semelhantes. E em geral o avatar tem um tamanho fixo, como 80x80. Para isso é necessário fazer o crop da imagem. Adicione o método resize_image, como abaixo, no seu modelo:

class Photo < ActiveRecord::Base
  belongs_to :owner, :class_name => "Person"
  has_attachment :content_type => :image,
    :processor => :rmagick,
    :storage => :file_system,
    :resize_to => '600>',
    :thumbnails => { 
      :album        => 'crop: 150x150',
      :icon         => '72>' }

  protected
  def resize_image(img, size)
    # resize_image take size in a number of formats, we just want
    # Strings in the form of "crop: WxH"
    reg = /^crop: (\d*)x(\d*)/i
    if (size.is_a?(String) && size =~ reg) ||
        (size.is_a?(Array) && size.first.is_a?(String) &&
          size.first =~ reg)
      img.crop_resized!($1.to_i, $2.to_i)
      # We need to save the resized image in the same way the
      # orignal does.
      self.temp_path = write_to_temp_file(img.to_blob)
    else
      super # Otherwise let attachment_fu handle it
    end
  end
end

Assim você sobrescreve o método original do attachment_fu para suportar uma sintaxe especial. Então quando você quiser um thumbnail ou até mesmo a imagem principal cortada de um tamanho específico, basta usar a string ‘crop: WxH’. Bom lembrar que isso só irá funcionar com o Rmagick. Eu dei uma olhada sobre como fazer usando o image_science, mas era um pouco mais chato. É bom setar: :processor => :rmagick na definição do has_attachment.

Eu também fiz um teste para garantir que os meus thumbnails estão sendo gerados no tamanho correto:

describe "photo resize" do
  it "should create thumbnails with correct size" do
    photo = new_photo
    photo.save!

    full, album, icon = *Magick::ImageList.new(
      photo.full_filename,
      photo.full_filename('album'),
      photo.full_filename('icon')
    )

    full.columns.should == 50
    full.rows.should == 64

    album.columns.should == 150
    album.rows.should == 150

    icon.columns.should == 50
    icon.rows.should == 64
  end
end

PS1: Sim, eu sou paranóico com testes e você também deveria ser!
PS2: Essa dica foi adaptada daqui.

3 comentários : 17.03.2009 07:04 PM

Lançamento da Revista TI Digital

Ontem na Livraria da Travessa, centro do Rio, foi o lançamento da Revista TI Digital de autoria da Arteccom. A mesma do Encontro de TI, do Encontro de Webdesign e da Revista Webdesign.

Grandes nomes da área de desenvolvimento nacional estão entre os colunistas, como Paulino Michelazzo e Guilherme Chapiewski. Eu tenho o honra de estar nesse time também! Vou escrever sempre sobre Ruby on Rails. Nesta primeira edição tivemos uma entrevista exclusiva com o David H. Hansson e comentários de nomes da nossa comunidade como Carlos Eduardo, Ronaldo Ferraz, Paulo Souza e Carlos Brando.

No primeiro artigo eu falei um pouco sobre REST e sobre como facilitar a sua vida usando o plugin resource_controller do James Golick. Então vamos em frente. Aceito sugestões para os próximos artigos!

Compre na banca mais próxima de você!

3 comentários : 06.03.2009 07:59 PM

mergulhaoinfo no imasters

Logo iMasters

Para quem acompanha meu blog agora uma ótima notícia! Está com algumas semanas de atraso, mas antes tarde do que nunca… o tempo está escasso. A partir de agora alguns dos meus posts, principalmente os técnicos, também serão publicados dentro do iMasters! O primeiro post já foi publicado, é a série de três vídeos da palestra no Encontro de TI. Acesse o post no iMasters.

1 comentário : 06.03.2009 07:20 PM

E mais um podcast gravado!

Saiu no guanabara.info o episódio no. 53 do GuanaCast e eu fui o entrevistado da semana! Além do Gustavo Guanabara também participaram do podcast o Kauê Linden e o Sharuto da Hostnet. Foi um bate papo bem descontraído e divertido, explicando o básico do Ruby e do Rails, da onde a coisa apareceu, por que está fazendo tanto sucesso, etc. Não deixem de conferir!

Para ouvir, visite a página do podcast.

5 comentários : 01.02.2009 11:25 PM

Problema ao usar resources com palavras sem plural

No V2V nós criamos um resource que é utilizado para o formulário de contato (formmail) do site. O nome do resource é contact_us. O nosso routes.rb ficava assim:

map.resources :contact_us

E temos uma configuração de inflector para informar ao Rails que essa é uma expressão incontável:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.uncountable %w(contact_us)
end

O problema é que ao tentar gerar a url com o método contact_us_url para o formulário acontece o seguinte erro:

contact_us_url failed to generate from {:controller=>"contact_us", :action=>"show"} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: ["contact_us", :id] - are they all satisfied?

Isso acontece pois o Rails sobrescreve o método que gera a url da coleção com o método que gera a url de instância dos objetos. Para resolver isso temos que informar no arquivo de rotas o nome que será utilizado nos métodos no caso de ser uma instância e não uma coleção:

map.resources :contact_us, :singular => :contact_us_item

Isso fará os métodos ficarem assim:

contact_us_path() => /contact_us
new_contact_us_item_path() => /contact_us/new
contact_us_item_path(1) => /contact_us/1
edit_contact_us_item_path(1) => /contact_us/1

Reparem que no caso de não ser usada a opção :singular o método de acesso a coleção e da instância ficam com nomes iguais:

contact_us_path() => /contact_us
contact_us_path(1) => /contact_us/1

Isso não acontece com expressões normais pois o Rails utiliza para a coleção a expressão com pluralize. Supondo que o nosso resource fosse de posts, os métodos seriam:

posts_path() => /posts
new_post_path() => /posts/new
post_path(1) => /posts/1
edit_post_path(1) => /posts/1

2 comentários : 22.01.2009 11:46 PM

acts_as_taggable_on_steroids e will_paginate

Num dos projetos que trabalho utilizo o plugin acts_as_taggable_on_steroids. Pela primeira vez fui fazer paginação de elementos com tags utilizando o will_paginate. O acts_as_taggable_on_steroids fornece o método de classe find_tagged_with que serve para buscar por items com uma determinada tag:

Post.find_tagged_with('rails')

E é possível fazer paginação utilizando o método paginate_tagged_with, mas o problema é que ele não funciona como deveria. Apesar de fazer a paginação funcionar corretamente, a sql que ele gera para fazer o count dos elementos não é correta. Se usado sem conditions, ele gera o número de páginas considerando todos os elementos da base. Eu escrevi o seguinte monkey patch, que coloquei no /lib do projeto:

module ActiveRecord
  module Acts
    module Taggable
      module SingletonMethods
        def paginate_tagged_with(tags, args = {})
          options = find_options_for_find_tagged_with(tags, :match_all => true)
          options.merge!(args)
          paginate(options.merge(:count => { :select => options[:select].gsub("#{table_name}.*", "#{table_name}.id") }))
        end
      end
    end
  end
end

Para usar basta colocar no seu controller algo como:

@posts = Post.paginate_tagged_with(params[:tag_id], :page => params[:page] || 1)

Também escrevi uma spec para testar o funcionamento. Ela só faz sentido dentro do contexto do meu projeto, mas basta você modificar as fixtures, os modelos, etc, de acordo com seu projeto.

describe Post do
  it "should return the correct total_pages to will_paginate" do
    post = posts(:blog_post)
    5.times do
      new_post = BlogPost.new(post.attributes)
      new_post.tag_list = 'global'
      new_post.save
    end
    paginator = BlogPost.paginate_tagged_with('global', :page => 1, :per_page => 2)
    paginator.total_entries.should == 6
    paginator.total_pages.should == 3
  end
end

2 comentários : 21.01.2009 12:09 AM

Empreendedorismo on Rails no Encontro de TI

Sairam os vídeos da minha apresentação no Encontro de TI. A palestra foi gentilmente cedida pelo Vinícius, originalmente apresentada no Rails Summit 2008.

4 comentários : 16.12.2008 09:10 PM

Como vocês fazem o "describe" das suas specs?

Isso pra mim sempre foi uma dúvida ao usar o rspec. Realmente não há forma correta. É uma opção pessoal no caso de um projeto particular ou de decisão em conjunto no caso de um projeto onde vários desenvolvedores participam.

Mas o fato é que eu nunca adotei nenhum padrão para isso. É uma coisa que tem me deixado um pouco incomodado. Em alguns modelos sigo o padrão de um describe para cada método, em outros segui padrões ligeiramente diferentes, como por exemplo testes relacionados a attributos em um describe, relacionamentos em outro describe e assim por diante. Ou seja, não há padrão. Há tempos atrás ouvi uma frase que nunca esqueci:

Quando dois padrões existem, não há padrão.

No Lucidus usávamos Test::Unit(quando o projeto começou o rspec ainda era muito pouco difundido) então o “padrão” para nós era pelo menos colocar os testes relativos juntos. Então os testes relativos a um mesmo método normalmente estavam juntos, em um “bloco”, um abaixo do outro. O rspec nos permite um pouco mais de organização. Mas fazer essa organização extra através dos describes proporciona algum benefício?

Então seguindo a idéia… nós precisamos discutir testes.

Qual opnião de vocês? Como vocês organizam seus describes? E o que vocês escrevem nele?

3 comentários : 28.11.2008 03:18 AM

Um "setup" global para todas as suas specs

Ficou bem difundido no rspec a forma em como fazer o setup antes das specs executarem, assim como existe também no Test::Unit.

describe Act do
  before(:each) do
    (...)
  end

  it "should have many persons associated" do
   (...)
  end
end

Eu estava precisando fazer o setup para todas as minhas specs (do planeta :), então descobri uma forma que está mal documentada(pelo menos eu não achei bom), mas é super simples de usar. É só editar o spec/spec_helper.rb e adicionar dentro do bloco:

Spec::Runner.configure do |config|
  (...)
end

O seguinte:

config.before(:each) do
  your_global_setup_here
end

Você também pode executar o setup somente para os controllers, models ou helpers assim:

config.before(:each, :behaviour_type => :controller) do
  your_global_controllers_setup_here
end

Ou

config.before(:each, :behaviour_type => :helper) do
  your_global_helpers_setup_here
end

Ou

config.before(:each, :behaviour_type => :model) do
  your_global_models_setup_here
end

Como se chama o “setup” no bdd? É setup mesmo?

4 comentários : 27.11.2008 02:50 AM