window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster already exists') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Ruby Domain Specific Language - The Codest
The Codest
  • About us
  • Services
    • Software Development
      • Frontend Development
      • Backend Development
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Data Engineers
      • Cloud Engineers
      • QA Engineers
      • Other
    • It Advisory
      • Audit & Consulting
  • Industries
    • Fintech & Banking
    • E-commerce
    • Adtech
    • Healthtech
    • Manufacturing
    • Logistics
    • Automotive
    • IOT
  • Value for
    • CEO
    • CTO
    • Delivery Manager
  • Our team
  • Case Studies
  • Know How
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
  • About us
  • Services
    • Software Development
      • Frontend Development
      • Backend Development
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Data Engineers
      • Cloud Engineers
      • QA Engineers
      • Other
    • It Advisory
      • Audit & Consulting
  • Value for
    • CEO
    • CTO
    • Delivery Manager
  • Our team
  • Case Studies
  • Know How
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
Back arrow GO BACK
2018-12-14
Software Development

Ruby Domain Specific Language

Bartlomiej Maziarz

Referring to definition, DSL (Domain Specific Language) is a computer language specialized to a particular application domain. This means it is developed to satisfy specific needs.

By reading this article you will learn what the DSL is and what it has in common with Ruby.

DSL, say welcome!

Referring to definition, DSL (Domain Specific Language) is a computer language specialized to a particular application domain. This means it is developed to satisfy specific needs. There are two types of DSL:

  • An external DSL which requires its own syntax parser. A good known example may be the SQL language – it allows to interact with database in a language in which the database was not created.

  • An internal DSL which itself does not have its own syntax but instead uses a syntax of a given programming language.

As you can probably guess we are going to stay focused on the second DSL type.

What does it do?

Basically, by making use of a Ruby metaprogramming, it allows to create your own mini-language. Metaprogramming is a programming technique that allows to write a code dynamically at runtime (on the fly). You may be unaware of this, but you probably use many different DSLs every day. To understand what a DSL can do, let’s take a look at a few examples below – all of these have one common element, but can you point it?

Rails routing

Rails.application.routes.draw do
root to: 'home#index'

resources :users do
get :search, on: :collection
end
end
```

Every person who have ever used Rails know a config/routes.rb file where we define application routes (mapping between HTTP verbs and URLs to controller actions). But have you ever wondered how does it work? In fact, it is just Ruby code.

Factory Bot

 FactoryBot.define do
   factory :user do
     company
     sequence(:email) { |i| "user_#{i}@test.com" }
     sequence(:first_name) { |i| "User #{i}" }
     last_name 'Test'
     role 'manager'
   end
 end

Writing tests often requires fabricating objects. Hence to avoid a waste of time, it would be a really good idea to simplify the process as much as possible. That is what the FactoryBot does – easy to remember keywords and a way of describing an object.

Sinatra

require 'sinatra/base'

class WebApplication < Sinatra::Base
get '/' do
'Hello world'
end
end
```

Sinatra is a framework which allows you to create web applications from scratch. Could it be easier to define request method, path and response?

Other DSL examples might be Rake, RSpec or Active Record. The key element of each DSL is the use of blocks.

Building time

Time to understand what is hiding under the hood and how the implementation can look like.

Let’s assume we have an application which stores data about different products. We want to extend it by giving possibility to import data from a user defined file. Also, the file should allow to calculate values dynamically if needed. To achieve that, we decide to create DSL.

A simple product representation may have following attributes (product.rb):

 class Product
   attr_accessor :name, :description, :price
 end

Instead of using a real database we will just simulate its work (fake_products_database.rb):

 class FakeProductsDatabase
   def self.store(product)
     puts [product.name, product.description, product.price].join(' - ')
   end
 end

Now, we will create a class that will be responsible for reading and handling file containing products data (dsl/data_importer.rb):

module Dsl
class DataImporter
module Syntax
def add_product(&block)
FakeProductsDatabase.store product(&block)
end

  private

  def product(&block)
    ProductBuilder.new.tap { |b| b.instance_eval(&block) }.product
  end
end

include Syntax

def self.import_data(file_path)
  new.instance_eval File.read(file_path)
end

end
end
```

The class has a method named import_data which expects a file path as an argument. The file is being read and the result is passed to the instance_eval method which is called on the class instance. What does it do? It evaluates the string as a Ruby code within the instance context. This means self will be the instance of DataImporter class. Thanks to the fact we are able to define desired syntax/keywords (for a better readability the syntax is defined as a module). When the add_product method is called the block given for the method is evaluated by ProductBuilder instance which builds Product instance. ProductBuilder class is described below (dsl/product_builder.rb):

module Dsl
class ProductBuilder
ATTRIBUTES = %i[name description price].freeze

attr_reader :product

def initialize
  @product = Product.new
end

ATTRIBUTES.each do |attribute|
  define_method(attribute) do |arg = nil, &block|
    value = block.is_a?(Proc) ? block.call : arg
    product.public_send("#{attribute}=", value)
  end
end

end
end
```

