Skip to main content
April 1, 2026Noble Desktop Publishing Team/12 min read

Gems: Plugins for Ruby

Master Ruby Gems for Enhanced Rails Development

Key Topics You'll Master

Gem Fundamentals

Understanding what gems are, their role as Ruby plugins, and how they extend Rails functionality beyond core features.

User Authentication

Implementing secure user sign-in, sign-out, and registration systems using the powerful devise gem.

Dependency Management

Working with Gemfile and Gemfile.lock to manage gem versions and dependencies across environments.

Topics Covered in This Ruby on Rails Tutorial:

Understanding Ruby Gems and their ecosystem, managing dependencies with Gemfile and Gemfile.lock, implementing secure gem installation practices, integrating sign-in and sign-out functionality, building robust user authentication systems, and configuring controlled user registration workflows

Exercise Preview

preview gems

Exercise Overview

Mastering Ruby gems is fundamental to becoming an effective Rails developer in today's development landscape. In this comprehensive exercise, we'll implement and configure the powerful devise gem to rapidly deploy enterprise-grade user authentication for our Flix application. You'll learn not just the mechanics of gem installation, but the strategic thinking behind choosing and implementing third-party solutions that can accelerate your development timeline while maintaining security and reliability.

What is a Gem?

Throughout this course, we've focused on Rails' built-in capabilities, which are impressive and comprehensive. However, virtually no production Rails application relies solely on the framework's core features. Modern web development demands specialized functionality—user authentication, analytics integration, payment processing, image manipulation—that would take months to build from scratch.

This is where Ruby's gem ecosystem becomes invaluable. A gem is essentially a packaged Ruby library that extends existing functionality or introduces entirely new capabilities. Think of gems as battle-tested, community-vetted solutions to common development challenges.

It's crucial to understand that gems aren't Rails-specific—they're part of Ruby's broader ecosystem. In fact, Rails itself is distributed as a collection of gems. When you ran gem install rails during your initial setup, you were leveraging this same packaging system that now powers over 175,000 available gems as of 2026.

  1. If you've completed the previous exercises in this series, you can proceed directly to the next section. For optimal learning, we strongly recommend completing exercises 3A–6B before continuing. If you haven't finished the prerequisite exercises, follow the setup instructions in the sidebar below.

    Essential Understanding

    A gem is essentially a plugin for Ruby with additional code that extends existing Ruby classes and creates new ones. Importantly, gems are not Rails-specific - Rails itself is packaged as a gem.

    Common Gem Categories

    Authentication Systems

    Gems like devise provide user account management, sign-in, and security features for web applications.

    Analytics & Tracking

    Integration gems for Google Analytics, user behavior tracking, and performance monitoring tools.

    E-commerce Solutions

    Payment processing, shopping cart functionality, and online store management capabilities.

If You Did Not Do the Previous Exercises (3A–6B)

  1. Close any files you may have open in your editor.
  2. Open the Finder and navigate to Class Files > yourname-Rails Class
  3. Launch Terminal.
  4. Type cd followed by a single space (do NOT press Return yet).
  5. Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
  6. Run rm -rf flix to remove your existing copy of the Flix site.
  7. Run git clone https://bitbucket.org/Noble Desktop/flix.git to clone the Flix repository.
  8. Type cd flix to enter the project directory.
  9. Type git checkout 5C to synchronize with the previous exercise's completion state.
  10. Run bundle install to install required gems.
  11. Run yarn install --check-files to install JavaScript dependencies.

Getting Started

Now that your environment is properly configured, let's begin integrating the devise gem into our existing Rails application. This process will demonstrate the typical workflow for adding third-party functionality to any Rails project.

  1. Open the Finder and navigate to Class Files > yourname-Rails Class

  2. Launch Terminal.

  3. Type cd followed by a single space (do NOT press Return yet).

  4. Drag the flix folder from the Finder to the Terminal window.

  5. Ensure Terminal is active and press Return to change into the project directory.

    With our development environment prepared, we're ready to enhance our Rails application with professional-grade user authentication capabilities.

Understanding Gemfile & Gemfile.lock

