Gems against pip
Ruby: Gemfile, gemspec, lock
A Ruby project declares its dependencies in a Gemfile and describes the package itself in a gemspec. The lock file is the resolved answer, and it is committed.
source "https://rubygems.org"
gem "minitest", "~> 5.20"
Gem::Specification.new do |spec|
spec.name = "calc-demo"
spec.version = "0.1.0"
spec.summary = "A tiny calculator used by the comparison lessons"
spec.authors = ["Arthur Isaev"]
spec.files = ["lib/calc.rb"]
spec.require_paths = ["lib"]
end
bundle install
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Bundle complete! 1 Gemfile dependency, 1 gem now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
The lock file records the exact version that Bundler chose:
head -8 Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
minitest (5.27.0)
PLATFORMS
ruby
x86_64-linux-musl
Python: pyproject, venv, pip
A Python project declares the package in pyproject.toml and installs it into a virtual environment, which is the Python answer to a per-project gem folder.
[project]
name = "calc-demo"
version = "0.1.0"
description = "A tiny calculator used by the comparison lessons"
requires-python = ">=3.9"
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
python3 -m venv .venv
.venv/bin/pip install -e .
Obtaining file:///tmp/vs/pkg
Successfully built calc-demo
Successfully installed calc-demo-0.1.0
-e is the editable install: the environment points at the source folder, so an edit is visible without reinstalling. The Ruby equivalent is bundle exec with the project on the load path, or gem install --local after a build.
.venv/bin/python -c 'import calc_demo; print(calc_demo.VERSION, calc_demo.add(2, 3))'
0.1.0 5
What differs
- Two files against one. Ruby splits the dependency list (Gemfile) from the package description (gemspec). Python puts both into pyproject.toml: the [project] table is the name, the version and the dependencies.
- The environment. Bundler installs into vendor/bundle when told to and into the user gem folder by default. A Python virtual environment is created first and is always local; there is no global default.
- The lock. Gemfile.lock and requirements.txt or a tool that writes a lock (uv, Poetry, pip-tools) play the same role: pin the resolved tree so two machines install the same versions.
- Running. bundle exec rake test versus .venv/bin/python -m pytest: both name the environment explicitly, which is what a pipeline needs.
What to watch for
Commit the lock file of an application and leave it out of a library, in
both ecosystems; the same rule, the same reason.
Do not install into the system Ruby or the system Python on a machine you
share. A project folder and a lock file are enough to reproduce the
environment, and the system stays clean.
Next: Threads in both
Article author: Arthur Isaev