Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
107: Building and Publishing a Gem
By now, you've probably written a few utility classes or helper modules that you find yourself copying and pasting from one project to another. Maybe it's a specific API wrapper or a custom string formatter. The natural instinct is to just keep doing that, or perhaps create a "shared" folder in your repositories. But once you hit that point, you're actually prime for building a gem. I've seen plenty of developers shy away from this because they think "gem development" is some arcane art reserved for the creators of Rails or Devise, but it's really just about packaging and distribution.
The "Just Make it Work" folder approach
The naive way to start is to manually create a directory, throw a .gemspec file in there, and write your code in a single lib/my_gem.rb file. You might think, "Why bother with a complex structure if my logic only takes up 50 lines?" On the surface, this is fast. You run gem build my_gem.gemspec and gem push my_gem-0.1.0.gem, and suddenly your code is on RubyGems.org.
But this is where things break. When your "simple" utility grows—say you're building a gem called WeatherSnap to fetch data from an API—you'll quickly realize that putting your API client, your response parser, and your error classes all in one file is a nightmare for testing and readability. More importantly, manual gemspecs are prone to errors. I've seen people forget to specify their Ruby version or leave "TODO" placeholders in the description, which RubyGems will actually reject upon upload. If you're managing the folder structure yourself, you're also likely to miss the exe/ directory for CLI tools or the spec/ directory for your tests, making your gem a "black box" that other developers are afraid to contribute to.
Leveraging Bundler's scaffolding
The professional way to do this is to let Bundler do the heavy lifting. Instead of manual folders, you run bundle gem weathersnap. This isn't just a shortcut; it sets up a standardized directory structure that every Rubyist expects. It separates the entry point (lib/weathersnap.rb) from the actual implementation (lib/weathersnap/). This distinction is crucial. The entry point should be lean—mostly just requiring other files—while the weathersnap/ directory holds the actual logic divided into logical files like client.rb or version.rb.
When you use the scaffold, you get a version.rb file. This is where the real trade-off happens. In the naive approach, you might just hardcode the version in the gemspec. But by putting it in a separate file, your gem can report its own version at runtime (WeatherSnap::VERSION), which is a lifesaver when you're debugging a production issue and need to know exactly which iteration of the code is running. I highly recommend sticking to Semantic Versioning (SemVer) here. If you change the method signature of your weather fetcher, bump the major version. If you just add a new feature, bump the minor. Don't just increment numbers randomly; your users will thank you.
Pushing to RubyGems without breaking the internet
Before you push, you have to handle dependencies. In the naive approach, people often add dependencies to their Gemfile and forget they aren't included in the .gemspec. Remember: the Gemfile is for your development environment; the .gemspec is for the people who install your gem. If WeatherSnap depends on httparty, it must be listed as spec.add_dependency "httparty". If it's not in the gemspec, the gem will install fine, but will crash the moment someone calls a method that relies on that missing library.
Once your gemspec is cleaned up—meaning no "TODO"s and correct metadata—you build and push. But here is my advice: always install your gem locally from the path first (gem install ./pkg/weathersnap-0.1.0.gem) and try to use it in a completely separate, empty project. It's incredibly humbling to publish a gem to the world only to realize you accidentally relied on a constant defined in your local environment that isn't actually part of the gem. Verify the "clean room" installation, and then you're ready to gem push.
📋 Practical Task
Build and Package the "GeoDistance" Gem
Your task is to create a functional Ruby gem called geo_distance that calculates the distance between two points on Earth using their latitude and longitude. Follow these requirements:
- Use
bundle gem geo_distanceto generate the structure. - Implement a
GeoDistance.calculate(lat1, lon1, lat2, lon2)method. You can use the Haversine formula for the calculation. - Ensure the logic is placed in
lib/geo_distance/calculator.rband required by the mainlib/geo_distance.rbfile. - Update the
.gemspecfile: replace all "TODO" strings with actual descriptions and a valid homepage URL (you can use a placeholder GitHub URL). - Add
rakeas a development dependency in the gemspec. - Build the gem locally using the
gem buildcommand and verify that the.gemfile is created in your directory.
Verification: To consider this complete, open a fresh terminal, install your generated .gem file, and successfully run GeoDistance.calculate(52.5200, 13.4050, 48.8566, 2.3522) (Berlin to Paris) in IRB.
There are no comments for now.