1up4developers logo

1up4developers

Nadando contra o Waterfall. tail -f /mind/realworld >> /blog

Extraindo Dados Da Internet Com Clojure

| | Comments


O problema

A Internet é um repositórios de dados gigantesco e frequentemente precisamos extrair algo que nos interessa de maneira automatizada. O grande problema é que esses dados normalmente são apresentados de forma não estruturada, e precisamos utilizar uma técnica chamada scrapping, que consiste em abrir uma página, carregar o HTML e navegar dentro desse código para extrair o que precisamos.

Com o uso das ferramentas certas isso não é complicado, mas pode ser trabalhoso e, uma vez que a página que você estiver lendo altere alguma coisa em sua estrutura, você terá que adaptar seu código às mudanças.

Vamos apresentar um exemplo simples, mas que vai te dar uma boa base de como extrair dados de uma página utilizando Clojure.

Quem escreveu mais livros na Casa do Código?

A Casa do Código é uma editora brasileira especializada em livros para desenvolvedores de software, empreendedores e webdesigners. Seus autores são profissionais conhecidos em suas respectivas áreas e precisamos saber quais deles escreveram mais livros.

Para isso, vamos abrir a página inicial da editora em http://www.casadocodigo.com.br/, que contém links para todos os livros publicados até o momento. Com esses links em mãos, vamos entrar em cada um deles e extrair os nomes dos autores para em seguida agrupá-los e apresentarmos o resultado.

Como fazer

É necessário ter algum conhecimento de Clojure e Leiningen para poder acompanhar este post. Dê uma lida no texto Fazendo mágica com o REPL do Clojure para aprender como criar um projeto.

Vamos criar um projeto chamado autores, definir o namespace inicial e adicionar a biblioteca Enlive, que vai nos permitir extrair os dados que queremos de dentro do código HTML. O nosso arquivo project.clj vai ficar parecido com o exemplo abaixo:

(defproject autores "0.1.0-SNAPSHOT"
  ; informações de licença e descrição do projeto

  :dependencies [[org.clojure/clojure "1.6.0"]
                 [enlive "1.1.5"]]
  :main autores.core)

Enlive é uma biblioteca criada por Christophe Grand, coautor de Clojure Programming, que permite que você gere código HTML escrevendo em Clojure, e permite também que você extraia textos de um arquivo HTML já existente.

Para utilizarmos o Enlive em nosso código, vamos referenciar as funções na nossa declaração de namespace. Vamos também adicionar o Pretty Print para exibir o resultado formatado e as bibliotecas do clojure.string para manipularmos o texto. Também vamos precisar importar a classe java.net.URL para tratarmos o endereço do site. Nosso código então começa assim:

(ns autores.core
  (:require [clojure.pprint :as pp]
            [net.cgrand.enlive-html :as en]
            [clojure.string :as str])
  (:import  [java.net URL]))

O primeiro passo é varrer a página inicial da editora e extrair os links dos livros. Abrindo o código fonte da página, percebemos que ela tem a seguinte estrutura, que aqui está devidamente resumida para fins de apresentação:

<html>
<head>
  <!-- titulo, meta, etc -->
</head>
<body>
  <nav>
    <!-- menu do topo -->
  </nav>
  <section>
    <!-- links e imagens com os livros -->
  </section>
  <footer>
    <!-- menu do rodapé -->
  </footer>
</body>
</html>

Visualmente, a página tem a aparência da imagem abaixo:

/images/uploads/2014/10/cdc-parts.png

Felizmente a página está bem estruturada e todos os links que nos interessam estão dentro da área section, o que vai nos poupar trabalho.

Vamos criar uma função chamada get-links, que vai receber a URL do site em formato texto e vamos extrair todo o código HTML usando a função html-resource do Enlive.

(defn- get-links [url]
  (en/html-resource (URL. url)))

Perceba que a função html-resource exige que você converta a URL de texto para um objeto java.net.URL, para só então o passarmos como parâmetro para o Enlive.

Para evitar que o encadeamento de muitas funções torne o código difícil de ler, vamos alterá-lo para utilizar o operador -> e vamos guardar o resultado em um binding chamado links. Perceba que essa abordagem permite que possamos adicionar funções no final da lista de argumentos de -> sem diminuir a legibilidade do código.

(defn- get-links [url]
  (let [links (-> url
                  URL.
                  en/html-resource)]
    (pp/pprint links)))

Vamos então usar a função select, também do Enlive, que recebe como argumento um vector com as tags que você deseja extrair. Nós queremos somente os links, formados pela tag <a></a>, que estão contidas entre <section> e </section>.

(defn- get-links [url]
  (let [links (-> url
                  URL. 
                  en/html-resource
                  (en/select [:body :section :a]))]))

A função select vai nos retornar uma lista contendo um map para cada elemento HTML que obedecer aos nossos requisitos. Cada map tem um item :attrs que contém os atributos da tag HTML, incluindo a página para a qual o link está apontando. Ainda dentro da função get-links, vamos converter essa lista de mapas em uma lista que contenha apenas os endereços e para isso vamos usar a função map, que recebe como parâmetros a função que vai transformar cada item da lista e a lista a ser transformada. Para fins de didática, vamos suprimir o código que está dentro de let, para só no final da explicação mostrarmos a função completa.

  (map #((% :attrs) :href) 
       links)

Agora nossa função está retornando uma lista de links como na listagem abaixo, devidamente resumida:

("/products/livro-programador-apaixonado"
 "/products/livro-aspnet-mvc"
 "/products/livro-jpa-eficaz"
 "/products/livro-photoshop"
 "/products/colecao-frameworks-java"
 ...
 "/products/livro-ciencia-da-computacao-com-jogos"
 "/products/vale-presente")

Os links são relativos à URL da página inicial, então vamos adicionar o endereço que que foi passado para a função get-links para torná-los absolutos. Note que nessa lista existem links que não são de livros, mas de coleções e do vale presente. Podemos eliminá-los usando a função filter, que recebe como parâmetros uma função que retorna true ou false de acordo com cada item da lista, e a lista a ser filtrada que é passada como terceiro parâmetro. Os itens que fizerem a função retornar true ficam, e os demais não são incluidos.

(filter
  #(. % contains "livro")
  (map #(str url ((% :attrs) :href)) 
       links))

E agora nossa função retorna os endereços absolutos de todos os livros da editora, conforme a listagem abaixo:

("http://www.casadocodigo.com.br/products/livro-programador-apaixonado"
 "http://www.casadocodigo.com.br/products/livro-aspnet-mvc"
 "http://www.casadocodigo.com.br/products/livro-jpa-eficaz"
 "http://www.casadocodigo.com.br/products/livro-photoshop"
 ...
 "http://www.casadocodigo.com.br/products/livro-ciencia-da-computacao-com-jogos")

Agora que concluímos o primeiro passo da nossa pesquisa, vamos editar o código da nossa função main para que a função get-links seja chamada:

(defn -main [& args]
  (pp/pprint
    (get-links "http://www.casadocodigo.com.br")))

Vamos criar agora uma função chamada get-author, que vai receber cada um dos links e, usando as funções do Enlive que já conhecemos, vai extrair o nome do autor, ou dos autores, de cada um dos livros.

Os nomes dos autores ficam dentro de uma tag span marcada com a classe product-author-link. Mais uma vez o site bem construído nos ajuda na tarefa. Para pesquisarmos uma tag que esteja utilizando determinada classe, vamos separar tag e classe com um ponto final, como se fosse um arquivo CSS. Assim, o nosso span com a classe product-author-link vai virar :span.product-author-link. O código inicial não vai ficar muito diferente do que fizemos em get-links:

(defn- get-author [url]
  (let [authors (-> url
                    URL.
                    en/html-resource
                    (en/select [:span.product-author-link]))]
      (pp/pprint authors)))

Vamos usar o link do primeiro livro para testar. O resultado traz toda a informação da tag span dentro de um map que está dentro de uma lista, e não só o nome do autor.

(get-author "http://www.casadocodigo.com.br/products/livro-programador-apaixonado")

({:tag :span,
  :attrs {:class "product-author-link"},
  :content
  ("\n          \n            Chad Fowler\n          \n        ")})

Novamente vamos suprimir o código que está dentro do let para não poluir o texto. Vamos selecionar o map que está dentro da lista usando a função first e, em seguida, utilizar somente o valor que está na chave :content:

((first authors) :content)

O valor que estava em :content também é uma lista contendo o nome do autor. Além do nome do autor, o texto está poluído com quebras de linhas, simbolizado por \n e espaços em branco. Vamos utilizar a função replace do namespace clojure.string para remover esses caracteres indesejados. Essa função recebe como parâmetros o texto original, uma expressão regular indicando o que deve ser alterado e, por último, o texto a ser utilizado na alteração.

Vamos utilizar o operador -> para facilitar a leitura:

