Построение CLI-Утилит на Ruby при помощи Thor

Пердисловие.

Продолжая цикл статей о других направления разработки на Ruby, кроме веб-разработки. Пришла очередь многим известного Thor, который позволяет делать удобные cli-утилиты с применением Ruby.

Знакомство.

Давайте сразу перейдем к простому примеру:

require 'thor'  class SayHi < Thor   desc "hi NAME", "say hello to NAME"    def hi(name)     puts "Hi #{name}!"   end end  SayHi.start(ARGV)

Если вы запустите это без каких-либо аргументов, вы должны получить что-то вроде этого на выходе:

Commands:   first_steps.rb help [COMMAND]  # Describe available commands or one specific command   first_steps.rb hi NAME         # say hello to NAME

С помощью всего нескольких строк кода у нас есть полное описание созданной нами команды! Конечно, если вы запустите скрипт с аргументами “Hi Danila”, вы должны получить ответ “Hello, Danila!”. Давайте разберем код.

Мы создали класс под названием “SayHi”, который является производным от класса Тhor. Затем в следующей строке говорится об описании конкретной команды. Первым аргументом функции “desc” является “hi NAME”, которое описывает, какая команда нам нужна. В этом случае в нем говорится, что мы хотим, чтобы пользователь мог ввести “hi”, а затем свое имя, которое будет передано в качестве переменной. Другим примером может быть “location LATITUDE LONGITUDE”, где пользователь может ввести “location 64.39 21.34”, и команда определения местоположения получит широту и долготу, указанные данными числами.

Что именно я подразумеваю под получением? Как только мы передаем аргумент, Thor
определяет, в какой формат он вписывается, и вызывает этот метод из нашего класса “SayHi”. В этом случае он вызывает метод “hi” с “именем” в качестве аргумента.

Наконец, у нас есть наш метод “hi”, который представляет собой просто стандартный скрипт на Ruby.

Приложения Thor обычно следуют такому шаблону; существует множество функций, которые можно использовать, но общие, основополагающие концепции остаются прежними.

Давайте напишем небольшую утилиту под названием file-op, в которой есть опция командной строки для вывода содержимого файла.

require 'thor' class FileOp < Thor desc 'output FILE_NAME', 'print out the contents of FILE_NAME' def output(file_name) puts File.read(file_name) end end FileOp.start(ARGV)

Это почти та же концепция, что и в примере “SayHi”, но на этот раз, если вы запустите его с “output FILENAME”, он выведет содержимое файла (которое обрабатывается с помощью метода “вывод” класса “FileOp”).

Одной из самых удивительных функций Thor является автоматическая “help generation”. Метод “desc”, используемый ранее, имеет второй аргумент, который фактически описывает, для чего предназначена каждая команда.

Итак, сделаем это помощью нашей утилиты для работы с файлами, если запустить:

ruby file-op.rb help output

Thor выводит приятное уведомление об использовании:

Usage:   file_op_v1.rb output FILE_NAME  print out the contents of FILE_NAME

Мы не только четко документируем команды для себя в скрипте, но и пользователь знает, что делает каждая команда!

Очевидно, что немногие значимые приложения командной строки будут удовлетворены такой базовой структурой опций. К счастью, Тhor может больше. Разве не было бы неплохо, если бы мы могли добавить флаг в нашу команду “вывод” для вывода файла в stderr? Я знаю, на самом деле это было бы не так уж приятно, но давай все равно сделаем это:

require 'thor'  class FileOp < Thor   desc 'output FILE_NAME', 'print out the contents of FILE_NAME'   option :stderr, :type => :boolean    def output(file_name)     #options[:stderr] is either true or false depending     #on whether or not --stderr was passed     contents = File.read(file_name)     if options[:stderr]       $stderr.puts contents     else       $stdout.puts contents      end   end end  FileOp.start(ARGV)

Мы добавили несколько строк, но самая важная — это опция :stderr. Эта строка сообщает Тору, что любая команда, которую мы только что определили (т.е. “вывод” в данном случае), может иметь флаг, переданный как логическое значение. Другими словами, он либо передается, либо не передается; к нему не привязано другое значение, как
—times15.

Итак, мы можем идти далее:

ruby file-ops-v2.rb output --stderr filename

который напечатал бы содержимое “filename” в stderr.

Как насчет команд, которые могут быть применены к любой команде? Что-то вроде:
some_utility.rb -v

У Thor и для этого есть решение. Эти параметры, которые не связаны с определенной командой, называются параметрами класса. Нам не помешала бы дополнительная информация, когда запустится наша утилита. Но обычно нам не нужна эта информация, поэтому мы включим опцию детализации:

require 'thor'  class FileOp < Thor   class_option :verbose, :type => :boolean   desc 'output FILE_NAME', 'print out the contents of FILE_NAME'   option :stderr, :type => :boolean   def output(file_name)     log("Starting to read file...")     #options[:stderr] is either true or false depending     #on whether or not --stderr was passed     contents = File.read(file_name)     log("File contents:")     if options[:stderr]       log("(in stderr)")       $stderr.puts contents     else       log("(in stdout)")       $stdout.puts contents      end   end    no_commands do      def log(str)       puts str if options[:verbose]     end   end    desc 'touch FILE_NAME', 'creates an empty file named FILE_NAME'   option :chmod, :type => :numeric   def touch(file_name)     log("Touching file...")     f = File.new(file_name, "w")     f.chmod(options[:chmod]) if options[:chmod]   end end  FileOp.start(ARGV)

Если вы запустите, выдаст это:

ruby file_op_v5.rb output some_file --verbose

Вы должны увидеть некоторые сообщения журнала в дополнение к содержимому файла. Давайте взглянем на код.

Мы добавили метод log, однако он содержится в блоке nocommands. Чтобы сообщить Тору, что журнал не связан с командой, мы должны поместить его в этот блок. В методе журнала мы распечатываем заданную строку, если Тор получает —подробный. Затем мы используем этот метод в командах вывода.

Thor — невероятно универсальная библиотека, которая делает синтаксический анализ командной строки простым и интуитивно понятным. Однако есть несколько ошибок, таких как блок «нет команд», о которых следует знать, чтобы избежать потенциальных ошибок.
Я использовал Thor во многих разных местах, много раз для коротких утилит, для перемещения файлов, или поиска в данных. Я считаю это очень полезным инструментом, и, надеюсь, вы тоже.

Источник для этой статьи можно найти здесь.


ссылка на оригинал статьи https://habr.com/ru/post/645811/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *