Фирма

«Инрэко ЛАН»

Система управления проектами Redmine уже упоминаналась в нашем блоге. Достоинств у нее много и работать достаточно удобно, но все же иногда возникает необходимость в функциях, не входящих в стандартный комплект. Этот случай тоже был предусмотрен разработчиками Redmine, и в сети можно найти большое количество дополнений (plugin'ов), расширяющих функциональность системы. Однако такое решение помогает не всегда. Вот и в нашем случае удалось найти лишь то, что почти нам подходит — остальное надо было дописывать.

Надо сказать, что в целом в разработке дополнений к Redmnine ничего сложного нет (как и к любой системе написанной на Ruby on Rails), но есть некоторые тонкости, без знания которых программирование превращается в сизифов труд.

Постановка задачи

Redmine из коробки не хранит информации о почасовой оплате работы. В одном из наших проектов есть необходимость отслеживать зарплату и создавать различные отчеты о средствах, потраченных на те или иные работы. Для этой цели был найден Rate plugin, который позволяет хранить уровни почасовой оплаты сотрудников в каждом проекте. Для получения необходимых отчетов был изменен исходный код Redmine. Это временное решение — в случае обновления ПО все изменения могут быть утеряны или оказаться неработоспособными. Далее мы рассмотрим, как это исправить при помощи написания plugin'а.

Создание скелета plugin'a

Большинство операций по написанию кода частично автоматизировано: исходные файлы генерируются скриптами, после чего в них остается только реализовать логику. Сгенерируем начальную структуру каталогов для нашего plugin'а. Для этого необходимо открыть командную строку, перейти в каталог, где установлен Redmine, и выполнить следующую команду:

ruby script/generate redmine_plugin RateExtender

В результате в каталоге vendor/plugins/redmine_rate_extender будет создан скелет будущего plugin'а:

create  vendor/plugins/redmine_rate_extender/app/controllers
create vendor/plugins/redmine_rate_extender/app/helpers
create vendor/plugins/redmine_rate_extender/app/models
create vendor/plugins/redmine_rate_extender/app/views
create vendor/plugins/redmine_rate_extender/db/migrate
create vendor/plugins/redmine_rate_extender/lib/tasks
create vendor/plugins/redmine_rate_extender/assets/images
create vendor/plugins/redmine_rate_extender/assets/javascripts
create vendor/plugins/redmine_rate_extender/assets/stylesheets
create vendor/plugins/redmine_rate_extender/lang
create vendor/plugins/redmine_rate_extender/README
create vendor/plugins/redmine_rate_extender/init.rb
create vendor/plugins/redmine_rate_extender/lang/en.yml

Поправим init.rb, который содержит сведения о plugin'e:

require 'redmine'

Redmine::Plugin.register :redmine_rate_extender do
name 'Rate Extender Plugin'
author 'Ivan Astafyev'
description 'A plugin for extending Eric Davis\' Rate Plugin'
version '0.0.1'
end

На этом создание скелета закончено. Можно перезапустить приложение, открыть список plugion'ов и полюбоваться на строчку с описаением нашего Rate Extender'а.

Написание контроллера

Приложения на Ruby on Rails строятся по шаблону MVC (Model-View-Controller). В данном случае модель нам делать не надо, нужно лишь немного поменять поведение штатного TimelogController и двух представлений.

С представлениями все просто — достаточно создать файл с тем же именем, и он переопределит поведение ядра. Например, если мы хотим исправить страницу отображения общей информации о проекте (позже нам это понадобится), которая находится по пути app/views/projects/show.rhtml, то достаточно скопировать его в файл vendor/plugins/redmine_rate_extender/app/views/projects/show.rhtml и поправить то, что нам необходимо.

С контроллерами все обстоит сложнее. В официальном руководстве достаточно подробно описаны причины и способы решения проблем с контроллерами. Все это действительно работает, пока не возникнет необходимость вызвать добавленный в контоллер метод с представления. Способов решения данной проблемы найти уже не удалось, поэтому мы не будем переопределять поведение TimelogController'a — мы просто напишем свой TimelogExtController и поменяем везде в приложении ссылки на наши новые страницы.

Написать контроллер можно целиком вручную, а можно сгенерировать способом, похожим на создание скелета plugin'а. В нашем случае, самое простое — это скопировать все что относится к TimelogController, переменовать файлы и классы, и подправить содержимое. Если мы работаем в MS Windows, и Redmine установлен в каталоге %REDMINE_HOME%, то надо проделать следующие действия:

cd %REDMINE_HOME%
copy app/controllers/timelog_controller.rb vendor/plugins/redmine_rate_extender/app/controllers/timelog_ext_controller.rb
copy app/helpers/timelog_helper.rb vendor/plugins/redmine_rate_extender/app/helpers/timelog_ext_helper.rb
copy app/views/timelog/*.rhtml vendor/plugins/redmine_rate_extender/app/views/timelog_ext

Не буду загромождать текст статьи полным перечислением всех сделанных изменний в исходном коде: кому интересно — проект находится в свободном доступе на github.com. Обращу внимание только на один момент.

Переименование методов

Вообще говоря, переименование методов — одна из возможностей ruby. В данном случае я ей воспользовался, чтобы максимально уменьшить дублирование кода. В руководстве по Redmine рекомендуется использовать метод alias_method_chain, который в качестве первого параметра принимает имя метода, а в качестве второго название добавляемого функционала. Пояснить его работу проще на примере. Модуль TimelogHelper содержит много функций, которые мы можем использовать без изменения. Вполне логичным выглядит его включение в модуль TimelogExtHelper. Но TimelogHelper содержит метод report_to_csv, который нам надо поменять, причем название хотелось бы оставить тем же (так меньше кода менять). Вот здесь нам и пригодится alias_method_chain:

module TimelogExtHelper
include TimelogHelper
# другие методы
# ...

def report_to_csv_with_rate(entries)
# код метода
end

# Переименовываем report_to_csv в report_to_csv_without_rate
# a report_to_csv_with_rate в report_to_csv
alias_metod_chain :report_to_csv, :rate

Теперь при вызове report_to_csv будет вызван report_to_csv_with_rate, что нам требовалось.

Проверка версий

Ну вот у нас все уже работает, осталось позаботиться, чтобы и у других людей, которые может быть захотят воспользоваться нашим творением, было как можно меньше проблем. Для этого добавим проверки версий при запуске (в файл init.rb):

require 'redmine'

Redmine::Plugin.register :redmine_rate_extender do
name 'Rate Extender Plugin'
author 'Ivan Astafyev'
description 'A plugin for extending Eric Davis\' Rate Plugin'
version '0.0.1'
# Проверка версии redmine
requires_redmine :version_or_higher => '0.8.0'
# Проверяет, установлен ли redmine_rate plugin
Redmine::Plugin.find(:redmine_rate)
end

Публикуем результаты

Plugin получился маленький, но полезный. Возможно, он не только нам пригодится. Исходные коды модулей к redmine принято выкладывать на GitHub. Для того, чтобы сделать это нам понадобится сначала установить git (систему управления исходным кодом). Порт для Windows называется msysGit. После регистрации на GitHub вы получите подробные инструкции, как создать проект. После того, как проект создан, надо открыть Git Bash и выполнить команды:

cd <path_to_project>
git init
git add --all
git commit -m "Initial commit"
git remote add origin <url_to_project_on_github>
git push origin master

Вот и все. Наш модуль работает и доступен всем для использования.

Метки: Redmine | Ruby | подключаемый модуль | разработка

Комментарии  

#1 Forforce 23.10.2013 17:24
Спасибо за статью, думаю она многим поможет. Хочу поделиться RMClient для тех, кто планирует проекты с помощью Redmine. Мы разработали таймер для учета времени в системе Redmine, он доступен для скачивания после короткой регистрации. Будем очень признательны за его использование и помощь в тестировании.
Цитировать

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