Määritelmän mukaan DSL (Domain Specific Language) on tiettyyn sovellusalueeseen erikoistunut tietokonekieli. Tämä tarkoittaa, että se on kehitetty täyttämään erityistarpeita.
Lukemalla tämän artikkelin opit, mikä DSL on ja mitä yhteistä sillä on Rubyn kanssa.
DSL, tervetuloa!
Määritelmän mukaan DSL (Domain Specific Language) on tiettyyn sovellusalueeseen erikoistunut tietokonekieli. Tämä tarkoittaa, että se on kehitetty täyttämään erityistarpeita. DSL-kieliä on kahdenlaisia:
-
An ulkoinen DSL, joka vaatii oman syntaksin jäsentäjänsä. Tunnettu esimerkki on SQL-kieli - sen avulla voidaan olla vuorovaikutuksessa tietokannan kanssa kielellä, jolla tietokantaa ei ole luotu.
-
An sisäinen DSL, jolla itsellään ei ole omaa syntaksia, vaan se käyttää tietyn ohjelmointikielen syntaksia.
Kuten varmaan arvaatkin, keskitymme toiseen DSL-tyyppiin.
Mitä se tekee?
Periaatteessa Rubyn metaohjelmoinnin avulla voit luoda oman minikielesi. Metaohjelmointi on ohjelmointitekniikka, jonka avulla voidaan kirjoittaa koodi dynaamisesti ajon aikana (lennossa). Saatat olla tietämätön tästä, mutta käytät luultavasti monia erilaisia DSL:iä päivittäin. Jotta ymmärtäisit, mitä DSL:llä voi tehdä, katsotaanpa muutamia esimerkkejä alla - kaikilla näillä on yksi yhteinen elementti, mutta osaatko osoittaa sen?
Rails reititys
Rails.application.routes.draw do
root to: 'home#index'
resources :users do
get :search, on: :collection
end
end
```
Jokainen henkilö, joka on koskaan käyttänyt Rails tietää a config/routes.rb tiedosto, jossa määritellään sovellusreitit (HTTP-verbien ja URL-osoitteiden yhdistäminen ohjaimen toimintoihin). Mutta oletko koskaan miettinyt, miten se toimii? Itse asiassa se on vain Ruby-koodia.
Tehtaan Bot
FactoryBot.define do
factory :user do
yritys
sequence(:email) { |i| "user_#{i}@test.com" }
sequence(:etunimi) { |i| "Käyttäjä #{i}" }
sukunimi 'Test'
role 'manager'
end
end
Testien kirjoittaminen edellyttää usein esineiden valmistamista. Ajanhukan välttämiseksi olisi siis hyvä yksinkertaistaa prosessia mahdollisimman paljon. Tätä varten on olemassa FactoryBot tekee - helposti muistettavia avainsanoja ja tapa kuvata kohdetta.
Sinatra
vaadi 'sinatra/base'
class WebApplication < Sinatra::Base
get '/' do
'Hello world'
end
end
```
Sinatra on kehys, jonka avulla voit luoda verkkosovelluksia tyhjästä. Voisiko pyynnön metodin, polun ja vastauksen määrittäminen olla helpompaa?
Muita esimerkkejä DSL:stä voisivat olla Rake, RSpec tai Aktiivinen ennätys. Kunkin DSL:n keskeinen osa on lohkojen käyttö.
Rakennusaika
On aika ymmärtää, mitä konepellin alla piilee ja miltä toteutus voi näyttää.
Oletetaan, että meillä on sovellus, joka tallentaa tietoja eri tuotteista. Haluamme laajentaa sitä antamalla mahdollisuuden tuoda tietoja käyttäjän määrittelemästä tiedostosta. Tiedoston pitäisi myös mahdollistaa arvojen laskeminen dynaamisesti tarvittaessa. Tämän saavuttamiseksi päätämme luoda DSL:n.
Yksinkertainen tuote voi olla seuraavat attribuutit (product.rb):
class Tuote
attr_accessor :name, :description, :price
end
Sen sijaan, että käyttäisimme todellista tietokantaa, simuloimme vain sen toimintaa (fake_products_database.rb):
class FakeProductsDatabase
def self.store(tuote)
puts [product.name, product.description, product.price].join(' - ')
end
end
Nyt luodaan luokka, joka vastaa tuotetietoja sisältävän tiedoston lukemisesta ja käsittelystä (dsl/data_importer.rb):
moduuli Dsl
luokka DataImporter
moduuli Syntaksi
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 Syntaksi
def self.import_data(tiedostopolku)
new.instance_eval File.read(file_path)
end
end
end
```
Luokassa on menetelmä nimeltä import_data joka odottaa tiedostopolun argumenttina. Tiedosto luetaan ja tulos välitetään komentoon instance_eval metodi, jota kutsutaan luokan instanssille. Mitä se tekee? Se arvioi merkkijonon Ruby-koodiksi instanssin kontekstissa. Tämä tarkoittaa itse on instanssi DataImporter luokka. Tämän ansiosta voimme määritellä halutun syntaksin/avainsanat (paremman luettavuuden vuoksi syntaksi on määritelty moduulina). Kun add_product metodia kutsutaan, metodille annettu lohko evaluoidaan ProductBuilder instanssi, joka rakentaa Tuote esimerkiksi. ProductBuilder luokka on kuvattu alla (dsl/product_builder.rb):
moduuli Dsl
luokka ProductBuilder
ATTRIBUTES = %i[nimi kuvaus hinta].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("#{attribuutti}=", arvo)
end
end
end
end
```
Luokka määrittelee syntaksin, joka sallitaan add_product lohko. Pienellä metaohjelmoinnilla se lisää metodeja, jotka antavat arvoja tuoteattribuuteille. Nämä metodit tukevat myös lohkon välittämistä suoran arvon sijasta, joten arvo voidaan laskea suoritusaikana. Attribuutinlukijan avulla saamme lopussa rakennetun tuotteen.
Lisätään nyt tuontiskripti (import_job.rb):
requirerelative 'dsl/dataimporter'
requirerelative 'dsl/tuotteenrakentaja'
requirerelative 'fakeproductsdatabase'
requirerelative 'tuote'
Dsl::DataImporter.import_data(ARGV[0])
```
Ja lopuksi - käyttämällä DSL:äämme - tiedosto, jossa on tuotteiden tiedot (dataset.rb):
```ruby
add_product do
name 'Charger'
description 'Hengenpelastava'
price 19.99
end
add_product do
nimi 'Auton romu'
description { "Romuttunut klo #{Time.now.strftime('%F %T')}" }
hinta 0.01
end
add_product do
nimi 'Lukkopuukko'
description 'Ovet eivät mene kiinni'
hinta 7.50
end
```
Tuodaksemme tiedot meidän on vain suoritettava yksi komento:
ruby import_job.rb dataset.rb
Ja tulos on..
Laturi - Hengenpelastus - 19.99
Auton romuttuminen - Romuttui 2018-12-09 09:47:42 - 0.01
Lukkopesä - Ovet eivät mene kiinni - 7.5
..menestys!
Päätelmä
Kun tarkastellaan kaikkia edellä mainittuja esimerkkejä, ei ole vaikea huomata DSL:n tarjoamia mahdollisuuksia. DSL:n avulla voidaan yksinkertaistaa joitakin rutiinitoimintoja piilottamalla kaikki tarvittava logiikka ja paljastamalla käyttäjälle vain tärkeimmät avainsanat. Se mahdollistaa korkeamman abstraktiotason ja tarjoaa joustavia käyttömahdollisuuksia (mikä on erityisen arvokasta uudelleenkäytettävyyden kannalta). Toisaalta, DSL:n lisääminen sinun projekti olisi aina harkittava hyvin - metaohjelmointia käyttävää toteutusta on varmasti paljon vaikeampi ymmärtää ja ylläpitää. Lisäksi se vaatii dynaamisuutensa vuoksi vankan testisarjan. DSL:n dokumentointi helpottaa sen ymmärtämistä, joten se kannattaa ehdottomasti tehdä. Vaikka oman DSL:n toteuttaminen voi olla palkitsevaa, on hyvä muistaa, että sen on kannattavaa.
Kiinnostuitko aiheesta? Jos olet, kerro meille - kerromme sinulle DSL:stä, jonka olemme hiljattain luoneet vastaamaan erään projektimme vaatimuksia.