What is RailsEngine?
A Rails engine is a self-contained piece of functionality that can be added to an existing Rails application, or used as the foundation for a new Rails application. Essentially, it's a miniature Rails application that can be packaged and reused across multiple projects.
Rails engines provide a modular way to organize and reuse code in a Rails application. They allow you to encapsulate related functionality (such as authentication, payments, or notifications) in a separate namespace, with its own models, views, controllers, routes, and assets.
An engine can include all the same features as a regular Rails application, including generators, migrations, tests, and configuration options. It can also have its own dependencies, such as gems or other engines.
Rails engines are typically developed as separate gems that can be installed and managed using Bundler. This makes it easy to share engines across multiple projects or with the wider community.
Rails engines are a powerful tool for building modular and reusable code in a Rails application, and can help developers to build more maintainable and scalable applications. They are used extensively in the Rails ecosystem, and many popular gems (such as Devise, ActiveAdmin, and Spree) are implemented as engines.
Here are the steps to create a Rails engine with error handling:
Open your terminal and navigate to the directory where you want to create your engine.
Run the following command to create a new Rails engine:
rails plugin new my_engine --mountable
This will create a new directory called my_engine with the basic structure of a Rails engine.
Open the my_engine.gemspec file and update the metadata to reflect the details of your engine, such as its name, version, and description.
Open the lib/my_engine/engine.rb file and add any required dependencies or configuration options.
Create a sample method in lib/my_engine/my_engine.rb that raises an error:
module MyEngine
class MyEngine
def self.my_method
raise StandardError, "An error occurred in My Engine"
end
end
end
Create a sample controller in app/controllers/my_engine/my_controller.rb
that calls the my_method
method:
module MyEngine
class MyController < ApplicationController
def index
begin
MyEngine.my_method
rescue StandardError => e
render plain: "Error: #{e.message}"
end
end
end
end
This controller defines an index action that calls the my_method method and handles any exceptions that are raised.
Create a sample view in app/views/my_engine/my/index.html.erb
:
<h1>Welcome to My Engine</h1>
<p>This is a sample view in My Engine.</p>
Update the config/routes.rb file to define a route for your engine:
MyEngine::Engine.routes.draw do
root to: "my#index"
end
Finally, mount your engine into a Rails application by adding the following line to the config/routes.rb
file:
mount MyEngine::Engine, at: "/my_engine"
That's it! You've now created a simple Rails engine with error handling. When you navigate to the root path of your engine (/my_engine), you should see the error message that was raised by the my_method method.
To call an engine method in a Rails application, you can simply require the engine and call its methods as you would with any other Ruby class. For example, you could add the following code to a controller in your Rails application:
require "my_engine"
class MyController < ApplicationController
def index
MyEngine::MyEngine.my_method
end
end
This code requires the my_engine
gem and calls the my_method
method on the MyEngine
class. Any errors that are raised by the method will be handled by the controller's exception handling code, as shown in the previous example.
Rails engines are a powerful tool for building modular and reusable code in a Rails application, and can help developers to build more maintainable and scalable applications. They can be used to encapsulate related functionality in a separate namespace, or to develop standalone applications that can be mounted into a larger Rails application. With error handling, you can ensure that your engine provides robust and reliable functionality to its users.
Here's an example of how to create a Rails engine with notification and payment functionality.
Open your terminal and navigate to the directory where you want to create your engine.
Run the following command to create a new Rails engine:
rails plugin new my_engine --mountable
This will create a new directory called my_engine with the basic structure of a Rails engine.
Open the my_engine.gemspec
file and update the metadata to reflect the details of your engine, such as its name, version, and description.
Open the lib/my_engine/engine.rb
file and add any required dependencies or configuration options.
Create a Notification model in app/models/my_engine/notification.rb:
module MyEngine
class Notification < ApplicationRecord
validates :message, presence: true
enum status: { unread: 0, read: 1 }
end
end
This model defines a Notification
class with a message
attribute and a status
enum.
Create a Payment model in app/models/my_engine/payment.rb
:
module MyEngine
class Payment < ApplicationRecord
validates :amount, presence: true, numericality: true
validates :currency, presence: true, inclusion: { in: %w[USD EUR GBP] }
enum status: { pending: 0, paid: 1, failed: 2 }
end
end
This model defines a Payment class with an amount, currency, and status attribute.
Create a NotificationsController
in app/controllers/my_engine/notifications_controller.rb
:
module MyEngine
class NotificationsController < ApplicationController
def index
@notifications = Notification.all
end
def mark_as_read
@notification = Notification.find(params[:id])
@notification.update(status: :read)
redirect_to my_engine_notifications_path
end
end
end
This controller defines an index
action that retrieves all notifications and a mark_as_read
action that updates a notification's status to "read".
Create a PaymentsController
in app/controllers/my_engine/payments_controller.rb
:
module MyEngine
class PaymentsController < ApplicationController
def new
@payment = Payment.new
end
def create
@payment = Payment.new(payment_params)
if @payment.save
redirect_to my_engine_payments_path, notice: "Payment created successfully."
else
render :new
end
end
private
def payment_params
params.require(:payment).permit(:amount, :currency)
end
end
end
This controller defines a new action that creates a new payment and a create action that saves the payment to the database. It also includes a payment_params method to sanitize the payment parameters.
Create views for the NotificationsController
in app/views/my_engine/notifications/
:
index.html.erb
:
<h1>Notifications</h1>
<table>
<thead>
<tr>
<th>Message</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% @notifications.each do |notification| %>
<tr>
<td><%= notification.message %></td>
<td><%= notification.status.capitalize %></td>
<td><%= link_to "Mark as Read", my_engine_mark_as_read_notification_path(notification), method: :put %></td>
</tr>
<% end %>
</tbody>
</table>
mark_as_read.html.erb
<p>Notification has been marked as read.</p>
<%= link_to "Back to Notifications", my_engine_notifications_path %>
Create views for the PaymentsController
in app/views/my_engine/payments/
:
new.html.erb
:
<h1>New Payment</h1>
<%= form_with(model: @payment, url: my_engine_payments_path) do |f| %>
<% if @payment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@payment.errors.count, "error") %> prohibited this payment from being saved:</h2>
<ul>
<% @payment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= f.label :amount %>
<%= f.text_field :amount %>
</div>
<div>
<%= f.label :currency %>
<%= f.select :currency, options_for_select([["USD", "USD"], ["EUR", "EUR"], ["GBP", "GBP"]]) %>
</div>
<div>
<%= f.submit "Create Payment" %>
</div>
<% end %>
index.html.erb
:
<h1>Payments</h1>
<% flash.each do |type, message| %>
<div class="<%= type %>"><%= message %></div>
<% end %>
<table>
<thead>
<tr>
<th>Amount</th>
<th>Currency</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<% @payments.each do |payment| %>
<tr>
<td><%= payment.amount %></td>
<td><%= payment.currency %></td>
<td><%= payment.status.capitalize %></td>
</tr>
<% end %>
</tbody>
</table>
<%= link_to "New Payment", my_engine_new_payment_path %>
Finally, add routes for the NotificationsController
and PaymentsController
in config/routes.rb
:
Rails.application.routes.draw do
mount MyEngine::Engine => "/my_engine"
namespace :my_engine do
resources :notifications do
put :mark_as_read, on: :member
end
resources :payments, only: [:new, :create, :index]
end
end
This will mount the engine at the /my_engine
URL and define routes for the NotificationsController
and PaymentsController
.
With these steps, you now have a functional Rails engine with notification and payment functionality. To call these engine methods in a Rails app, you can mount the engine in the config/routes.rb
file of your app and use the generated engine routes to access the engine's controllers and models. For example:
Rails.application.routes.draw do
mount MyEngine::Engine => "/my_engine"
get "/notifications", to: "my_engine/notifications#index"
post "/notifications/mark_as_read/:id", to: "my_engine/notifications#mark_as_read", as: :mark_as_read_notification
get "/payments/new", to: "my_engine/payments#new"
post "/payments", to: "my_engine/payments#create"
get "/payments", to: "my_engine/payments#index"
end
This will allow you to access the notifications and payments functionality provided by the engine in your Rails app by visiting the appropriate URLs. For example, you can create a link to the MyEngine
notifications page using the following code in one of your views:
<%= link_to "My Notifications", my_engine_notifications_path %>
This will create a link to the notifications index page provided by the engine, which you can style and modify as desired to fit your app's design.
To call the notification and payment functionality provided by the Rails engine in a Rails application, you can use the routes and helper methods generated by the engine.
Here's an example of how you could use these helper methods in a view to display a list of notifications and a link to the payment page:
<!-- app/views/my_app/index.html.erb -->
<h1>Welcome to My App</h1>
<h2>Notifications</h2>
<ul>
<% MyEngine::Notification.all.each do |notification| %>
<li>
<%= notification.message %>
<% unless notification.read %>
<%= link_to "Mark as read", mark_as_read_notification_path(notification) %>
<% end %>
</li>
<% end %>
</ul>
<h2>Payments</h2>
<%= link_to "Make a payment", my_engine_payments_path %>
In this example, we're using the MyEngine::Notification
model provided by the engine to display a list of notifications, along with a link to mark each notification as read. We're also using the mark_as_read_notification_path
helper method provided by the engine to generate the appropriate URL for marking a notification as read.
Finally, we're using the my_engine_payments_path
helper method provided by the engine to generate a link to the payment page. When the user clicks this link, they'll be taken to the payment page provided by the engine, where they can make a payment using the payment functionality provided by the engine.
Overall, integrating a Rails engine into a Rails application is similar to using any other external library or gem in a Rails app. By using the routes and helper methods provided by the engine, you can easily access the functionality provided by the engine and integrate it into your application as needed.
Top comments (4)
Excellent article.
I needed an article like this with tests using RSpec, but it was already very useful.
Thanks @nemuba
Excellent article. Thanks for writing this!
Thanks