nashbridges.me - Gem глазами потребителя









Search Preview

Gem глазами потребителя

nashbridges.me
Какова инфраструктура кодообмена в Ruby, какую роль в ней играют Rubygems, bundler и RVM. Как стоит трактовать номера версий. Как не погибнуть под завалом установленных gem'ов.
.me > nashbridges.me

SEO audit: Content analysis

Language Error! No language localisation is found.
Title Gem глазами потребителя
Text / HTML ratio 61 %
Frame Excellent! The website does not use iFrame solutions.
Flash Excellent! The website does not have any flash contents.
Keywords cloud в и не на gem с что Rubygems require gem'ов gem'а версии Ruby вы как для это будет к
Keywords consistency
Keyword Content Title Description Headings
в 101
и 92
не 52
на 49
gem 49
с 43
Headings
H1 H2 H3 H4 H5 H6
2 2 14 0 0 0
Images We found 0 images on this web page.

SEO Keywords (Single)

Keyword Occurrence Density
в 101 5.05 %
и 92 4.60 %
не 52 2.60 %
на 49 2.45 %
gem 49 2.45 %
с 43 2.15 %
что 35 1.75 %
33 1.65 %
Rubygems 27 1.35 %
require 26 1.30 %
gem'ов 24 1.20 %
gem'а 24 1.20 %
версии 24 1.20 %
Ruby 24 1.20 %
вы 23 1.15 %
как 22 1.10 %
для 21 1.05 %
это 18 0.90 %
будет 16 0.80 %
к 16 0.80 %

SEO Keywords (Two Word)

Keyword Occurrence Density
gem 'prawn' 8 0.40 %
=> true 8 0.40 %
Bundler — 6 0.30 %
у вас 6 0.30 %
gem install 6 0.30 %
на Ruby 5 0.25 %
true irbmain0020> 5 0.25 %
в папку 5 0.25 %
на машине 5 0.25 %
с помощью 5 0.25 %
require 'prawn' 5 0.25 %
исполняемые файлы 5 0.25 %
и для 4 0.20 %
Successfully installed 4 0.20 %
irbmain0020> require 4 0.20 %
же gem'а 4 0.20 %
то что 4 0.20 %
на момент 4 0.20 %
работать с 4 0.20 %
bundle install 4 0.20 %

SEO Keywords (Three Word)

Keyword Occurrence Density Possible Spam
=> true irbmain0020> 5 0.25 % No
require 'prawn' => 3 0.15 % No
и того же 3 0.15 % No
'prawn' => true 3 0.15 % No
Ruby on Rails 3 0.15 % No
на вашей машине 3 0.15 % No
gem 'prawn' '>= 3 0.15 % No
одного и того 3 0.15 % No
того же gem'а 3 0.15 % No
true irbmain0020> require 3 0.15 % No
Там где бессильны 2 0.10 % No
actionpack 235 rack 2 0.10 % No
Gemloaded_specs prawn=> 2 0.10 % No
puts Gemloaded_specs prawn=> 2 0.10 % No
RVM как альтернатива? 2 0.10 % No
где бессильны Rubygems 2 0.10 % No
gem install rails 2 0.10 % No
не ниже 0111 2 0.10 % No
version=101> ttfunk=> 2 0.10 % No
require Уточнение версий 2 0.10 % No

SEO Keywords (Four Word)

Keyword Occurrence Density Possible Spam
одного и того же 3 0.15 % No
и того же gem'а 3 0.15 % No
require 'prawn' => true 3 0.15 % No
=> true irbmain0020> require 3 0.15 % No
ttfunk=> => 2 0.10 % No
urslocallibrubyvendor_ruby191i686linux urslocallibrubyvendor_ruby urslocallibruby191 urslocallibruby191i686linux 2 0.10 % No
Установка gem'а под микроскопом 2 0.10 % No
urslocallibrubysite_ruby191 urslocallibrubysite_ruby191i686linux urslocallibrubysite_ruby urslocallibrubyvendor_ruby191 2 0.10 % No
urslocallibrubysite_ruby191i686linux urslocallibrubysite_ruby urslocallibrubyvendor_ruby191 urslocallibrubyvendor_ruby191i686linux 2 0.10 % No
urslocallibrubysite_ruby urslocallibrubyvendor_ruby191 urslocallibrubyvendor_ruby191i686linux urslocallibrubyvendor_ruby 2 0.10 % No
urslocallibrubyvendor_ruby191 urslocallibrubyvendor_ruby191i686linux urslocallibrubyvendor_ruby urslocallibruby191 2 0.10 % No
urslocallibruby191 urslocallibruby191i686linux => nil 2 0.10 % No
urslocallibrubyvendor_ruby urslocallibruby191 urslocallibruby191i686linux => 2 0.10 % No
gem'ов Магический ? require 2 0.10 % No
о gem'ах Минутка лингвистики 2 0.10 % No
знать о gem'ах Минутка 2 0.10 % No
нужно знать о gem'ах 2 0.10 % No
вам нужно знать о 2 0.10 % No
Подключение gem'ов Магический ? 2 0.10 % No
вы уверены что ваше 2 0.10 % No

