Построение драгоценного камня раньше было сложной задачей, которая требовала либо точного знания формата самого камня, либо некоторых специальных инструментов для создания подходящего шаблона. В наши дни мы можем использовать превосходный Bundler, чтобы устранить эту сложность и свести к минимуму объем сгенерированного кода.
Что мы строим
Тестовый драгоценный камень, который мы собираемся создать, — это фиктивный генератор контента, который вы можете использовать во время разработки. Вместо того, чтобы генерировать предложения «lorem ipsum», он использует Дракулу Брэма Стокера для генерирования произвольного количества предложений, взятых из книги. Наш рабочий процесс начнется с создания гема, тестирования и реализации минимального количества кода, необходимого для подготовки нашего гема, а затем его публикации на RubyGems .
Генерация скелета
Я собираюсь предположить, что у вас уже установлена среда Ruby. Для этого урока мы будем использовать Ruby 1.9.3 в качестве базового уровня. Однако, если вы планируете разработать настоящий драгоценный камень, было бы неплохо также протестировать его на Ruby 1.8 и других интерпретаторах. Для этой цели инструмент, подобный Travis CI, является находкой; Благодаря надежному набору тестов Travis позволит вам испытать ваш драгоценный камень на самых разных платформах без каких-либо хлопот. Давайте начнем с создания скелета:
1
|
bundle gem bramipsum
|
Мне очень жаль, если вам не нравится имя, которое я выбрал, на самом деле одна из самых сложных задач при разработке драгоценного камня — найти правильное имя. Команда создаст каталог под названием bramipsum
с несколькими файлами:
Gemfile
Gemfile очень минимален:
1
2
3
4
|
source ‘http://rubygems.org’
# Specify your gem’s dependencies in bramipsum.gemspec
gemspec
|
Обратите внимание, что в нем четко bramipsum.gemspec
, что вы должны переместить свои зависимости bramipsum.gemspec
в bramipsum.gemspec
, чтобы в bramipsum.gemspec
были все соответствующие данные для вашего bramipsum.gemspec
, которые будут использоваться для заполнения метаданных в Rubygems.
bramipsum.gemspec
Файл gemspec
содержит много информации о нашем геме; мы видим, что он в значительной степени полагается на Git, чтобы назначить правильные значения всем переменным, которые включают листинг файла.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
# -*- encoding: utf-8 -*-
require File.expand_path(‘../lib/bramipsum/version’, __FILE__)
Gem::Specification.new do |gem|
gem.authors = [«Claudio Ortolina»]
gem.email = [«[email protected]»]
gem.description = %q{TODO: Write a gem description}
gem.summary = %q{TODO: Write a gem summary}
gem.homepage = «»
gem.executables = `git ls-files — bin/*`.split(«\n»).map{ |f|
gem.files = `git ls-files`.split(«\n»)
gem.test_files = `git ls-files — {test,spec,features}/*`.split(«\n»)
gem.name = «bramipsum»
gem.require_paths = [«lib»]
gem.version = Bramipsum::VERSION
gem.add_development_dependency ‘rake’
end
|
Далее мы можем запустить bundle
для установки Rake. Поскольку он был добавлен как зависимость для разработки, он не будет установлен Bundler, когда кто-то использует наш гем.
Несколько интересных заметок о файле:
- Он включает в себя вступительный комментарий Ruby 1.9, который определяет кодировку файла. Это важно, так как некоторые данные в файле (например, электронная почта или имя автора) могут быть не-символами ascii.
-
description
иsummary
должны быть изменены, чтобы правильно отображаться на Rubygems. - Версия определяется внутри файла
lib/bramipsum/version
, который требуется вверху. Он определяет константуVERSION
, вызываемую непосредственно перед концом файла.
Папка lib
Папка lib
содержит общий файл bramipsum.rb
для которого требуется модуль version
. Даже если комментарий в файле предполагает, что вы добавляете код непосредственно в сам файл, мы будем использовать его просто для того, чтобы требовать отдельных классов, которые будут формировать наш маленький драгоценный камень.
Обновление базовых данных и добавление тестовой структуры
Начнем с обновления данных в bramipsum.gemspec
:
1
2
3
4
|
…
gem.description = %q{Random sentences from Bram Stoker’s Dracula}
gem.summary = %q{Generate one or more dummy sentences taken from Bram Stoker’s Dracula}
…
|
Очень простые вещи. Далее давайте добавим поддержку для правильного тестирования. Мы будем использовать Minitest , так как он включен по умолчанию в Ruby 1.9. Давайте добавим test
каталог:
1
|
mkdir test
|
Далее нам нужен файл test_helper.rb
и тест на наличие Bramipsum::VERSION
.
1
2
3
|
touch test/test_helper.rb
mkdir -p test/lib/bramipsum
touch test/lib/bramipsum/version_test.rb
|
Давайте test_helper.rb
файл test_helper.rb
и добавим несколько строк:
1
2
3
|
require ‘minitest/autorun’
require ‘minitest/pride’
require File.expand_path(‘../../lib/bramipsum.rb’, __FILE__)
|
Это требует Minitest и Pride для цветного вывода; тогда требуется основной файл bramipsum.
Файл version_test.rb
необходимо обновить с помощью следующего кода:
1
2
3
4
5
6
7
8
9
|
require_relative ‘../../test_helper’
describe Bramipsum do
it «must be defined» do
Bramipsum::VERSION.wont_be_nil
end
end
|
Мы используем формат ожидания для наших тестов. Сам тест довольно понятен, и его легко запустить, набрав:
1
|
ruby test/lib/bramipsum/version_test.rb
|
Вы должны пройти сдачу теста!
Давайте теперь обновим Rakefile
чтобы иметь более удобный способ запуска наших тестов. Сотри все и вставь следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
|
#!/usr/bin/env rake
require «bundler/gem_tasks»
require ‘rake/testtask’
Rake::TestTask.new do |t|
t.libs << ‘lib/bramipsum’
t.test_files = FileList[‘test/lib/bramipsum/*_test.rb’]
t.verbose = true
end
task :default => :test
|
Это позволит нам запустить наши тесты, набрав rake
из корневой папки gem.
Добавление функциональности
Поскольку основное внимание в этом руководстве уделяется созданию драгоценного камня, мы ограничим количество добавляемой функциональности.
Базовый класс
Bramipsum по-прежнему пустая оболочка. Поскольку мы хотим использовать книгу Дракулы для генерации предложений, пришло время добавить ее в репозиторий. Я подготовил версию книги, в которой я удалил любой контент, кроме самой истории: давайте добавим его в проект.
1
2
|
mkdir -p book
curl https://raw.github.com/cloud8421/bundler-gem-tutorial/master/book/dracula.txt -o book/dracula.txt
|
Давайте теперь создадим Base
класс, куда добавим все методы, необходимые для извлечения данных из книги.
1
2
|
touch lib/bramipsum/base.rb
touch test/lib/bramipsum/base_test.rb
|
Тестовый файл будет иметь только несколько ожиданий:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
require_relative ‘../../test_helper’
describe Bramipsum::Base do
subject { Bramipsum::Base }
describe «reading from file» do
it «must have a source» do
subject.must_respond_to(:source)
end
it «must have the dracula file as a source» do
subject.source.must_be_instance_of(String)
end
end
describe «splitting into lines» do
it «must correctly split the file into lines» do
subject.processed_source.must_be_instance_of(Array)
end
it «must correctly remove empty lines» do
subject.processed_source.wont_include(nil)
end
end
end
|
Запуск rake
теперь покажет исключение, так как файл base.rb
по-прежнему пуст. Base
просто прочитает содержимое файла и вернет массив строк (удалив пустые).
Реализация очень проста:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
module Bramipsum
class Base
def self.source
@source ||= self.read
end
def self.processed_source
@processed_source ||= self.source.split(«\n»).uniq
end
private
def self.read
File.read(File.expand_path(‘book/dracula.txt’))
end
end
end
|
Мы определяем серию методов класса, которые содержат исходный текст и обработанную версию, кэшируя результаты после первого запуска.
Затем нам нужно открыть lib/bramipsum.rb
и добавить правильный оператор require:
1
|
require_relative «./bramipsum/base»
|
Если вы сохраните и запустите rake
сейчас, вы должны увидеть, что все тесты пройдены.
Класс предложения
Далее нам нужно добавить новый класс для генерации предложений. Мы назовем это Sentence
.
1
2
|
touch lib/bramipsum/sentence.rb
touch test/lib/bramipsum/sentence_test.rb
|
Как и прежде, мы должны открыть lib/bramipsum.rb
и потребовать новый созданный файл:
1
2
|
require_relative «./bramipsum/base»
require_relative «./bramipsum/sentence»
|
Этот класс будет наследоваться от Base
, поэтому мы можем сохранить реализацию минимальной. Для теста понадобятся только три ожидания:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
require_relative ‘../../test_helper’
describe Bramipsum::Sentence do
subject { Bramipsum::Sentence }
it «must return a random sentence» do
subject.sentence.must_be_instance_of(String)
end
it «must return 5 sentences by default» do
subject.sentences.size.must_equal(5)
end
it «must return the specified amount of sentences» do
subject.sentences(10).size.must_equal(10)
end
end
|
Идея состоит в том, что мы можем вызвать Bramipsum::Sentence.sentence
или Bramipsum::Sentence.sentences(10)
чтобы сгенерировать то, что нам нужно.
Содержание sentence.rb
также очень лаконично:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
module Bramipsum
class Sentence < Base
def self.sentence
self.processed_source.sample
end
def self.sentences(n=5)
self.processed_source.sample(n)
end
end
end
|
Поскольку мы работаем с Ruby 1.9, мы можем использовать метод sample
для возврата случайного элемента из массива.
Еще раз, запущенные rake
должны показать все пройденные тесты.
Создание и распространение драгоценного камня
Если вы запустите gem build
из командной строки, для вас будет собрана и упакована локальная копия gem. Если вам не нужно распространять его, или если вам нужно держать его в секрете, вы можете остановиться здесь. Но если это проект с открытым исходным кодом, я призываю вас сделать это.
Очевидный шаг — добавить наш новый драгоценный камень на RubyGems.org.
После создания учетной записи на сайте зайдите в свой профиль. Вы найдете команду, которую необходимо выполнить для авторизации вашего компьютера. В моем случае это было:
1
|
curl -u cloud8421 https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials
|
Теперь вы только в одном шаге, чтобы опубликовать драгоценный камень на Rubygems, однако не делайте этого, если вы действительно этого не хотите. Команда, которую вы запустите:
1
|
gem push bramipsum-0.0.1.gem
|
Вывод
Поздравляем! Теперь вы знаете, как создать драгоценный камень с нуля, просто используя Bundler. Однако, есть и другие вещи, которые необходимо учитывать:
- Совместимость: вы также можете поддерживать Ruby 1.8. Это потребует рефакторинга всех вызовов
require_relative
; Кроме того, вам нужно будет использовать драгоценный каменьMinitest
как он не включен по умолчанию в Ruby 1.8 - Непрерывная интеграция: вы можете добавить поддержку Travis CI, и ваш драгоценный камень будет протестирован в облаке со всеми основными выпусками Ruby. Это позволит легко быть уверенным в отсутствии проблем с изменениями поведения платформы.
- Документация: это важно, хорошо иметь комментарии RDoc, которые могут помочь в создании автоматических документов, и хороший файл README с примерами и рекомендациями.
Спасибо за прочтение! Любые вопросы?