Ruby do Zero: Organização de Código — require, load e Estrutura de Pastas

Capítulo 15

Tempo estimado de leitura: 7 minutos

+ Exercício

Por que organizar código em arquivos?

À medida que um programa cresce, manter tudo em um único arquivo torna mais difícil: encontrar responsabilidades, reutilizar partes, testar e evoluir sem quebrar outras áreas. Organizar o código em arquivos e pastas ajuda a manter coesão (cada arquivo faz uma coisa bem definida) e separação de responsabilidades (camadas diferentes não se misturam).

Em Ruby, você normalmente divide o código em arquivos dentro de lib/ (biblioteca do projeto) e cria scripts executáveis em bin/ que apenas orquestram o uso dessas bibliotecas.

Carregando arquivos: require, require_relative e load

require

require carrega um arquivo uma única vez por processo. Se você chamar require novamente para o mesmo arquivo, Ruby não recarrega (ele “lembra” do que já foi carregado). Isso evita redefinições acidentais e melhora desempenho.

  • Usado para bibliotecas padrão e gems (quando existirem).
  • Também pode ser usado para arquivos do seu projeto, desde que o caminho esteja no $LOAD_PATH (lista de diretórios onde Ruby procura arquivos).
require "json"

require_relative

require_relative carrega um arquivo usando um caminho relativo ao arquivo atual (o arquivo que está executando o require_relative), e também só carrega uma vez. É a forma mais simples e previsível para projetos pequenos e médios, porque não depende do $LOAD_PATH.

require_relative "../lib/my_app/calculator"

load

load carrega e executa o arquivo toda vez que for chamado, mesmo que já tenha sido carregado antes. É útil em cenários específicos (por exemplo, recarregar código durante desenvolvimento), mas em projetos comuns você tende a preferir require/require_relative para evitar redefinições.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

load "./lib/my_app/calculator.rb"

Resumo prático

ComandoRecarrega se chamado de novo?Resolve caminho como?Uso típico
requireNãoPor nome/caminho, usando $LOAD_PATHBibliotecas padrão, gems, ou projeto com $LOAD_PATH configurado
require_relativeNãoRelativo ao arquivo atualArquivos do próprio projeto
loadSimCaminho informado (geralmente relativo ao diretório atual)Recarregar código intencionalmente

Estrutura simples de pastas (sem depender de plataforma)

Uma estrutura mínima e bem comum:

meu_projeto/  bin/    app  lib/    meu_projeto/      version.rb      text/        sanitizer.rb        word_counter.rb      report.rb  spec/ (opcional)    ...  (arquivos de teste)

O que vai em cada pasta

  • lib/: código reutilizável (módulos, classes, regras de negócio).
  • bin/: scripts executáveis (entrada do programa). Devem ter pouca lógica; apenas chamam o que está em lib/.
  • spec/ (opcional): testes. Mesmo sem ferramentas externas, você pode manter arquivos de verificação e rodá-los com Ruby puro, se quiser.

Convenções de nomes

  • Arquivos em snake_case: word_counter.rb, sanitizer.rb.
  • Classes e módulos em CamelCase: WordCounter, Sanitizer.
  • Pastas e namespaces combinam: lib/meu_projeto/text/word_counter.rb define MeuProjeto::Text::WordCounter.

Exemplo prático: separando responsabilidades em módulos/classes

Vamos montar um mini-projeto que recebe um texto, “limpa” o conteúdo e gera um relatório com contagem de palavras. A ideia é:

  • Sanitizer: responsabilidade de normalizar/limpar texto.
  • WordCounter: responsabilidade de contar palavras.
  • Report: responsabilidade de formatar o resultado.
  • bin/app: responsabilidade de orquestrar (ler entrada, chamar classes, imprimir saída).

Passo 1: criar os arquivos em lib/

1.1) Versão do projeto (opcional, mas comum)

lib/meu_projeto/version.rb

module MeuProjeto  VERSION = "0.1.0"end

1.2) Sanitização de texto

lib/meu_projeto/text/sanitizer.rb

module MeuProjeto  module Text    class Sanitizer      def initialize(text)        @text = text.to_s      end      def call        normalized = @text.downcase        normalized = normalized.gsub(/[^a-z0-9\s]/i, " ")        normalized.gsub(/\s+/, " ").strip      end    end  endend

Notas de design:

  • call é um padrão simples: você instancia e chama call para obter o resultado.
  • O gsub remove pontuação e normaliza espaços, deixando o contador mais previsível.

1.3) Contagem de palavras

lib/meu_projeto/text/word_counter.rb