Before installing any gems, it's essential to understand Rails' dependency management system. This knowledge will serve you throughout your career as you work on increasingly complex applications with dozens or hundreds of gem dependencies.

  1. Open the flix folder in your preferred code editor (we recommend using a project-aware editor like VS Code or Sublime Text for optimal workflow).

  2. In your code editor, open flix > Gemfile

    IMPORTANT: Notice there's also a file called Gemfile.lock—these serve different but complementary purposes. Ensure you're opening the file named simply Gemfile without any extension.

  3. Examine the file's contents carefully. You'll see several gems already specified, many with explicit version constraints. These weren't added arbitrarily—Rails generated this initial gem manifest when we created the application, establishing a foundation of essential dependencies for modern web development.

    The Ruby gem ecosystem is remarkably dynamic, with thousands of gems receiving regular updates. While this innovation drives the platform forward, it also creates potential compatibility challenges. Some gems depend on specific versions of other gems, creating complex dependency trees that must be carefully managed in production environments.

    The Gemfile serves as your application's dependency specification. Every gem your Rails application requires must be declared here, creating a clear contract of what your application needs to function correctly.

  4. Locate the code around line 6 that demonstrates version specification:

    # Use Puma as the app server
    gem 'puma', '~> 5.0'

    The ~> operator (called the "pessimistic operator") specifies that we want version 5.0 or higher, but less than 6.0. This approach allows patch-level updates while protecting against potentially breaking major version changes. However, explicit version specification isn't always required—that's where Gemfile.lock provides precise version control.

  5. Now open flix > Gemfile.lock to see the complete dependency resolution.

  6. Notice that this file contains significantly more detail than the Gemfile. This file is automatically generated and maintained by Bundler—you should never edit it manually, as doing so can create deployment inconsistencies.

    Gemfile.lock records the exact versions and complete dependency trees of every installed gem. This ensures that whether you're deploying to staging, production, or onboarding a new team member, everyone works with identical gem versions. This consistency is crucial for maintaining stable, predictable application behavior across all environments.

Gemfile vs Gemfile.lock

FeatureGemfileGemfile.lock
PurposeSpecify required gemsLock exact versions
Manual editingYes, requiredNever edit manually
Version controlGeneral constraintsPrecise versions
Generated byDeveloperRails automatically
Recommended: Always edit Gemfile manually, let Rails manage Gemfile.lock automatically
Version Syntax

The ~> operator means 'use this version or higher, but less than the next major version'. For example, '~> 5.0' accepts 5.0 through 5.9, but not 6.0.

Installing the Devise Gem

We'll now implement devise, one of Rails' most trusted authentication solutions. Used by thousands of production applications, devise provides enterprise-grade security features while remaining developer-friendly. Let's explore how to properly research, install, and configure a new gem.

