Slide 1

Slide 1 text

{ DSLs JavaScript } por @nuxlli

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

azuki

Slide 4

Slide 4 text

DSL Domain Specific Language Linguagem de domínio específico

Slide 5

Slide 5 text

LINGUAGEM DE DOMÍNIO ESPECÍFICO É o inverso de uma linguagem de propósito geral

Slide 6

Slide 6 text

azk Docs Gitbook HTML

Slide 7

Slide 7 text

< /> HTML Domínio: marcação de conteúdo

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

1/2 WOLFRAM LANGUAGE Domínio: matemática e fórmulas

Slide 10

Slide 10 text

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)

Slide 11

Slide 11 text

RAILS (MIGRATIONS) Domínio: database scheme

Slide 12

Slide 12 text

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)

Slide 13

Slide 13 text

MOCHA (BDD STYLE) Domínio: specs mocha

Slide 14

Slide 14 text

var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World!'); }); EXPRESS.JS EXPRESS.JS

Slide 15

Slide 15 text

EXPRESS.JS Ainda que definida usando uma linguagem de propósito geral, é uma DSL

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

NATIVA OU SUBSET HTML tem interpretadores nativos, Rails Migrations e Express.js são subsets de uma linguagem de propósito geral

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

// 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

Slide 20

Slide 20 text

PLUG-IN OU API Não precisa aprender todos os detalhes internos para usar uma API ou estender uma aplicação

Slide 21

Slide 21 text

azk

Slide 22

Slide 22 text

ORQUESTRADOR DE AMBIENTES DE DESENVOLVIMENTO Ferramenta simples e open source que vai lhe ajudar a manter seu ambiente de desenvolvimento azk

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

