पाठ 19 / 25

Creating and Publishing Gems

Structure a gem, version it and publish it.

Packaging Ruby code

A gem packages Ruby code with metadata for reuse. bundle gem price_tools scaffolds one: a lib/price_tools.rb entry file, lib/price_tools/version.rb, a gemspec describing name, version, authors, summary, licence, files, required Ruby version and dependencies, plus test, README and CI templates. Library code goes under lib/ in a namespace module matching the gem name, and Ruby loads it with require "price_tools". Follow Semantic Versioning: patch versions for fixes, minor for backward-compatible features, major for breaking changes. In a gem's own gemspec, declare runtime dependencies with ranges (spec.add_dependency "bigdecimal", "~> 3.1") rather than exact pins, and keep development dependencies in the Gemfile; gems do not commit their Gemfile.lock for consumers. Build and release with gem build and gem push, or bundle exec rake release, and protect your RubyGems.org account with multi-factor authentication. Many companies host private gems on a private server or GitHub Packages.

Anatomy of a gem

lib/ holds the code, the gemspec describes it, and tests and docs travel alongside.

A package box opened to show a folder tree, a label card and a small test checklist.
Figure 7.1 — Files in a Ruby gem.

Gemspec and library entry point

Namespaced code, a version constant and dependency ranges.

# price_tools.gemspec
Gem::Specification.new do |spec|
  spec.name          = "price_tools"
  spec.version       = PriceTools::VERSION
  spec.authors       = ["Platform Team"]
  spec.summary       = "GST and discount calculations"
  spec.license       = "MIT"
  spec.required_ruby_version = ">= 3.2"
  spec.files         = Dir["lib/**/*.rb", "README.md", "LICENSE.txt"]
  spec.add_dependency "bigdecimal", "~> 3.1"
  spec.metadata["rubygems_mfa_required"] = "true"
end

# lib/price_tools/version.rb
module PriceTools
  VERSION = "1.2.0"
end

# lib/price_tools.rb
require "bigdecimal"
require_relative "price_tools/version"

module PriceTools
  module_function

  def with_gst(amount, rate: BigDecimal("0.18"))
    (BigDecimal(amount.to_s) * (1 + rate)).round(2)
  end
end

# usage: require "price_tools"; PriceTools.with_gst("100")   # 118.0 as a BigDecimal

Do not pin exact versions in a gemspec

A gem that requires rack = 3.0.8 forces every application using it onto that exact version, causing conflicts. Use pessimistic ranges in gemspecs and let applications lock exact versions.

त्वरित जाँच: In Semantic Versioning, which version bump signals a breaking change?

  • Major (1.2.3 -> 2.0.0)
  • Patch (1.2.3 -> 1.2.4)
  • Minor (1.2.3 -> 1.3.0)
  • None
Answer

Major (1.2.3 -> 2.0.0) — Major versions communicate incompatible API changes.