The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги ]

Руководство по Rake (Ruby make) для Rails-разработчиков - перевод (ruby rails web)


<< Предыдущая ИНДЕКС Правка src / Печать Следующая >>
Ключевые слова: ruby, rails, web,  (найти похожие документы)
From: Немытченко И.В. <inem@bk.ru.> Newsgroups: email Date: Mon, 11 Jun 2007 14:31:37 +0000 (UTC) Subject: Руководство по Rake (Ruby make) для Rails-разработчиков - перевод Оригинал расположен здесь Не забудте туда заглянуть, тем есть забавные картинки и фотографии авторов блога. Автор статьи - Gregg Pollack. Перевод также опубликован здесь: http://rubyroid.org/blog/rake-tutorial/ Как Rails-разработчик, вы наверняка использовали "rake" при тестировании или запускали "rake db:migrate" для выполнения миграций. Но понимаете ли вы, что происходит внутри этих Rake tasks? Вы в курсе, что можно написать свои задания или даже создать библиотеку полезных Rake-файлов? Вот несколько примеров того, для чего я использую Rake tasks: * Создание списка пользователей для почтовой рассылки * Ночные вычисления и формирование отчетов * Сброс и перегенерация закэшированных страниц * Резервное копирование базы данных и репозитория * Выполнение каких угодно скриптов для манипуляции данными * Разлив выпивки по стаканам Почему make? Экскурс в историю. Прежде чем понять, откуда взялся Rake, давайте сначала вспомним про его прадедушку Make. Давайте мысленно перенесемся в то время, когда каждый кусок кода должен был компилироваться, когда интерпретируемые языки и айФоны еще не покорили Землю. В то время вы получали программу в виде кучи исходного кода и shell-скрипта. Этот скрипт содержал весь неодходимый код, необходимый вашему компьютеру, чтобы собрать приложение. Вы запускали "install_me.sh" (этот самый shell-скрипт), выполнялась строчка за строчкой (обычно каждая строка - это компиляция одного исходного файла), и в итоге вы получали исполняемый файл, который уже можно было запустить. Впринципе это подходило большинству людей, за исключением тех, кого угораздило эту программу разрабатывать. Каждый раз даже после незначительного изменения в коде, если вы хотели его проверить, вы должны были снова запускать этот скрипт и перекомпилировать все заново. Очевидно, это могло занимать много времени для больших програм. В 1977 (в этом году я родился) Stuart Feldman из Bell Labs создал "Make", которые решал проблему долгой перекомпиляции. Make тоже использовался для компиляции программ, но с двумя существенными отличиями: 1. Make распознавал какие исходные файлы изменились с момента последней компиляции. Используя эту информацию, при повторной компиляции Make компилировал только изменившиеся исходные файлы. Это дало огромный прирост в скорости перекомпиляции больших програм. 2. Кроме этого Make содержал в себе отслеживание зависимостей, так что вы могли сказать компилятору, что чтобы нормально скомпилироваться, исходному файу А нужен исходный файл Б, а файлу Б требуется файл В. Таким образом, если Make-у нужно скомпилировать файл А и файл Б еще не скомпилирован, Б компилировался в первую очередь. Следует также объяснить, что Make - это просто исполняемый файл, такой же как "dir" или "ls". Для того чтобы объяснить Make-у, как скомпилировать программу,нужно создать "makefile", который содержал бы все инструкции и зависимости для компиляции "исходников". У makefile-ов есть свой хитровывернутый синтаксис, который нам здесь знать не обязательно. Через некоторое время Make эволюционировал и им стали пользоваться во всяких разных языках программирования. Действительно, много Ruby-программистов пользовались им, пока ему на смену не пришел Rake. , спросите вы. Да, Ruby - это интерпретируемый язык, и нам не надо компилировать наш код, так почему программисты на Ruby использовали Make? Ну, по двум большим причинам: 1. Создание заданий - В каждом большом приложении почти всегда возникает необходимость в скриптах, которые можно было бы запускать из командной строки. Вместо создания десяти отдельных скриптов (или одного универсального), вы можете создать один "Makefile", в котором все можно разделить на задания. После этого их можно выполнять, просто набирая в командной строке "make stupid", где stupid - это название вашего задания. 2. Отслеживание зависимостей между заданиями - Когда вы начинаете писать библиотеку для повседневных задач, то сталкиваетесь с тем, что некоторые задачи частично повторяют друг друга. Например, оба задания "migrate" и "schema:dump" требуют соединения с базой данных. Я мог бы создать задание "connect_to_database", и установить, что "migrate" и "schema:dump" зависят от него. Таким образом, в следующий раз, когда я запущу "migrate", перед ним автоматически запустится "connect_to_database" Откуда у нас появился Rake? Несколько лет назад Jim Weirich работал над Java-проектом, в котором он использовал Make. Во время написания Makefile он подумал, что было бы удобно писать небольшие куски кода на Ruby прямо внутри мэйкфайлов. Ну и короче он написал rake. Тут я малость опустил про Джима Вейриха, кому интересно загляните в оригинал, там и фотка есть (Jim Weirich в полосатой футболке :) Ну и как rake уже в конце концов работает? Допустим, мы хотим напиться, какие шаги нам нужно предпринять? 1. Купить водки (purchaseAlchohol) 2. Замешать коктейль (mixDrink) 3. Нажратсо (getSmashed) Если бы я захотел использовать Rake для вызова таких задач, я бы создал Rake-файл с примерно таким содержимым: task :purchaseAlchohol do puts "Купил водки" end task :mixDrink do puts "Замешал коктейль 'Мохнатый пупок'" end task :getSmashed do puts "Ччувак, чота ты какой-то размытый.. И-и-ик, накатим еще по стопарю?" end После этого я могу исполнять эти задания(находясь в той же директории), что-то типа такого: $ rake purchaseAlchohol Купил водки $ rake mixDrink Замешал коктейль 'Мохнатый пупок' $ rake getSmashed Ччувак, чота ты какой-то размытый.. И-и-ик, накатим еще по стопарю? Круто, да? Тем не менее с точки зрения зависимостей, я могу выполнять эти задания в любом порядке. Но вообще говоря, если бы я пожелал "нажратсо" перед "замешать коктейль" или "купить водки", то обычно это находится за пределами человеческих возможностей (это не про русских, да - прим. переводчика) Итак, как мне использовать зависимости в Rake? task :purchaseAlchohol do puts "Купил водки" end task :mixDrink => :purchaseAlchohol do puts "Замешал коктейль 'Мохнатый пупок'" end task :getSmashed => :mixDrink do puts "Ччувак, чота ты какой-то размытый.. И-и-ик, накатим еще по стопарю?" end Итак, теперь я говорю, что если я хочу "замешать коктейль", то надо бы сначала "купить водки", а если хочу "нажратсо", то сначала надо "замешать коктейль". Как вы уже наверное догадались, в итоге получаем что-то такое: $ rake purchaseAlchohol Purchased Vodka $ rake mixDrink Купил водки Замешал коктейль 'Мохнатый пупок' $ rake getSmashed Купил водки Замешал коктейль 'Мохнатый пупок' Ччувак, чота ты какой-то размытый.. И-и-ик, накатим еще по стопарю? Как видно, теперь при попытке "Нажратсо", вызываются зависимые задания "купить водки" и "замешать коктейль". Через какое-то время вам возможно захочется усугубить ваши пристрастия и расширить ваш Rakefile. И даже может быть захочется привлечь к этому своих друзей. Сделаем вид, что мы как будто бы в настоещем софтверном проекте, соответственно если мы хотим добавить людей в команду, то надо убедиться что все хорошо задокументировано. Итак, закономерный следующий пункт: Как документируются Rake-задания? Собственно в Rake с этим все очень просто. Вызываем desc и вперед: desc "Это задание купит нам водки" task :purchaseAlchohol do puts "Купил водки" end desc "Это задание замешает нам отличный коктейль" task :mixDrink => :purchaseAlchohol do puts "Замешал коктейль 'Мохнатый пупок'" end desc "Это задание позволит нам как следует нажраться" task :getSmashed => :mixDrink do puts "Ччувак, чота ты какой-то размытый.. И-и-ик, накатим еще по стопарю?" end Ну вот, теперь у наших заданий есть описания. Теперь я или мои друзья могут вызвать команду "rake -T" или "rake --tasks" $rake --tasks rake getSmashed # Это задание позволит нам как следует нажраться rake mixDrink # Это задание замешает нам отличный коктейль rake purchaseAlcohol # Это задание купит нам водки Ну несложно, да? Пространства имен в Rake Ну раз уж мы окончательно пристрастились к алкоголю, мы теперь используем множество разных Rake-заданий, и нам не помешало бы их категоризировать. Для этого используем пространства имен. Для нашего примера это будет выглядеть так: namespace :alcoholic do desc "Это задание купит нам водки" task :purchaseAlchohol do puts "Купил водки" end desc "Это задание замешает нам отличный коктейль" task :mixDrink => :purchaseAlchohol do puts "Замешал коктейль 'Мохнатый пупок'" end desc "Это задание позволит нам как следует нажраться" task :getSmashed => :mixDrink do puts "Ччувак, чота ты какой-то размытый.. И-и-ик, накатим еще по стопарю?" end end Пространства имен позволяют нам группировать задания по категориям и, О ДА, может быть более одного пространство имен в Rakefile-е. Если теперь запустить "rake --tasks", то вот что мы увидим: $rake --tasks rake alcoholic:getSmashed # Это задание позволит нам как следует нажраться rake alcoholic:mixDrink # Это задание замешает нам отличный коктейль rake alcoholic:purchaseAlcohol # Это задание купит нам водки Ну и чтобы вызвать наши задания, очевидно нужно выполнить команду . Как мне написать свои задания на Ruby? Ну собственно берем и пишем на Ruby. Без шуток. Недавно мне понадобился скрипт, который создавал несколько дерикторий, в общем законченное задание выглядит так: desc "Create blank directories if they don't already exist" task(:create_directories) do # The folders I need to create shared_folders = ["icons","images","groups"] for folder in shared_folders # Check to see if it exists if File.exists?(folder) puts "#{folder} exists" else puts "#{folder} doesn't exist so we're creating" Dir.mkdir "#{folder}" end end end По умолчанию, rake имеет доступ ко всему, что есть в File Utils, но ничто не мешает вам подключить все что душе угодно, лишь бы оно было на Ruby. Как мне написать написать свои Rake-задания для моего Rails-приложения? В Rails уже имеется некоторое количество готовых rake заданий, которые можно просмотреть с помощью "rake --tasks". Если вы еще этого не сделали, тогда сделайте, я подожду... Чтобы создать новые задания для вашего рельсового приложения, вам надо открыть директорию /lib/tasks (что вы уже должны были сделать). Если в этой директории вы создадите свой Rakefile "something.rake", соответствующие задания будут автоматически подгружены. Они будут добавлены в список существующих заданий и вы можете запускать их обычным способом. Вот так будет выглядеть предыдущий пример в Rails-окружении: namespace :utils do desc "Create blank directories if they don't already exist" task(:create_directories) do # The folders I need to create shared_folders = ["icons","images","groups"] for folder in shared_folders # Check to see if it exists if File.exists?("#{RAILS_ROOT}/public/#{folder}") puts "#{RAILS_ROOT}/public/#{folder} exists" else puts "#{RAILS_ROOT}/public/#{folder} doesn't exist so we're creating" Dir.mkdir "#{RAILS_ROOT}/public/#{folder}" end end end end Заметьте, что здесь мы можем использовать #{RAILS_ROOT}, чтобы получить полный путь. После добавления такого rake-файла, "rake --tasks" выдаст нам следующее: ... rake tmp:pids:clear # Clears all files in tmp/pids rake tmp:sessions:clear # Clears all files in tmp/sessions rake tmp:sockets:clear # Clears all files in tmp/sockets rake utils:create_directories # Create blank directories if they don't alrea dy exist ... Замечательно, теперь можно рассмотрим пример, где это просто суперполезно.. Могу я получить доступ к своим Rails-моделям из Rake-задания? Всенепременно! Собственно, для этого по большей части я и использую Rake: Пишу задания, которые необходимо вызывать периодически в ручную или автоматически по крону. Как я уже сказал в начале статьи, я использую Rake для следующих задач: * Создание списка пользователей для почтовой рассылки * Ночные вычисления и формирование отчетов * Сброс и перегенерация закэшированных страниц * Резервное копирование базы данных и репозитория * Выполнение каких угодно скриптов для манипуляции данными Обалденно удобно, и легко. Вот rake задание, которое находит пользователей, у которых истекает срок подписки и рассылает им уведомления: require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") namespace :utils do desc "Finds soon to expire subscriptions and emails users" task(:send_expire_soon_emails => :environment) do # Find users to email for user in User.members_soon_to_expire puts "Emailing #{user.name}" UserNotifier.deliver_expire_soon_notification(user) end end end Как видно, доступ к нашим моделям производится с помощью "require ..." и "=> :environment": 1. require File.expand_path(File.dirname(FILE) + "/../../config/environment") 2. task(:send_expire_soon_emails => :environment) do Запускаем задание на development-базе даных: На "боевой" базе данных: Чтобы задание выполнялось по ночам, можно дабавить в cron следующее: 0 0 * * * cd /var/www/apps/rails_app/ && /usr/local/bin/rake RAILS_ENV=production utils:send_expire_soon_emails Вот! Где еще можно почитать про Rake? Теперь, когда вы уже знаете достаточно, чтобы начать писать свои суперполезные rake-задания, можно дать вам несколько ссылок по теме. Лучший способ улучшить свои программистские навыки - это чтение чужого кода, несколько следующих ссылок - это как раз наборы готовых rake tasks. * Новые rake tasks в Edge Rails для создания и сброса БД * Craig Ambrose написал Rake task для резервного копирования базы данных. * Adam Greene собрал в кучу несколько Rake tasks, которые позволяют ва бэкапить ваши данные на Amazon S3 * Jay Fields рассказал про использование rake tasks для тестирования * Err the blog написал про новый способ установки RAILS_ENV и про то, как можно загрузить Mysql-консоль из rake (не забудте почитать комментарии). * И наконец, Rake Bookshelf Books и руководство Мартина Фаулера Using the Rake Build Language. Обе ссылки дают довольно исчерпывающее описание Rake, но они уже немного устарели. В догонку Тут вот товарищ Джим прислал письмо с объяснением, как можно упростить мой скрипт для создания директорий: # This is needed because the existing version of directory in Rake # is slightly broken, but Jim says it'll be fixed in the next version. alias :original_directory :directory def directory(dir) original_directory dir Rake::Task[dir] end # Do the directory creation namespace :utils do task :create_directories => [ directory('public/icons'), directory('public/images'), directory('public/groups'), ] end

<< Предыдущая ИНДЕКС Правка src / Печать Следующая >>

Обсуждение [ RSS ]
 
  • 1, Andrey Zenkov, 16:10, 12/09/2013 [ответить] [смотреть все]
  • +/
    Спасибо большое за статью , очень помогла!
     
  • 2, Павел, 12:26, 17/09/2015 [ответить] [смотреть все]
  • +/
    Спасибо! Мне пригодилось!
     
  • 3, Дед Мороз, 19:46, 08/06/2016 [ответить] [смотреть все]
  • +/
    Очень хорошая статья! Очень хорошо все изложл автор.
     

    Ваш комментарий
    Имя:         
    E-Mail:      
    Заголовок:
    Текст:





      Закладки на сайте
      Проследить за страницей
    Created 1996-2017 by Maxim Chirkov  
    ДобавитьРекламаВебмастеруГИД  
    Hosting by Ihor