The class defines syntax allowed within add_product block. With a bit of metaprogramming it adds methods which assign values to product attributes. These methods also support passing a block instead of a direct value, so a value can be calculated at runtime. Using attribute reader, we are able to obtain a built product at the end.

Now, let’s add the import script (import_job.rb):

requirerelative 'dsl/dataimporter'
requirerelative 'dsl/productbuilder'
requirerelative 'fakeproductsdatabase'
requirerelative 'product'

Dsl::DataImporter.import_data(ARGV[0])
```

And finally - using our DSL - a file with products data (dataset.rb):

```ruby
add_product do
name 'Charger'
description 'Life saving'
price 19.99
end

add_product do
name 'Car wreck'
description { "Wrecked at #{Time.now.strftime('%F %T')}" }
price 0.01
end

add_product do
name 'Lockpick'
description 'Doors shall not close'
price 7.50
end
```

To import the data we just need to execute one command:

 ruby import_job.rb dataset.rb

And the result is..

 Charger - Life saving - 19.99
 Car wreck - Wrecked at 2018-12-09 09:47:42 - 0.01
 Lockpick - Doors shall not close - 7.5

..success!

Conclusion

By looking at the all examples above, it is not hard to notice the possibilities offered by DSL. DSL allows to simplify some routine operations by hiding all required logic behind and exposing to user only the most important keywords. It allows you to get a higher level of abstraction and offers flexible use possibilities (what is especially valuable in terms of reusability). On the other hand, adding DSL to your project should be always well considered – an implementation using metaprogramming is definitely much harder to understand and maintain. Moreover, it requires solid tests suite due to its dynamism. Documenting DSL furthers its easier understanding, so it is definitely worth doing. Although implementing your own DSL can be rewarding, it is good to remember that it must pay off.

Did you get interested in the topic? If so let us know – we will tell you about DSL which we have recently created to meet the requirements in one of our projects.

Related articles

Software Development

Build Future-Proof Web Apps: Insights from The Codest’s Expert Team

Discover how The Codest excels in creating scalable, interactive web applications with cutting-edge technologies, delivering seamless user experiences across all platforms. Learn how our expertise drives digital transformation and business...

THECODEST
Software Development

Top 10 Latvia-Based Software Development Companies

Learn about Latvia's top software development companies and their innovative solutions in our latest article. Discover how these tech leaders can help elevate your business.

thecodest
Enterprise & Scaleups Solutions

Java Software Development Essentials: A Guide to Outsourcing Successfully

Explore this essential guide on successfully outsourcing Java software development to enhance efficiency, access expertise, and drive project success with The Codest.

thecodest
Software Development

The Ultimate Guide to Outsourcing in Poland

The surge in outsourcing in Poland is driven by economic, educational, and technological advancements, fostering IT growth and a business-friendly climate.

TheCodest
Enterprise & Scaleups Solutions

The Complete Guide to IT Audit Tools and Techniques

IT audits ensure secure, efficient, and compliant systems. Learn more about their importance by reading the full article.

The Codest
Jakub Jakubowicz CTO & Co-Founder

Subscribe to our knowledge base and stay up to date on the expertise from the IT sector.

    About us

    The Codest – International software development company with tech hubs in Poland.

    United Kingdom - Headquarters

    • Office 303B, 182-184 High Street North E6 2JA
      London, England

    Poland - Local Tech Hubs

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warsaw, Poland

      The Codest

    • Home
    • About us
    • Services
    • Case Studies
    • Know How
    • Careers
    • Dictionary

      Services

    • It Advisory
    • Software Development
    • Backend Development
    • Frontend Development
    • Staff Augmentation
    • Backend Developers
    • Cloud Engineers
    • Data Engineers
    • Other
    • QA Engineers

      Resources

    • Facts and Myths about Cooperating with External Software Development Partner
    • From the USA to Europe: Why do American startups decide to relocate to Europe
    • Tech Offshore Development Hubs Comparison: Tech Offshore Europe (Poland), ASEAN (Philippines), Eurasia (Turkey)
    • What are the top CTOs and CIOs Challenges?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2025 by The Codest. All rights reserved.

    en_USEnglish
    de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek en_USEnglish