module Transform
VERSION = "0.1.0"
# The same input data used in the example at the
# beginning of the chapter.
INPUT_DATA = %([{"id":1,"author":{"name":"Jim"}},{"id":2,
"author":{"name":"Bob"}}])
Process.run(
"jq",
[%([.[] | {"id": (.id + 1), "name": .author.name}])],
input: IO::Memory.new(INPUT_DATA),
output: :inherit
)
end
Сначала мы определяем константу с входными данными, которые использовались в предыдущем примере. Process.run
запускает процесс и ожидает его завершения. Затем мы вызываем его, используя jq в качестве команды вместе с массивом аргументов (в данном случае только фильтр). Мы передаем ввод-вывод из памяти в качестве входных данных для команды. Не обращайте на это особого внимания; более подробно это будет рассмотрено в следующей главе. Наконец, мы устанавливаем для выходных данных команды значение :inherit
, что заставляет программу наследовать выходные данные своего родительского модуля, которым является наш терминал.
Выполнение этого файла через crystal src/transform.cr
приводит к тому же результату, что и в предыдущем примере jq, который удовлетворяет второму требованию нашего CLI. Однако нам все еще нужно выполнить требования 1 и 3. Давайте начнем с этого.
Преобразование данных
Следуя предыдущей рекомендации, я собираюсь создать новый файл, который будет содержать логику преобразования. Для начала создайте файл
require "yaml"
require "json"
module Transform::YAML
def self.deserialize(input : String) : String
::YAML.parse(input).to_json
end
def self.serialize(input : String) : String
JSON.parse(input).to_yaml
end
end
Кроме того, не забудьте запросить этот файл в require "./ yaml"
в начало файла.
Crystal поставляется с довольно надежной стандартной библиотекой общих / полезных функций. Хорошим примером этого являются модули https://crystal-lang.org/api/YAML.html и https://crystal-lang.org/api/JSON.html, которые упрощают написание логики преобразования. Я определил два метода: один для обработки YAML => JSON, а другой для обработки JSON => YAML. Обратите внимание, что я использую ::YAML
для ссылки на модуль стандартной библиотеки. Это связано с тем, что метод уже определен в пространстве имен YAML. Без ::
Crystal будет искать метод .parse
в своем текущем пространстве имен вместо того, чтобы обращаться к стандартной библиотеке. Этот синтаксис также работает с методами, что может пригодиться, если вы случайно определите свой собственный метод #raise
, а затем захотите, например, также вызвать реализацию стандартной библиотеки.
Затем я обновил файл
require "./yaml"
module Transform
VERSION = "0.1.0"
INPUT_DATA = <←YAML
---
- id: 1
author:
name: Jim
- id: 2
author:
name: Bob
YAML
output_data = String.build do |str|
Process.run(
"jq",
[%([.[] | {"id": (.id + 1), "name": .author.name}])],
input: IO::Memory.new(
Transform::YAML.deserialize(INPUT_DATA)
),
output: str
)
end
puts Transform::YAML.serialize(output_data)
end
Код в основном тот же, но теперь он предоставляет входные данные на языке YAML и включает нашу логику преобразования. Стоит отметить, что теперь мы используем String.build
для создания строки в коде, как вы могли видеть на своем терминале ранее. Основная причина этого заключается в том, что строка нужна нам для того, чтобы преобразовать ее обратно в YAML перед выводом на экран нашего терминала.
На данный момент у нас есть рабочая базовая реализация, которая соответствует нашим целям, но код на самом деле не пригоден для повторного использования, поскольку все это определено на верхнем уровне нашего пространства имен transform
. Нам следует исправить это, прежде чем мы сможем назвать это завершенным.
Улучшение возможности повторного использования