Выбрать главу

2. Передайте преобразованные данные в jq.

3. Преобразуйте выходные данные JSON в YAML.

Важно помнить, что конечной целью этого упражнения является демонстрация того, как различные концепции Crystal могут применяться для создания функционального и удобного приложения CLI. Таким образом, мы не собираемся уделять слишком много внимания попыткам сделать его на 100% надежным для каждого варианта использования, а вместо этого сосредоточимся больше на различных инструментах/концепциях, используемых в рамках реализации.

Имея это в виду, давайте перейдем к написанию первоначальной реализации, начав с чего-то простого и повторяя его, пока не получим полностью работающую реализацию. Начнем с самого простого случая: вызовите jq с жестко закодированными данными JSON, чтобы показать, как эта часть будет работать. К счастью для нас, стандартная библиотека Crystal включает тип https://crystal-lang.org/api/Process.html, который позволяет напрямую вызывать процесс jq, установленный в данный момент. Таким образом, мы можем использовать все его функции без необходимости переносить их в Crystal.

Откройте src/transform.cr в выбранной вами IDE и обновите его, чтобы он выглядел следующим образом:

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. Давайте начнем с этого.

Преобразование данных

Следуя предыдущей рекомендации, я собираюсь создать новый файл, который будет содержать логику преобразования. Для начала создайте файл src/yaml.cr со следующим содержимым:

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

Кроме того, не забудьте запросить этот файл в src/transform.cr, добавив 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, а затем захотите, например, также вызвать реализацию стандартной библиотеки.

Затем я обновил файл src/transform.cr, чтобы он выглядел следующим образом:

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