(-> ((first authors) :content)
    first
    (str/replace #"(\n|  )" ""))

Ao executar a função, temos o nome do autor limpo e sem caracteres indesejados:

(get-author "http://www.casadocodigo.com.br/products/livro-programador-apaixonado")
=> "Chad Fowler"

Tudo certo quando estamos lidando com um livro escrito por apenas um autor, mas quando temos coautorias a nossa função já não faz o que é esperado. Por exemplo, no livro de ASP.NET MVC, nossa função retorna o autor como Fabrício Sanchez e Márcio Fábio Althmann, e no livro de Lógica de Programação temos Paulo Silveira, Adriano Almeida. Tanto , como e são usados para separar os nomes dos autores. Vamos fazer a nossa função retornar um vector com os nomes dos autores separados utilizando a função split, do namespace clojure.string. Essa função recebe como argumentos o texto a ser dividido e uma expressão regular indicando onde o texto deve ser dividido:

(-> ((first authors) :content)
    first
    (str/replace #"(\n|  )" "")
    (str/split #"(, | e )"))

Agora temos os nomes dos autores separados corretamente dentro um vector, como no exemplo abaixo:

(get-author "http://www.casadocodigo.com.br/products/livro-programacao")
=> ["Paulo Silveira" "Adriano Almeida"]

Voltando à nossa função -main, vamos fazer com que a função get-author pega os autores de cada um dos links retornados pela função get-links. Vamos usar o operador ->> para deixar mais fácil de entender as transformações que estamos fazendo nos dados. A diferença para o -> é que o operador ->> passa o resultado da expressão como último parâmetro da expressão seguinte, enquanto o operador -> passa como primeiro. Isso é necessário para podermos passar a lista retornada por get-links para a função map da expressão seguinte:

(defn -main [& args]
  (pp/pprint
    (->> "http://www.casadocodigo.com.br"
         get-links
         (map get-author))))

A nossa função -main retornou uma lista contendo vários vector com os nomes dos autores. Um livro com mais de um autor vai retornar um vector com mais de um nome.

(["Chad Fowler"]
 ["Fabrício Sanchez" "Márcio Fábio Althmann"]
 ...
 ["Mauricio Tollin" "Rodrigo Gomes" "Anderson Leite"]
 ...
 ["André Backes"]
 ["Bruno Feijó" "Esteban Clua" "Flávio S. Correa da Silva"])

Vamos utilizar a função flatten para converter essa lista de vectors de diversos tamanhos em uma lista de uma dimensão. Isso vai nos permitir calcular quantas vezes cada nome aparece na lista por meio da função frequencies. Como cada vez que o nome aparece é um livro escrito por aquele autor, podemos entender que frequencies vai atribuir o número de livros que aquele autor tem publicado pela Casa do Código:

(defn -main [& args]
  (pp/pprint
    (->> "http://www.casadocodigo.com.br"
         get-links
         (map get-author)
         flatten
         frequencies)))

Agora temos uma lista não ordenada com o nome do autor e a quantidade de livros publicados:

{"Caio Ribeiro Pereira" 1,
 "Fabrício Sanchez" 1,
 "Alexandre Saudate" 2,
 "Chad Fowler" 1,
 ...
 "Gabriel Schade Cardoso" 1,
 "Guilherme Moreira" 1}

Nosso próximo passo é ordenar essa lista, deixando os autores com maior número de livros no topo. Para isso vamos usar a função sort, que permite que você informe uma função para selecionar o critério de ordenação, que no nosso caso vai ser a função last, que retorna o último item de uma lista, e também a função que vai ser usada para comparar um item com outro e definir quem vem primeiro, que nosso caso vai ser >. No caso do last, convém deixar claro que vamos pegar o último item do que vai ser comparado. No caso de "Caio Ribeiro Pereira" 1, o último item é o número 1. Se quisessemos ordenar por nome, usariámos a função first.

Em seguida vamos dividir essa lista em n grupos, de acordo com a quantidade de livros publicados usando a função partition-by. No nosso exemplo teremos um grupo com autores que tenham dois livros publicados e outro com autores com apenas um livro. Como nos interessa apenas os autores que mais publicaram livros, vamos usar a função first para selecionarmos apenas o primeiro grupo. Com isso nosso código ficará assim:

(defn -main [& args]
  (pp/pprint
    (->> "http://www.casadocodigo.com.br"
         get-links
         (map get-author)
         flatten
         frequencies
         (sort-by last >)
         (partition-by last)
         first)))

Agora temos o resultado abaixo:

(["Alexandre Saudate" 2]
 ["Sérgio Lopes" 2]
 ["Paulo Silveira" 2]
 ["Tárcio Zemel" 2]
 ["Anderson Leite" 2]
 ["Mauricio Aniche" 2]
 ["Gilliard Cordeiro" 2]
 ["Hébert Coelho" 2]
 ["Eduardo Guerra" 2]
 ["Rafael Steil" 2])

Agora que temos a lista dos maiores autores, vamos otimizar um o nosso código adicionando uma dose de processamento paralelo. Note que a função get-links retorna uma lista de endereços que é passada como parâmetro para a função map, que passa endereço por endereço para a função get-author, sequencialmente. Se trocarmos map por pmap, serão criadas threads que executarão get-author em paralelo, melhorando o tempo total do nosso código. A quantidade de thread criadas por pmap está diretamente ligada ao número de núcleos ou processadores que sua máquina tiver.

Porém, o uso de pmap pode trazer um problema: é comum que a aplicação fique congelada por até um minuto após a execução do pmap por conta de questões internas de timeout de threads e configurações do pool utilizado pelo Clojure. Para resolver isso, devemos solicitar ao Clojure que finalize todas as thread que não estiverem sendo usadas, liberando-as para que o programa possa ser encerrado. Para isso usamos a função shutdown-agents na última linha da função -main.

Agora temos o nosso código funcionando alguns segundos mais rápido e trazendo os autores que mais publicaram livros pela Casa do Código.

Código completo

(ns autores.core
  (:require [clojure.pprint :as pp]
            [net.cgrand.enlive-html :as en]
            [clojure.string :as str])
  (:import  [java.net URL]))

(defn- get-links [url]
  (let [links (-> url
                  URL. 
                  en/html-resource
                  (en/select [:body :section :a]))]
      (filter
        #(. % contains "livro")
        (map #(str url ((% :attrs) :href)) 
             links))))

(defn- get-author [url]
  (let [authors (-> url
                    URL.
                    en/html-resource
                    (en/select [:span.product-author-link]))]
    (str/split
      (str/replace
        (first
          ((first authors) :content))
        #"(\n|  )" "")
      #"(, | e )")))

(defn -main [& args]
  (println "Os autores com mais publicações na Casa do Código:")
  (pp/pprint
    (->> "http://www.casadocodigo.com.br"
         get-links
         (pmap get-author)
         flatten
         frequencies
         (sort-by last >)
         (partition-by last)
         first))
  (shutdown-agents))

Fazendo Mágica Com O REPL Do Clojure

| | Comments


Caso você não esteja íntimo com o REPL do Clojure, recomendo a leitura do texto anterior para aprender o básico.

É recomendado também que você leia as mensagens emitidas pela aplicação com a voz do Cid Moreira. Para uma versão em inglês do artigo estou considerando sugerir as vozes do Morgan Freeman ou do Stephen Fry.

As pessoas me perguntam se um REPL é equivalente ao IRB do Ruby ou o modo interativo do Python. No artigo anterior eu comentei que o REPL é bem mais do que isso, mas ainda não fui convincente o bastante.

Nesse artigo eu pretendo demonstrar como podemos modificar a aplicação enquanto ela é executada, sem necessidade de reiniciá-la ou de esperar para que a compilação termine.

Primeiro, vamos instalar uma ferramenta chamada Leiningen, que pode ser facilmente instalada a partir de http://leiningen.org/#install.

O Leiningen é uma ferramenta que automatiza uma série de processos cotidianos do desenvolvimento de uma aplicação, além de também cuidar das dependências da aplicação. Pense nele como um primo bonito do Maven ou do Gradle.

Primeiro, vamos criar uma aplicação usando o Leiningen.

No terminal, digite:

lein new misterm

Se for a primeira vez que você utiliza o Leiningen, algumas dependências serão baixadas para a sua máquina.

$ lein new misterm
Generating a project called misterm based on the 'default' template.
The default template is intended for library projects, not applications.
To see other templates (app, plugin, etc), try `lein help new`.

Será criado um diretório chamado misterm, onde teremos nossa aplicação, e dentro desse diretório, edite um arquivo project.clj.

; O seu arquivo project.clj vai ter essa cara
(defproject misterm "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]])

O conteúdo do arquivo project.clj nada mais é do que um código Clojure válido, sendo defproject uma macro que armazena as configurações do projeto em algum estado global. Vou deixar para explicar macros em outro artigo.

Para que a mágica funcione, vamos adicionar o nREPL na nossa aplicação. nREPL é uma biblioteca que permite que o REPL do Clojure seja acessado remotamente, normalmente através de uma porta TCP. O próprio REPL do Clojure disponibilizado pelo Leiningen já vem com o nREPL, como vamos ver mais para frente.

Para criarmos um projeto executável, precisamos informar no projeto em qual namespace estará o entrypoint da aplicação. Pense num namespace como um package do Java, apesar de existirem grandes diferenças entre ambos. Para a nossa explicação, considerar um como o outro basta.

Vamos editar o arquivo, adicionando a nova dependência dentro do vetor :dependencies e também a opção :main misterm.core antes do parêntese final.

O final do arquivo vai ficar assim:

; final do arquivo project.clj
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/tools.nrepl "0.2.5"]]
  :main misterm.core)

Agora vamos editar o arquivo core.clj, que está dentro de src/misterm. Perceba que o nome da aplicação é usado por padrão como parte do nome do namespace.

Encontraremos um arquivo assim:

(ns misterm.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

Aqui está sendo declarado o namespace misterm.core, e em seguida uma função foo que simplesmente imprime o texto Hello, World!.

Vamos modificar a declaração do namespace para que possamos usar o nREPL em nossa aplicação.

(ns misterm.core
    (:require [clojure.tools.nrepl.server :as nrepl]))

Em seguida vamos iniciar o servidor do nREPL na porta 12345.

(defonce server (nrepl/start-server :port 12345))

Estamos usando defonce aqui para garantir que, ao acessarmos a aplicação via nREPL, o servidor não seja iniciado mais de uma vez. Caso isso aconteça teremos um erro porque a porta 12345 já estará em uso.

Vamos apagar a função foo e vamos criar uma chamada magic:

(defn magica []
  (Thread/sleep 2000)
  (println "Mostre-nos o seu segredo, Mister M"))

Nossa função simplesmente espera dois segundos e, sem seguida, imprime na tela um texto.

Agora vamos criar a função -main, que serve como entrypoint da aplicação, assim como o método main de uma aplicação Java.

(defn -main [& args]
  (loop []
    (magica)
    (recur)))

O argumento & args indica que a função -main aceita uma quantidade indefinida de parâmetros, ou mesmo nenhum, exatamente como acontece com o método main(String ... args) do Java.

Os operadores loop e recur são usados para simularmos tail call recursion no Clojure, o que é assunto para outro texto. Para o que precisamos, basta saber que criamos um loop infinito com eles. Dentro desse loop infinito estamos invocando a função magica.

Tudo pronto, vamos salvar o arquivo e voltar para o terminal. Execute o comando lein run para executar a aplicação e a cada dois segundos será exibida a mensagem. Lembre-se de usar a voz do Cid Moreira ao ler.

$ lein run
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M

Vamos abrir um novo terminal e executar o comando lein repl :connect 127.0.0.1:12345.

$ lein repl :connect 127.0.0.1:12345
Connecting to nREPL at 127.0.0.1:12345
REPL-y 0.3.5, nREPL 0.2.6
Clojure 1.6.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0-b132
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=>

O prompt indica que estamos no namespace padrão do Clojure, chamado user. Vamos para o namespace onde estão as nossas funções para verificar se estamos mesmo conectados na aplicação.

(ns misterm.core)

Agora vamos executar a função magica. Se tudo foi feito corretamente até agora, vamos esperar dois minutos e então ler a mensagem Mostre-nos o seu segredo, Mister M.

(magica)
; Mostre-nos o seu segredo, Mister M

Tudo certo. Agora vamos modificar a aplicação enquanto ela está sendo executada.

Dê uma olhada no primeiro terminal. A essa altura a nossa mensagem já encheu a tela.

De volta ao segundo terminal, o que está com o REPL aberto, reescreva o conteúdo da função magica:

(defn magica []
  (Thread/sleep 1000)
  (println "Aaaaaahhhhh, Mister M!"))

A função magica já existia, mas estamos redefinindo em tempo de execução. Se você tentar fazer isso no IRB, por exemplo, receberá uma mensagem de erro.

Agora olhe novamente no primeiro terminal, aquele que estava com a tela cheia de mensagens.

Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Mostre-nos o seu segredo, Mister M
Aaaaaahhhhh, Mister M!
Aaaaaahhhhh, Mister M!
Aaaaaahhhhh, Mister M!

A mensagem mudou! Não só isso. Agora ela leva um segundo para ser exibida na tela, ao invés dos dois originais. Nós mudamos a aplicação enquanto ela estava sendo executada.

Claro que toda mágica tem lá seus pontos a serem considerados. Se você reiniciar a aplicação a mensagem original será novamente exibida, já que alteramos a aplicação enquanto ela estava sendo executada, e não seu código fonte.

Esse recurso é muito útil para corrigir erros sem derrubar a aplicação, para desenvolver aplicações de maneira verdadeiramente incremental e iterativa, e também para aumentarmos a velocidade do ciclo de desenvolvimento ao máximo. Pense que dessa forma não precisamos mais derrubar a aplicação, recompilar, executar novamente, aguardar o tempo de carga da JVM, que não é pouco. Numa visão otimista, caso você ganhe dez segundos em cada iteração do seu ciclo de desenvolvimento, ao final do dia você terá economizado muito tempo, além de ter mantido o foco naquilo que realmente importa. É sabido que essas mudanças de contexto, por mais rápidas que sejam, atrapalham a concentração e a produtividade.

Editores como Emacs e ViM têm plugins que se conectam ao nREPL, fazendo com que você nem ao menos precise fechar a janela de edição de código para alterar a aplicação. Já o Lighttable oferece um modo chamado InstantREPL, onde seu código é avaliado na própria edição, exibindo os valores enquanto você programa.

O REPL é uma ferramenta poderosa não só no Clojure, mas em praticamente todos os dialetos LISP. Experimente fazer isso com a sua linguagem preferida e veja se consegue ter tanto ganho de produtividade.

P.S.: Jon Garret conseguiu inclusive corrigir um bug em um satélite em órbita graças ao uso do REPL. Leia mais aqui.

Integração Contínua E Entrega Contínua De Modo Fácil Com Wercker

| | Comments


TL;DR Wercker é uma plataforma de integração contínua, baseado em containers e focado em facilitar o build e deploy de aplicações na nuvem. Totalmente integrado com Github, BitBucket, Heroku, AWS e OpenShift, em questão de minutos e com apenas alguns cliques é possível configurar o build e deploy da sua aplicação. O serviço está em beta, permitindo repositórios privados grátis!

Como funciona

Cada vez que você faz um git push, o Wercker recebe um sinal que houve atualização, baixa o código do repositório, monta o ambiente do build definindo variáveis de ambiente, rodando as migrations, etc, e executa os testes ou qualquer outro passo que você definir, como compilar os assets, etc.

Uma vez que o build execute com sucesso, fica disponível para deploy na plataforma que escolher, como Heroku ou AWS. Da mesma forma do build, o deploy consiste em passos definidos, como rodar o Capistrano ou fazer push no repositório do Heroku.

Durante as duas etapas deste processo de build e deploy, o Wercker ainda pode notificar seu HipChat ou Slack :)

Por que é diferente?

Existem vários serviços de CI com a mesma proposta, como o Travis ou o Codeship. Mas o Wercker permite total customização do ambiente do build e do deploy, uma vez que é baseado em containers.

Você pode escolher um container oficial para seu build, como Ubuntu com Ruby instalado, por exemplo, ou pode procurar um container disponibilizado por outro usuário no diretório do Wercker.

Mas se precisar de algo muito específico, você pode criar seu próprio container (ou box) com pacotes e serviços que desejar, versões, etc, e utilizá-lo no seu build. E você também pode disponibilizar este box no diretório do Wercker para outros usuários utilizarem!

Boxes

Um box é um ambiente virtual empacotado e versionado com pacotes e serviços necessários para executar o build. Em outras palavras, é o container “base” que rodará seu build.

Para utilizar um box, basta declarar no início do wercker.yml:

1
box: wercker/ubuntu12.04-ruby2.0.0

Tem box para Ruby, Node.js, Go, Java/Android e até para Docker. Na prática, os boxes oficiais vão atender a maioria dos casos, mas você pode criar seu próprio box como desejar.

Para definir um box, basta criar um repositório com o arquivo wercker-box.yml descrevendo a base (Ubuntu, por exemplo), o provisionamento (Chef, Puppet, etc, e adicioná-lo ao build pipeline do Wercker.

Uma vez que o build passar, é possível fazer o deploy para disponibilizá-lo no diretório do Wercker. Assim, é possível utilizar este box no processo de build da sua aplicação.

Mais informações: http://devcenter.wercker.com/articles/boxes/

Services

Serviços são boxes prontos disponibilizados pelo Wercker, como MySql, PostgreSql, MongoDB, Redis, etc. Em outras palavras, é um container configurado com um serviço ready to use.

Para utilizar um service, basta adicionar ao seu wercker.yml:

1
2
3
4
box: wercker/ruby
services:
    - wercker/mongodb
    - wercker/redis

Mais informações: http://devcenter.wercker.com/articles/services/

Steps

As steps são os “comandos” executados pelo Wercker no build ou deploy da aplicação, como bundle-install, database-migrate, script, etc.

Você pode utilizar as steps disponibilizadas pelo Wercker ou criar suas próprias steps, executando os comandos que julgar necessário no seu build ou deploy.

Para definir as steps do seu build, basta adicionar o wercker.yml:

1
2
3
4
5
6
7
8
9
10
11
12
build:
    steps:
        - rvm-use:
            version: 2.1.2
        - bundle-install
        - rails-database-yml
        - script:
            name: db migrate
            code: bundle exec rake db:migrate
        - script:
            name: rspec
            code: bundle exec rspec

Mais informações: http://devcenter.wercker.com/articles/steps/

Deploy pipeline

O Wercker permite fazer o deploy da sua aplicação em vários PaaS como Heroku automaticamente ou definir o processo de deploy manualmente, utilizando o Capistrano ou executando um script de sua preferência.

Durante a processo de deploy, é possível definir chaves SSH específicas, variáveis de ambiente, etc.

Para configurar seu deploy, adicione ao wercker.yml (por exemplo):

1
2
3
4
5
6
7
8
deploy:
    steps:
        - heroku-deploy
        - s3sync:
            key_id: $AWS_ACCESS_KEY_ID
            key_secret: $AWS_SECRET_ACCESS_KEY
            bucket_url: $AWS_BUCKET_URL
            source_dir: build/

Mais informações: http://devcenter.wercker.com/articles/deployment/

#comofas?

O Wercker é legal, fácil, bla bla bla. Vamos à um exemplo prático, passo-a-passo, de como configurar seu projeto no Wercker, fazer o build e o deploy automaticamente no Heroku.

0 – Crie sua conta :)

1 – Escolha o repositório, adicione as chaves SSH, finalize;

2 – Adicione o arquivo wercker.yml ao seu projeto, parecido com este:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
box: wercker/rvm
services:
    - wercker/postgresql
build:
    steps:
        - rvm-use:
            version: 2.1.2
        - bundle-install
        - rails-database-yml
        - script:
            name: db migrate
            code: bundle exec rake db:migrate RAILS_ENV=test
        - script:
            name: rspec
            code: bundle exec rspec -f d
    after-steps:
        - hipchat-notify:
            token: $HIPCHAT_TOKEN
            room-id: 123456
            from-name: wercker

Mais informações: http://devcenter.wercker.com/articles/werckeryml

3 – Dispare o build com git push;

1
2
git commit -m "Configurando Wercker"
git push origin master

4 – Configure o deploy no Heroku:

5 – Dispare o build/deploy com git push ou manualmente:

6 – \o/

Espalhe a palavra!

Fácil né? Não há mais choro desculpas para não utilizar um CI em seu projeto.

Configure seu projeto, tire um print screen do primeiro build com sucesso e Tuite para o @wercker para receber stickers de grátis!

Compartilhe este post! Dúvidas e sugestões nos comentários abaixo. Sucesso!

Alguns Truques Com O REPL Do Clojure

| | Comments


O REPL é uma das ferramentas mais úteis para se programar em Clojure. Se você está chegando do Ruby ou do Python está mais do que acostumado a usar o IRB ou o modo interativo do Python. Veremos no decorrer do capítulo que o REPL é bem mais do que um prompt da linguagem, que serve apenas para que instruções sejam testadas.

Existem alguns atalhos e funções auxiliares que tornam o uso do REPL bem mais produtivo. Por mais que escrever diretamente no REPL não seja tão confortável quando no seu editor preferido, algumas vezes isso acaba sendo necessário.

Qual é mesmo o nome daquela função?

As funções da biblioteca padrão do Clojure vem com um texto explicativo, onde você pode se situar sobre como utilizá-las.

Podemos pesquisar alguma palavra que estiver dentro desses textos para encontrar a função que queremos, mas não lembramos o nome. Para isso, usamos find-doc, seguido da palavra ou trecho de texto relacionado ao que queremos.

Vamos supor que eu esteja procurando algo sobre ::sockets::. Basta digitar (find-doc “socket”) no REPL.

(find-doc "socket")
; -------------------------
; clojure.tools.nrepl/connect
; ([& {:keys [port host transport-fn], :or {transport-fn
;  transport/bencode, host "localhost"}}])
;  Connects to a socket-based REPL at the given host (defaults to
;  localhost) and port, returning the Transport (by default clojure.

; e mais um monte de coisas

No nosso exemplo, encontramos a função connect, que está no namespace clojure.tools.nrepl.

Se você lembra de alguma parte do nome da função, então pode usar a função apropos, passando como parâmetros o trecho do nome ou uma expressão regular. Não se preocupe com expressões regulares agora, pois veremos esse assunto em detalhes mais para frente.

Vamos supor que eu esteja manipulando vetores e não lembre o nome da função, mas saiba que a estrutura chama-se vector:

(apropos "vector")
; (vector-of vector vector? vector-zip)

E agora você pode usar a função doc para ver a documentação daquela que mais se parecer com o que você estiver procurando:

(doc vector?)
; -------------------------
; clojure.core/vector?
; ([x])
;   Return true if x implements IPersistentVector

Existe uma variação de apropos chamada apropos-better, que informa também o namespace da função quando ela não estiver dentro do namespace clojure.core ou dentro do namespace em que você estiver no momento:

(apropos-better "vector")
; (vector vector-of vector? clojure.zip/vector-zip)

Um pouco de Bash na sua vida

Quando você usa o REPL por dentro do Leiningen, alguns atalhos já conhecidos pelos usuários de Bash estão disponíveis, mesmo para quem está usando o Leiningen no Windows.

O primeiro deles é a tecla ::TAB::, que exibe os nomes de funções que começam com o que você já digitou.

Por exemplo, vou digitar map e pressionar ::TAB::

(map
; map           map-indexed   map?          mapcat        mapv

Outra combinação que agiliza bastante o trabalho é a combinação ::Control L::, ou ::Command L:: se você estiver usando MacOS, que limpa os resultados das expressões anteriores e mantém apenas a expressão que você estiver digitando no momento.

Existe também a combinação ::Control R::, ou ::Command R::, que completa o que você estiver digitando usando o histórico de comandos do REPL. Pressionando essa combinação mais de uma vez vai alternar entre todas as combinações já utilizadas que contenham o texto que você já digitou.

Usar as setas ::para cima:: ou ::para baixo:: permite que você navegue nos comandos utilizados recentemente.

Recuperando os últimos resultados

Existem também símbolos especiais que guardam os resultados das últimas expressões e exceções. Eles são 1, 2 e 3 para os valores e e para a última exceção, ou erro, que ocorreu:

(+ 1 2)
; 3

(* 2 4)
; 8

(/ 8 2)
; 4

(println "Resultados anteriores:" *1 *2 *3)
; Resultados anteriores: 4 8 3

(/ 1 0)
; ArithmeticException Divide by zero

(println "Último erro:" *e)
; Último erro: #<ArithmeticException java.lang.ArithmeticException:
;   Divide by zero>

Consultando o código fonte

Algumas vezes é bom ter acesso ao código fonte de determinada função ou macro para que possamos entender melhor como ela funciona. Enquanto eu escrevia este artigo, fiz isso constantemente para descobrir como as coisas funcionam por baixo dos panos.

Infelizmente, nem sempre é simples ir até o site onde o código fonte do Clojure está disponível e procurar o arquivo em que aquela função está definida.

Pior ainda quando a versão que está lá é diferente da versão que você está usando no momento. E fica ainda pior quando você não tem acesso ao código fonte da biblioteca que estiver utilizando.

Para nos ajudar, existe a macro source, que recebe como parâmetro o nome da função, sem aspas, e exibe o respectivo código fonte, quando possível.

Existem casos em que isso não é possível, como quando você tentar ler o fonte de uma forma especial ou de um código que foi compilado utilizando AOT (veremos isso em detalhes mais para frente).

Vamos exibir o código fonte da função +, responsável por somar dois ou mais números:

(source +)
; (defn +
;   "Returns the sum of nums. (+) returns 0. Does not auto-promote
;   longs, will throw on overflow. See also: +'"
;   {:inline (nary-inline 'add 'unchecked_add)
;    :inline-arities >1?
;    :added "1.2"}
;   ([] 0)
;   ([x] (cast Number x))
;   ([x y] (. clojure.lang.Numbers (add x y)))
;   ([x y & more]
;      (reduce1 + (+ x y) more)))

Note que temos acesso a todos os detalhes internos da função +, incluindo sua documentação e mais algumas informações que são úteis para o compilador ou para alguma função que gere documentação automaticamente.

Ao tentarmos ver o código fonte de uma forma especial ou de algum código escrito nativamente em Java, receberemos uma mensagem de que o código fonte não foi encontrado:

(source Thread/sleep)
; Source not found

Há muito mais recursos no REPL do Clojure, inclusive no que diz respeito a integração com o seu editor preferido, mas vou deixar isso para outro artigo.

Sowing the Seeds of Code

| | Comments


Seguindo a filosofia de tirar a bunda da cadeira, tive a oportunidade de falar em excelentes eventos esse ano, conhecer muita gente interessante e dar os primeiros passos no sentido de disseminar a linguagem Clojure no Brasil e organizar comunidades em São Paulo e no Rio de Janeiro.

Coding Dojo

Em Maio tive a honra de ter sido convidado a apresentar um coding dojo de Clojure na Das Dad, que acabou tendo uma aceitação bem maior do que eu esperava.

Tirando o fato do trânsito de São Paulo ter me derrubado e me feito chegar atrasado, conseguimos dar uma boa olhada na linguagem e atiçar a curiosidade. Quem quiser dar uma olhada no que foi feito, o código está no GitHub.

Anhanguera

Ainda em Maio, Alexandre Borba e eu fomos convidados a palestrar na unidade de Osasco da Anhanguera Educacional. Borba apresentou os conceitos de Coding Dojo, Katas e o formato Randori para os alunos, enquanto eu apresentei conceitos de orientação a objetos com JavaScript.

Os organizadores do evento prepararam um espaço para os palestrantes atrás do palco que era muito melhor do que qualquer camarim que eu já tenha visto nas vezes em que toquei por aí.

Alguns alunos perguntaram sobre o uso de JavaScript no mercado de trabalho, o que me deu a certeza de que uma palestra menos técnica e mais focada na realidade deles teria sido melhor aproveitada.

2º ENCATEC

O pessoal da Adapti, empresa júnior encubada no CEUNES-UFES, em São Mateus-ES, me convidou para o Segundo Encontro Norte-Capixaba de Tecnologia.

Lá tive o prazer de assistir a palestra da Professora Mariella Berger, do projeto IARA, que apresentou o “famoso carro que atropelou a Ana Maria Braga” e o projeto da Universidade Federal do Espírito Santo que junta automatização, robótica, inteligência artificial, sistema de visão e mais um monte de coisas bacanas usando soluções de código aberto.

Tive a oportunidade também de dividir espaço com meus ídolos Álvaro Justen, o Turicas, que falou sobre software livre; Ricardo Valeriano, que fez uma apresentação hardcore sobre Paralelismo e Concorrência com Ruby; e Luiz Rocha apresentando conceitos de sistemas distribuídos.

Eu falei sobre as aplicações de programação funcional em ambientes corporativos, visto que os alunos da UFES “aprendem” programação funcional e Haskell logo no primeiro semestre de uma maneira bem deficiente.

Assim como aconteceu na Anhanguera, saí com a impressão de que um tema menos técnico teria sido melhor aproveitado pelos alunos. O fato dos alunos serem pagos com horas de atividades extra-curriculares também fez com que muitos estivessem ali obrigados, estando pouco interessados nos temas apresentados.

TDC SP

Em Julho tivemos a etapa de São Paulo do The Developers Conference, evento itinerante organizado pela GlobalCode.

Eu tive a oportunidade de apresentar a primeira palestra da trilha de JavaScript e HTML5, na Quarta-feira, e apresentei vários recursos menos conhecidos do JavaScript, além de ter sorteado uma cópia do meu livro.

Como eu utilizei o manjadíssimo recurso de usar animais para explicar herança, um dos presentes comentou depois, no Twitter, que acha idiota a analogia com animais, visto que ele nunca utilizou animais nos sistemas que já ajudou a desenvolver. Eu achei que seria desnecessário dizer, mas o problema aí mora no fato do colega não perceber que eu estava explicando um conceito de uma forma que qualquer pessoa possa entender. Uma vez que você entenda o conceito, você pode extrapolá-lo para a sua realidade, seja utilizando herança para definir Pessoas Físicas e Jurídicas, seja para definir classes fiscais e naturezas de operação da forma que for mais conveniente.

O ponto negativo ficou por conta da organização, que atrasou o início das palestras em pouco mais de uma hora, não apresentou justificativas nem um pedido de desculpas aos presentes.

No Domingo, último dia do evento, fui mentor de Clojure no Coding Dojo que aconteceu na trilha de Ruby. Ao mesmo tempo e na mesma sala havia máquinas com Clojure, Scala e Ruby para resolver o mesmo problema. Os participantes saiam de uma máquina e iam para outra, onde podiam ver as diferenças e características de cada uma das linguagens.

Fiquei feliz ao perceber que o Clojure causou uma boa impressão e, novamente, despertou o interesse.

QCon SP

O grande evento do ano para desenvolvedores, infelizmente, caiu nos mesmos dias do RubyConf, evento da Locaweb voltado para a comunidade Ruby e Rails. Ouvi coisas muito boas sobre o RubyConf desse ano, e foi uma pena que eu não estive por lá.

Como acontece todos os anos, o nível das palestras foi muito bom e, pela primeira vez, tive a honra de ter sido convidado para falar sobre Clojure na trilha “Fronteiras do Desenvolvimento”.

Para minha surpresa, a sala ficou lotada. Muita gente em pé, muita gente interessada e várias pessoas vieram falar comigo após a palestra para tirar dúvidas ou mesmo para contar suas experiências ao adotar Clojure em suas empresas.

Não tenho dúvidas de que, profissionalmente falando, esse foi o ponto alto do ano para mim. Só tenho a agradecer aos presentes e à organização pelo interesse e pela oportunidade.

O ponto negativo, e até certo ponto divertido, ficou por conta da novelinha criada por um dos palestrantes ao falar mal de SOA. Os representantes de um dos patrocinadores, que vendem consultoria no assunto, ao se sentirem ofendidos, iniciaram um festival de resmungos passivo-agressivos como se um bom diálogo fosse construído dessa forma. O evento em si e a organização nada tiveram a ver com isso, mas foi interessante acompanhar algumas reações e ver quem as pessoas realmente são quando o calo dói.

(clj-sp)

Entre um evento e outro, percebendo o interesse crescente na linguagem Clojure, resolvi chamar os desenvolvedores das listas Clojure Brasil e Clojure BR para participar do (clj-sp), o Grupo de Usuários Clojure no Brasil. Somos o primeiro grupo do tipo no Brasil e, por mais estranho que pareça, nosso primeiro encontro foi no Rio de Janeiro.

Organizado pelo nosso amigo Giuliani, sete desenvolvedores se reuniram num bar da Lapa, o equivalente carioca da Vila Madalena e trocamos experiências sobre o uso de programação funcional em geral e Clojure em particular em nossos respectivos trabalhos.

Dois dias depois tivemos o primeiro encontro em São Paulo, nas dependências da iMasters, contando com uma apresentação minha sobre a sintaxe do Clojure, Konrad Scorciapino falando sobre Datalog e Jonas Fagundes compartilhando sua experiência com Clojure em startups.

O auditório da iMasters ficou pequeno para tanta gente interessada e eu fiquei impressionado como apareceu muito mais gente do que eu estava esperando.

Dia 26 de Setembro, última Quinta-feira do mês, teremos o segundo encontro. Caso você tenha interesse, as informações estão em [http://www.meetup.com/clj-sp/].

O que vem por aí

  • 7masters JavaScript Já 25 de Setembro será dia de 7masters de JavaScript na iMasters. Eu e mais seis especialistas vamos apresentar lightining talks de sete minutos sobre algum assunto bacana relacionado à linguagem. Acesse [setemasters.imasters.com.br/edicoes/javascript/] e compareça.

  • DevDay Em outubro vou falar novamente sobre JavaScript no DevDay, um evento muito bacana voltado para desenvolvedores de software que vai acontecer em Belo Horizonte.

Vai ser uma chance bacana de conhecer vários desenvolvedores que eu admiro e aprender com quem realmente conhece do assunto.

  • Programação funcional E, finalizando, estou escrevendo meu segundo livro, mais focado em programação funcional e como isso pode salvar a sua pele no seu trabalho ou nos seus projetos pessoais. É um livro bem mais denso e especializado que o primeiro, mas estou tentando manter o mesmo tom didático e fácil de assimilar.

Esse ano está sendo o ano da colheita de tudo o que eu plantei em 2012 e, enquanto isso, estou me preparando para colher mais frutos no ano que vem. Vamos ver no que dá.

[Off-Topic] Transforme Seu Roteador Wireless Em Um NAS, MediaServer UPnP/DLNA E BitTorrent Client Com OpenWRT

| | Comments


TL;DR OpenWRT é uma distro Linux embarcável em roteadores que permite customizar e instalar serviços sem a necessidade de compilar um novo firmware. Em outras palavras, é um “firmware on steroids” para roteadores.

Mas por quê?

  • Economia. Apenas substituindo o firmware do roteador é possível adicionar funcionalidade e recursos, economizando uma grana preciosa com dispositivos semelhantes, como AppleTV, NAS dedicado, etc.
  • Funcionalidade. Provavelmente o roteador fica ligado 100% do tempo na sua casa, sendo utilizado apenas para compartilhar a internet. Praticamente um servidor disponível 24 horas por dia sem utilizaçao! Não mais…
  • Diversão. Substituir o firmware do seu roteador por uma distro baseada em Linux e configurar todos os serviços manualmente… é diversão pura!
  • Por que eu quero (e posso)!

Instalação

Atenção: existe a possibilidade (embora pequena) de algo sair muito errado e você ganhar um peso de papel. Prossiga por sua conta e risco… ou se tiver coragem!

O primeiro passo é descobrir o modelo do seu roteador e verificar a compatibilidade nesta página: http://wiki.openwrt.org/toh/start. Se não encontrar o modelo ou houver um aviso de não-compatibilidade, sinto muito mas não vai rolar para você.

Se seu roteador for compatível, haverá o target da imagem e um link para uma wiki com um how-to específico do modelo, instruções de instalação, resolução de problemas, etc. Procure pelo nome da release que você deverá baixar daqui http://downloads.openwrt.org. A imagem tem o formato openwrt-TARGET-generic-MODELO-VERSAO-squashfs-factory.bin

Por exemplo, meu roteador é um TP-Link WR1043ND. Procurando na página http://wiki.openwrt.org/toh/start, o target compatível com meu roteador é ar71xx:

Acessei a página de downloads, naveguei pelos diretórios da release, depois target e encontrei o arquivo openwrt-ar71xx-generic-tl-wr1043nd-v1-squashfs-factory.bin para download.

Quando baixar a imagem, é só fazer a atualização de firmware normalmente no seu roteador pelo admin:

Confirme e reze muito! Aguarde o roteador atualizar e reiniciar.

Deste ponto em diante, será necessário conectar no roteador com cabo, para setar uma senha de root e configurar o wifi. Este processo é relativamente trivial, basta utilizar a interface web do admin, sem segredo…

Uma vez definida a senha e o wifi configurado, é possível acessar seu roteador via ssh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ssh root@192.168.1.1
root@192.168.1.1's password:

BusyBox v1.19.4 (2013-03-14 11:28:31 UTC) built-in shell (ash)
Enter 'help' for a list of built-in commands.

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 ATTITUDE ADJUSTMENT (12.09, r36088)
 -----------------------------------------------------
  * 1/4 oz Vodka      Pour all ingredients into mixing
  * 1/4 oz Gin        tin with ice, strain into glass.
  * 1/4 oz Amaretto
  * 1/4 oz Triple sec
  * 1/4 oz Peach schnapps
  * 1/4 oz Sour mix
  * 1 splash Cranberry juice
 -----------------------------------------------------
root@OpenWrt:~#

Se você chegou até aqui, parabéns! Você teve coragem. E felizmente a parte difícil já passou…

Nota: dependendo do seu roteador, a versão do OpenWRT pode variar. Leia a wiki do modelo do seu roteador para instruções específicas e resolução de problemas.

Importante: caso o pior aconteça (como acabar a energia no meio do processo de flash da firmware) e você não queira utilizar seu roteador como peso de papel, tente seguir os procedimento de “debriking” em http://wiki.openwrt.org/doc/howto/generic.debrick

Configurando o NAS

Para o NAS, vamos montar as partições do HD externo e configurar uma partição de swap pois o roteador provavelmente não tem memória interna suficiente para dar conta do recado.

Neste exemplo, vou usar meu HD de 1TB com uma partição formatada em ext4 e outra partição de 1GB formatada como linux+swap. Não vou usar FAT32 nem NTFS e nem recomendo! A idéia aqui é deixar o HD plugado eternamente no roteador e acessá-lo pela rede.

Instalando os pacotes necessários

Para nossa sorte, o OpenWRT conta com um gerenciador de pacotes que facilita (e muito) a instalação das libs e módulos necessários para montar o HD externo e compartilhá-lo na rede via Samba ou NFS.

Logue no roteador via ssh e instale os seguintes pacotes:

1
2
root@OpenWrt:~# opkg update
root@OpenWrt:~# opkg install kmod-usb-storage block-mount kmod-fs-ext4

O ultimo pacote vai depender do sistema de arquivos do seu HD. Por exemplo, kmod-fs-ext2, etc. Veja os módulos disponíveis em http://wiki.openwrt.org/doc/howto/storage

Verifique se as partições já foram detectadas rodando blkid:

1
2
3
root@OpenWrt:~# blkid
/dev/mtdblock2: TYPE="squashfs"
/dev/sda1: LABEL="MEDIA" UUID="1ecad5f1-0000-0000-000f-e8b7f6ac651d" TYPE="ext4"

Montando as partições do HD

Agora vamos configurar o fstab para montar as partições automaticamente quando o roteador for ligado. Edite o arquivo /etc/config/fstab com o seguinte (use o vi):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
config global automount
        option from_fstab 1
        option anon_mount 0

config global autoswap
        option from_fstab 1
        option anon_swap 0

config mount
        option target   /mnt/media
        option device   /dev/sda1
        option fstype   ext4
        option options  rw,sync
        option enabled  1
        option enabled_fsck 0

config swap
        option device   /dev/sda2
        option enabled  1

No meu caso, vou montar a partição /dev/sda1 em /mnt/media, então é necessário criar o diretório de destino, ativar e iniciar o serviço fstab:

1
2
3
root@OpenWrt:~# mkdir /mnt/media
root@OpenWrt:~# /etc/init.d/fstab enable
root@OpenWrt:~# /etc/init.d/fstab start

Pronto, seu HD externo pode ser acessado em /mnt/media e o swap foi montado na segunda partição.

Compartilhando na rede

Vou utilizar NFS para compartilhar a partição na rede, mas você também pode utilizar Samba seguindo http://wiki.openwrt.org/doc/howto/cifs.server

1
2
root@OpenWrt:~# opkg update
root@OpenWrt:~# opkg install nfs-kernel-server

Edite (ou crie) o arquivo /etc/exports com o conteúdo:

1
/mnt/media 192.168.1.0/255.255.255.0(rw,sync,no_subtree_check)

Ative e inicie os serviços necessários:

1
2
3
4
root@OpenWrt:~# /etc/init.d/portmap start
root@OpenWrt:~# /etc/init.d/portmap enable
root@OpenWrt:~# /etc/init.d/nfsd start
root@OpenWrt:~# /etc/init.d/nfsd enable

Pronto! O server (roteador) está configurado. Para montar esta partição no client, por exemplo Ubuntu, siga:

1
2
3
$ sudo apt-get install nfs-common
$ sudo mkdir /media/nas
$ sudo mount -t nfs 192.168.1.1:/mnt/media /media/nas

Groovy! Seu NAS foi montado no seu desktop em /media/nas. Pode compartilhar seus arquivos a vontade!

Configurando o MediaServer (minidlna)

Logue no roteador e instale os pacotes necessários:

1
2
root@OpenWrt:~# opkg update
root@OpenWrt:~# opkg install minidlna

Para configurar, basta editar o arquivo /etc/config/minidlna como segue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
config minidlna config
  option 'enabled' '1'
  option port '8200'
  option interface 'br-lan'
  option friendly_name 'OpenWrt DLNA Server'
  option db_dir '/mnt/media/minidlna/db'
  option log_dir '/mnt/media/minidlna/log'
  option inotify '1'
  option enable_tivo '0'
  option strict_dlna '0'
  option presentation_url ''
  option notify_interval '900'
  option serial '12345678'
  option model_number '1'
  option root_container '.'
  list media_dir '/mnt/media'
  option album_art_names 'Cover.jpg/cover.jpg/AlbumArtSmall.jpg/albumartsmall.jpg/AlbumArt.jpg/albumart.jpg/Album.jpg/album.jpg/Folder.jpg/folder.jpg/Thumb.jpg/thumb.jpg'

Ative e inicie o serviço:

1
2
root@OpenWrt:~# /etc/init.d/minidlna enable
root@OpenWrt:~# /etc/init.d/minidlna start

Pronto! O MediaServer está configurado e compartilhando seus arquivos via DLNA na rede. Aqui na minha SmartTV aparece assim:

MiniDLNA na Samsung SmartTV

Configurando o BitTorrent client (transmission)

Vamos instalar os pacotes:

1
2
root@OpenWrt:~# opkg update
root@OpenWrt:~# opkg install transmission-daemon

E configurar o serviço editando /etc/config/transmission e alterando as opções a seguir (mantenha todas as outras configs):

1
2
3
4
5
6
7
8
9
10
11
12
13
config transmission
  option enabled 1
  option config_dir '/mnt/media/transmission/config'
  option download_dir '/mnt/media/transmission/complete'
  option incomplete_dir '/mnt/media/transmission/incomplete'
  option incomplete_dir_enabled true
  option ratio_limit 2.0000
  option ratio_limit_enabled true
  option rpc_authentication_required false
  option rpc_password ''
  option rpc_username ''
  option speed_limit_up 5
  option speed_limit_up_enabled true

Ative e inicie o serviço:

1
2
root@OpenWrt:~# /etc/init.d/transmission enable
root@OpenWrt:~# /etc/init.d/transmission start

O Transmission roda como um daemon, sendo controlado remotamente. Para isso, será necessário adicionar a seguintes regra de farewall, adicionando no final do arquivo /etc/config/firewall:

1
2
3
4
5
config rule
        option src *
        option proto tcp
        option dest_port 9091
        option target ACCEPT

Reinicie o firewall executando /etc/init.d/firewall restart

Para gerenciar seus torrents, instale a extensão .torrent to Transmission no Chrome, baixe o Transmission Remote GUI para Windows, Mac e Linux ou ainda o Remote Transmission para Android.

Transmission GUI

Caso seu roteador tenha espaço disponível, você pode instalar o pacote transmission-web para gerenciar seus torrents diretamente do navegador.

Ao adicionar um .torrent pelo GUI, o mesmo será baixado diretamente no seu roteador!

Conclusão

Embora relativamente complicado, todo procedimento para instalação e configuração não é nada diferente do que estamos acostumados a fazer diariamente no Linux, como devops, seja configurando uma VPS ou montando um NAS na rede.

A liberdade que o OpenWRT oferece é imensa. O repositório é recheado com pacotes bem úteis e muito fáceis de configurar. Sem falar na economia comparando com um aparelho como AppleTV (que não ofecererá tantos recursos) ou outros media servers do mercado.

E aí, tem coragem??? Poste sua experiência nos comentários! :)

Referências

[QuickTips] Do Wordpress Para Octopress/Jekyll No GitHub Pages

| | Comments


Quando migramos nosso blog do Wordpress para o GitHub Pages, escrevi um email para nossos autores com instruções resumidas para configurar e postar com Octopress/Jekyll. Percebi que dando um tapa nesse email, poderia publicá-lo aqui no blog como um guia rápido e talvez incentivar outros blogueiros a fazer o mesmo.

Por que GitHub Pages?

Corte de custos! Manter o blog no Wordpress requer um hosting, um banco de dados e um domínio. Reduzimos as despesas apenas para o registro de domínio (por enquanto).

Performance! GitHub Pages é estático, e conteúdo estático é servido naturalmente mais rápido.

Desafio! Estavamos “acostumados” ao Wordpress. Aprender Jekyll e a postar “commitando em um projeto” permite que tenhamos novas idéias, ou no pior dos casos, aprendamos novas tecnologias.

Requisitos

Para utilizar o GitHub Pages, crie um repositório com o nome usuariogithub.github.io, incluindo o “github.io”. O GitHub gerencia este repositório e publica o conteúdo estático no endereço http://usuariogithub.github.io

Agora, para gerar o conteúdo estático vamos usar o Octopress.

Instalação

Basta clonar o repositório do Octopress localmente:

1
$ git clone git@github.com:imathis/octopress.git

instalar as gems necessárias e em seguida rodar a rake para configuração:

1
$ rake setup_github_pages

e informar o seu repositório do GitHub Pages:

git@github.com:username/username.github.io.git

Pronto! Os remoting points do projeto serão configurados para seu repositório do GitHub, como segue:

1
2
3
4
5
6
$ git remote -v

octopress git@github.com:imathis/octopress.git (fetch)
octopress git@github.com:imathis/octopress.git (push)
origin     git@github.com:username/username.github.io.git (fetch)
origin     git@github.com:username/username.github.io.git (push)

Postando

Para criar um novo post, basta rodar a rake:

1
$ rake new_post["o titulo do seu post"]

o que vai criar o arquivo source/_posts/2013-09-17-o-titulo-do-seu-post.markdown. Escreva o conteúdo do seu post normalmente em Markdown (recomendo utilizar o Markup Editor) e execute:

1
$ rake generate

para gerar o site estático no diretório _deploy. Caso queira dar um preview no que será publicado, basta rodar:

1
$ rake preview

e acessar no browser http://localhost:4000.

Publicando

Quando terminar seu post, basta rodar:

1
$ rake deploy

para publicar o site no seu repositório do GitHub Pages.

Pronto! Não se esqueça de subir os fontes do site (branch source), commitando suas alterações e executando o classico git push.

Migrando

Caso já tenha um site publicado no Wordpress, você pode seguir este guia para importar todo o conteúdo na estrutura do Jekyll:

How to Migrate from WordPress to Jekyll Running on Github

Referências

Introdução a Linux Control Groups (CGroups)

| | Comments


Em tempos de Metodologias Àgeis, iniciativas como DevOps, adoção de Cloud Computing e derivados (SaaS, IaaS e PaaS), aplicações que demorariam meses, senão anos para estar na www, hoje em questão de dias, e por que não horas, é possível estar disponíveis ao usuário final.

Com a necessidade de ter os aplicativos de forma mais rápida em produção, a adoção e criação de PaaS (Platform as A Service) tem sido a nova “onda do verão” e tecnologias como LXC, Docker e CGroups atuam como o cerne dessa “wave”.

O que são CGroups?

CGroups é uma feature do Kernel que provê mecanismos para organização de Processos em forma de grupos e limita recursos de máquina como Consumo de CPU, memória e I/O para estes.

Curioso pra ver como funciona?

Situação de Exemplo

Para este exemplo teremos duas aplicações Sinatra e nosso objetivo será dedicar um grupo para cada aplicação limitando o consumo de memória para cada uma elas.

Para rodar o exemplo estarei utilizando um Ubuntu 12.04 64 bits.

Pré-Requisitos

Antes de mais nada precisamos instalar algumas dependências:

1
sudo apt-get install cgroup-bin libcgroup1

Com a instalação dos pacotes acima veremos que um novo filesystem foi montado em /sys/fs/cgroup

1
2
3
4
5
6
7
8
9
ls -al /sys/fs/cgroup

drwxr-xr-x 7 root root 140 Aug  6 09:38 .
drwxr-xr-x 6 root root   0 Aug  6 09:37 ..
drwxr-xr-x 6 root root   0 Aug  6 09:38 cpu
drwxr-xr-x 6 root root   0 Aug  6 09:38 cpuacct
drwxr-xr-x 6 root root   0 Aug  6 09:38 devices
drwxr-xr-x 6 root root   0 Aug  6 09:38 freezer
drwxr-xr-x 6 root root   0 Aug  6 09:38 memory

CGroups estão organizados por subsistemas conhecidos também como “resource controllers” responsáveis por gerenciar memória, cpu, dispositivos, entre outras coisas. Na organização acima cada diretório representa um Resource Controller.

CGConfig Service

Para gerenciar CGroups iremos utilizar a utilitário cgconfig instalado como o pacote libcgroup1. É interessante checar se o serviço está rodando antes de continuar :

1
sudo service cgconfig status

Caso não esteja inicie o serviço

1
sudo service cgconfig start

Existem duas formas de configurar CGroups com cgconfig, diretamente no arquivo de configuração /etc/cgconfig.conf’ ou via linha de comando, que será o meio que iremos utilizar.

Criando Grupos

Para criar um grupo, utilizamos o comando cgcreate passando como argumento quais controllers estarão associados a ele.

1
2
sudo cgcreate -g cpu,cpuacct,devices,memory,freezer:/sinatra1
sudo cgcreate -g cpu,cpuacct,devices,memory,freezer:/sinatra2

O argumento /sinatra* indica o caminho relativo do grupo dentro de cada Resource Controller. Ex : /sys/fs/cgroup/<resource_controller>/

Executando programas em um Grupo

Para executar determinado processo em um grupo utilizamos o comando cgexec passando como argumentos quais controllers estarão associados ao processo e o caminho do grupo que ele estará associado.

1
2
sudo cgexec -g *:/sinatra1 sh -c 'cd <path_to_sinatra1> && exec rackup -p 4567 -D'
sudo cgexec -g *:/sinatra2 sh -c 'cd <path_to_sinatra2> && exec rackup -p 4568 -D'

O asterisco (*) acima significa que o processo estará associado a todos os controllers.

Para checar a hierarquia criada:

1
2
3
4
ps xawf -eo pid,cgroup,args | grep ruby
 1476              \_  5:freezer:              \_ grep --color=auto ruby
 1418  5:freezer:/sinatra1?4:memo /usr/bin/ruby1.9.1 /usr/local/bin/rackup -p 4567 -D
 1454  5:freezer:/sinatra2?4:memo /usr/bin/ruby1.9.1 /usr/local/bin/rackup -p 4568 -D

Para setar os valores em determinado controller utilizamos o comando cgset. No caso abaixo estamos limitando o consumo de memória para o grupo sinatra1 em 256MB e para o grupo sinatra2 em 128MB.

1
2
sudo cgset -r memory.limit_in_bytes='256M' sinatra1
sudo cgset -r memory.limit_in_bytes='128M' sinatra2

Para checar a alteração:

1
2
cat /sys/fs/cgroup/memory/sinatra1/memory.limit_in_bytes
cat /sys/fs/cgroup/memory/sinatra2/memory.limit_in_bytes

Conclusão

O intuito deste artigo foi demonstrar um dos possíveis usos de CGroups. Caso a aplicação sinatra1 cair por estouro de memória ou alguma outra falha que não seja a destruição da máquina, a aplicação sinatra2 continuará funcionando.

Há mais a se explorar, poderíamos inserir limitação de I/O, consumo de banda, entre outras coisas. Poderíamos até criar nossa própria implementação de LXC, mas isso é assunto para um próximo encontro.

Os links abaixo exploram mais detalhes sobre o assunto :

Divirtam-se!

Extendendo Ruby Com C - Só Um Aperitivo

| | Comments


Extender Ruby em C não é complicado. É claro, você deve ao menos ter o conhecimento básico da linguagem C.

Vamos criar uma extensão que retorna uma simples String.

Primeiramente criamos o diretório onde estará nossa extensão :

1
$ mkdir <your_path>/1up4dev

Crie um arquivo chamado 1up4dev.c e dentro dele inclua o header “ruby.h”

1
#include <ruby.h>

Tudo em Ruby relaciona-se com o tipo VALUE. Para nosso exemplo, vamos criar um VALUE m1up4dev representando um módulo.

1
VALUE m1up4dev;

E para representar uma classe, abaixo deste módulo, a qual chamaremos de Talker, criaremos uma VALUE cTalker:

1
VALUE cTalker;

Nossa classe Talker precisa fazer algo, vamos adicionar uma simples função que retorna uma String.

1
2
3
4
static VALUE say_yeah(VALUE self){
  const char *sentence= "YEAH YEAH!";
  return rb_str_new2(sentence);
}

Na função say_yeah, VALUE self representa o objeto associado a função, sentence a String de retorno e a função rb_str_new2 converte o *char em uma Ruby String.

Para deixar esse código acessível no mundo Ruby, criaremos uma função chamada ‘Init_1up4dev’. Por convenção estas funções sempre começam com o prefixo ‘Init_’.

1
2
3
4
5
void Init_1up4dev(){
  m1up4dev = rb_define_module("1up4dev");
  cTalker = rb_define_class_under(m1up4dev, "Talker", rb_cObject);
  rb_define_singleton_method(cTalker, "say_yeah", say_yeah, 0);
}

A função ‘rb_define_module’ define um módulo no topo da hierarquia. Algo como:

1
2
module 1up4dev
end

A função ‘rb_define_class_under’ define uma classe abaixo de um módulo ou outra classe. Isso irá gerar:

1
2
3
4
5
module 1up4dev
  class Talker

  end
end

A função ‘rb_define_singleton_method’ é responsável por criar um método singleton em uma classe ou módulo, neste caso ele estará atrelado a class Talker.

Para rodar nosso exemplo, crie um arquivo chamado ‘extconf.rb’ contendo:

1
2
require 'mkmf'
create_makefile('1up4dev')

Executando o script, irá ser gerado um arquivo Makefile para executar o build da extensão.

1
$ ruby extconf.rb

Compile e instale a extensão:

1
$ make && make install

Para ver o código funcionando basta digitar o código abaixo em um ‘irb’ ou algo do gênero :

1
2
3
4
$irb(main):001:0> require '1up4dev'
true
$irb(main):002:0> 1up4dev::Talker.say_yeah
"YEAH YEAH!"

YEAH YEAH!!

Entendendo LISP, Finalmente.

| | Comments


A sintaxe invertida

Ao olhar um código LISP pela primeira vez, você se assusta.

Eu me assustei e não havia ninguém para me ajudar a entender.

Que bom que você está lendo isto para entender bem depressa e perder o medo.

Acredite ou não, o LISP não é invertido: as outras linguagens é que são inconsistentes.

Matematicamente falando, funções são expressas dessa forma:

y = f(x)

Para calcularmos o dobro de um número, teríamos:

y = dobro(21)

Note que estamos usando uma notação diferente: primeiro vem o operador dobro e, em seguida, vem o operando, ou parâmetro, 21. Chamamos isso de notação prefixa.

Já para executar um cálculo matemático, usamos a forma abaixo:

y = 21 * 2

Primeiro temos um operador 21, depois temos um operando responsável pela multiplicação e, finalmente, o segundo operando 2. Chamamos essa forma de notação infixa.

Nota: se você é um desenvolvedor Ruby, ignore essa última expressão. Em Ruby o cálculo acima utiliza internamente a notação prefixa onde 21 é um objeto, * é um método (ou uma mensagem, se preferir) e 2 é um parâmetro.

A coisa fica bagunçada quando misturamos as duas formas:

y = dobro(7 * 3)

Na expressão acima misturamos notação prefixa com infixa. Não há problema algum com isso, mas não é um bom exemplo de consistência.

Quando falamos em LISP, o primeiro item de uma lista é um operador e todos os demais são operandos.

Todo operador é uma função, macro ou forma especial, inclusive os operadores matemáticos. Não se preocupe em entender agora o que são macros ou formas especiais. Todo o resto da lista é considerado um valor, parâmetro ou operando.

Imagine agora que o símbolo + é uma função. Para calcularmos uma soma usaríamos o seguinte código:

+(1, 2)

Movendo os parênteses e removendo as vírgulas, a nossa soma inicial ficaria:

(+ 1 2)

Sabemos que dobro também é uma função. Para calcular dobro, usaríamos:

(dobro 12)

Percebam que agora temos uma regra que se aplica a todos os casos. Repetindo a expressão acima que mistura as notações infixa e prefixa usando as regras do LISP, teríamos:

(dobro (+ 7 3))

Talvez pela sua origem acadêmica e fortemente influenciada pela matemática, as implementações de LISP levam muito a sério a questão da consistência.

Os parênteses

Quando eu estava na quarta série, aprendi uma coisa chamada expressão numérica, que consistia em resolver um cálculo extenso atacando um pedaço por vez, organizadamente.

Cada pedaço desse cálculo ficava dentro de parênteses, colchetes ou chaves, dependendo do quão aninhado estivesse a expressão. Eu nunca mais vi esse tipo de hierarquia, mas era um jeito bacana de manter a organização.

Uma expressão numérica tem essa cara:

x = {1 + [3 * (5 + 7)]}

Resolvemos a expressão de dentro para fora:

x = {1 + [3 * (12)]}

x = {1 + [36]}

x = {37}

x = 37

Simples, não?

Agora vamos extrapolar o que aprendemos na quarta série para uma linguagem de programação, trocando chaves e colchetes por parênteses:

x = (1 + (3 * (5 + 7)))

Vamos substituir a nossa conhecida notação infixa pela prefixa.

x = (+ 1 (* 3 (+ 5 7)))

Pronto. Você tem uma expressão numérica com a cara do LISP, resolvendo da forma como a professora ensinou lá na quarta série: primeiro você resolve os parênteses de dentro, depois os próximos, até terminar.

Qualquer LISP que você encontrar pela frente, incluindo o Clojure, funciona exatamente dessa maneira.

Uma vantagem que isso traz é que você não precisa ficar se preocupando com precedência de operadores.

Imagine que você tem o código abaixo:

x = 3 * 2 + 1

y = 1 + 2 * 3

Os valores de x e y serão iguais? Sim, ambas as variáveis contém o número 7, mas para saber disso você precisou ler em algum outro lugar que o operador de multiplicação tem precedência sobre o operador de adição. É algo que você espera que seja assim e age como se realmente fosse.

E o que aconteceria se você estiver usando uma linguagem em que a adição tem precedência sobre a multiplicação? Ou pior ainda: os operadores são executados da esquerda para a direita conforme forem aparecendo.

No primeiro caso, x e y continuariam sendo igual, mas ambos teriam o valor 9. No segundo caso, x seria igual a 7 e y seria igual a 9.

Seria mais fácil se as expressões fossem escritas assim:

x = (3 * 2) + 1

y = 1 + (2 * 3)

Agora está claro para qualquer pessoa o que vai ser executado primeiro, independente do modo como a expressão seja interpretada pela linguagem. Pois saiba que é exatamente assim que um LISP trabalha. Usando a notação prefixa, as expressões acima ficariam:

x = (+ (* 3 2) 1)

y = (+ 1 (* 3 2))

Primeiro será executada a multiplicação, que está nos parênteses mais internos e, em seguida, será executada a adição. Tudo isso sem se preocupar com regras ocultas ou peculiaridades do compilador.

Qualquer código em qualquer dialeto LISP, mesmo com suas características particulares, fica fácil de entender se você lembrar dessas regrinha.