Статьи

Грабли 301

В этой последней статье рассматриваются FileList, Pathmap, CLEAN, CLOBBER и передаваемые аргументы. Они не очень важны для новичков сразу, но они, безусловно, пригодятся позже — действительно, бесценны.

  • Передача аргументов
  • FileList
  • Pathmap
  • Чистый и Клоббер
  • Для дороги

У вас есть два варианта передачи аргументов в задачи Rake. Вы можете сделать это, используя переменные Bash или используя сам синтаксис Rake.

Если вы раньше не играли с Bash — или Bash звучит для вас как бред, — давайте возьмем пять и начнем с самого начала.

Bash в вашей оболочке предлагает два вида переменных: глобальные (иначе среды) и локальные. Оба написаны в верхнем регистре. Переменные среды являются глобальными, что означает, что они доступны во всех оболочках и не исчезают при закрытии одной из них — в отличие от локальных переменных Bash, которые доступны только в текущей оболочке.

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

В нашем контексте использования Rake у вас есть возможность получать доступ как через Ruby, так и фактически передавать переменные из командной строки.

Немного в стороне, если вы env или ENV в своей оболочке, вы получите доступ к целому ряду переменных среды. Я отредактировал список, но для лучшего понимания, что такое переменные среды и что они включают, я призываю вас запустить его для себя.

1
env
01
02
03
04
05
06
07
08
09
10
11
TERM_PROGRAM=Apple_Terminal
TERM=screen-256color
SHELL=/bin/bash
TMUX=/private/var/folders/4z/3np9k5ks62b1xpbn_w_lmrgh0000gr/T/tmux-504/default,4146,0
EDITOR=vim
LANG=en_US.UTF-8
TMUX_PANE=%1
is_vim=echo «#{pane_current_command}» |

Если вы хотите увидеть список локальных переменных Bash, вы можете запустить set .

1
( set -o posix ; set ) |

Команда set дает вам намного больше выходных данных, но вышеприведенный момент показывает вам соответствующие биты.

Ruby предлагает способ использования окружения и локальных переменных Bash через хеш-подобный метод доступа. Для наших нужд, когда мы передадим переменную в задачу Rake, это будет локальная переменная Bash, которую вы можете найти в списке переменных, в которых запущен set или его разновидности. Ruby может прочитать его, используя ENV['VARIABLE'] .

1
rake prepare_book BOOKTITLE=’Confessions of a unicorn’

Однако я хочу пояснить, что эта переменная не будет добавлена ​​в список ENV, который использует ваша система, — то, что вы видели при вызове env из оболочки. Чтобы добавить его в этот список, вам необходимо его «экспортировать». Это другая история , но я подумал, что должен это прояснить.

1
2
3
4
task :prepare_book do
  book_title = ENV[‘BOOKTITLE’] ||
  puts «Do something with the #{book_title}»
end

В этом определении задачи вы можете увидеть, как мы подготовились принять или включить переменную, переданную в вызов задачи. ENV[BASHVARIABLE] Руби ENV[BASHVARIABLE] делает всю тяжелую работу. Если бы BOOKTITLE был глобальной переменной среды, мы могли бы получить к ней доступ и в этом определении задачи с помощью этого синтаксиса.

Второй подход — использование чистого синтаксиса Rake. Вы просто передаете переменные в квадратные скобки. Такой подход лучше, и вы можете держать вещи более изолированными. Зачем привлекать Bash, если Rake вполне способен справиться с этим? Кроме того, у вас нет переменных Bash, плавающих таким образом. Если вы хотите передать несколько аргументов в задачу, это также намного элегантнее.

1
rake «create_mi6_agent[James, Bond, 007]»
1
2
3
task :create_mi6_agent, [:first_name, :last_name, :number] do |t, args|
  puts «Number #{args.number} is commander #{args.first_name} #{args.last_name}.»
end

Когда вы передаете больше аргументов, чем вы определили в своей задаче, вы можете просто получить к ним доступ через args . args.extras отображает массив всех дополнительно переданных параметров. args.to_a показывает вам все параметры — конечно же, в массиве.

