Podle definice je DSL (Domain Specific Language) počítačový jazyk specializovaný na určitou aplikační oblast. To znamená, že je vyvinut pro uspokojení specifických potřeb.
Přečtením tohoto článku se dozvíte, co je to DSL a co má společného s DSL. Ruby.
DSL, vítejte!
Podle definice je DSL (Domain Specific Language) počítačový jazyk specializovaný na určitou aplikační oblast. To znamená, že je vyvinut pro uspokojení specifických potřeb. Existují dva typy DSL:
-
. externí DSL, který vyžaduje vlastní syntaktický parser. Dobrým známým příkladem může být jazyk SQL - umožňuje komunikovat s databází v jazyce, ve kterém databáze nebyla vytvořena.
-
. interní DSL, který sám o sobě nemá vlastní syntaxi, ale používá syntaxi daného programovacího jazyka.
Jak asi tušíte, budeme se věnovat druhému typu DSL.
K čemu slouží?
Využitím metaprogramování jazyka Ruby lze v podstatě vytvořit vlastní minijazyk. Metaprogramování je programovací technika, která umožňuje napsat programovací jazyk. kód dynamicky za běhu (za běhu). Možná o tom nevíte, ale pravděpodobně denně používáte mnoho různých DSL. Abychom pochopili, co DSL umí, podívejme se na několik příkladů níže - všechny mají jeden společný prvek, ale dokážete na něj ukázat?
Směrování Rails
Rails.application.routes.draw do
root to: 'home#index'
resources :users do
get :search, on: :collection
end
end
```
Každý člověk, který někdy používal Rails, ví. config/routes.rb kde definujeme trasy aplikace (mapování mezi slovesy HTTP a adresami URL na akce řadiče). Ale zajímalo vás někdy, jak to funguje? Ve skutečnosti je to jen kód jazyka Ruby.
Tovární robot
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
Psaní testů často vyžaduje výrobu objektů. Proto, aby nedocházelo k plýtvání časem, by bylo opravdu dobré tento proces co nejvíce zjednodušit. To je to, co FactoryBot dělá - snadno zapamatovatelná klíčová slova a způsob popisu objektu.
Sinatra
require 'sinatra/base'
třída WebApplication < Sinatra::Base
get '/' do
'Hello world'
end
end
```
Sinatra je framework, který umožňuje vytvářet webové aplikace od nuly. Mohlo by být jednodušší definovat metodu požadavku, cestu a odpověď?
Dalšími příklady DSL mohou být Hrábě, RSpec nebo Aktivní záznam. Klíčovým prvkem každého DSL je použití bloků.
Doba výstavby
Je čas pochopit, co se skrývá pod kapotou a jak může vypadat implementace.
Předpokládejme, že máme aplikaci, která ukládá data o různých produktech. Chceme ji rozšířit o možnost importu dat z uživatelem definovaného souboru. Soubor by měl také umožňovat dynamický výpočet hodnot v případě potřeby. Abychom toho dosáhli, rozhodneme se vytvořit DSL.
Jednoduchý produkt reprezentace může mít následující atributy (product.rb):
třída Product
attr_accessor :name, :description, :price
end
Místo použití skutečné databáze budeme pouze simulovat její práci (fake_products_database.rb):
třída FakeProductsDatabase
def self.store(product)
puts [product.name, product.description, product.price].join(' - ')
end
end
Nyní vytvoříme třídu, která bude zodpovědná za čtení a zpracování souboru obsahujícího data o produktech (dsl/data_importer.rb):
modul Dsl
třída DataImporter
modul Syntaxe
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 Syntaxe
def self.import_data(file_path)
new.instance_eval File.read(file_path)
konec
konec
end
```
Třída má metodu s názvem import_data který jako argument očekává cestu k souboru. Soubor je načten a výsledek je předán příkazu instance_eval metoda, která je volána na instanci třídy. Co dělá? Vyhodnotí řetězec jako kód Ruby v kontextu instance. To znamená vlastní bude instance DataImporter třída. Díky tomu můžeme definovat požadovanou syntaxi/klíčová slova (pro lepší čitelnost je syntaxe definována jako modul). Když add_product metoda je zavolána, blok zadaný pro metodu je vyhodnocen pomocí ProductBuilder instance, která vytváří Produkt instance. ProductBuilder třída je popsána níže (dsl/product_builder.rb):
modul Dsl
třída ProductBuilder
ATTRIBUTES = %i[název popis cena].freeze
attr_reader :product
def inicializovat
@product = Product.new
end
ATTRIBUTES.each do |atribut|
define_method(atribut) do |arg = nil, &block|
value = block.is_a?(Proc) ? block.call : arg
product.public_send("#{atribut}=", value)
konec
konec
end
end
```
Třída definuje syntaxi povolenou v rámci add_product blok. S trochou metaprogramování přidává metody, které přiřazují hodnoty atributům produktu. Tyto metody také podporují předávání bloku místo přímé hodnoty, takže hodnotu lze vypočítat za běhu. Pomocí čtečky atributů jsme schopni na konci získat sestavený produkt.
Nyní přidáme importní skript (import_job.rb):
requirerelative 'dsl/dataimporter'
requirerelative 'dsl/productbuilder'
requirerative 'fakeproductsdatabase'
requirerative 'product'
Dsl::DataImporter.import_data(ARGV[0])
```
A nakonec - pomocí našeho DSL - soubor s daty o produktech (dataset.rb):
```ruby
add_product do
name 'Charger'
description 'Life saving'
cena 19.99
end
add_product do
name "Vrak auta
description {"Vrak auta na #{Time.now.strftime('%F %T')}" }
price 0.01
end
add_product do
name "Lockpick
description "Dveře se nezavírají
price 7.50
end
```
Pro import dat stačí provést jeden příkaz:
ruby import_job.rb dataset.rb
A výsledkem je..
Nabíječka - záchrana života - 19.99
Vrak auta - Zničeno v 2018-12-09 09:47:42 - 0.01
Lockpick - Dveře se nezavírají - 7.5
..úspěch!
Závěr
Při pohledu na všechny výše uvedené příklady není těžké si všimnout možností, které DSL nabízí. DSL umožňuje zjednodušit některé rutinní operace tím, že za ně skryje veškerou potřebnou logiku a uživateli zpřístupní pouze nejdůležitější klíčová slova. Umožňuje získat vyšší úroveň abstrakce a nabízí flexibilní možnosti použití (což je cenné zejména z hlediska znovupoužitelnosti). Na druhou stranu, přidání DSL do vašeho projekt je třeba vždy dobře zvážit - implementace využívající metaprogramování je rozhodně mnohem náročnější na pochopení a údržbu. Navíc vyžaduje solidní sadu testů kvůli své dynamičnosti. Dokumentace DSL přispívá k jejímu snadnějšímu pochopení, takže se rozhodně vyplatí ji provést. Přestože implementace vlastního DSL může být přínosná, je dobré mít na paměti, že se musí vyplatit.
Zaujalo vás toto téma? Pokud ano, dejte nám vědět. nás víte - povíme vám o DSL, který jsme nedávno vytvořili pro splnění požadavků v jednom z našich projektů.