Use o código e tenha 10% de desconto!

Ruby on Rails coloque sua aplicação web nos trilhos

Vinícius Baggio Fuentes
Capa

Ruby on Rails

Agradecimentos

Monsters are real, and ghosts are real too. They live inside us, and sometimes, they win.

Este livro não existiria sem a ajuda dos meus grandes amigos Matheus Bodo, Willian Molinari, Sérgio Schezar e Vinícius Uzêda, que me acompanharam nesse processo quase todos os dias, revisando, criticando e opinando minuciosamente o conteúdo desse livro. Muito obrigado!

Muito obrigado também à família Casa do Código e Caelum, pela oportunidade de escrever esse livro e pelos ensinamentos, especialmente ao Adriano Almeida, pelo difícil trabalho de colocar ordem nas minhas palavras.

Agradecimentos especiais também ao GURU-SP (Grupo de Usuários Ruby de São Paulo), à PlataformaTec e aos amigos do ICMC-USP, pois se sei alguma coisa, devo tudo a eles.

Agradeço também a minha família e amigos, pela força e por tolerarem meses sem notícias enquanto me mudo para outro país.

Por fim, agradeço principalmente a você leitor, por investir seu tempo a aprender uma tecnologia que eu pessoalmente gosto tanto. Espero sinceramente que seja uma jornada divertida e lucrativa ao mesmo tempo!

