Conceitos essenciais da linguagem Ruby explicados a fundo, um de cada vez.
Decorator, Observer e Singleton já vêm com o Ruby como delegate, observer e singleton, mas a maioria das bases de código reimplementa os três à mão. SimpleDelegator encaminha tudo que você não sobrescreveu (e permite trocar o alvo), DelegateClass(Array) é composição onde você teria feito subclasse de Array, e Singleton fecha os vazamentos de dup/clone/Marshal mais a corrida de thread que um @instance ||= new feito à mão sempre deixa passar.
O container experimental do Ruby 4.0 para isolar constantes, monkey patches e bibliotecas carregadas dentro de um único processo; o que ele de fato separa, e por que é isolamento de namespace, não paralelismo.
obj.method(:name) te entrega um valor chamável que você pode compor com >> e <<, curry, e inspecionar; unbind remove o receptor para que a implementação de um módulo possa ser bind_call'd em um objeto que nunca o incluiu.
Por que class_eval define métodos de instância e instance_eval define métodos de classe, as variantes _exec que corrigem a armadilha da ivar desaparecida, e como DSLs internas estilo RSpec são construídas sobre a mesma troca de self.
self controla onde @var mora e quem recebe uma chamada de método nua; classes singleton explicam por que "métodos de classe" são só métodos definidos no próprio objeto classe.
method_missing vs. define_method, source_location, ObjectSpace, e os hooks included/extended por trás do ActiveSupport::Concern.
Como include, extend e prepend de fato mudam a cadeia de busca de métodos, e por que isso importa mais do que decorar as três palavras-chave.
O Ruby traz a interface Fiber::Scheduler, mas nenhuma implementação; async é a implementação de produção, e faz código concorrente parecer Ruby bloqueante comum. O Falcon transforma isso em um servidor web onde uma requisição custa uma fiber em vez de uma thread, então um único worker segura milhares de conexões lentas de I/O que o pool de threads do Puma jamais caberia.
Coletando resultados com join/value, por que a exceção de uma thread espera em silêncio até você dar join nela, Mutex além do synchronize, e o modelo de pausa e retomada cooperativo do Fiber.
Por que threads em Ruby dão concorrência de I/O, mas não paralelismo de CPU, e quando Ractors são a ferramenta que de fato muda isso.
A armadilha do valor padrão de Hash.new que silenciosamente compartilha um único objeto entre todas as chaves, fetch/merge com bloco/slice/transform_* e Hash#to_proc, depois Set, unicidade ordenada por inserção, todo o vocabulário |, &, -, ^, um <=> que compara por subconjunto e retorna nil, e add?/delete? retornando nil em vez de false.
Implemente each e ganhe map/select/reduce de graça via Enumerable, depois conduza sequências de fora com Enumerator, iteração externa, geradores customizados, e .lazy para pipelines infinitos.
As closures que tornam o Enumerable do Ruby idiomático, e a diferença de aridade e semântica de return entre proc e lambda que confunde a maioria dos iniciantes.
A separação de argumentos nomeados na 3.0, Ractors, Set virando core, Prism como parser padrão na 3.4, e *nil não convertendo mais via to_a na 4.0, a linha do tempo que os outros conceitos de Ruby desta plataforma assumem que você já conhece.
O Ruby é dinamicamente mas fortemente tipado, e duas camadas de tipagem opcional muito diferentes ficam por cima; RBS vem com o Ruby, mantém assinaturas em arquivos separados sig/*.rbs, e nunca afeta o runtime, enquanto o Sorbet (Stripe) coloca blocos sig dentro dos seus arquivos .rb e os verifica em tempo de execução também, levantando um TypeError real em um argumento errado.
Os recursos de regex além de /pattern/, literais %r{}, match? vs. os globais $~, /i /m /x /o, grupos nomeados, substituição via Hash e bloco, Regexp.union, além do backtracking catastrófico, por que acontece, o que a correspondência em tempo linear do Ruby 3.2 cobre (e o que não cobre), e como Regexp.timeout= transforma uma CPU travada em uma exceção tratável.
As armadilhas que não produzem erro nenhum, um setter sem self. virando uma variável local, um initialize com erro de digitação que nunca roda, {} vs do...end vinculando o bloco à chamada errada, além de source_location, sync = true e freeze como as ferramentas que tornam bugs silenciosos ruidosos.
O tipo de um objeto é o conjunto de mensagens ao qual ele responde, não sua classe, mais a divisão entre conversão explícita (to_s) e implícita (to_str), que decide se o próprio Ruby vai tratar seu objeto como substituível por um tipo embutido.
A hierarquia de exceções, ensure/retry, e por que catch/throw é a ferramenta mais barata para sair de código aninhado que não é um erro.
Desestruturando arrays, hashes e objetos customizados com case/in, correspondência estrutural, binding, guardas e o operador pin.
Profilers de tracing vs. sampling, o viés em cada modo de cronometragem, o kit de memória do MRI, do ObjectSpace ao memory_profiler, e por que um ganho de 12x em micro-benchmark pode não mudar nada.
Por que o YJIT só compensa em processos de longa duração, o que TruffleRuby e JRuby de fato trocam por velocidade e interoperabilidade com Java, e quando trocar de runtime é a resposta errada.
Por que o heap do MRI nunca encolhe depois de um pico, como diferenciar inchaço de um vazamento real, e quando fragmentação por arena de thread e jemalloc de fato importam.
A maior parte do Ruby que roda em produção não é um handler de requisição, é um script, uma tarefa de deploy, ou um job Rake. As flags -n/-p/-a, ARGV vs ARGF, OptionParser, as regras reais de escopo de ENV, e o grafo de tarefas do Rake são a metade da linguagem que código de framework nunca toca.
A sintaxe do Ruby tolera quase qualquer escolha de formatação, e é exatamente por isso que times de verdade rodam um linter, os cops e severidades configuráveis do RuboCop, ou a resposta sem configuração do Standard para o mesmo problema, além de como adotar um dos dois em uma base de código que nunca teve um.
O Gemfile diz o que você está disposto a rodar; o Gemfile.lock é o que você está de fato rodando. Entender bundle exec, groups e restrições de versão é a diferença entre deploys reproduzíveis e o clássico "funciona na minha máquina".
puts vs. p vs. pp, pausando a execução com a gem oficial debug e o rdbg, e o modelo de REPL como navegação do Pry, com show-source para ler a implementação real de uma gem ao vivo.