В предыдущих примерах мы вручную собирали списки файлов, которые нуждаются в некотором преобразовании. Это утомительно, правда? FileList — один из тех тонкостей, который делает Rake мощным инструментом. Просто слишком просто определить шаблон глобуса для нужных вам файлов и автоматически обновлять его при добавлении или удалении файлов из этого места назначения. Имея это в нашем распоряжении, фильтрация списков может быть настолько простой или сложной, насколько нам нужно. Регулярные выражения — это лишь верхушка айсберга, хотя, конечно, очень удобно.

Необходимость списков файлов для обработки очень распространена для инструментов сборки, и облегчение работы с ними является одной из сильных сторон Rake. FileList делает ваш Rakefile меньше, умнее и способен обрабатывать произвольное количество файлов, которыми вам не нужно управлять. Вы можете оставить Rake ответственным.

Так что же такое FileList? Думайте об этом как о массиве файлов, которые соответствуют данному образцу. Это специализированный Ruby Array, который ориентирован на обработку списков файлов — хранение их в виде строк. После того, как они собраны, они готовы к тому, чтобы вы перебрали и применили преобразования.

1
2
3
image_list = FileList[‘images/*.png’]
 
=> [«images/jim-weirich.png», «images/zen-rake.png»]

Управление этими файлами вручную — один из надежных способов построить на песке. И, конечно же, Rake проверяет временные метки этого списка и перестраивает только те файлы, которые устарели. FileList также ленив. Он не захватывает файлы, пока они не понадобятся. Если у вас есть несколько списков файлов, они ведут себя очень разумно и умно из-за этого. Списки, которые не используются активно, облегчают работу, не затрагивая файловую систему. Так эффективнее.

Как вы можете видеть ниже, вы также можете предоставить несколько шаблонов глобуса для списка.

1
2
3
image_list = FileList[‘images/*.png’, ‘images/*.jpg’]
 
=> [«images/happy-jim.jpg», «images/jim-weirich.png», «images/zen-rake.png»]

С большими наборами файлов исключения очень полезны, например, если мы хотим отфильтровать временные файлы, файлы резервных копий из редакторов, файлы Git или определенные ненужные каталоги. Короче говоря, правила исключения предназначены для файлов, которые вам не нужны в вашей сборке.

1
2
3
4
5
articles = Rake::FileList.new(‘_posts/**/*.{markdown, md}’) do |files|
  files.exclude(‘/_posts/drafts/*.{markdown, md}’)
end
 
=> [«_posts/published/2016/2016-02-02-some-article.md», «_posts/published/2015/2015-12-12-another-article.markdown»]

Мы можем передать файлы FileList через его инициализатор, который принимает список масок файлов. Вы обрабатываете любые исключения внутри блока. Мы упростили список желаемых расширений файлов с помощью {markdown, md} чтобы сохранить вещи сухими. Кроме того, вы можете связать эти исключения столько, сколько вам нужно. Здесь мы могли бы даже удобно проверить, являются ли файлы, включенные в FileList, пустыми ( zero? ) И таким образом исключить их из массива.

1
2
3
4
5
6
7
articles = Rake::FileList.new(‘_posts/**/*.md’) do |files|
  files.exclude(‘/_posts/drafts/*.{markdown, md}’)
  files.exclude(‘_posts/~*’)
  files.exclude do |file|
    File.zero?(file)
  end
end

Мы в основном предоставляем несколько шаблонов glob для сбора только тех файлов, которые нам нужны, в FileList. По любой причине вы можете пойти противоположным путем и включить файлы в FileList.

1
2
FL = FileList[‘images/*.png’]
FL.include(‘images/private/*.jpg)

Это секретное оружие Rake и показывает его истинную силу, позволяя вам манипулировать путями к файлам. Его можно вызывать в списке файлов через FileList или в отдельных файлах. Не забывайте, что он работает со строками. Это часть расширения класса Ruby’s String .

Давайте поиграем с простым файлом и изменим простое расширение. Конечно, мы могли бы сделать это с помощью удобного метода ext .

1
2
3
«/mi6/q/secret_gadgets.xml».ext(«html»)
 
# => ‘/mi6/q/secret_gadgets.html’

Метод ext позволяет довольно легко заменить расширение файла. Давайте посмотрим, что мы можем сделать с этим файлом, когда поиграемся с pathmap . Я думаю, что это лучший способ показать вам, что он приготовил для вас. Мы можем достичь того же самого, что и это.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%X.html’)
 
# => ‘/mi6/q/secret_gadgets.html’

Как видите, это немного элегантнее. Мы предоставляем pathmap с указанием того, что нам нужно из этой строки через % .

  • %X

Используя это, мы получаем все, кроме расширения файла. Затем мы просто добавляем нужное нам расширение. Это всего лишь царапина на поверхности. pathmap имеет много полезных маркеров, которые позволяют вам быть более креативным. Манипуляции с путями к файлам не могут быть проще с этим.

  • %p

Если вам нужен полный путь.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%p’)
 
# => «mi6/q/secret_gadgets.xml»
  • %f

Если вам просто нужно имя данного пути. Нет каталогов, но с расширением файла.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%f’)
 
# => «secret_gadgets.xml»
  • %n

Если вам нужно имя файла по указанному пути без расширения файла.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%n’)
 
# => «secret_gadgets»
  • %d

Если вам нужен только список каталогов по заданному пути.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%d’)
 
# => «mi6/q»
  • %x

Извлекает только расширение файла.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%x’)
 
# => «.xml»
  • %s

Показывает только разделитель файлов.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%s’)
 
# => «/»
  • %nd

Если вы хотите указать определенное количество каталогов, которые вам нужны. Удобно для глубоко вложенных файловых структур.

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%1d’)
 
# => «mi6»
1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%2d’)
 
# => «mi6/q»

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

1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%-2d’)
 
# => «mi6/q»
1
2
3
«/mi6/q/secret_gadgets.xml».pathmap(‘%-1d’)
 
# => «q»

Как видите, этот маленький метод решает все различные проблемы, которые могут возникнуть при сопоставлении одного списка файлов с другим списком файлов. Прикрепите его к FileList и волшебство вступит в силу. Это действительно мощный инструмент для манипулирования именами файлов.

1
2
images = FileList[‘images/*.png’]
thumbs = images.pathmap(‘thumbs/%n-thumbs%x’)

Здесь, например, мы берем список изображений и сопоставляем их с новыми именами файлов, извлекая имена файлов и добавляя суффикс -thumbs плюс расширение извлеченного файла, помещая их в каталог thumbs . Я уверен, что вы найдете очень хорошее применение для pathmap .

Мы хотим иметь возможность вернуть проект в первозданное состояние. В этом отношении FileList не только удобен для подготовки файлов, которые должны быть преобразованы, но также позволяет легко собирать файлы, которые вы хотите очистить после выполнения ваших задач. CLEAN и CLOBBER на самом деле также являются FileLists — у них просто есть две очень специфичные задачи — удаление.

1
2
3
4
require ‘rake/clean’
 
CLEAN.include(‘*.intermediate_files’)
CLOBBER.include(‘*.intermediate_files’, ‘built_files/*’)

Эти две задачи, конечно, глупы, и вам нужно кормить их списками файлов с помощью нашего удобного include . Когда вы запустите rake clean или rake clobber , эти собранные файлы исчезнут. Поскольку это необязательный модуль, вам необходимо сначала указать его в своем Rakefile. Приятной особенностью CLEAN и CLOBBER является то, что они дают вам центральное место для очистки ваших файлов сборки. Конечно, вы могли бы самостоятельно написать задачи Rake, чтобы справиться с этим, но и CLEAN, и CLOBBER решают это за вас, не изобретая колесо.

Мы не ставим все в чистую задачу, потому что удобно различать промежуточные файлы и файлы сборки. Допустим, нам нужно было создать HTML-файлы, чтобы создать окончательные PDF-версии наших файлов Markdown. Мы включили бы файлы HTML в наш список CLEAN . И .html и окончательные файлы .pdf будут CLOBBER в CLOBBER . Концептуально, список CLOBBER должен удалить все в обоих списках.

Почему мы заботимся об этих файлах сборки? Иногда вы хотите восстановить все и стереть старые файлы, чтобы получить новую сборку. Поэтому вам нужен способ удалить все файлы, которые были сгенерированы с сохранением исходных файлов, необходимых для сборки — чаще всего это файлы, которые находятся под контролем версий. Эти списки легко устаревают, когда вы решаете это вручную. Поэтому обработка их как нашего старого доброго друга FileList делает этот процесс намного более эффективным.

  • Компоненты

Rake, по сути, предназначен для управления задачами, конечно. Разбейте их на наиболее полезные составляющие и создайте их, чтобы создавать большие задачи. Думай ООП! То же самое касается ваших файлов. Rails делает это очень легко для вас с помощью tasks/lib . В других проектах вы можете создать каталог с именем rakelib и встроить в него компоненты Rake. Rake автоматически загружает файлы Rakefile и rakelib/*.rake Rake.

  • Использование rake --dry-run

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

  • ПОЦЕЛУЙ

Будь проще! Рейк умел делать минимально возможное количество. Так и должно быть. Хорошая особенность Rake — это то, что он обеспечивает отличный DSL, не давая вам много веревки, чтобы навредить себе, изобретая колесо без необходимости.

  • Пространства имен

Пространства имен дешевы и не дают вам столкнуться с конфликтующими именами задач. Это особенно важно, если у вас есть Rake-файлы, которые поступают из разных источников — и от нескольких разработчиков.

1
2
3
4
5
6
7
8
9
task :fight_bad_dude do
  …
end
 
namespace :bond do
  task :fight_bad_dude
    …
  end
end
  • Файловые манипуляции

Используйте FileUtils и держитесь подальше от манипуляций с файлами оболочки внутри ваших задач. Немного грязно, когда Rake делает их уже доступными для вас.

  • Рубиновые команды

Вы можете запускать файлы Ruby в файлах Rake. Это может пригодиться время от времени.

1
2
3
task :some_task do
  ruby ‘ruby_program.rb’
end
  • Динамически генерируемые задачи

Используйте правила, если у вас много файлов вместо динамически генерируемых задач. Почему? Запустите rake -P и вы получите список всех из них. Это может очень быстро выйти из-под контроля. Кроме того, неиспользованию правил часто просто не хватает элегантности. Возможно, вы еще не сократили шаблон до его ядра. Самое главное, это сделает повторное использование легче, будучи СУХИМ, конечно.

  • Списки

Вместо того, чтобы определять коллекции для файлов самостоятельно, а также обновлять этот список, мы бы предпочли, чтобы Rake отвечал за это. Как мы уже видели, сбор файлов для ваших задач совсем не сложен в Rake.

  • Рубин

Используйте методы Ruby для более сложных вещей. Извлекать методы для повторного использования, где вы можете. То, что мы пишем код в файлах Rake, не должно мешать нам правильно инкапсулировать и выполнять ООП.

  • Поиск задач
1
rake -T secret_service_agent

Это, например, будет выполнять поиск граблей с «secret_service_agent» в них. Соответствует имени задачи, но не описанию.

1
rake -W create_mi6_agent

Это показывает нам, где create_mi6_agent задача create_mi6_agent .

Rake — это мощный механизм управления задачами и их исполнения. Программное обеспечение с открытым исходным кодом в лучшем виде, если вы спросите меня. Сначала я был очень удивлен, узнав, сколько загрузок он накопил за последние пару лет. То, что этот маленький инструмент сборки является самым популярным Gem на сегодняшний день и имеет более 100 миллионов загрузок, кажется сумасшедшим.

Но если взглянуть глубже на то, что он может предложить, то сразу становится ясно, кем на самом деле был мастер-программист Джим Вейрих — настоящим героем Ruby, которого мы все должны восхищаться за его работу, наследие и страсть. Я оставлю вас с хорошим видео- интервью, где Джим обсуждает Рейка. В Интернете есть множество других видео его выступлений. Иди, посмотри на них всех!