Professional development requires thorough research before integrating any third-party dependency. A gem's documentation, particularly its README and installation instructions, provides critical insights into compatibility requirements, configuration options, and potential gotchas.

  1. Switch to your browser and navigate to github.com/heartcombo/devise

    Take a moment to scan the repository. Notice the extensive documentation, active issue discussions, and regular commit history—these are hallmarks of a well-maintained, production-ready gem. Devise is particularly complex, offering features like two-factor authentication, OAuth integration, and advanced session management that we won't cover in this exercise but demonstrate the gem's enterprise capabilities.

  2. Scroll down to the README.md section, then locate the Getting started section.

  3. Study the installation instructions presented in the gray code blocks. These represent the standard installation workflow that the devise maintainers have refined over years of community feedback. We'll follow these instructions precisely, as deviating from established installation procedures often leads to configuration issues.

  4. Copy the first installation command from the gray code block:

    gem 'devise'
  5. Keep the GitHub page open for reference, then switch to Gemfile in your code editor (NOT Gemfile.lock).

  6. Locate the sqlite3 gem entry and add the devise gem declaration immediately after it:

    gem 'devise'

    While gem order in the Gemfile rarely affects functionality, organizing related gems together improves maintainability as your application grows.

  7. Following Rails conventions, add a descriptive comment above the gem declaration to document its purpose:

    # Use devise for user authentication
    gem 'devise'

    Professional Rails applications often include dozens of gems. Clear comments help team members (including your future self) understand each dependency's role in the application.

  8. Save the file to persist your changes.

  9. Switch to Terminal and execute the bundle command:

    bundle install
  10. Let's examine what the bundle install command accomplishes, as understanding Bundler is crucial for professional Rails development. Bundler serves as Rails' dependency resolution engine, ensuring consistent gem environments across development, testing, and production. When you execute bundle install, Bundler performs several critical operations:

    • Analyzes Gemfile.lock to identify currently installed gems and their exact versions
    • Resolves dependencies for any new gems, finding compatible versions across the entire dependency tree
    • Downloads and installs missing gems and their dependencies
    • Updates Gemfile.lock with complete information about all installed gems

    This process ensures that complex dependency relationships are managed automatically, preventing the version conflicts that plagued earlier development environments.

  11. Examine Terminal's output to understand what Bundler accomplished.

    For previously installed gems, you'll see messages like Using webpacker (5.4.3). For newly installed gems, Terminal displays Installing devise (4.9.2) along with several additional gems.

    Notice that Bundler automatically installed multiple gems beyond just devise: bcrypt for password hashing, orm_adapter for database abstraction, responders for controller response handling, and warden for authentication middleware. This demonstrates how modern gems leverage existing solutions rather than reimplementing common functionality.

    Bundler's dependency resolution also handles version conflicts intelligently:

    • If compatible versions exist, Bundler installs the most recent version that satisfies all constraints
    • If version conflicts arise, Bundler finds the newest version that maintains compatibility across all dependencies
    • If conflicts cannot be resolved, Bundler reports an error rather than creating an unstable environment
  12. Return to the devise GitHub page in your browser.

  13. Copy the second installation command from the Getting Started section:

    rails generate devise:install
  14. Switch to Terminal and execute this command.

    This generator creates devise's configuration files and displays important setup notes. Always read these notes carefully—they often contain critical configuration steps or security considerations.

  15. Return to the devise documentation in your browser.

  16. Notice the third installation step:

    rails generate devise MODEL

    This command generates a devise-managed model. The placeholder MODEL should be replaced with your desired model name—typically "User" for most applications.

  17. Switch to Terminal and generate the User model:

    rails generate devise user

    This generator creates several files including a User model, database migration, and route configurations. Devise follows Rails conventions, so these generated files integrate seamlessly with your existing application structure.

  18. Apply the database migration to create the users table:

    rails db:migrate

    This creates the users table with all necessary columns for authentication, including encrypted password storage, session management, and account recovery fields.

Implementing Sign In & Sign Out Links

