Many times we need to include dynamic data in our text. There are many ways to do this, just by having gsub
method.
I'd like to share my favorite approach for placeholders replacing.
Let's assume we have Article
class and we want it to generate a dynamic title that can be used for the SEO purpose.
The "Article" has name:string
and reviews_count:integer
attributes.
We need to generate a dynamic title: [name] - [reviews_count] reviews
. The title pattern can be customized in our back-office.
article.title_patern = '[name] - [reviews_count] reviews'
Let's use the Ruby on Rails metaprogramming techniques to implement this functionality:
class Article < ApplicationRecord
def title
title_pattern.gsub(/\[(.*?)\]/) { |key| dynamic_value(key) }.strip
end
private
DYNAMIC_CMDS = %w[
name
reviews_count
].freeze
def dynamic_value(key)
cmd = key[/\[(.*?)\]/, 1]
DYNAMIC_CMDS.include?(cmd) ? send(cmd) : ''
end
end
Now, we can do:
article = Article.new(name: 'Amazing Ruby', reviews_count: 123)
article.title # "Amazing Ruby - 123 reviews"
Let's review the code together.
def title
title_pattern.gsub(/\[(.*?)\]/) { |key| dynamic_value(key) }
end
We are using gsub
with a block pattern that helps us to use logic to decide how to replace something. We are searching for any words in square brackets [ANYTHING]
. Of course, you can define your own pattern, for example: {{NAME}}
or %NAME%
. Just change the Regex accordingly.
In our case, the title_patern
is [name] - [reviews_count] reviews
. So the gsub
will search for each key in brackets. We have two keys:
[name]
[reviews_count]
for each key, will call dynamic_value
method.
I'm using ruby send. There are various options dynamically call the method or attribute in ruby. But send
method has the best performance.
Note: dynamic methods can be dangerous. Especially, send
can invoke private methods. This is the reason, DYNAMIC_CMDS
check is required to keep your code safe.