Comandos Ace
Ace é uma ferramenta de linha de comando poderosa criada para AdonisJs.
Até agora, você usou vários comandos Ace para gerar controladores, modelos ou para executar migrações.
Neste guia, aprendemos sobre os internos do Ace e como criar comandos.
Introdução
Todo projeto AdonisJs tem um arquivo ace
na raiz do projeto, que é um arquivo JavaScript regular, mas sem a extensão .js
.
O arquivo ace
é usado para executar comandos específicos do projeto. Para comandos reutilizáveis, você deve agrupá-los como pacotes npm.
Execute o código a seguir para ver a lista de comandos Ace disponíveis:
node ace
# Saída:
Usage:
command [arguments] [options]
Global Options:
--env Set NODE_ENV before running the commands
--no-ansi Disable colored output
Available Commands:
seed Seed database using seed files
migration
migration:refresh Refresh migrations by performing rollback and then running from start
migration:reset Rollback migration to the first batch
migration:rollback Rollback migration to latest batch or a specific batch number
migration:run Run all pending migrations
migration:status Check migrations current status
OBSERVAÇÃO
Por conveniência, o comando adonis
faz proxy de todos os comandos para um determinado projeto. Por exemplo, executar adonis migration:run
tem o mesmo resultado que executar node ace migration:run
.
Criando comandos
Vamos construir rapidamente um comando Ace para extrair citações aleatórias de Paul Graham por meio da API do Wisdom e enviá-las para o terminal.
Configuração
adonis make:command Quote
# Saída:
✔ create app/Commands/Quote.js
┌───────────────────────────────────────────────────────────┐
│ Register command as follows │
│ │
│ 1. Open start/app.js │
│ 2. Add App/Commands/Quote to commands array │
└───────────────────────────────────────────────────────────┘
Seguindo as instruções de saída, registre seu comando recém-criado dentro do array commands
no arquivo start/app.js
:
// .start/app.js
const commands = [
'App/Commands/Quote',
]
Agora, se executarmos adonis
, devemos ver o comando quote
dentro da lista de comandos disponíveis.
Mostrando aspas
Substitua tudo dentro do arquivo de comando pelo seguinte código:
// .app/Commands/Quote.js
'use strict'
const { Command } = use('@adonisjs/ace')
const got = use('got')
class Quote extends Command {
static get signature () {
return 'quote'
}
static get description () {
return 'Shows inspirational quote from Paul Graham'
}
async handle (args, options) {
const response = await got('https://wisdomapi.herokuapp.com/v1/author/paulg/random')
const quote = JSON.parse(response.body)
console.log(`${this.chalk.gray(quote.author.name)} - ${this.chalk.cyan(quote.author.company)}`)
console.log(`${quote.content}`)
}
}
module.exports = Quote
NOTA
Certifique-se de instalar o pacote got via npm, que é usado para consumir a API HTTP no código de comando acima.
Executar adonis quote
imprime a citação recuperada no terminal.
Assinatura do comando
A assinatura do comando define o nome do comando, opções obrigatórias/opcionais e sinalizadores.
A assinatura é definida como uma string de expressão, assim:
static get signature () {
return 'greet { name: Name of the user to greet }'
}
Na assinatura de exemplo acima:
greet
é o nome do comando{ name }
é um argumento obrigatório a ser passado ao executar o comando- Tudo após o
:
é a descrição do nome do argumento que o precede
A assinatura do comando pode abranger várias linhas usando literais de modelo ES6:
static get signature () {
return `
greet
{ name : Name of the user to greet }
{ age? : User age }
`
}
Argumentos opcionais
Os argumentos podem ser opcionais anexando ?
ao nome do argumento:
'greet { name? : Name of the user to greet }'
Valor padrão
Você também pode definir um valor padrão para um argumento assim:
'greet { name?=virk : Name of the user to greet }'
Flags
Os flags são prefixados com --
e têm a mesma assinatura que argumentos:
static get signature () {
return `
send:email
{ --log : Log email response to the console }
`
}
Usando a assinatura de exemplo acima, você passaria o sinalizador --log
quando o comando fosse executado assim:
adonis send:email --log
Sinalizadores com valores
Às vezes, você pode querer aceitar valores com sinalizadores.
Isso pode ser feito ajustando a expressão de assinatura da seguinte forma:
static get signature () {
return `
send:email
{ --driver=@value : Define a custom driver to be used }
`
}
No exemplo acima, =@value
instrui o Ace a garantir que um valor seja sempre passado com o sinalizador --driver
.
Ação de comando
O método handle
na classe de comando é invocado toda vez que um comando é executado e recebe um objeto de argumentos
e flags
:
async handle (args, flags) {
console.log(args)
console.log(flags)
}
NOTA:
Todos os argumentos e flags são passados no formato camel case. Por exemplo, um sinalizador --file-path
seria definido como a chave filePath
dentro do objeto flags
passado.
Perguntas
Dentro do seu comando, você pode solicitar respostas aos usuários e aceitar valores fazendo perguntas interativas.
ask(question, [defaultAnswer])
Solicita ao usuário uma entrada textual:
async handle () {
const name = await this
.ask('Enter project name')
// with default answer
const name = await this
.ask('Enter project name', 'yardstick')
}
secure(question, [defaultAnswer])
O método secure
é semelhante ao ask
, mas a entrada do usuário é oculta (útil ao solicitar informações confidenciais, por exemplo, uma senha):
const password = await this
.secure('What is your password?')
confirm(question)
Solicita ao usuário uma resposta sim/não
:
const deleteFiles = await this
.confirm('Are you sure you want to delete selected files?')
multiple(title, choices, [selected])
Solicita ao usuário respostas para uma pergunta de múltipla escolha:
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
'Roasted vegetable lasagna',
'Vegetable & feta cheese filo pie',
'Roasted Cauliflower + Aubergine'
])
Os valores da matriz choices
podem ser objetos:
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
{
name: 'Roasted Cauliflower + Aubergine',
value: 'no 1'
},
{
name: 'Carrot + Tabbouleh',
value: 'no 2'
}
])
Você também pode passar uma matriz de valores pré-selecionados:
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
'Roasted vegetable lasagna',
'Vegetable & feta cheese filo pie',
'Roasted Cauliflower + Aubergine'
], [
'Roasted vegetable lasagna',
])
choice(question, choices, [selected])
Solicita ao usuário uma única resposta para uma pergunta de múltipla escolha:
const client = await this
.choice('Client to use for installing dependencies', [
'yarn', 'npm'
])
Os valores da matriz choices
podem ser objetos:
const client = await this
.choice('Client to use for installing dependencies', [
{
name: 'Use yarn',
value: 'yarn'
},
{
name: 'Use npm',
value: 'npm'
}
])
Você também pode passar um valor pré-selecionado:
const client = await this
.choice('Client to use for installing dependencies', [
{
name: 'Use yarn',
value: 'yarn'
},
{
name: 'Use npm',
value: 'npm'
}
], 'npm')
Saída colorida
Ace usa kleur para enviar mensagens de log coloridas para o terminal.
NOTA
Você pode acessar a instância do comando kleur via this.chalk
.
Métodos auxiliares
Os seguintes métodos auxiliares registram mensagens consistentemente estilizadas no terminal.
info(message)
Registra uma mensagem de informação no console com a cor ciano:
this.info('Something worth sharing')
success(message)
Registra uma mensagem de sucesso no console com a cor verde:
this.success('All went fine')
warn(message)
Registra uma mensagem de aviso no console com a cor amarela:
this.warn('Fire in the hole')
NOTA
warn
usa console.warn
em vez de console.log
.
error(message)
Registra uma mensagem de erro no console com a cor vermelha:
this.error('Something went bad')
NOTA
error
usa console.error
em vez de console.log
.
completed(action, message)
Imprime uma ação com mensagem para o console:
this.completed('create', 'config/app.js')
# Saída:
create: config/app.js
failed(action, message)
Imprime uma ação com falha com mensagem para o console:
this.failed('create', 'config/app.js')
NOTA
failed
usa console.error
em vez de console.log
.
table(head, body)
Imprime dados tabulares no console:
const head = ['Name', 'Age']
const body = [['virk', 22], ['joe', 23]]
this.table(head, body)
# Saída:
┌──────┬─────┐
│ Name │ Age │
├──────┼─────┤
│ virk │ 22 │
├──────┼─────┤
│ joe │ 23 │
└──────┴─────┘
A cor da linha de cabeçalho também pode ser definida:
const head = ['Name', 'Age']
const body = [['virk', 22], ['joe', 23]]
const options = { head: ['red'] }
this.table(head, body, options)
icon(type)
Retorna um ícone colorido para um tipo fornecido:
console.log(`${this.icon('success')} Completed`)
# Saída:
✔ Completed
Tipo | Cor | Ícone |
---|---|---|
info | cyan | ℹ |
success | green | ✔ |
warn | yellow | ⚠ |
error | red | ✖ |
Gerenciamento de arquivos
Ace simplifica a interação com o sistema de arquivos ao oferecer uma API Promise first.
writeFile(location, contents)
Grava o arquivo em um local fornecido (cria automaticamente os diretórios ausentes):
await this.writeFile(Helpers.appRoot('Models/User.js'), '…')
ensureFile(location)
Garantir que o arquivo exista, caso contrário, crie um arquivo vazio:
await this.ensureFile(Helpers.appRoot('Models/User.js'))
ensureDir(location)
Garantir que o diretório exista, caso contrário, crie um diretório vazio:
await this.ensureDir(Helpers.appRoot('Models'))
pathExists(location)
Retorna um booleano indicando se o caminho existe ou não:
const exists = await this.pathExists('some-location')
if (exists) {
// do something
}
removeFile(location)
Remover o arquivo de um local fornecido:
await this.removeFile('some-location')
removeDir(location)
Remover o diretório de um local fornecido:
await this.removeDir('some-location')
readFile(location)
Ler o conteúdo de um arquivo fornecido:
const contents = await this.readFile('some-location', 'utf-8')
copy(src, dest)
Copiar arquivo/diretório de um local para outro:
await this.copy(src, dest)
move(src, dest)
Mover arquivo/diretório de um local para outro:
await this.move(src, dest)
Gerenciamento de conexão de banco de dados
Ao usar o acesso ao banco de dados em um comando Ace (via Lucid ou diretamente), você deve se lembrar de fechar manualmente a conexão do banco de dados:
Database.close()
Um exemplo mais completo:
const Database = use('Database')
class Quote extends Command {
static get signature () {
return 'quote'
}
static get description () {
return 'Shows inspirational quote from Paul Graham'
}
async handle (args, options) {
let quote = await Quote.query().orderByRaw('rand()').first()
console.log(quote.content)
// Without the following line, the command will not exit!
Database.close()
}
}