Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
DSLs JavaScript
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Everton Ribeiro
September 24, 2015
Programming
5
300
DSLs JavaScript
Entendo DSLs através do exemplo prático do Azkfile.js, o arquivo manifesto do projeto
http://azk.io
Everton Ribeiro
September 24, 2015
Tweet
Share
More Decks by Everton Ribeiro
See All by Everton Ribeiro
Arquiteturas Executáveis - WeOp Summit
nuxlli
3
110
Arquiteturas Executáveis
nuxlli
5
120
Other Decks in Programming
See All in Programming
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.3k
【卒業研究】会話ログ分析によるユーザーごとの関心に応じた話題提案手法
momok47
0
190
Rust 製のコードエディタ “Zed” を使ってみた
nearme_tech
PRO
0
140
AIエージェント、”どう作るか”で差は出るか? / AI Agents: Does the "How" Make a Difference?
rkaga
4
2k
AIによる高速開発をどう制御するか? ガードレール設置で開発速度と品質を両立させたチームの事例
tonkotsuboy_com
7
2k
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
380
CSC307 Lecture 09
javiergs
PRO
1
830
CSC307 Lecture 04
javiergs
PRO
0
660
Architectural Extensions
denyspoltorak
0
270
例外処理とどう使い分ける?Result型を使ったエラー設計 #burikaigi
kajitack
16
6k
なるべく楽してバックエンドに型をつけたい!(楽とは言ってない)
hibiki_cube
0
140
MDN Web Docs に日本語翻訳でコントリビュート
ohmori_yusuke
0
640
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Ruling the World: When Life Gets Gamed
codingconduct
0
140
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
140
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
100
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
62
49k
A better future with KSS
kneath
240
18k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
97
Mobile First: as difficult as doing things right
swwweet
225
10k
How GitHub (no longer) Works
holman
316
140k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
1.7k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
3.9k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
300
Transcript
{ DSLs JavaScript } por @nuxlli
None
azuki
DSL Domain Specific Language Linguagem de domínio específico
LINGUAGEM DE DOMÍNIO ESPECÍFICO É o inverso de uma linguagem
de propósito geral
<!DOCTYPE HTML> <html lang="en-US" > <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible"
content="IE=11; IE=10; IE=9; IE=8; IE=7; IE=EDGE" /> <title>azk Docs Gitbook</title> </head> <body> <!-- ... --> </body> </html> HTML
< /> HTML Domínio: marcação de conteúdo
Manipulate[ Graphics[ Table[{ If[EvenQ[i], Black, White], Disk[{0, If[EvenQ[i], 0, Rescale[i,
{0, n}, (d/n) {1, -1}]]}, Rescale[i, {0, n}, {1, r}]] }, {i, 0, n}]], {{n, 31, "resolution"}, 1, 51}, {{d, 1, "displacement"}, -10, 10}, {{r, 0, "radius"}, 0, 1} ] WOLFRAM LANGUAGE
1/2 WOLFRAM LANGUAGE Domínio: matemática e fórmulas
ActiveRecord::Schema.define(version: 20141102103617) do create_table "users", force: true do |t| t.string
"password_digest" t.datetime "created_at" t.datetime "updated_at" t.boolean "setup_complete" t.string "api_key" end end RAILS (MIGRATIONS)
RAILS (MIGRATIONS) Domínio: database scheme
describe("azk config module", function() { // Don’t change ‘env’ in
test var env = config('env'); afterEach(() => set('env', env)); it("should get a env key", function() { h.expect(config('env')).to.equal('test'); h.expect(get('env')).to.equal('test'); }); }); EXPRESS.JS MOCHA (BDD STYLE)
MOCHA (BDD STYLE) Domínio: specs mocha
var express = require('express'); var app = express(); app.get('/', function
(req, res) { res.send('Hello World!'); }); EXPRESS.JS EXPRESS.JS
EXPRESS.JS Ainda que definida usando uma linguagem de propósito geral,
é uma DSL
var express = require('express'); var app = express(); app.get('/', function
(req, res) { res.send('Hello World!'); }); app.delete('/user', function (req, res) { res.send('Got a DELETE request at /user'); }); EXPRESS.JS EXPRESS.JS
NATIVA OU SUBSET HTML tem interpretadores nativos, Rails Migrations e
Express.js são subsets de uma linguagem de propósito geral
INTERNA VS EXTERNA Ainda que limitada, uma DSL interna é
mais fácil de se criar pois não precisa de um parser ou gramáticas próprias
// Express.js DSL var express = require('express'); var app =
express(); app.get('/', function (req, res) { res.send('Hello World!'); }); // No DSL var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World!'); }).listen(9615); EXPRESS.JS DSL vs NO-DSL
PLUG-IN OU API Não precisa aprender todos os detalhes internos
para usar uma API ou estender uma aplicação
azk
ORQUESTRADOR DE AMBIENTES DE DESENVOLVIMENTO Ferramenta simples e open source
que vai lhe ajudar a manter seu ambiente de desenvolvimento azk
None
FAZ ISSO ATRAVÉS DE UM ARQUIVO MANIFESTO SIMPLES: Azkfile.js O
Azkfile.js provê uma descrição sucinta dos componentes que formam a arquitetura da aplicação Azkfile JS
systems({ api: { //... }, mysql: { //... }, });
systems({ api: { image: { docker: "azukiapp/ruby" }, }, mysql:
{ image: { docker: "azukiapp/mysql" }, }, });
systems({ api: { image : { docker: "azukiapp/ruby" }, depends:
[ "mysql" ], //... }, mysql: //..., });
systems({ api: { image : { docker: "azukiapp/ruby" }, depends:
[ "mysql" ], ports : { http: "80/tcp" }, }, mysql: { image: { docker: "azukiapp/mysql" }, ports: { data: "3306/tcp" }, }, });
systems({ api: { image : { docker: "azukiapp/ruby" }, depends:
[ "mysql" ], ports: { http: "80/tcp" }, http : { domains: [ "#{system.name}.dev.azk.io" ]}, }, mysql: { image: { docker: "azukiapp/mysql" }, ports: { data: "3306/tcp" }, }, });
system("mysql-cron", { extends: "mysql", ports: { 3306: disable }, });
setDefault("api");
systems({ api: { image : { docker: "azukiapp/ruby" }, depends:
[ "mysql" ], ports: { http: "80/tcp" }, http : { domains: [ "#{system.name}.dev.azk.io" ]}, }, mysql: { image: { docker: "azukiapp/mysql" }, ports: { data: "3306/tcp" }, }, });
Azkfile.js É UMA DSL SIMPLES E INTUITIVA EM JAVASCRIPT Fácil
de entender e personalizar {.js}
DSLs de configuração não eram flexíveis o suficiente FÁCIL DE
ENTENDER E PERSONALIZAR Azkfile JS
{ // Comments are not allowed "systems""": { "api": {
"images": { "docker": "azukiapp/node" }, "command": "npm start", "http": { "domains": [ "api.dev.azk.io" ] } }, "mysql": { "images": { "docker": "azukiapp/mysql" } } }, defaultSystem: "api", } EXPRESS.JS Azkfile.json
{ // Comments are not allowed "systems""": { "api": {
"images": { "docker": "azukiapp/node" }, "command": "npm start", "http": { "domains": [ "api.dev.azk.io" ] } }, "mysql": { "images": { "docker": "azukiapp/mysql" } } } defaultSystem: "api", } EXPRESS.JS Azkfile.json
systems: api: image: docker: "azukiapp/node" command: "npm start" http: domains:
- azk.dev.azk.io mysql: &mysql image: docker: "azukiapp/mysql" mysql-cron: << *mysql ports: 3306: null default: systems EXPRESS.JS Azkfile.yml
systems: api: image: docker: "azukiapp/node" command: "npm start" http: domains:
- azk.dev.azk.io mysql: &mysql image: docker: "azukiapp/mysql" mysql-cron: << *mysql ports: 3306: null default: systems EXPRESS.JS Azkfile.yml
.INI, .TOML ETC. Limitados, confusos, verbosos e principalmente: falta um
padrão
DEVE SER SIMPLES DE LER E ENTENDER Menos atrito e
traduções
JavaScript <3 Todo desenvolvedor conhece pelo menos o básico. JS
É padronizado!! JavaScript <3 <3 JS
Existe implementação para todas plataformas e SOs, além dos binds
para as mais diversas linguagens JavaScript <3 <3 <3 JS
Mais flexível e poderoso do que linguagens de configuração padrão
JavaScript <3 <3 <3 <3 JS
function domain(prefix) { var sufix = (env.NODE_ENV === "production") ?
".azk.io" : ".dev.azk.io"; return prefix + sufix; } system("api", { image: { docker: "azukiapp/ruby" }, http: { domains: [ domain("#{system.name}") ] }, });
- Simples para o usuário entender; - Extensível; - Escopo
fechado; - Não-verboso; - Fácil de manipular (parser e geração) Azkfile.js Azkfile JS
{ IMPLEMENTANDO DSLs EM JavaScript }
fs.readFileAsync("file.json").then(JSON.parse).then(function(val) { console.log(val.success); }) .catch(SyntaxError, function(e) { console.error("invalid json in
file"); }) .catch(function(e) { console.error("unable to read file"); }); EXPRESS.JS BlueBird
describe("azk config module", function() { // Don’t change ‘env’ in
test var env = config('env'); afterEach(() => set('env', env)); it("should get a env key", function() { h.expect(config('env')).to.equal('test'); h.expect(get('env')).to.equal('test'); }); }); EXPRESS.JS MOCHA (BDD STYLE)
DSLs FOR DESCRIPTION STATE MACHINES Bonitas, porém pouco flexíveis. E
nada simples de implementar: exigem muita manipulação do contexto http://to.azk.io/composing-dsls-in-javascript
{do: [ {ask: "Enter file name: ", type: "file"}, {fetchFile:
{showProgress: "progress_bar"}, reportInterval: 1.0}, {spawn: {withRetry: {uploadToDropbox: {user: "cat", password: "meow"}}, maxTries: 5, onfail: {do: [ {deleteTempFiles: null}, {ask: "Dropbox upload failed. Try again?", type: "yes/no", yes: {retry: true}, no: {raise: "Give up"}} ]}}}, {cacheInLocalStore: "prefix"}, {showInElement: "element_id"} ]} EXPRESS.JS J EXPRESSIONS
J EXPRESSIONS: {opname: input_object} Simples de implementar mas oferece poucos
ganhos em relação ao JSON padrão http://to.azk.io/dsls-j-expressions {.js}
LINGUAGEM EXTENDS Simples e elegante, mas pode ser perigosa {.js}
system("mysql-cron", { extends: "mysql", ports: { 3306: disable }, });
setDefault("api");
PRIMITIVAS: systems, env, setDefault, disable… Precisam ser carregas ou colocadas
no escopo global {.js}
var system = require('azkfile').system; var disable = require('azkfile').disable; var setDefault
= require('azkfile').setDefault; system("mysql-cron", { extends: "mysql", ports: { 3306: disable }, }); setDefault("api");
REQUIRE? VISH!!! Permite ao usuário carregar qualquer coisa, inclusive coisas
que ele não devia! {.js}
module.exports = function(conf) { conf.system("mysql-cron", { extends: "mysql", ports: {
3306: conf.disable }, }); conf.setDefault("api"); }; // require('./Azkfile.js')(dsl);
NODE MODULE Não deixa claro o require, mas além dele
estar lá, ainda exige entender o que é um "module.exports"
{ require(‘vm') }
require('vm').runInNewContext Compila o código, contextifica uma sandbox existente (se informada)
ou cria uma nova sandbox para aquele contexto http://to.azk.io/runInNewContext {.js}
var util = require('util'); var vm = require('vm'), var sandbox
= { animal: 'cat', count: 2 }; vm.runInNewContext('count += 1; name = "kitty"', sandbox); console.log(util.inspect(sandbox)); // { animal: 'cat', count: 3, name: 'kitty' }
var util = require('util'); var vm = require(‘vm’), fs =
require('fs'); var azkfile_sandbox = { system : function() { /* ... */ }, disable: null, /* ... */ }; var azkfile = fs.readFile('./Azkfile.js'); vm.runInNewContext(azkfile, azkfile_sandbox);
SIMPLES E FLEXÍVEL Um contrato claro é estabelecido, apenas aquilo
que queremos está disponível no Azkfile.js {.js}
GLOBALS NÃO!!! Nada do que é definido no Azkfile.js vaza
para o contexto global e compromete o funcionamento do azk {.js}
MAS AINDA ESTÃO FALTANDO ALGUMAS COISAS… No mundo ideal, o
usuário nunca escreveria um Azkfile.js inválido. No mundo ideal ... {.js}
sistema("mysql-cron", { ports: { 3306: disable }, });
None
var file = "./Azkfile.js" var content = fs.readFile(file); try {
vm.runInNewContext(content, azkfile_sandbox, file); } catch (e) { var stack = e.stack.split('\n'); var msg = stack[0] + "\n" + stack[1]; throw new ManifestError(file, msg); }
None
system("mysql-cron", { ports: { 3306: disable }, }
None
https://npmjs.com/package/syntax-error
var check = require('syntax-error'); var file = "./Azkfile.js" var content
= fs.readFile(file); var err = check(content, file); if (err) { throw new ManifestError(file, err); } else { try { vm.runInNewContext(content, azkfile_sandbox, file); } catch (e) { var stack = e.stack.split('\n'); var msg = stack[0] + "\n" + stack[1]; throw new ManifestError(file, msg); } }
None
{ require(‘dsl-helper’) }
https://github.com/azukiapp/dsl-helper
var DSLHelper = require('dsl-helper').DSLHelper; // Creating the DSL by primitives
var dsl = new DSLHelper({ console: console, log: function() { console.log(this); } }); // call with scope and code dsl.execute({ name: "David" }, "log();"); // { name: "David" }
PRIMITIVAS + ESCOPO + CÓDIGO = DSL
dsl-helper: SUPORTE A NODE.JS E IO.JS (AKA NODE.JS 4.0) O
io.js introduziu tratamento adequado de erros de sintaxe no runInNewContext
{ Geradores de DSL }
NPM INIT Baseado em um wizard, mas gera apenas um
“dump" de uma estrutura JSON >_
{ "name": "example", "version": "1.0.0", "description": "npm init example", "main":
"index.js", "dependencies": {}, "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Everton Ribeiro <
[email protected]
>", "license": "Apache-2" }
AZK INIT (ATUALMENTE) Usa um template (o que permite ir
além de um dump), mas não permite customização depois de gerado >_
// Adds the systems that shape your system systems({ {{~#each
systems}} {{&hash_key @key}}: { // Dependent systems depends: {{&json depends}}, // ... },{{/each}} }); {{#if defaultSystem}} // Sets a default system setDefault("{{&defaultSystem}}"); {{~/if}}
TEMPLATES SÃO DIFÍCEIS DE SE MANTER Diferente de um template
HTML, espaçamentos e quebras de linha são importantes
NÃO É POSSÍVEL ADICIONAR CÓDIGO Uma vez que o usuário
modificou o arquivo, não é possível adicionar mais código dinamicamente
{ AST (AVANÇADO) }
https://www.npmjs.com/package/recast
var recast = require("recast"); // Original code var code =
[ "function add(a, b) {", " return a +", " // Weird formatting, huh?", " b;", "}" ].join("\n"); var ast = recast.parse(code); // Parse the code var output = recast.print(ast).code; // Generate code by ast console.log(code === output); // true
var recast = require("recast"); // Original code var code =
"var a = 'foo'"; var ast = recast.parse(code); // Rename var `a` to `bar` ast.program.body[0].declarations[0].id.name = "bar"; console.log(recast.print(ast).code); // var bar = 'foo'
MANIPULAÇÃO DA AST É O CAMINHO E A VIDA Com
recast, eu posso não apenas manipular o código do Azkfile.js, como posso também manter todas as customizações do usuário
AZK INIT (FUTURO) Usa manipulação de AST para gerar o
Azkfile.js, evitando problemas com formatação do template >_
AZK ADD (FUTURO) Comando que vai permitir adicionar novos systems
ao Azkfile.js sem mudar a formatação ou remover comentários >_
{ CONCLUSÃO }
DSLs NÃO SÃO BALAS DE PRATA DSLs são mais fáceis
de se aprender, mas são novas linguagens a serem aprendidas
MEDO TER VOCÊ NÃO DEVE: “MEDO É O CAMINHO PARA…"
Não precisa começar por algo elaborado. Experimente e observe os exemplos: existem vários muito bons no mundo JS
DE ALGUMA FORMA, VOCÊ JÁ ESTÁ USANDO UMA azk, gulp,
grunt, mocha, express.js, sass…
{ MAIS INFORMAÇÕES }
DOCUMENTAÇÃO http://docs.azk.io NOSSO GITHUB https://github.com/azukiapp/
BLOG http://medium.com/azuki-news CHAT http://gitter.im/azukiapp/azk/
{ CONTRIBUINDO COM A AZUKI }
UTILIZE E NOS DÊ SEU FEEDBACK Issues e Pull Request
são sempre bem vindos no Github
ESTRELAS NO https://github.com/azukiapp/azk Faça agora, é simples e pode ajudar
mais do que você imagina ;)
OBRIGADO Éverton Ribeiro // @nuxlli
[email protected]
Sponsors