module MeuProjeto  module Text    class WordCounter      def initialize(text)        @text = text.to_s      end      def call        return Hash.new(0) if @text.strip.empty?        counts = Hash.new(0)        @text.split(" ").each do |word|          counts[word] += 1        end        counts      end    end  endend

Repare que a classe não sabe nada sobre terminal, arquivos ou impressão. Ela só recebe texto e devolve um Hash com contagens.

1.4) Formatação do relatório

lib/meu_projeto/report.rb

module MeuProjeto  class Report    def initialize(counts)      @counts = counts    end    def top(n = 5)      @counts.sort_by { |word, count| [-count, word] }.first(n)    end    def to_text(n = 5)      lines = []      lines << "Top #{n} palavras:"      top(n).each_with_index do |(word, count), idx|        lines << "#{idx + 1}. #{word} (#{count})"      end      lines.join("\n")    end  endend

Aqui o relatório decide como ordenar e como transformar em texto. O script em bin/ só imprime o resultado.

Passo 2: criar um arquivo agregador (entrypoint da lib)

É comum ter um arquivo principal em lib/ que exige (require) os demais. Isso simplifica o script executável: ele precisa carregar apenas um arquivo.

lib/meu_projeto.rb

require_relative "meu_projeto/version"require_relative "meu_projeto/text/sanitizer"require_relative "meu_projeto/text/word_counter"require_relative "meu_projeto/report"

Esse arquivo não define classes; ele apenas organiza dependências internas do seu projeto.

Passo 3: criar o script executável em bin/

bin/app

#!/usr/bin/env rubyrequire_relative "../lib/meu_projeto"text = ARGF.readsanitized = MeuProjeto::Text::Sanitizer.new(text).callcounts = MeuProjeto::Text::WordCounter.new(sanitized).callreport = MeuProjeto::Report.new(counts)puts report.to_text(5)

Pontos importantes:

  • #!/usr/bin/env ruby permite executar como script em sistemas Unix-like (opcional, mas comum).
  • ARGF.read lê de arquivo(s) passados na linha de comando ou do stdin. Isso mantém o script flexível.
  • O script não implementa regras de sanitização, contagem ou formatação: ele apenas conecta as peças.

Passo 4: executar

No diretório do projeto:

ruby bin/app < texto.txt

Ou passando um arquivo diretamente:

ruby bin/app texto.txt

Quando usar require_relative vs ajustar $LOAD_PATH

Em projetos pequenos, require_relative costuma ser suficiente e evita surpresas. Em projetos maiores, você pode preferir adicionar lib/ ao $LOAD_PATH para usar require "meu_projeto" de qualquer lugar. Um jeito simples (feito no script) é:

$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))require "meu_projeto"

Mesmo assim, manter um arquivo agregador (lib/meu_projeto.rb) continua sendo uma boa prática para centralizar dependências internas.

Armadilhas comuns e como evitar

1) Caminho relativo errado

Se você usar require_relative, pense sempre “relativo a este arquivo”. Em bin/app, o caminho para lib/ geralmente começa com ../lib.

2) Dependências circulares

Acontece quando A exige B e B exige A. Para evitar:

  • Extraia código compartilhado para um terceiro arquivo (C) e faça A e B dependerem de C.
  • Use um arquivo agregador para controlar a ordem de carregamento.

3) Usar load sem intenção

Se você trocar require por load sem perceber, pode redefinir classes/módulos e causar comportamentos estranhos. Reserve load para casos em que recarregar é desejado.

4) Misturar IO com regra de negócio

Se uma classe em lib/ começa a ler do terminal ou imprimir diretamente, ela fica difícil de reutilizar. Prefira: classes recebem dados e retornam dados; scripts em bin/ fazem a interação com o usuário/ambiente.

Agora responda o exercício sobre o conteúdo:

Em um projeto Ruby organizado com pastas lib/ e bin/, qual abordagem melhor segue a separação de responsabilidades ao lidar com entrada e saída (IO) e regras de negócio?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

A separação de responsabilidades recomenda que lib/ contenha regras de negócio reutilizáveis (recebe dados e devolve dados) e que bin/ apenas orquestre o fluxo, cuidando de IO como leitura e impressão.

Próximo capitúlo

Ruby do Zero: Módulos — Namespaces, Mixins e Métodos de Módulo

Arrow Right Icon
Capa do Ebook gratuito Ruby do Zero: Fundamentos, Coleções, Blocos e Organização de Código
71%

Ruby do Zero: Fundamentos, Coleções, Blocos e Organização de Código

Novo curso

21 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.