systems({ api: { //... }, mysql: { //... }, });

Slide 26

Slide 26 text

systems({ api: { image: { docker: "azukiapp/ruby" }, }, mysql: { image: { docker: "azukiapp/mysql" }, }, });

Slide 27

Slide 27 text

systems({ api: { image : { docker: "azukiapp/ruby" }, depends: [ "mysql" ], //... }, mysql: //..., });

Slide 28

Slide 28 text

systems({ api: { image : { docker: "azukiapp/ruby" }, depends: [ "mysql" ], ports : { http: "80/tcp" }, }, mysql: { image: { docker: "azukiapp/mysql" }, ports: { data: "3306/tcp" }, }, });

Slide 29

Slide 29 text

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" }, }, });

Slide 30

Slide 30 text

system("mysql-cron", { extends: "mysql", ports: { 3306: disable }, }); setDefault("api");

Slide 31

Slide 31 text

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" }, }, });

Slide 32

Slide 32 text

Azkfile.js É UMA DSL SIMPLES E INTUITIVA EM JAVASCRIPT Fácil de entender e personalizar {.js}

Slide 33

Slide 33 text

DSLs de configuração não eram flexíveis o suficiente FÁCIL DE ENTENDER E PERSONALIZAR Azkfile JS

Slide 34

Slide 34 text

{ // 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

Slide 35

Slide 35 text

{ // 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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

.INI, .TOML ETC. Limitados, confusos, verbosos e principalmente: falta um padrão

Slide 39

Slide 39 text

DEVE SER SIMPLES DE LER E ENTENDER Menos atrito e traduções

Slide 40

Slide 40 text

JavaScript <3 Todo desenvolvedor conhece pelo menos o básico. JS

Slide 41

Slide 41 text

É padronizado!! JavaScript <3 <3 JS

Slide 42

Slide 42 text

Existe implementação para todas plataformas e SOs, além dos binds para as mais diversas linguagens JavaScript <3 <3 <3 JS

Slide 43

Slide 43 text

Mais flexível e poderoso do que linguagens de configuração padrão JavaScript <3 <3 <3 <3 JS

Slide 44

Slide 44 text

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}") ] }, });

Slide 45

Slide 45 text

- 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

Slide 46

Slide 46 text

{ IMPLEMENTANDO DSLs EM JavaScript }

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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)

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

{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

Slide 51

Slide 51 text

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}

Slide 52

Slide 52 text

LINGUAGEM EXTENDS Simples e elegante, mas pode ser perigosa {.js}

Slide 53

Slide 53 text

system("mysql-cron", { extends: "mysql", ports: { 3306: disable }, }); setDefault("api");

Slide 54

Slide 54 text

PRIMITIVAS: systems, env, setDefault, disable… Precisam ser carregas ou colocadas no escopo global {.js}

Slide 55

Slide 55 text

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");

Slide 56

Slide 56 text

REQUIRE? VISH!!! Permite ao usuário carregar qualquer coisa, inclusive coisas que ele não devia! {.js}

Slide 57

Slide 57 text

module.exports = function(conf) { conf.system("mysql-cron", { extends: "mysql", ports: { 3306: conf.disable }, }); conf.setDefault("api"); }; // require('./Azkfile.js')(dsl);

Slide 58

Slide 58 text

NODE MODULE Não deixa claro o require, mas além dele estar lá, ainda exige entender o que é um "module.exports"

Slide 59

Slide 59 text

{ require(‘vm') }

Slide 60

Slide 60 text

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}

Slide 61

Slide 61 text

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' }

Slide 62

Slide 62 text

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);

Slide 63

Slide 63 text

SIMPLES E FLEXÍVEL Um contrato claro é estabelecido, apenas aquilo que queremos está disponível no Azkfile.js {.js}

Slide 64

Slide 64 text

GLOBALS NÃO!!! Nada do que é definido no Azkfile.js vaza para o contexto global e compromete o funcionamento do azk {.js}

Slide 65

Slide 65 text

MAS AINDA ESTÃO FALTANDO ALGUMAS COISAS… No mundo ideal, o usuário nunca escreveria um Azkfile.js inválido. No mundo ideal ... {.js}

Slide 66

Slide 66 text

sistema("mysql-cron", { ports: { 3306: disable }, });

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

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); }

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

system("mysql-cron", { ports: { 3306: disable }, }

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

https://npmjs.com/package/syntax-error

Slide 73

Slide 73 text

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); } }

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

{ require(‘dsl-helper’) }

Slide 76

Slide 76 text

https://github.com/azukiapp/dsl-helper

Slide 77

Slide 77 text

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" }

Slide 78

Slide 78 text

PRIMITIVAS + ESCOPO + CÓDIGO = DSL

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

{ Geradores de DSL }

Slide 81

Slide 81 text

NPM INIT Baseado em um wizard, mas gera apenas um “dump" de uma estrutura JSON >_

Slide 82

Slide 82 text

{ "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 ", "license": "Apache-2" }

Slide 83

Slide 83 text

AZK INIT (ATUALMENTE) Usa um template (o que permite ir além de um dump), mas não permite customização depois de gerado >_

Slide 84

Slide 84 text

// 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}}

Slide 85

Slide 85 text

TEMPLATES SÃO DIFÍCEIS DE SE MANTER Diferente de um template HTML, espaçamentos e quebras de linha são importantes

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

{ AST (AVANÇADO) }

Slide 88

Slide 88 text

https://www.npmjs.com/package/recast

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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'

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

AZK INIT (FUTURO) Usa manipulação de AST para gerar o Azkfile.js, evitando problemas com formatação do template >_

Slide 93

Slide 93 text

AZK ADD (FUTURO) Comando que vai permitir adicionar novos systems ao Azkfile.js sem mudar a formatação ou remover comentários >_

Slide 94

Slide 94 text

{ CONCLUSÃO }

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

DE ALGUMA FORMA, VOCÊ JÁ ESTÁ USANDO UMA azk, gulp, grunt, mocha, express.js, sass…

Slide 98

Slide 98 text

{ MAIS INFORMAÇÕES }

Slide 99

Slide 99 text

DOCUMENTAÇÃO http://docs.azk.io NOSSO GITHUB https://github.com/azukiapp/

Slide 100

Slide 100 text

BLOG http://medium.com/azuki-news CHAT http://gitter.im/azukiapp/azk/

Slide 101

Slide 101 text

{ CONTRIBUINDO COM A AZUKI }

Slide 102

Slide 102 text

UTILIZE E NOS DÊ SEU FEEDBACK Issues e Pull Request são sempre bem vindos no Github

Slide 103

Slide 103 text

ESTRELAS NO https://github.com/azukiapp/azk Faça agora, é simples e pode ajudar mais do que você imagina ;)

Slide 104

Slide 104 text

OBRIGADO Éverton Ribeiro // @nuxlli [email protected] Sponsors