Internal links in - nashbridges.me

все теги
Теги, которыми отмечены статьи
зачем и для кого
О блоге
rss
Ruby и точка
Установка Ruby on Rails на Windows
Установка Ruby on Rails на Windows
Git
Статьи с тегом «Git»
Linux
Статьи с тегом «Linux»
SSH
Статьи с тегом «SSH»
Введение в объектно-ориентированный Ruby
Введение в объектно-ориентированный Ruby
класс
Статьи с тегом «класс»
модуль
Статьи с тегом «модуль»
объект
Статьи с тегом «объект»
философия
Статьи с тегом «философия»
Ruby
Статьи с тегом «Ruby»
Ресурсы и книги по Ruby и Ruby on Rails
Ресурсы и книги по Ruby и Ruby on Rails
литература
Статьи с тегом «литература»
Проки и лямбды
Проки и лямбды
блок
Статьи с тегом «блок»
замыкание
Статьи с тегом «замыкание»
DSL
Статьи с тегом «DSL»
лямбда
Статьи с тегом «лямбда»
Ruby on Rails
Статьи с тегом «Ruby on Rails»
Sinatra
Статьи с тегом «Sinatra»
yield
Статьи с тегом «yield»
Gem глазами потребителя
Gem глазами потребителя
bundler
Статьи с тегом «bundler»
джемы
Статьи с тегом «джемы»
Github
Статьи с тегом «Github»
require
Статьи с тегом «require»
Rubygems
Статьи с тегом «Rubygems»
RVM
Статьи с тегом «RVM»
Блоки в Ruby
Блоки в Ruby
итератор
Статьи с тегом «итератор»
цикл
Статьи с тегом «цикл»

Nashbridges.me Spined HTML


