This episode dissects the Ruby package ecosystem: gem search and gem install, gemspec structure, creating gems with bundle gem, Gemfile and bundle install, Gemfile.lock with checksums, multi-gem sources, and development and production dependency groups.

No modern application is built from scratch without libraries. Ruby provides a mature package ecosystem: RubyGems for distributing libraries, and Bundler for managing dependencies between projects reproducibly. Understanding both is a prerequisite for working in a team or opening an open-source project.
This episode 12 dissects the entire flow: searching for and installing gems, reading and writing gemspecs, creating your own gem with bundle gem, then managing project dependencies through Gemfile, bundle install, and Gemfile.lock, which now includes checksums. At the end, you'll understand how the gems you used in episodes 0 to 11 get to your machine.
RubyGems is Ruby's official package manager. Its primary source is rubygems.org. The workflow starts with a search, then installation:
gem search sinatra
gem install sinatra
gem list sinatragem search sinatra searches rubygems.org for gems with the keyword sinatra. gem install sinatra installs the latest version along with its dependencies. gem list sinatra shows the gems installed on the system. Global installation without Bundler should be avoided for real projects — that's why we need Bundler.
Every gem is defined by a gemspec file containing metadata: name, version, summary, authors, and packaging rules. A gemspec is Ruby code that calls Gem::Specification.new:
Gem::Specification.new do |spec|
spec.name = "perkenalan_ruby"
spec.version = "0.1.0"
spec.summary = "Gem contoh untuk belajar"
spec.authors = ["Arman Dwi Pangestu"]
spec.required_ruby_version = ">= 3.4"
spec.files = Dir["lib/**/*.rb"]
endThe gemspec defines the minimum Ruby version (spec.required_ruby_version), the list of packaged files (spec.files), and runtime dependencies via spec.add_dependency. Every gem you install is a similar manifest built into a .gem file and then published to rubygems.org.
Bundler provides a generator for creating a new gem structure complete with a gemspec, lib folder, and CI configuration:
bundle gem perkenalan_ruby
cd perkenalan_ruby
gem build perkenalan_ruby.gemspec
ls *.gembundle gem perkenalan_ruby creates a gem project folder with all the standard scaffolding. gem build perkenalan_ruby.gemspec compiles the gemspec into a .gem file that can be installed anywhere. A gem created this way is ready to be published to rubygems.org.
The gem's main code goes in lib. Bundler creates lib/perkenalan_ruby.rb as the entry point that exposes the public class or module. Every file under lib gets packaged because of the spec.files list in the gemspec. To test during development, use bin/console, which the generator provides — it opens an IRB session with the gem already loaded.
The Gemfile declares a project's dependencies. Bundler resolves mutually compatible versions, and Groups separate dependencies by environment:
source "https://rubygems.org"
gem "sinatra", "~> 4.0"
gem "puma", "~> 6.4"
group :development, :test do
gem "rspec"
gem "rubocop", require: false
endThe line source "https://rubygems.org" sets the gem source. The constraint "~> 4.0" allows minor updates but holds the major version. The :development, :test group loads rspec and rubocop only in the development and test environments, not in production. This keeps the production footprint lean.
After defining a Gemfile, run install to lock down all dependencies:
bundle install
bundle exec ruby main.rb
bundle listbundle install resolves dependencies and writes Gemfile.lock — a manifest that freezes the exact version of every gem. Since RubyGems 4.0, Gemfile.lock includes a checksum for every gem so artifact tampering can be detected. bundle exec ruby main.rb runs a command in the context of the locked gems, and bundle list shows the list.
Sometimes a gem is only available from an internal source (for example GitHub or a private registry). Bundler supports multiple sources; just declare the gem from the right source:
source "https://rubygems.org"
gem "sinatra", "~> 4.0"
gem "gem_internal", source: "https://gem.internal.example.com"gem "gem_internal", source: "https://gem.internal.example.com" pulls that gem from a specific source while the rest come from rubygems.org. Note: per-gem specific sources are written as a hash argument, unlike the global source block.
Tip
Always commit Gemfile.lock for applications (not libraries). The lockfile ensures every developer and CI server runs exactly the same dependencies, and it's now protected by checksums for integrity.
Episode 12 connects you with the Ruby distribution ecosystem: searching for and installing gems with RubyGems, gemspec structure, creating gems with bundle gem, and managing dependencies through Gemfile, bundle install, checksum-backed Gemfile.lock, multi-gem sources, and environment groups.
Key takeaways:
gem search and gem install manage gems globally; Bundler manages them per project.bundle gem creates a publish-ready gem structure in one command.Gemfile declares dependencies; groups separate development and production environments.bundle exec runs commands in the lockfile context.Gemfile.lock now includes checksums for dependency integrity.In the next episode, episode 13, we will discuss networking and HTTP — TCP sockets with Happy Eyeballs v2 (RFC 8305), which has been the default since Ruby 3.4, UDP sockets, basic socket servers, and HTTP clients with Net::HTTP, URI, and modern gems like httparty and faraday. The inter-service communication skills every backend developer must master.