With devise installed and configured, we'll now integrate authentication controls into our application's user interface. This involves adding conditional navigation that adapts based on the user's authentication state—a common pattern in modern web applications.

  1. Start the Rails development server:

    rails server
  2. Open your browser to explore the authentication interface that devise has automatically generated.

  3. Navigate to localhost:3000/users/sign_in

  4. Examine the comprehensive authentication interface devise created without any custom code. Notice the professional styling and complete functionality including sign-in, registration, and password recovery—features that would typically require weeks of development time.

  5. Since we don't have any user accounts yet, click the Sign up link to create our first account.

  6. Complete the registration form with credentials you'll remember:

    Email: test@example.com
    Password: testtest

    Note that devise enforces secure password requirements by default, requiring a minimum of 8 characters.

  7. Click Sign up to create the account. You should be redirected to the index with a confirmation message: Welcome! You have signed up successfully.

  8. Observe that the MY ACCOUNT link in the Flix navbar is non-functional—it's still pointing to a static HTML file.

  9. Let's replace this with dynamic authentication controls that change based on the user's login status. Switch to your code editor.

  10. Open app > views > layouts > application.html.erb

  11. Locate the navigation code around line 43:

    <li class="last">
       <a href="login.html"><span>My Account</span></a>
    </li>
  12. Replace this static link with a conditional structure that adapts to authentication state:

    <li class="last">
       <% if user_signed_in? %>
    
       <% end %>
    </li>

    The user_signed_in? helper method is provided by devise. You can find comprehensive documentation of all devise helpers in the GitHub repository under "Controller filters and helpers."

  13. Add the sign-out functionality for authenticated users:

    <% if user_signed_in? %>
       <%= link_to "<span>Sign Out</span>".html_safe, %>
    <% end %>
  14. We need to determine the correct route for sign-out functionality. Stop the Rails server with CTRL–C and examine available routes:

    rails routes
  15. Scan the output for the sign-out route. You should find:

    destroy_user_session  DELETE  /users/sign_out(.:format)       devise/sessions#destroy

    Notice this is a DELETE route, which requires explicit specification in Rails link helpers for security reasons. This follows RESTful conventions where destructive actions use appropriate HTTP verbs.

  16. Return to application.html.erb and complete the sign-out link:

    <% if user_signed_in? %>
       <%= link_to "<span>Sign Out</span>".html_safe, destroy_user_session_path, method: :delete %>
    <% end %>
  17. Add the alternative branch for non-authenticated users:

    <% if user_signed_in? %>
       <%= link_to "<span>Sign Out</span>".html_safe, destroy_user_session_path, method: :delete %>
    <% else %>
       <%= link_to "<span>Sign In</span>".html_safe, %>
    <% end %>
  18. Reference the routes output to find the sign-in path:

    new_user_session  GET  /users/sign_in(.:format)       devise/sessions#new

    Since this is a GET route, we don't need to specify the method explicitly.

  19. Complete the sign-in link:

    <% else %>
       <%= link_to "<span>Sign In</span>".html_safe, new_user_session_path %>
    <% end %>
  20. Save the file to persist your changes.

  21. Restart the development server:

    rails server
  22. Switch to your browser and refresh the page. You should now see a Sign Out link in the navigation bar.

  23. Test the sign-out functionality by clicking the Sign Out button. You should see a confirmation message: Signed out successfully.

  24. Verify the complete authentication flow:

    • Click the Sign In link in the navigation
    • Enter the email and password you used during registration
    • Click Log in

    You should receive a Signed in successfully confirmation. Your authentication system is now fully functional!

Adding Basic User Authentication

Authentication without authorization provides limited value. Let's implement access controls to restrict certain functionality to authenticated users only. This demonstrates how to build secure applications that protect sensitive operations while maintaining good user experience.

We'll start by restricting movie editing capabilities to authenticated users—a common pattern where anonymous users can browse content but must authenticate to make modifications.

As documented in devise's GitHub repository under "Controller filters and helpers," devise provides several methods for implementing authentication requirements. The before_action :authenticate_user! filter is the most straightforward approach for protecting entire controllers or specific actions.

Authentication Strategies

Pros
authenticate_user! provides blanket protection for entire controllers
White-list approach is safer than black-list for security
Devise helpers integrate seamlessly with Rails controllers
Automatic redirection to sign-in page for unauthorized access
Cons
Controller-wide authentication may be too restrictive for public pages
Requires careful planning of which actions need protection
Can impact user experience if applied too broadly
Security Best Practice

Use a white-list strategy for authentication - specify which actions DON'T require authentication rather than listing all that do. This prevents accidentally exposing sensitive actions.

Key Takeaways

1Gems are Ruby plugins that extend functionality beyond core Rails features, with Rails itself being distributed as a gem package.
2Gemfile specifies required gems while Gemfile.lock maintains exact versions - never manually edit Gemfile.lock as it's automatically managed.
3The bundle command intelligently handles gem installation, dependency resolution, and version conflict prevention across environments.
4Devise gem provides comprehensive user authentication with built-in helpers like user_signed_in? and authenticate_user! for conditional logic.
5Always research gems on GitHub for proper installation instructions and follow the documented setup process including generators.
6Dynamic navigation using authentication status creates better user experiences with context-appropriate Sign In/Sign Out links.
7Controller-level authentication using before_action :authenticate_user! provides blanket security for sensitive application areas.
8White-list security strategies are safer than black-list approaches, preventing accidental exposure of protected functionality.

RELATED ARTICLES