Credited to: Suman Awal
sub/gsub
is the widely used substitution method in ruby. These methods replace (substitute) content of the string with the
new string based on the provided logic. In SAAS application, we offen encounter the condition where we need to generate dynamic content for a single action based on the customer. For example generating a dynamic welcome message to the customer for the different client. There are lots of ways to get the result however in this article we will use the one of the mostly used ruby method sub
and gsub
sub
and gsub
ruby methods
Before we get started, let's understand what sub
and gsub
do:
-
sub
: Replaces the first occurrence ofpattern
in a string withreplacement string
. -
gsub
: Replaces all occurrences ofpattern
in a string withreplacement string
.
Both methods use a regular expression as the pattern
and a string or a block as the replacement
. Here we will explain using a block (hash) for dynamic replacement based on our hash.
Here's a simple example:
replacements = {
'name' => 'Glenn Maxwell',
'country' => 'Australia'
}
template = "Hi, my name is {{name}} and I am from {{country}}."
result = template.gsub(/{{(.*?)}}/) { |match| replacements[$1] || match }
puts result # Output: "Hi, my name is Glenn Maxwell and I am from Australia"
In this example:
- We define a
replacements
hash containing the key-value pairs we want to use for the replacement in the string. - We define a
template
string containing placeholders enclosed in double curly braces ({{}}
). - We use
gsub
with the regular expression/{{(.*?)}}/
to find all occurrences of these placeholders. - The block is executed for each match. Inside the block:
Using sub
for Single Replacements
If you only need to replace the first occurrence of a pattern, you can use sub
instead of gsub
. The logic remains the same.
replacements = {
'name' => 'Glenn Maxwell'
}
template = "Hi, my name is {{name}} and my friend's name is also {{name}}."
result = template.sub(/{{(.*?)}}/) { |match| replacements[$1] || match }
# Output: Hi, my name is Glenn Maxwell and my friend's name is also {{name}}.
Real-World Rails Examples
This technique is useful in various Rails scenarios:
- Generate dynamic emails: You can store email templates with placeholders in your database and replace them with user-specific data.
- Create dynamic reports: Generate reports with data pulled from various sources, using a hash to map placeholders to the correct values.
- Localize content: Store localized strings in a hash and replace placeholders in your views based on the user's locale.
Here you can find one of the widely used example to Generate dynamic emails for the SAAS application.
Generate dynamic emails using hash replacement
Scenario
You have a Rails application that serves multiple clients. Each client has their own set of customers. When a new customer registers for a specific client, the application sends a welcome email. The content of the welcome email is dynamically generated based on a template stored in the database, which is specific to each client.
Sample codes
- Create models
# app/models/client.rb
class Client < ApplicationRecord
has_many :customers
has_one :welcome_email_template
end
# app/models/customer.rb
class Customer < ApplicationRecord
belongs_to :client
end
# app/models/welcome_email_template.rb
class WelcomeEmailTemplate < ApplicationRecord
belongs_to :client
end
- Migrations
# db/migrate/xxxxxx_create_clients.rb
class CreateClients < ActiveRecord::Migration[7.1]
def change
create_table :clients do |t|
t.string :name
t.string :subdomain # For identifying clients (e.g., client1.example.com)
t.timestamps
end
end
end
# db/migrate/xxxxxx_create_customers.rb
class CreateCustomers < ActiveRecord::Migration[7.1]
def change
create_table :customers do |t|
t.string :email
t.string :name
t.references :client, foreign_key: true
t.timestamps
end
end
end
# db/migrate/xxxxxx_create_welcome_email_templates.rb
class CreateWelcomeEmailTemplates < ActiveRecord::Migration[7.1]
def change
create_table :welcome_email_templates do |t|
t.references :client, foreign_key: true
t.text :template # The email template with placeholders
t.timestamps
end
end
end
- Database seed
# db/seeds.rb
Client.destroy_all
Customer.destroy_all
WelcomeEmailTemplate.destroy_all
client1 = Client.create!(name: 'Client One', subdomain: 'client1')
client2 = Client.create!(name: 'Client Two', subdomain: 'client2')
WelcomeEmailTemplate.create!(
client: client1,
template: "Welcome, {{customer_name}}!\n\nThank you for joining Client One. Your account has been created.\n\nBest regards,\nThe Client One Team"
)
WelcomeEmailTemplate.create!(
client: client2,
template: "Hello {{customer_name}},\n\nWelcome to Client Two! We're excited to have you on board.\n\nSincerely,\nThe Client Two Team"
)
- Customer Registration
# app/controllers/customers_controller.rb
class CustomersController < ApplicationController
before_action :set_client
def new
@customer = @client.customers.build
end
def create
@customer = @client.customers.build(customer_params)
if @customer.save
send_welcome_email(@customer)
redirect_to root_path, notice: 'Customer registered successfully!'
else
render :new, status: :unprocessable_entity
end
end
private
def set_client
# Assumes you have a way to identify the client, e.g., via subdomain
@client = Client.find_by(subdomain: request.subdomain)
unless @client
render plain: "Client not found", status: :not_found
end
end
def customer_params
params.require(:customer).permit(:email, :name)
end
def send_welcome_email(customer)
template = @client.welcome_email_template.template
welcome_message = generate_welcome_message(customer, template)
CustomerMailer.welcome_email(customer, welcome_message).deliver_later
end
def generate_welcome_message(customer, template)
replacements = {
'customer_name' => customer.name
}
template.gsub(/{{(.*?)}}/) { |match| replacements[$1] || match }
end
end
- Routes
# config/routes.rb
constraints subdomain: 'client1' do
scope module: 'client1', as: 'client1' do
resources :customers, only: [:new, :create]
end
end
constraints subdomain: 'client2' do
scope module: 'client2', as: 'client2' do
resources :customers, only: [:new, :create]
end
end
# Non-subdomain routes (e.g., for admin panel)
resources :clients
This example provides a basic application for handling multiple clients with customized welcome messages for their customer.
Conclusion
Using sub
and gsub
with hash replacement provides a flexible and efficient way to dynamically generate string and can be used in real application.
Top comments (0)