Gem глазами потребителя Ruby и . все статьи все теги зачем и для кого rss Gem глазами потребителя сложность материала: для начинающих версия Ruby: 1.9 необходимые знания: вызов методов, метод require, public и private методы, консоль irb теги: джемы require Rubygems bundler RVM DSL Github Всё, что вам нужно знать о gem'ах Минутка лингвистики История Что такое gem? Репозиторий Как возникают gem'ы? Как их искать? Версии Установка gem'а под микроскопом Подключение gem'ов Магический (?) require Уточнение версий Там, где бессильны Rubygems Bundler RVM как альтернатива? Послемыслия и работа над ошибками уточнение к «активации» gem'ов при использовании Bundler (07.08.11) переписан раздел RVM (07.08.11) Наряду с основными заповедями разработчика «Не продублируй» (DRY) и «Будь проще» (KISS), есть еще одна важная: «Не изобрети велосипед». К счастью, в Ruby сообществе уже довольно давно существует стандартный формат для обмена готовыми велосипедами — gem. Статья призвана дать представление о том, как выглядит система распространения библиотек на Ruby, и помочь понять проблему использования на машине разных версий одного и того же gem'а. Всё, что вам нужно знать о gem'ах Минутка лингвистики «Gem» переводится с английского как «драгоценный камень». Дав формату такое название, авторы недвусмысленно указали на связь с рубином (Ruby — рубин). Произносится он как «джем» [ʤem], хотя в русскоговорящем сообществе нередко можно услышать «гем». Чтобы никого не смущать, я буду использовать оригинальное написание этого слова, т. е. gem. История С инициативой стандартного формата для распространения библиотек, написанных на Ruby или C (т. н. расширений), в 2001 году выступил Райан Ливенгуд (Ryan Leavengood), он же является автором первой версии системы управления пакетами Rubygems. На тот момент рубисты использовали для этих целей архив приложений (RAA), который, по сути, являлся каталогом ссылок. Для того, чтобы подключить в свой проект стороннюю библиотеку, требовалось: Зайти на сайт RAA. Через поиск найти страницу интересующего проекта. Скачать по приведенной ссылке архив с файлами (при условии, что она рабочая, т. к. вела на сторонний ресурс, например, домашнюю страницу разработчика). Распаковать его куда-нибудь. Перейти на страницы проектов, которые указаны в разделе «Зависимости». Скачать оттуда конкретные версии библиотек-зависимостей. Распаковать их куда-нибудь. Подключить нужные файлы с помощью require, вспомнив, где находится «куда-нибудь». Этот нудный процесс просто умолял «Автоматизируйте меня!» И надо сказать, Райан не стал в этих прериях первопроходцем. Системы управления пакетами программ уже имелись как в операционных системах (например, dpkg для ОС, построенных на Debian), так и для языков программирования (CPAN для Perl). Сам Ливенгуд описывал Rubygems как помесь dpkg с jar архивами в Java. Тем не менее, проект не пошел в массы, до тех пор пока в 2003 году Рич Килмер (Rich Kilmer), Чад Фаулер (Chad Fowler), Девид Блек (David Black), Пол Бреннан (Paul Brannan) и Джим Вайрих (Jim Weirich) не написали свою версию системы управления пакетами. Название ей, с разрешения Ливенгуда, дали Rubygems, хотя от прежней библиотеки в ней не было ни строчки. Проект продолжает развиваться (в последнее время, не без скандалов) и для официальной реализации Ruby, начиная с версии 1.9, является системной библиотекой (т. е. не требует явного подключения с помощью require). Официальная реализация Ruby написана на C. Что такое gem? Технически файл с расширением .gem представляет собой обычный архив, внутри которого находится файл спецификации и исходный код библиотеки в состоянии на момент релиза. Релизом обычно считают момент присвоения библиотеке нового номера версии (version bump) с одновременной сборкой и публикацией gem'а. В спецификации содержится достаточно много информации, но самое главное: название и версия данного gem'а; названия и версии gem'ов, без которых работа будет невозможна (зависимости); данные об авторе и описание gem'а. Кроме библиотечных файлов, которые вы подключаете в коде своего приложения, в состав gem'а могут входить исполняемые файлы, которые после установки «видны» на системном уровне. На самом деле это не бинарные, а текстовые файлы — программы, написанные на Ruby. Когда вы запускаете их, ОС вызывает интерпретатор ruby, который и занимается их выполнением. Должно быть, самые известные исполняемые файлы — это rails, rake и gem. Репозиторий Никто не может вам запретить собрать свой gem, выложить на файлообменнике и дать пользователям ссылку на скачивание. Gem'ы действительно можно устанавливать локально из файлов, но если бы все так поступали, то — снова здравствуй, 2001 год и ненавистная рутинная работа. К счастью, система Rubygems подразумевает наличие как минимум одного централизованного, доступного 24 часа в сутки, хранилища gem'ов. Благодаря этому утилита gem может не только выполнить установку, запросив в репозитории одно лишь название gem'а, но и тут же автоматически доустановить все отсутствующие зависимости, исходя из спецификации. Исполняемый файл gem входит в поставку клиентской части Rubygems. На момент написания этой статьи gem'ы хранятся на Amazon S3, дирижированием ссылок занимается репозиторий rubygems.org. Все его службы написаны на Ruby. Как возникают gem'ы? Любой gem появляется как следствие борьбы с проблемой, с которой пришлось столкнуться разработчику. Например, ему была поставлена задача организовать на сайте генерацию отчетов в формате Microsoft Word XP. Если готовых решений для этого не нашлось или не устроила их реализация, разработчик пишет такой генератор самостоятельно. Поскольку данная задача является типовой, хорошим тоном считается ее gem'ификация, под которой понимают выделение из общего приложения в отдельный проект кода, отвечающего за эту задачу. Внутри проекта структуру папок и наименования файлов приводят к негласному стандарту, а также создают спецификацию. Чтобы теперь назвать это gem'ом, достаточно упаковать папку с проектом с помощью утилиты gem и отправить на rubygems.org. Публикация gem'а влечет за собой несколько весьма положительных последствий: резко упрощается повторное использование кода в своих же проектах; происходит интенсивное тестирование за счет использования в чужих проектах, что позволяет рано обнаружить возможные ошибки; обнаруженные ошибки могут исправить сторонние разработчики; тешится собственное самолюбие. Для того, чтобы другие разработчики могли исправить ошибки не только у себя, но и у автора, исходники gem'а обязательно размещают на Github'е. Часто страница проекта на Гитхабе является и основным источником документации. Git и Github являются стандартом де-факто для Ruby разработчика. Как их искать? К сожалению, формат спецификации gem'а не предусматривает указания его категорий или тегов, поэтому если вы пытаетесь найти gem под конкретные нужды, следует надеятся на то, что автор упомянул ключевые слова в описании. Поиск по описанию можно выполнять как на rubygems.org, так и на Гитхабе (выбрав Ruby в списке языков). Отдельно стоит упомянуть Ruby Toolbox, где gem'ы рассортированы по категориям и популярности. Новые gem'ы добавляет туда вручную автор сайта, поэтому не стоит рассчитывать при поиске только на этот ресурс. Версии Система Rubygems подразумевает, что разработчики придерживаются целесообразной политики назначения номеров версий gem'ов. Так, к сожалению, поступают не все, но понимать, в чем она заключается, нужно. Например, чтобы спать спокойно, когда происходит обновление чужого gem'а. Номер версии должен состоять из трех чисел, разделенных точками: А.Б.В Число В увеличивается, когда меняется внутренняя реализация библиотеки с сохранением первоначальной логики. Например, это может быть исправление текущих ошибок, могут появиться/быть убраны private методы. Алгоритм работы может быть переписан так, что библиотека заработает в десять раз быстрее! Но эта реорганизация кода абсолютно прозрачна для текущих пользователей gem'а: ничего по существу не добавилось, ничего у себя исправлять не надо. Число Б увеличивается (и обнуляет В), когда добавляется новый функционал. Например, могут появиться новые public методы или текущие могут начать принимать дополнительные аргументы. Обновившиеся до этой версии пользователи могут внести изменения в код своих приложений, чтобы начать использовать новые возможности, но могут ничего не трогать, и всё должно работать, как и раньше. Число А увеличивается (и обнуляет Б и В), когда происходят изменения, для которых невозможно обеспечить обратную совместимость. Если перейти на эту версию и ничего не изменить в своем приложении, gem не будет работать или будет работать с ошибками. Такой переход может потребовать переписывания приложения с нуля. Из всего вышесказанного можно сделать вывод, что после версии 0.9.9 совсем не обязательно должна появиться 1.0.0, вполне ожидаемой будет как раз версия 0.9.10 или 0.10.0. Установка gem'а под микроскопом Чтобы хорошо понять проблему подключения gem'ов в приложении, нужно разобраться, в каком виде они хранятся на машине. Для этого давайте проследим, что происходит, когда вы набираете в командной строке gem install rails Rubygems проверяет, установлен ли у вас этот gem и какой версии. Ищет в текущей директории файл rails-*.gem (где * может быть любым номером версии). Если такой файл найден, дальнейшая установка будет происходить прежде всего с его использованием. Если gem файл не найден, считывает локальный список репозиториев (по умолчанию там находится только rubygems.org). Запрашивает у них последнюю версию этого gem'а, а также названия и версии его зависимостей. Если Rails у вас не установлены или в репозитории появилась более новая версия, скачивает в папку ../cache их gem файл. Если на момент запроса актуальной версией Ruby on Rails является 3.0.9, будет загружен rails-3.0.9.gem. Параллельно проверяет, установлены ли у вас зависимости, и скачивает в ../cache то, что нужно установить или обновить (загружаются activesupport-3.0.9.gem, builder-2.1.2.gem и т. д.). Распаковывает загруженные файлы в папку ../gems, названия подпапок будут совпадать с названиями gem файлов (например, там появится /rails-3.0.9). Подпапки со старыми версиями никуда не исчезают. Откладывает (для себя) спецификации gem'ов в папку ../specifications. Запускает утилиту RDoc, которая извлекает из распакованных файлов комментарии к исходному коду, формирует на их основе html файлы с документацией и складывает их в папку ../doc. Если в состав gem'ов входят исполняемые файлы, создает в папке /usr/local/bin одноименные исполняемые файлы для их запуска (подробнее об этом ниже). И действительно, там появляются bundler, rails, thor и т. д. Для краткости я не пишу полные пути к папкам. В linux (без RVM) их стоит искать в /usr/local/lib/ruby/gems/1.9.1/. В windows аналогичный путь будет, скорее всего, /Ruby192/lib/ruby/gems/1.9.1/. Генерация документации во время установки — довольно затратная по времени операция. Чтобы пропустить ее, gem install вызывают с параметрами --no-ri и --no-rdoc, например, "gem install rails --no-ri --no-rdoc". Таким образом, команда gem install не выполняет никаких сверхъестественных операций: она только скачивает и распаковывает. Более того, она никоим образом не взаимодействует с уже установленными gem'ами и не трогает окружение, т. е. эта команда сама по себе безвредна. Но можно ли считать процедуру установки абсолютно безопасной? Да, если речь идет о новых gem'ах, и нет, если подразумевается обновление уже установленных, поскольку это косвенно может привести к конфликтам (об этом, как и о способах решения проблемы — ниже). В качестве бонуса: если вы из любопытства после установки заглянете в папку /usr/local/lib/ruby/gems/1.9.1/gems/rails-3.0.9, то возможно, будете удивлены, что в ней вообще не содержится никакого кода. Так и есть, трижды героический gem Rails — всего лишь пустышка, тянущая за собой целый пучок зависимостей. Подключение gem'ов Магический (?) require Итак, вы понемногу осваиваете работу с gem'ами и решили поэкспериментировать с созданием PDF документов. Для этого хорошо подходит Prawn: you@your-comp:~$ gem install prawn Fetching: Ascii85-1.0.1.gem (100%) Fetching: pdf-reader-0.10.0.gem (100%) Fetching: ttfunk-1.0.1.gem (100%) Fetching: prawn-0.11.1.gem (100%) Successfully installed Ascii85-1.0.1 Successfully installed pdf-reader-0.10.0 Successfully installed ttfunk-1.0.1 Successfully installed prawn-0.11.1 4 gems installed Prawn потянул за собой три зависимости. На главной странице сайта Prawn есть пример использования этой библиотеки: require 'prawn' Prawn::Document.generate('hello.pdf') do |pdf| pdf.text("Hello Prawn!") end Вот так просто, оказывается, подключаются gem'ы. Но понимаете ли вы, что делает строка require 'prawn'? Для начала давайте вспомним, что require — метод, который загружает в память и выполняет содержимое файла (если он не был загружен ранее). Когда не указан полный путь к файлу (наш случай), Ruby всё равно пытается обнаружить файл. Поскольку физически невозможно во время каждого require просматривать все имеющиеся папки на компьютере, область его поиска ограничена только несколькими. Список просматриваемых папок Ruby хранит в глобальной переменной-массиве $LOAD_PATH: you@your-comp:~$ irb irb(main):001:0> puts $LOAD_PATH /urs/local/lib/ruby/site_ruby/1.9.1 /urs/local/lib/ruby/site_ruby/1.9.1/i686-linux /urs/local/lib/ruby/site_ruby /urs/local/lib/ruby/vendor_ruby/1.9.1 /urs/local/lib/ruby/vendor_ruby/1.9.1/i686-linux /urs/local/lib/ruby/vendor_ruby /urs/local/lib/ruby/1.9.1 /urs/local/lib/ruby/1.9.1/i686-linux => nil Теперь поищите на машине папку со свежеустановленным Prawn (вспоминайте пошаговую процедуру установки). Она тут: /usr/local/lib/ruby/gems/1.9.1/gems/prawn-0.11.1, в ней еще одна папка — /lib, где и лежит искомый файл — prawn.rb. Как же так получается, пути ../prawn-0.11.1/lib/ в массиве $LOAD_PATH нет, но Ruby ухитряется найти файл? Дело в том, что Rubygems во время загрузки (а в Ruby 1.9 это происходит автоматически) перезаписывают родной метод require своим. Когда Ruby не находит файл в текущих папках области поиска (как в случае с prawn.rb), возбуждается стандартная ошибка. Но Rubygems перехватывают ее, и затем по названию файла пытаются определить, к какому gem'у он относится. Если это удается сделать, пути к этому gem'у и его зависимостям добавляются в $LOAD_PATH. Gem помечается как активированный, после чего снова вызывается родной require, который, конечно же, находит и загружает файл. Как видите, за удобство приходится платить двойным выполнением require, а ведь это влияет на время загрузки приложения. irb(main):002:0> require 'prawn' => true irb(main):003:0> puts $LOAD_PATH /usr/local/lib/ruby/gems/1.9.1/gems/Ascii85-1.0.1/lib /usr/local/lib/ruby/gems/1.9.1/gems/pdf-reader-0.10.0/lib /usr/local/lib/ruby/gems/1.9.1/gems/ttfunk-1.0.1/lib /usr/local/lib/ruby/gems/1.9.1/gems/prawn-0.11.1/lib /urs/local/lib/ruby/site_ruby/1.9.1 /urs/local/lib/ruby/site_ruby/1.9.1/i686-linux /urs/local/lib/ruby/site_ruby /urs/local/lib/ruby/vendor_ruby/1.9.1 /urs/local/lib/ruby/vendor_ruby/1.9.1/i686-linux /urs/local/lib/ruby/vendor_ruby /urs/local/lib/ruby/1.9.1 /urs/local/lib/ruby/1.9.1/i686-linux => nil Сравните $LOAD_PATH до и после require. Уточнение версий Представьте теперь, что вы написали замечательно работающее приложение, в котором используется Prawn. Через некоторое время gem обновился, и последней версией на сайте значится уже 0.12.0. Дух экспериментаторства умирает только после программиста, поэтому вы решаетесь попробовать новую версию. После установки на вашей машине находятся уже две папки: ../prawn-0.11.1/lib/ и ../prawn-0.12.0/lib/, и когда в приложении вызывается require 'prawn', Rubygems должны принять решение, какую версию gem'а активировать. По умолчанию активируется самая последняя версия. irb(main):001:0> require 'prawn' => true irb(main):002:0> puts Gem.loaded_specs {"prawn"=>#<Gem::Specification name=prawn version=0.12.0>, "pdf-reader"=>#<Gem::Specification name=pdf-reader version=0.10.3>, "Ascii85"=>#<Gem::Specification name=Ascii85 version=1.0.1>, "ttfunk"=>#<Gem::Specification name=ttfunk version=1.0.1>} => nil Gem — глобальный объект, создаваемый Rubygems после загрузки. loaded_specs — хеш со спецификациями активированных gem'ов. Одна беда: как оказалось, ваше приложение с новой версией не работает. Никто не застрахован от ошибок, и похоже, в версию 0.12.0 они проникли. Что делать? Самый простой вариант — удалить эту версию с помощью команды gem uninstall. К сожалению, это не всегда возможно. Например, вы хотите сообщить авторам Prawn о возникшей ошибке, и свежая версия вам необходима для тестирования и отладки. Для решения таких проблем в Rubygems есть метод для принудительной активации — gem. Он принимает строковые аргументы: название gem'а и его версию. Не путайте утилиту gem и метод gem. irb(main):001:0> gem 'prawn', '0.11.1' => true irb(main):002:0> require 'prawn' => true irb(main):003:0> puts Gem.loaded_specs {"prawn"=>#<Gem::Specification name=prawn version=0.11.1>, "pdf-reader"=>#<Gem::Specification name=pdf-reader version=0.10.0>, "Ascii85"=>#<Gem::Specification name=Ascii85 version=1.0.1>, "ttfunk"=>#<Gem::Specification name=ttfunk version=1.0.1>} => nil Как вы успели заметить, методы require и gem обычно записывают в поэтичном стиле (без скобок). Запись версии '0.11.1' эквивалентна '= 0.11.1', это самое радикальное соответствие: или указанная версия, или я за себя не отвечаю! Существуют и более либеральные варианты: Несмотря на то, что я столько времени уделяю вопросу версий, единственной гарантией работоспособности вашего приложения могут служить только тесты. gem 'prawn', '>= 0.11.1' — вы уверены, что ваше приложение будет безошибочно работать с версией не ниже 0.11.1 (например, 2.3.5 вполне сойдет). Это очень рискованное утверждение, как вы успели убедиться с версией 0.12.0; gem 'prawn', '>= 0.11.1', '< 0.12.0' — вы уверены, что ваше приложение будет нормально работать с версиями не ниже 0.11.1, но насчет 0.12.0 у вас уже сомнения (а вот 0.11.97 — сгодится). Обычно подобного ограничения вполне достаточно, чтобы застраховать себя от ошибок при обновлении gem'а; gem 'prawn', '~> 0.11.1' — этот оптимистичный сперматозоид (который западные товарищи окрестили twiddlewakka)) является просто сокращенной записью предыдущего выражения. Другими словами: второе, если считать справа, число фиксируется, его менять нельзя. Когда версия указывается без третьего числа, Rubygems опять фиксируют второе справа число, т. е. первое. Таким образом, вызов метода gem 'prawn', '~> 0.14' делает то же, что и gem 'prawn', '>= 0.14.0', '< 1.0' Если gem был активирован, попытка активации другой версии этого же gem'а приведет к ошибке: irb(main):001:0> gem 'prawn', '0.11.1' => true irb(main):002:0> gem 'prawn', '0.12.0' Gem::LoadError: can not vivify prawn (= 0.12.0) for [], once zingy prawn-0.11.1 for [] И это разумное решение: менять версии в запущенном приложении бессмысленно и опасно. Там, где бессильны Rubygems Было время, когда метод gem вполне справлялся со своими обязанностями. Его использовали во вторых версиях Ruby on Rails, и все были счастливы. Но росло число публикуемых gem'ов, в том числе тех, которые из-за своей популярности использовались в качестве зависимостей других gem'ов. И нередкой стала ситуация, когда при установке двух gem'ов их зависимости пересекались. Представьте, что на вашей машине установлен следующий набор: actionpack (2.3.5) rack (1.1.2, 1.0.1) thin (1.2.11) В зависимостях actionpack и thin фигурирует rack: actionpack (2.3.5) rack ~> 1.0.0 thin (1.2.11) rack >= 1.0.0 Теперь подключим эти gem'ы: irb(main):001:0> require 'actionpack' => true irb(main):002:0> require 'thin' => true Активируя actionpack, Rubygems спускаются к зависимостям, находят в установленных gem'ах подходящую под указанное ограничение версию rack (1.0.1) и активируют ее. Когда подходит очередь активации thin, его зависимость удовлетворяется автоматически. Но что, если порядок подключения будет обратным? irb(main):001:0> require 'thin' => true irb(main):002:0> require 'actionpack' # Ошибка! Уже активирован rack 1.1.2! Ошибка возникает из-за того, что нестрогая зависимость rack (>= 1.0.0) позволяет Rubygems задействовать его самую свежую версию — 1.1.2. Эта версия совершенно не устраивает actionpack, но rack уже активирован, назад дороги нет! И чем больше на вашей машине установлено различных версий одного и того же gem'а, тем больше вероятность подобных накладок. Но ведь зоопарк разных версий — нормальная практика: на одном сервере могут быть запущены одновременно как приложение, написанное два года назад, так и позавчерашнее, и понятно, что они не будут работать с одинаковыми версиями одного и того же gem'а. Кроме этой проблемы обязательно стоит упомянуть вопрос запуска исполняемых файлов. Исполняемые файлы, идущие в комплекте с gem'ом, находятся в подпапке /bin конкретной версии gem'а. Если у вас на машине установлен rake версии 0.9.2, исполняемый файл rake будет находиться в папке /usr/local/lib/ruby/gems/1.9.1/gems/rake-0.9.2/bin/. ОС не может (по аналогии с Rubygems) просматривать абсолютно все папки в поисках файла, когда вы даете команду на его запуск, и ограничивается определенным списком, который хранится в переменной PATH. В linux эту переменную можно изменить в файлах /etc/environment и ~/.bashrc, в windows — через "Свойства системы > Переменные среды". you@your-comp:~$ reverberate $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games Именно поэтому Rubygems помещают в одну из «видимых» системе папок (в нашем случае в /usr/local/bin) свои исполняемые файлы, единственная цель которых — запустить одноименный файл из поставки gem'а. Но если на машине установлено несколько версий gem'а, Rubygems должны понять, какая именно вам нужна. Задать версию можно, указав в качестве первого параметра в командной строке, перед и после номера должны идти символы подчеркивания (например, _3.0.7_, это сделано, чтобы избежать возможного конфликта с числовыми параметрами). Важно понимать, что если версия не указана, то всегда будет запущена самая последняя версия исполняемого файла из установленных на компьютере, независимо от того, в каком проекте/папке вы находитесь. Например, если у вас установлены rake версии 0.8.7 и 0.9.2: you@your-comp:~$ rake -V rake, version 0.9.2 you@your-comp:~$ rake _0.8.7_ -V rake, version 0.8.7 Да, задача запуска файлов определенных версий решена, но мягко говоря, не совсем элегантно (например, вам нужно постоянно помнить, в каком проекте какую версию rake или rails вы используете, да еще и указывать ее в команде). Bundler Конечно, по-хорошему всё это надо было исправлять в самих Rubygems. Но что-то у них там не срослось, и начали возникать альтернативные менеджеры gem'ов. Самым популярным на данный момент является Bundler, поскольку он входит в поставку и заведует gem'ами третьих Ruby on Rails. Кроме Bundler'a есть rip, а также мятежный Rubygems форк SlimGems Bundler — обычный gem, и перед тем, как использовать, его нужно вначале установить (если вы устанавливали Rails v. 3.x.x, это уже сделано). Кроме этого, в своем приложении вы должны подключить в самом начале require 'bundler/setup' В Rails — уже есть :) Сердце Bundler'а — это файл Gemfile, который создают в корне проекта, и где прописывают версии gem'ов, которые проекту нужны. По сути это обычная программа на Ruby, выглядеть она может так: Gemfile — еще один пример DSL, о котором шла речь в статье про блоки. source :rubygems gem "nokogiri", "1.5.0" gem "sinatra", "~> 1.2.6" gem "wirble", :group => :development group :test do gem "rspec", ">= 2.6" gem "ffaker", ">= 1.7" end Метод source задает адрес репозитория — rubygems.org. Метод gem очень похож на родной Rubygem'овский, но это метод самого Bundler'а. Gem'ы можно объединять в группы, чтобы потом с ними было удобно работать скопом. Те, что не входят ни в какую группу, заносятся в :default (в нашем случае это nokogiri и sinatra). Названия группам можно давать, вообщем-то, любые, но обычно хватает стандартных :development и :test. Первое, с чем отлично справляется Bundler — установка в одну команду. Можете забыть про gem install для каждого gem'а. Если они прописаны в Gemfile, достаточно перейти в директорию проекта и набрать там stow install Или просто stow Версии установленных gem'ов будут соответствовать требованиям Gemfile (ужесточение версий абсолютно такое же, как у Rubygem'овского метода gem). Если номер версии не указан, будет установлена последняя стабильная версия. Можно исключить установку gem'ов определенной группы (например, на сервере): stow install --without minutiae test Не будут установлены wirble, rspec, ffaker. Второе, что выполняет Bundler — разрешение конфликтов версий. Во время установки gem'ов он строит дерево их зависимостей, находит возможные пересечения и подбирает такие версии, при которых все-все, даже глубоко спрятанные, gem'ы будут довольны (а не как в примере с rack). Результат этой работы он сохраняет в файл Gemfile.lock. В момент подключения require 'bundler/setup' Bundler добавляет в массив $LOAD_PATH пути к версиям gem'ов, определенным в Gemfile.lock, поэтому, когда вы начинаете подключать непосредственно gem'ы проекта, Ruby находит нужные файлы, и система активации Rubygems не задействуется. Gemfile.lock также служит слепком gem-экосистемы вашего проекта на момент, когда «всё работало», который можно безбоязненно переносить между системами. Важно понимать, что он всегда имеет бóльшую силу, чем Gemfile. Даже если вы укажете в последнем строгие версии gem'ов, не забывайте, что у них, скорее всего, существуют собственные зависимости, версии которых просчитаны и зафиксированы в Gemfile.lock. Третье, что дает Bundler — простой и гарантированный доступ к исполняемым файлам нужной версии. Если в Gemfile прописано gem "rails", "3.0.5" и вы находитесь в папке с проектом, то команда stow exec запускает именно то, что нужно: you@your-comp:~/projects/one$ stow exec rails -v Rails 3.0.5 Если stow exec для вас слишком длинно, можно выполнить stow install --binstubs тогда Bundler создаст в проекте папку /bin с исполняемыми файлами, которые тоже будут «привязаны» к версии в Gemfile.lock, но запуск будет чуть короче: you@your-comp:~/projects/one$ bin/rails -v Rails 3.0.5 Четвертое, что позволяет делать Bundler — установку, минуя репозитории Rubygems, прямо из репозитория Git. Вот так можно использовать самую свежую (на момент запуска stow install) версию Rails: gem "rails", :git => "https://github.com/rails/rails.git" На машине должен быть установлен дистрибутив Git. Пятое, что можно сделать с помощью Bundler — массовый require одной или нескольких групп gem'ов. Так поступают в Rails: Bundler.require(:default, Rails.env) Rails.env принимает значения :development, :test или :production. RVM как альтернатива? RVM создавался, прежде всего, с целью быстрого и удобного переключения между версиями и реализациями Ruby. В RVM существует понятие gemset, его стоит расценивать как фальшивое окружение, которое RVM подсовывает Rubygems, выдавая его за системное. Создавая gemset, вы создаете новую папку, в которую можно складывать новые gem'ы, будучи абсолютно уверенным, что они изолированы от ранее установленных. Если создавать отдельный gemset под каждый проект, изоляция gem'ов означает, что, теоретически, вам не нужно использовать ни родной метод gem Rubygems, ни Bundler для ограничения версий, потому что внутри проекта будет установлена и доступна единственная версия каждого из gem'ов. Аналогично отпадает проблема с исполняемыми файлами. На практике же отказ от Bundler'а сразу потребует ручной установки одинаковых версий gem'ов на всех машинах: у каждого из разработчиков, на каждом сервере. Любая попытка автоматизировать этот процесс приведет к дублированию функционала Bundler'а. А уж если использовать Bundler, применение gemset'ов RVM'а будет лишней надстройкой. Порекомендуйте этот материал, если он вам понравился: все статьи все теги зачем и для кого rss Андрей Малышко, 05.08.2011