表演和优雅!使用 ScyllaDB 和 Ruby 的 CLI CRUD 工具
Boas pessoas desenvolvedoras precisam saber fazer CRUD não é mesmo?您是否可以使用 CRUD 产品和 NoSQL 银行进行高级升级并使用优雅而简单的语言?呐?无论如何,您可以使用 CLI 构建一个宝石,或者使用 ScyllaDBdry-cli
银行来构建一个宝石cassandra-driver
Para saber um pouco mais sobre o que é ScyllaDB e em quais contextos essa ferramenta é util, recomendo ler a documentação oficial
免责声明:Esse artigo 假设银行的基本信息是与 ruby 结合使用的,因此 Scylla DB 推荐的具体信息如下:
- Os artigos criados pelo 开发者倡导者 da ScyllaDB DanielHe4rt no Dev.To
- ScyllaDB 大学无偿提供 ScyllaDB 优惠
目录
1. 开始或项目
1.1 驱动程序安装系统的解决方案
使用 ScyllaDB 和cassandra 驱动程序进行通讯的绝妙方法,可以与 cassandra 银行相关的系统相关的图书馆进行安装,安装 cassandra 所需的图书馆的安装形式非常简单纳马奎纳:
免责声明:请不要使用卡桑德拉银行的任何实用程序,请务必安装宝石巴士作为必要的图书馆。访问 ScyllaDB 的原始基础,使用安静的 Cassandra 模式。
在 MacOS 上安装 cassandra:
brew install cassandra
在 Linux(Debian) 上安装 cassandra:
sudo apt-get install -y cassandra
Caso sua Distro/Sistema operacional não tenha sido listada, recomendo semper recorrer a Documentação Oficial
1.2 开始或项目
com 作为安装相关系统的图书馆,启动项目使用捆绑程序:
mkdir project_scylla && cd project_scylla && bundle init
1.3 项目依赖的安装
Agora Podemos Finalmente 是项目所需的宝石:
bundle add cassandra-driver
bundle add dry-auto_inject
bundle add dry-system
bundle add zeitwerk
bundle add dry-cli
bundle add dotenv
1.4 定义 REPL
如果没有相关项目,那么 IRB 的设置是否有问题?
Para isso crie um script executável em bin/console
com:
touch bin/console && chmod +x bin/console
Nesse arquivo vamos 包括 um setup básico com IRB + Dotenv:
#!/usr/bin/env ruby
require 'dotenv/load'
require 'irb'
IRB.start
非常好! Agora temos o nosso setup concluído! Vamos 程序定义了依赖项的注入和与 ScyllaDB 功能合并的验证器。
2. 依赖注入的定义
下一步是使用 sinatra 的模型,并根据 sinatra 的设置进行设置,并参考esse 链接。
2.1 Criando o 容器主体
根据描述,没有任何相关参考信息,因此需要定义容器主体的依赖功能,以提供与总线依赖和新注册提供商相关的参考服务。
Agora vamos criar um arquivo em config/application.rb
com o seguinte conteúdo:
# frozen_string_literal: true
require 'dry/system'
class Application < Dry::System::Container
configure do |config|
config.root = Pathname('.')
config.component_dirs.loader = Dry::System::Loader::Autoloading
config.component_dirs.add 'lib'
config.component_dirs.add 'config'
end
end
loader = Zeitwerk::Loader.new
loader.push_dir(Application.config.root.join('lib').realpath)
loader.push_dir(Application.config.root.join('config').realpath)
loader.setup
下一步,我们将定义一个本地化的组件,该组件在应用程序中很长一段时间,这是一个自动加载文件的原则,该文件将永远精确地用于使用阿尔古玛类阿尔古姆阿尔基沃require
。
容器定义可以通过修改 REPL 脚本来定义容器的应用程序和方法finalize!
。
#!/usr/bin/env ruby
require 'dotenv/load'
require_relative '../config/application'
Application.finalize!
require 'irb'
IRB.start
Também vamos criar um arquivo main.rb
na raiz do nosso projeto apenas com o finalize!
paraservir como ponto de entrada na nossa CLI。
require 'dotenv/load'
require_relative '../config/application'
Application.finalize!
2.2 Criando o 银行提供商
Agora 是一个应用程序容器主体,定义了一个与 ScyllaDB 相关的项目。
Para isso crie um arquivo em config/provider/database.rb
com o seguinte conteúdo:
# frozen_string_literal: true
Application.register_provider(:database) do
prepare do
require 'cassandra'
require_relative '../../lib/migration_utils'
require_relative '../constants'
end
start do
cluster = Cassandra.cluster(
username: ENV.fetch('DB_USER', nil),
password: ENV.fetch('DB_PASSWORD', nil),
hosts: ENV.fetch('DB_HOSTS', nil).split(',')
)
connection = cluster.connect
MigrationUtils.create_keyspace(session: connection) if MigrationUtils.keyspace_exist?(session: connection)
MigrationUtils.create_table(session: connection) if MigrationUtils.table_exist?(session: connection)
connection = cluster.connect(KEYSPACE_NAME)
register('database.connection', connection)
end
end
作为推荐使用云服务 ScyllaDB 的凭证,您可以快速使用集群并获取所有超级简单的凭证。
嗯,例如 .env pode ser visto abaixo:
DB_USER=scylla
DB_PASSWORD=password
DB_HOSTS=node-0.amazonaws,node-1.amazonaws,node-2.amazonaws
如果您想了解更多相关信息,请参阅以下内容:
定义为常量 para nosso projeto
使用键空间名称和常量定义的表格,可以更改接收参数或环境变量,以简化常量。
Crie um arquivo em config/constants.rb
com o seguinte conteúdo:
# frozen_string_literal: true
KEYSPACE_NAME = 'media_player'
TABLE_NAME = 'playlist'
Criando uma Classe utilitária para criar nosso banco
与前面提供的示例不同,我们使用了一系列产品活动,包括MigrationUtils
键空间和表格的初始化。
需要先采取必要的方法来处理键空间和表格。
Checando se um keyspace ou tabela 存在
前面的内容是通过键空间和表格进行的,并验证这些元素是否存在并执行所需的功能。接下来,请执行以下布尔操作方法:
首先,criaremos um arquivo chamado migration_utils.rb
、localizado em lib/migration_utils.rb
、eo preencheremos com 或 código descrito abaixo:
class MigrationUtils
# @param session [Cassandra#Cluster]
# @return [Boolean]
def self.keyspace_exist?(session:)
query = <<~SQL
select keyspace_name from system_schema.keyspaces WHERE keyspace_name=?
SQL
session.execute_async(query, arguments: [KEYSPACE_NAME]).join.rows.size.zero?
end
# @param session [Cassandra#Cluster]
# @return [Boolean]
def self.table_exist?(session:)
query = <<~SQL
select keyspace_name, table_name from system_schema.tables where keyspace_name = ? AND table_name = ?
SQL
session.execute_async(query, arguments: [KEYSPACE_NAME, TABLE_NAME]).join.rows.size.zero?
end
end
因此,您可以在配置依赖项之前执行配置方法。通过此操作,您可以在银行中进行相关操作。请参阅execute_async
CQL 的使用方法或环境咨询方法。任何方法都允许使用占位符?
来参数和特定值以及对象arguments: []
。
使用这种方法可以实现未来的目标,也可以使用该方法join
来实现未来的最终目标。对于重要的对象,可以将所有行发送到列表中,作为查询最常用的链接。
完成布尔恢复方法的实施,将列表设置为当前列表或不设置列表或零列表.size.zero?
免责声明:使用枚举器时的注意事项
.size.zero?
,但不适用或方法.empty?
Criando os 键空间和 tabelas
讨论按键空间和表格中的存在问题的响应方法,具体的方法是什么?
Para isso, vamos continuar trabalhando na classe localizada em lib/migration_utils.rb
com o seguinte conteúdo:
class MigrationUtils
# @param session [Cassandra#Cluster]
# @return [void]
def self.create_keyspace(session:)
query = <<~SQL
CREATE KEYSPACE #{KEYSPACE_NAME}
WITH replication = {
'class': 'NetworkTopologyStrategy',
'replication_factor': '3'
}
AND durable_writes = true
SQL
session.execute_async(query).join
end
# @param session [Cassandra#Cluster]
# @return [void]
def self.create_table(session:)
query = <<~SQL
CREATE TABLE #{KEYSPACE_NAME}.#{TABLE_NAME} (
id uuid,
title text,
album text,
artist text,
created_at timestamp,
PRIMARY KEY (id, created_at)
);
SQL
session.execute_async(query).join
end
end
Aqui vamos seguir or mesmo padrão onde recebemos or session
como paraâmetro eo usamos para executar uma query assíncrona com execute_async
,como não precisamos litdar com or retorno dessas query podemos apenas usar or join
para esperar ela Finalizar.
注意:请参考ScyllaDB 大学的推荐链接作为查询。
2.3 服务或新供应商的应用
银行的定义和相关内容、应用程序的主要注意事项:
我们无需main.rb
添加或要求:
require_relative 'config/provider/database'
我没有bin/console
吃东西:
require_relative '../config/provider/database'
3. CLI 的定义或样板
集市上的银行卡马达需要立即投入使用,vamos 会使用一个额外的宝石项目来定义我们的 CLI 操作。各位dry-cli
先生们!
最初的时刻是不是需要预先定义 CLI 的样板,是否需要真正的实现?
Para isso, vamos definir or modulo que vai registrar todos os comandos, localizado em lib/cli.rb
com o seguinte conteúdo:
# frozen_string_literal: true
require 'dry/cli'
module Cli
extend Dry::CLI::Registry
register 'add', Commands::Add
end
使用DSL来对注册商进行模数初始操作register
,然后将 DSL 和模数扩展器进行模数化Dry::CLI::Registry
。
Agora que registramos um comando Add
, vamos criar a classereferente a ele localizada em lib/cli/commands/add.rb
com o seguinte conteúdo:
# frozen_string_literal: true
require 'dry/cli'
module Cli
module Commands
class Add < Dry::CLI::Command
desc 'This command add a new song to the playlist'
argument :title, type: :string, required: true, desc: 'The title of the song'
argument :album, type: :string, required: true, desc: 'The name of the album of that song'
argument :artist, type: :string, required: true, desc: 'The name of the artist of band'
def call(title:, album:, artist:)
puts "Add command -> Title: #{title}, Album: #{album}, Artist: #{artist}"
end
end
end
end
Nessa classe podemos ver outra DSL fornecida por herdar a classe Dry::CLI::Command
,com ela podemos prover uma descrição para o comando usando desc
,声明quais argumentos vamos receber para esse comando junto com seu Tipo,验证和描述com argument
e muito mais!
徽标定义为我们的指挥官的元数据,定义了所有call
参数定义的方法。
Em nosso arquivo main.rb
podemos inicializar a nossa CLI com:
require 'dry/cli'
Dry::CLI.new(Cli).call
最后,执行应用程序ruby main.rb
并继续输出:
$ ruby main.rb
Commands:
main.rb add TITLE ALBUM ARTIST # This command add a new song to the playlist
4. 实施我们的突击队
Agora que temos um 样板和 um entendimento basic quanto a funcionamento da gem dry-cli
podemos nos preocupar em 实现alguns comandos simples para nosso CRUD。
4.1 执行主命令添加
如果您想要执行以下命令,请执行以下操作call
:
# frozen_string_literal: true
require 'dry/cli'
module Cli
module Commands
class Add < Dry::CLI::Command
desc 'This command add a new song to the playlist'
argument :title, type: :string, required: true, desc: 'The title of the song'
argument :album, type: :string, required: true, desc: 'The name of the album of that song'
argument :artist, type: :string, required: true, desc: 'The name of the artist of band'
def initialize
super
@repo = Application['database.connection']
end
def call(title:, album:, artist:)
query = <<~SQL
INSERT INTO #{KEYSPACE_NAME}.#{TABLE_NAME} (id,title,artist,album,created_at) VALUES (now(),?,?,?,?);
SQL
@repo.execute_async(query, arguments: [title, artist, album, Time.now]).join
puts "Song '#{title}' from artist '#{artist}' Added!"
end
end
end
end
下一步,我们将首先实施与银行相关的银行,以建立与银行相关的银行,并使用该银行的相关方法来构建新的银行execute_async
。
这是重要的查询功能now()
,它是银行的原生功能,可以为 ScyllaDB 提供新的 UUID 服务,该格式是快速激光雷达的格式,可直接使用 UUID 语言。
Bem simples certo? vamos 继续 com os próximos comandos do nosso CRUD seguindo a mesma arquitetura já proposta.
4.2 执行命令列表
详细介绍了一些新的音乐,并准备了一些关于未来的音乐列表。 Para isso vamos criar um arquivo em lib/cli/commands/list.rb
com o seguinte conteúdo:
# frozen_string_literal: true
require 'dry/cli'
module Cli
module Commands
class List < Dry::CLI::Command
desc 'This command shows all the created songs'
def initialize
super
@repo = Application['database.connection']
end
def call
query = <<~SQL
SELECT * FROM #{KEYSPACE_NAME}.#{TABLE_NAME};
SQL
@repo.execute_async(query).join.rows.each do |song|
puts <<~MSG
ID: #{song['id']} | Song Name: #{song['title']} | Album: #{song['album']} | Created At: #{song['created_at']}
MSG
end
end
end
end
end
新的,我们的查询SELECT
很简单,但最终的结果是没有任何结果,rows
重要的是,访问的字段与对应的row['title']
字段相同,但没有CREATE TABLE
提供者。
注册员的权限可以修改或修改lib/cli.rb
:
# frozen_string_literal: true
require 'dry/cli'
module Cli
extend Dry::CLI::Registry
register 'add', Commands::Add
register 'list', Commands::List # <= Novo comando
end
佩菲托! Agora podemos tanto adicionar Quanto listar musicas :D
Uma simples demo do que temos agora tanto com 添加双币种列表:
4.3 执行或命令删除
Vamos 探索集市和comando bastante interessante。接下来,我将列出索引的伴奏音乐列表,允许您选择对应位置的特定音乐。
为了实现这一点,请执行以下操作:将列表中的音乐响应为与数字相关的方法。不过,请注意不要使用任何输入。
接下来,vamos criar um novo arquivo localizado em lib/cli/commands/delete.rb
com o seguinte conteúdo:
# frozen_string_literal: true
require 'dry/cli'
module Cli
module Commands
class Delete < Dry::CLI::Command
desc 'This command will prompt for a song to be deleted and then delete it'
def initialize
super
@repo = Application['database.connection']
end
def call
songs = @repo.execute_async("SELECT * FROM #{KEYSPACE_NAME}.#{TABLE_NAME}").join.rows
song_to_delete_index = select_song_to_delete(songs:)
query = <<~SQL
DELETE FROM #{KEYSPACE_NAME}.#{TABLE_NAME} WHERE id = ?
SQL
song_to_delete = songs.to_a[song_to_delete_index]
@repo.execute_async(query, arguments: [song_to_delete['id']]).join
end
private
# @param songs [Array<Hash>]
def select_song_to_delete(songs:)
songs.each_with_index do |song, index|
puts <<~DESC
#{index + 1}) Song: #{song['title']} | Album: #{song['album']} | Artist: #{song['artist']} | Created At: #{song['created_at']}
DESC
end
print 'Select a song to be deleted: '
$stdin.gets.chomp.to_i - 1
end
end
end
end
没有方法select_song_to_delete
可以接收音乐列表并根据使用的方法或方法进行操作,each_with_index
以确保没有模型1) Song: | Album: | Artist: | Created At:
。 Ainda nesse 方法esperamos 或输入usuário com$stdin.gets.chomp
或repassamos 不返回como um 整数转换com .to_i
。
没有任何方法call
,请按照音乐注册表咨询所有内容。接下来,请结合使用方法来选择特定的具体信息并返回信息。使用时,请先阅读特定的音乐,然后再进行DELETE
删除咨询。
免责声明:选择具体项目的前提,请先将数组转换为
.to_a
枚举器。
Uma 演示了大多数操作或使用命令的实践:
注册员的权限可以修改或修改lib/cli.rb
:
# frozen_string_literal: true
require 'dry/cli'
module Cli
extend Dry::CLI::Registry
register 'add', Commands::Add
register 'list', Commands::List
register 'delete', Commands::Delete # <= Novo comando
end
4.4 实施和命令更新
Agora vamos criar um comando que vai juntar todos os conceitosmostrados
anteriormente, este será um update
e vai Performar da seguinte forma:
- Vamos aceitar parâmetros como
title
,album
,artist
para usar como parte do update (semelhante ao que fizemos no comandoadd
) - Vamosmostrar para o usuário uma lista das musicas cadastradas e esperar o input do usuário com um índice (semelhante ao que fizemos no comando
delete
)
佩菲托! Vamos criar um comando localizado em lib/cli/commands/update.rb
com o seguinte conteúdo:
# frozen_string_literal: true
require 'dry/cli'
module Cli
module Commands
class Update < Dry::CLI::Command
desc 'This command will prompt for a song to be updated and use the argument information to updated it'
argument :title, type: :string, required: true, desc: 'The title of the song'
argument :album, type: :string, required: true, desc: 'The name of the album of that song'
argument :artist, type: :string, required: true, desc: 'The name of the artist of band'
def initialize
super
@repo = Application['database.connection']
end
def call(title:, album:, artist:)
songs = @repo.execute_async("SELECT * FROM #{KEYSPACE_NAME}.#{TABLE_NAME}").join.rows
song_to_update_index = select_song_to_update(songs:)
query = <<~SQL
UPDATE #{KEYSPACE_NAME}.#{TABLE_NAME} SET title = ?, artist = ?, album = ? WHERE id = ? AND created_at = ?
SQL
song_to_update = songs.to_a[song_to_update_index]
@repo.execute_async(query, arguments: [title, artist, album, song_to_update['id'] song_to_update['created_at']]).join
end
private
# @param songs [Array<Hash>]
def select_song_to_update(songs:)
songs.each_with_index do |song, index|
puts <<~DESC
#{index + 1}) Song: #{song['title']} | Album: #{song['album']} | Artist: #{song['artist']} | Created At: #{song['created_at']}
DESC
end
print 'Select a song to be updated: '
$stdin.gets.chomp.to_i - 1
end
end
end
end
Como pode ver, esse comando é realmente uma junção entre os conceitos do comando add
(arguments) and conceitos do comando delete
(método select pelo índice)。
重要的查询是update
ScyllaDB 没有主键(实际上是这样id
)created_at
,请使用它作为与银行相关的信息。
Uma 演示了命令的大部分功能:
结论
Espero que esse artigo tenha sido útil!最大程度地集成 Ruby 和 ScyllaDB,但我们无法使用简单的语言来启动。有关 ScyllaDB 的具体指示,建议加强DanielHe4rt和ScyllaDB 大学的工作。
我们将继续进行这个项目! Deixo como um desafio abusca por aprimoramentos na arquitetura que construímos。 Abaixo,elenco alguns pontos de melhoria 明显,porém,encorajo você 一个识别的结尾 que possam ter passado despercebidos por mim。 A prática é o segredo para se Tornar uma pessoa desenvolvedora habilidosa!
😄
- 作为 funções
select_song_to_delete
eselect_song_to_update
são iguais,talvez mover ela para algum local comum? - 没有指挥官在打印中
add
成功地完成我们的任务,如果没有任务结束,您将获得更好的经验吗?
愿原力与你同在!🍒
文章来源:https://dev.to/scylladb/performance-e-elegancia-escrevendo-uma-cli-crud-utilizando-scylladb-e-ruby-1452