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.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
load "./lib/my_app/calculator.rb"Resumo prático
| Comando | Recarrega se chamado de novo? | Resolve caminho como? | Uso típico |
|---|---|---|---|
require | Não | Por nome/caminho, usando $LOAD_PATH | Bibliotecas padrão, gems, ou projeto com $LOAD_PATH configurado |
require_relative | Não | Relativo ao arquivo atual | Arquivos do próprio projeto |
load | Sim | Caminho 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á emlib/.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.rbdefineMeuProjeto::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"end1.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 endendNotas de design:
callé um padrão simples: você instancia e chamacallpara obter o resultado.- O
gsubremove 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 endendRepare 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 endendAqui 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 rubypermite executar como script em sistemas Unix-like (opcional, mas comum).ARGF.readlê 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.txtOu passando um arquivo diretamente:
ruby bin/app texto.txtQuando 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.