Sumário

  • 1 - Introdução
    • 1.1 - Nova edição, atualizado para Rails 4!
    • 1.2 - Para quem é este livro
    • 1.3 - Organização
    • 1.4 - Socorro, estou perdido! Ajude-me!
  • 2 - Conhecendo a aplicação
    • 2.1 - Arquitetura de aplicações web
    • 2.2 - Recursos ao invés de páginas
    • 2.3 - Recursos no Colcho.net
    • 2.4 - Conhecendo os componentes
    • 2.5 - Os modelos
    • 2.6 - Controle
    • 2.7 - Apresentação
    • 2.8 - Rotas
    • 2.9 - Suporte
    • 2.10 - Considerações finais
  • 3 - Primeiros passos com Rails
    • 3.1 - Instalação do Rails
    • 3.2 - Gerar o alicerce da aplicação
    • 3.3 - Os ambientes de execução
    • 3.4 - Os primeiros comandos
    • 3.5 - Os arquivos gerados pelo scaffold
  • 4 - Implementação do modelo para o cadastro de usuários
    • 4.1 - O usuário
    • 4.2 - Evite dados errados. Faça validações
  • 5 - Tratando as requisições Web
    • 5.1 - Roteie as requisições para o controle
    • 5.2 - Integre o controle e a apresentação
    • 5.3 - Controle o ::mass-assignment::
    • 5.4 - Exibição do perfil do usuário
    • 5.5 - Permita a edição do perfil
    • 5.6 - Reaproveite as apresentações com partials
    • 5.7 - Mostre os erros no formulário
    • 5.8 - Configure a ação raiz (root)
  • 6 - Melhore o projeto
    • 6.1 - Lição obrigatória: sempre aplique criptografia para armazenar senhas
    • 6.2 - Como adicionar plugins ao projeto?
    • 6.3 - Usando has_secure_password no modelo
    • 6.4 - Migração da tabela users
    • 6.5 - Automatizando tarefas de manutenção com rake
    • 6.6 - Melhoria de templates e CSS
    • 6.7 - Trabalhe com layout e templates para melhorar sua apresentação
    • 6.8 - O que é o Asset Pipeline?
    • 6.9 - Criando os novos stylesheets
    • 6.10 - Feedback em erros de formulário
    • 6.11 - Duplicação de lógica na apresentação nunca mais. Use os Helpers
  • 7 - Faça sua aplicação falar várias línguas
    • 7.1 - O processo de internacionalização (I18n)
    • 7.2 - Traduza os templates
    • 7.3 - Extra: alterar o idioma do site
  • 8 - O cadastro do usuário e a confirmação da identidade
    • 8.1 - Entenda o ActionMailer e use o MailCatcher
    • 8.2 - Templates de e-mail, eu preciso deles?
    • 8.3 - Mais e-mails e a confirmação da conta de usuário
    • 8.4 - Um pouco de callbacks para realizar tarefas pontuais
    • 8.5 - Roteamento com restrições
    • 8.6 - Em resumo
  • 9 - Login do usuário
    • 9.1 - Trabalhe com a sessão
    • 9.2 - Controles e rotas para o novo recurso
    • 9.3 - Sessões e cookies
    • 9.4 - Consultas no banco de dados
    • 9.5 - Escopo de usuário confirmado
  • 10 - Controle de acesso
    • 10.1 - Helpers de sessão
    • 10.2 - Não permita edição do perfil alheio
    • 10.3 - Relacionando seus objetos
    • 10.4 - Relacione quartos a usuários
    • 10.5 - Limite o acesso usando relacionamentos
    • 10.6 - Exibição e listagem de quartos
  • 11 - Avaliação de quartos, relacionamentos muitos para muitos e organização do código
    • 11.1 - Relacionamentos muitos-para-muitos
    • 11.2 - Removendo objetos sem deixar rastros
    • 11.3 - Criando avaliações com pitadas de AJAX
    • 11.4 - Diga adeus a regras complexas de apresentação: use presenters
    • 11.5 - jQuery e Rails: fazer requisições AJAX ficou muito fácil
    • 11.6 - Média de avaliações usando agregações
    • 11.7 - Aplicações modernas usam fontes modernas
    • 11.8 - Eu vejo estrelas - usando CSS e JavaScript para melhorar as avaliações
    • 11.9 - Encerrando
  • 12 - Polindo o Colcho.net
    • 12.1 - Faça buscas textuais apenas com o Rails
    • 12.2 - URLs mais amigáveis através de slugs
    • 12.3 - Paginação de dados de forma descomplicada
    • 12.4 - Upload de fotos de forma simples
    • 12.5 - Coloque a aplicação no ar com o Heroku
  • 13 - Próximos passos
    Capítulo1

    Introdução

    Quem, de três milênios, / Não é capaz de se dar conta / Vive na ignorância, na sombra, / À mercê dos dias, do tempo.

    Se você desenvolve para a Web, provavelmente já ouviu falar sobre Ruby on Rails. O Ruby on Rails (ou também apenas "Rails") é um framework open-source para desenvolvimento de aplicações web, criado por David Heinemeier Hansson (ou "DHH"). O framework, escrito na linguagem Ruby, foi extraído de um produto de sua empresa, o Basecamp® (http://basecamp.com) em 2003.

    Desde então ele ficou muito famoso, levando também a linguagem Ruby, anteriormente apenas conhecida no Japão e em poucos lugares dos Estados Unidos, ao mundo todo. Mas por que cresceu tanto? O que a linguagem e o framework trouxeram de novo para sair do anonimato e praticamente dominar o mercado de startups nos Estados Unidos e no mundo?

    Quanto ao Rails, na época em que foi lançado, trouxe uma visão diferente ao desenvolvimento Web. Naquele momento, desenvolver para Web era cansativo, os frameworks eram complicados ou resultavam em sistemas difíceis de se manter e de baixa qualidade.

    O DHH, ao desenvolver o Basecamp, pensou principalmente nos seguintes aspectos:

    • "Convention over configuration", ou convenção à configuração: ao invés de configurar um conjunto de arquivos XML, por exemplo, adota-se a convenção e apenas muda-se o que for necessário;
    • "Dont Repeat Yourself", ou "não se repita": nunca você deve fazer mais de uma vez o que for necessário (como checar uma regra de negócio);
    • Automação de tarefas repetidas: nenhum programador deve perder tempo em tarefas repetitivas e sim investir seu tempo em resolver problemas interessantes.

    Esses conceitos são amplamente explorados no clássico The Pragmatic Programmer: From Journeyman to Master *, leitura recomendada. Esta soma de tecnologias e práticas são bastante prazerosas de se trabalhar; é possível realizar muito com pouco tempo e você verá, capítulo a capítulo, como essas ideias são representadas em todos os aspectos do framework.

    1.1 - Nova edição, atualizado para Rails 4!

    Na primeira edição, tínhamos duas partes, a primeira, na qual víamos uma introdução a Ruby e, na segunda, cobríamos Rails. Nesta edição, vamos fazer uma coisa diferente. Vou assumir que você, leitor, já sabe Ruby, e vamos cobrir Ruby on Rails 4, recentemente lançado! São muitas novidades, e portanto vamos dedicar o livro completamente ao Rails. Este livro também possui uma revisão completa do texto e do conteúdo técnico, com a colaboração dos leitores da edição anterior.

    Se você não sabe Ruby, recomendo que você adquira o livro "Ruby: Aprenda a programar na linguagem mais divertida", de Lucas Souza. Você pode comprar a sua cópia em formato físico ou digital no site da Casa do Código: http://www.casadocodigo.com.br/products/livro-ruby.

    1.2 - Para quem é este livro

    O objetivo deste livro é apresentar um pouco da linguagem Ruby e aprender os primeiros passos a desenvolver com o framework Ruby on Rails. Mais além, vamos aprender a desenvolver aplicativos com Rails. Algumas vezes usaremos inclusive componentes feitos em Ruby puro. Esses momentos são muito importantes para aprendermos também algumas boas práticas.

    Tendo isso em mente, este livro serve para pessoas que:

    • Já leram a primeira edição do livro, mas querem conhecer as novidades da nova versão do Rails;
    • Já conhecem as versões anteriores do Rails, mas querem ficar por dentro das mudanças do framework;
    • Já conhecem Rails, mas não estão confortáveis em como fazer aplicações bem organizadas;
    • Já conhecem Rails superficialmente, mas querem aprimorar conhecimentos e boas práticas.

    1.3 - Organização

    A primeira parte do livro é dedicada a entender o contexto que o Ruby on Rails trabalha. Essa é a parte teórica, na qual vamos entender quais são os principais conceitos por trás do framework. Vamos ver também, em alto nível, quais são os componentes do Rails e como eles se relacionam.

    A segunda parte é onde vamos fazer a aplicação com Rails. Passo a passo, vamos implementar uma aplicação do início ao fim e, durante a construção de cada funcionalidade, aprender como juntar as partes do framework. Vamos comparar o que existia nas versões anteriores e o que veio na nova versão. Vamos revisitar funcionalidades, aprimorando-as, e mostrar como é o processo de criação de uma aplicação real, de forma que você aprenda uma das possíveis maneiras de construir suas aplicações no futuro.

    Vamos construir um aplicativo chamado Colcho.net. O Colcho.net é um site para você publicar um espaço sobrando na sua casa para hospedar alguém por uma ou mais noites. O site vai ter:

    • Cadastro de usuário, com encriptação de senha;
    • Login de usuários;
    • Envio de emails;
    • Internacionalização;
    • Publicação e administração de quartos;
    • Avaliação de quartos e ranking;
    • Busca textual;
    • URL slugs;
    • Uploads e thumbnails de fotos.
    • Deploy da aplicação no Heroku

    Embora este livro tenha sido construído de forma que a leitura progressiva seja fácil, ele pode servir como consulta. Vamos dividir o sistema que será construído em algumas funcionalidades principais e desenvolvê-las individualmente em cada capítulo.

    1.4 - Socorro, estou perdido! Ajude-me!

    Acompanhado desse livro, existe uma lista de discussões no Google Groups, localizado em http://colcho.net/lista. Fique à vontade para mandar dúvidas sobre o seu progresso com o livro, ou sobre qualquer coisa relacionada com Rails. Nela, eu e outros leitores voluntários poderão te ajudar com qualquer aspecto relacionado a Ruby, Rails e ao livro. Você pode mandar sugestões, críticas e correções ao livro nessa lista.

    Capítulo2

    Conhecendo a aplicação

    Para cada fato existe um conjunto infinito de hipóteses. Quanto mais você observa, mais você enxerga.

    Antes de começarmos, você sabe qual a diferença entre um framework e uma biblioteca? Quando usamos uma biblioteca, nós, programadores, escrevemos nosso código, chamando a biblioteca quando necessário. A camada entre a biblioteca e o nosso código é bastante distinta.

    O mesmo não acontece quando usamos um framework. Para se ter uma ideia, muitas vezes o ponto inicial do sistema não é um código que você escreve. Nosso código faz o intermédio com diversas outras bibliotecas que compõem o framework, de forma que o resultado torna-se mais poderoso do que a soma das partes.

    O Ruby on Rails não é diferente. Ele é um framework usando uma estrutura chamada MVC — Model View Controller, bastante conveniente para a construção de aplicativos Web. O Rails também é usado por muitos desenvolvedores já há bastante tempo, portanto já foi testado em diversas situações, como alta carga, ou grande número de usuários. É fácil começar com Rails pois ele faz muito trabalho por você, mas se aprofundar no Rails lhe dá ainda mais poder.

    É muita coisa para aprender, mas não se preocupe, este livro está em seu poder justamente para facilitar esta tarefa. Vamos então ver como vamos modelar nossa aplicação e em seguida vamos ver como o Ruby on Rails vai nos ajudar a construí-la.

    2.1 - Arquitetura de aplicações web

    Construir aplicações bem feitas em Ruby on Rails é um pouco mais complicado do que simplesmente criar páginas atrás de páginas. A razão disso é que ele é preparado para criar aplicações modernas e arrojadas. Isso significa que não somente deve responder HTML aos usuários, mas também responder de maneira adequada para aplicações ricas em client-side, interagindo com frameworks como Backbone.js (http://backbonejs.org/), Ember.js (http://emberjs.com/) ou outros.

    O que são aplicações ricas em client-side?

    Depois da repopularização do JavaScript com o uso intensivo de AJAX, os browsers têm se tornado muito mais poderosos do que antigamente, inclusive com os benefícios incorporados pelo HTML 5. Tendo isso em mente, programadores estão criando aplicações cada vez mais complexas, executando grande parte, ou até mesmo totalmente no browser.

    Nos últimos anos, diversos frameworks têm sido lançados, tal como os mencionados anteriormente. Dessa forma, existem aplicações Web que a parte de back-end tornou-se uma camada de persistência que está totalmente independente da apresentação final ao usuário.

    2.2 - Recursos ao invés de páginas

    Para que nossas aplicações fiquem elegantes, precisamos parar de pensar na maneira antiquada de se criar aplicações Web cheias de páginas e interações complexas entre si, e pensarmos em recursos. É uma mudança não trivial, mas vamos usar este pensamento durante o livro todo, então até o final, você ficará mais confortável com essa ideia.

    Se você já ouviu falar ou sabe o que é REST (Representational State Transfer, ou Transferência de estado de representação), entender como vamos usar recursos para modelar nossa aplicação será bem mais fácil. Se você quiser se aprofundar no assunto, recomendo a leitura do livro Rest in Practice *.

    2.3 - Recursos no Colcho.net

     

    Um recurso, em uma aplicação web, é qualquer coisa que uma aplicação pode servir aos seus usuários. Por exemplo, em uma rede social, recursos podem ser mensagens, fotos, vídeos. Uma aplicação web, quando serve um recurso, permite que você interaja com ele. Por exemplo, é possível criar novas mensagens, listá-las ou até mesmo deletá-las. Um recurso também possui uma apresentação, como por exemplo um documento HTML ou um objeto em JSON.

    O Colcho.net é um aplicativo no qual seus usuários podem mostrar quartos vagos, ou até um colchonete que pode ser usado em sua sala em troca de uma grana e, quem sabe, fazer novas amizades.

    Assim, o primeiro recurso que podemos identificar é o "Quarto". Em nosso sistema, será possível listar quartos, exibir detalhes de um quarto, criar um novo quarto, editar um quarto já existente e, por fim, removê-lo.

    Recursos assim são fáceis de serem mapeados, pois estão diretamente ligados a uma unidade lógica do sistema. Porém, existem recursos que são menos óbvios. Um exemplo de recurso não tão trivial é o recurso "Sessão". Poderemos criar uma sessão ou destruí-la, porém nada mais além disso.

    Em sistemas REST, ações com recursos são mapeados em duas partes: um verbo HTTP (uma operação), uma URL (identificação do recurso). Podemos também ter uma representação, porém opcional. Veja os seguintes exemplos:

    O que são verbos HTTP?

    Verbos HTTP são ações que podem ser executadas em um recurso identificado por uma URL. A implementação do comportamento fica a critério do servidor, não existe um padrão. Os verbos usados no Ruby on Rails são:

    • GET — para retornar um recurso específico ou uma coleção;
    • POST — para enviar um novo elemento a uma coleção;
    • PUT — para alterar um elemento existente;
    • DELETE — para remover um elemento.
    • GET /rooms.html: verbo GET na URL /rooms mapeia para a listagem de todos os quartos, com uma representação em HTML;
    • POST /rooms: verbo POST na URL /rooms mapeia para a inserção de mais um novo quarto na coleção de quartos, e sua representação é dada pelo cabeçalho HTTP 'Content-type';
    • DELETE /rooms/123: verbo DELETE na URL /rooms/123 mapeia para a remoção do quarto cujo identificador único é "123", sendo a representação irrelevante, pois não há a transmissão de dados, apenas uma ação.

    O Rails considera esses conceitos em seu design, portanto, se seguirmos esses conceitos, o Rails nos ajuda bastante e de quebra ganhamos facilidade ao usar frameworks de client-side quando necessário.

    Essa é a visão externa da arquitetura de aplicações Web. Vamos entrar agora na arquitetura interna.

    2.4 - Conhecendo os componentes

    O Ruby on Rails é um framework que adota o padrão de arquitetura chamado Model-View-Controller (MVC), ou Modelo, Apresentação e Controle.

    Modelos possuem duas responsabilidades: eles são os dados que sua aplicação usa, normalmente persistidos em um ou mais banco de dados (seu perfil de usuário, por exemplo). Eles também fazem parte da regra de negócio, ou seja, cálculos e outros procedimentos, como verificar se uma senha é válida ou o fechamento de uma fatura.

    O Controle é a camada intermediária entre a Web e o seu sistema. Ele traduz os dados que vem de representações web (sejam por parâmetros na URL, formulários enviados ou dados JSON enviados por uma aplicação cliente) e repassa para os modelos, que vão fazer o trabalho pesado. Em seguida, preparam os resultados de forma a serem usados na camada de Apresentação.

    A Apresentação é como o aplicativo mostra o resultado das operações e os dados. Normalmente podem ser uma bela página usando as novas tecnologias de CSS 3 e HTML 5 a até representações de objetos em JSON ou XML.

    2.5 - Os modelos

    Quando criamos aplicações Ruby on Rails, basicamente usamos um componente chamado ActiveRecord. O ActiveRecord é uma implementação de Object-Relational-Mapping, ou seja, uma biblioteca que faz o mapeamento de estruturas relacionais (leia-se SQL e bancos de dados relacionais) a objetos Ruby, transformando, por exemplo, chaves estrangeiras em relacionamento entre objetos através de métodos.

    O ActiveRecord internamente usa uma biblioteca bastante poderosa chamada Arel. Com o uso do Arel o ActiveRecord consegue transformar chamadas de métodos Ruby em complexas consultas SQL e depois mapear de volta em objetos Ruby. Isso tudo é muito conveniente, veja alguns exemplos:

    Room.all
    #  SELECT "rooms".* FROM "rooms"
    
    Room.where(:location => ['São Paulo', 'Rio de Janeiro'])
    # SELECT "rooms".* FROM "rooms" WHERE "rooms"."location"
    #                             IN ('São Paulo', 'Rio de Janeiro')
    

    Além disso, o ActiveRecord traz diversas facilidades para integração com o restante de uma aplicação web, através do ActiveModel. O ActiveModel é um componente que inclui diversas ferramentas para validação de atributos, callbacks em momentos oportunos (como antes de atualização ou criação), integração com formulários, tradução de atributos, entre outras.

    Os modelos não necessariamente precisam ser objetos ActiveRecord. É bastante comum separarmos regras complexas em diversas classes em Ruby puro. Isso é importante para evitar que os modelos ou controles fiquem grandes e complexos. Vamos ver um exemplo disso no capítulo "* Login do usuário".

    No caso do Colcho.net, o Quarto se encaixa perfeitamente como um modelo. Assim, podemos criar validações como a presença de uma localidade ou calcular a disponibilidade dele em uma certa data.

    2.6 - Controle

    A camada de controle é o intermédio entre os dados que vem dos usuários do site e os Modelos. Outro principal papel da camada de controle é gerenciar sessão e cookies de usuário, de forma que um usuário não precise enviar suas credenciais a todo momento que fizer uma requisição.

    Após obter os dados na camada de modelos, é papel da camada de Controle determinar a melhor maneira de representar os dados, seja via a renderização de uma página HTML ou na composição de um objeto JSON.

    No Ruby on Rails, os componentes que trabalham nessa camada são o ActionDispatch e o ActionController, ambos parte do pacote denominado ActionPack. O ActionDispatch trabalha no nível do protocolo HTTP, fazendo o parsing dos cabeçalhos, determinando tipos MIME, sessão, cookies e outras atividades.

    Já o ActionController dá ao desenvolvedor o suporte para escrever o seu código de tratamento das requisições, invocando os modelos e as regras de negócio adequadas. Ele também dá suporte a filtros, para, por exemplo, verificar se um usuário está logado no sistema para fazer uma reserva no site.

    O ActionDispatch é um componente de menor nível, então não vamos interagir diretamente com este componente. Porém, o ActionController é um dos componentes centrais de aplicações Rails, então vamos, a todo momento, usar suas facilidades.

    2.7 - Apresentação

    A camada de apresentação é onde prepara-se a resposta para o usuário, depois da execução das regras de negócio, consultas no banco de dados ou qualquer outra tarefa que sua aplicação deva realizar.

    As maneiras mais comuns de se exibir dados atualmente na web é através de páginas HTML. No Ruby on Rails, conseguimos construí-las com o auxílio da biblioteca ActionView (também membro do ActionPack). Também é possível ter a representação JSON dos dados, via a serialização de objetos (transformação de objetos Ruby puro em JSON) ou via a construção de templates com o JBuilder, por exemplo.

    Depois que a apresentação final é montada, ela é entregue ao usuário via um servidor web, como por exemplo, uma página mostrando todas as informações de um quarto, ou um objeto JSON.

    Por fim, o Rails ainda possui uma estrutura complexa para gerenciar imagens, stylesheets e javascripts, chamada Asset Pipeline, pré-processando os arquivos e preparando-os para entregar da melhor forma para o usuário.

    2.8 - Rotas

    É necessário informar ao Rails quais as URLs que a aplicação é capaz de responder e qual Controle deverá ser invocado. Esses mapeamentos são chamados de rotas. O mapeamento de rotas no Rails é baseado na forma de recursos (veja na seção *), mas é possível também fazer rotas customizadas.

    Veja a figura *. Nela é possível ver a interação entre as diversas camadas MVC e o roteador quando o usuário requisita uma página de edição do recurso "quarto":

    Fluxo do MVC
    Figura 2.1: Fluxo do MVC


    2.9 - Suporte

    Em volta de todo os componentes que mencionamos antes, temos o ActiveSupport. O ActiveSupport não é uma camada em si, pois está presente em todas as outras camadas. É no ActiveSupport que ficam diversas ferramentas e extensões ao Ruby, para deixá-lo ainda mais fácil de usar. Veja o exemplo a seguir:

    2.days.ago
    # => Sun, 03 Jun 2012 01:08:33 BRT -03:00
    

    Essa linha de código imprime a data de dois dias atrás. Essa funcionalidade não existe por padrão no Ruby, mas, por ser bastante conveniente, o ActiveSupport faz essa extensão para você.

    Existem vários outros métodos implementados pelo ActiveSupport, mas não vale a pena entrar em detalhe sobre cada método. Vale a pena ler a documentação oficial sobre esse módulo em: http://guides.rubyonrails.org/active_support_core_extensions.html.

    2.10 - Considerações finais

    Não se preocupe em decorar todos os nomes que foram vistos nesse capítulo. Você pode consultar o livro sempre que precisar saber. É importante, porém, saber que eles existem para que você possa procurar ajuda ou documentação.

    Aliás, falando em documentação, você aprenderá com o decorrer do livro que a API do Rails é bem extensa e cheia de métodos interessantes. Ambiente-se a desenvolver com o site da documentação oficial do Rails aberta. Ela está disponível em http://api.rubyonrails.org/. Sempre que tiver alguma dúvida sobre algum método do Rails, você pode procurar que estará lá. Outra ferramenta bastante útil são os Rails Guides, ou o Guia do Rails, ótimo para tirar dúvidas gerais sobre cada componente. O Guia do Rails fica em http://guides.rubyonrails.org/.

    O que é extremamente importante entender desse capítulo são as ideias de recursos e entender o que cada parte do MVC faz devidamente. Isso significa que, sempre que possível, devemos modelar nossas aplicações como recursos e que nunca, jamais, devemos colocar regras importantes de negócio em Controles, por exemplo, pois não é o seu papel.

    Entendido isso, vamos continuar. Agora é hora de voltar a programar!

    Dados do produto

    Número de páginas:
    303
    ISBN:
    978-85-66250-03-9
    Data publicação:
    05/2012

    Compartilhe!

    Compartilhe no Facebook Compartilhe no Twitter