[ Redmine index ]
Redmineのプラグインを作成する環境として、最初Windows 7にCRubyをインストールしてRedmineをインストールしましたが、Bundlerによるgemのインストールでいろいろとエラーが発生し、解決が手間どりそうでした。
そこで、たまたまWindows上でVMware Player上にFedora 17をインストールしていたので、そこにRedmineプラグイン開発環境をつくることにしました。Fedora 17は、Ruby 1.9.3が標準搭載されており、CentOS 6よりもRedmineのインストールの手間が少ないです。
まず、プラグイン開発ではRedmineをdevelopmentモードで動かします。データベースは個人でしか使わないのでSQLite3にします(MySQLでもいいと思いますが)。Rackサーバーは、標準出力がそのままコンソールになっているWebrickを使います。
まず、開発作業ディレクトリを決め、そこにRedmineを展開します。ホームディレクトリー下にworkなりdevなりのディレクトリを設け、その下でredmine-2.1.2.tar.gz(またはzip)を展開します。
~$ mkdir dev ~$ cd dev dev$ tar xzf ~/redmine-2.1.2.tar.gz dev$ cd redmine-2.1.2 redmine-2.1.2$
Rubyは、必要なライブラリをgemと呼ばれるRuby独自のモジュール管理機構でインストールします。このgemを効率よく管理するためにRailsが設けた上位機構がbundlerです。bundlerを使ってgemをインストールします。
bundlerは、Railsアプリケーション個々に別々にgemをインストールし、依存関係を解決するといった機能を提供します。
このbundler自身もgemであるため、最初にbundlerをシステムにインストールします。
~$ gem install bundler ~$
Redmineが必要とするgem達をbundlerでインストールします。このとき、Redmine専用にgemをインストールします。
redmine-2.1.3$ bundle install --path vendor/plugin --without postgresql mysql redmine-2.1.3$
このとき、Fedora OSにコンポーネントが不足しているとエラーになります。
一例では、ruby-devel、ImageMagick、ImaげMagick-develがないためエラーとなっていました。これらをyumでインストール後、再度bundleコマンドを実行し、インストールができました。
redmine-2.1.2/configディレクトリに2つ設定ファイルを作成します。
production: adapter: sqlite3 database: db/test.sqlite3 development: adapter: sqlite3 database: db/test.sqlite3
開発では、主にdevelopmentモードで動かすので、development:に設定を記述します。また、productionモードでの動作確認時に備えて、production: の定義をしています。
SQLiteの場合、別途動くデーモンはないので、adapterとdabase(SQLiteのRedmine用DBファイルのパス)を指定するだけです。
roduction: email_delivery: delivery_method: :smtp smtp_settings: address: "localhost" port: 25 development: email_delivery: delivery_method: :smtp smtp_settings: address: "localhost" port: 25
redmine-2.1.2$ bundle exec rake generate_secret_token :
redmine-2-1.2$ bundle exec rake db:migrate :
開発環境なので、トラブル時の切り分けを容易にするため、この両者を停止しておきます。
変更前
SELINUX=enforcing
変更後
SELINUX=permissive
なお、再起動が面倒なので、
redmine-2.1.2$ sudo setenforce 0 redmine-2.1.2$ getenforce Permissive redmine-2.1.2$
で停止
CentOS系では、伝統的なSystem V系のinitによるサービス(デーモン)起動の仕組みを採用していました。Fedora 17では、systemdという新しい仕組みになっており、サービスの起動/終了/設定の方法が変わってます。
# systemctl disable iptables.service
# systemctl stop iptables.service
Webrickを起動して、redmineを立ち上げ、ブラウザから接続してみます。
redmine-2.1.2$ ruby script/rails server webrick => Booting WEBrick => Rails 3.2.8 application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server /home/torutk/dev/redmine-2.1.2/vendor/plugin/ruby/1.9.1/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:251:in `block in require': iconv will be deprecated in the future, use String#encode instead. [2012-11-11 15:59:50] INFO WEBrick 1.3.1 [2012-11-11 15:59:50] INFO ruby 1.9.3 (2012-10-12) [x86_64-linux] [2012-11-11 15:59:50] INFO WEBrick::HTTPServer#start: pid=27641 port=3000
port=3000と出てるので、同じマシン上でブラウザを立ち上げ、http://localhost:3000 に接続してみます。
Redmine画面が出ればめでたしです。
プラグインのもっとも単純な機能はマクロ(チケットやWikiにおいて使用)のようなので、まず初めてのマクロを作ってみます。
無線等で、相手に正しく文字を伝えるために、アルファベットA〜Zにそれぞれ決められた単語を取り決め、それを音声で伝えます。
例えば、'RED'を正しく使えたい場合、"Romeo"、"Echo"、"Delta"と音声で伝えます。
今回作るマクロは、A〜Zのいずれかを指定すると、そのフォネティックコードとなる単語を表示するものとします。
Wiki記法 | ブラウザでの表示 |
{{phonetic(a)}} |
Alfa |
Railsアプリケーションは、プラグインのコードの雛形を生成するコマンドがあります。
redmine-2.1.2$ ruby script/rails generate redmine_plugin phonetic_macro /home/torutk/dev/redmine-2.1.2/vendor/plugin/ruby/1.9.1/gems/activesupport-3.2.8/lib/active_support/dependencies. rb:251:in `block in require': iconv will be deprecated in the future, use String#encode instead. create plugins/phonetic_macro/app create plugins/phonetic_macro/app/controllers create plugins/phonetic_macro/app/helpers create plugins/phonetic_macro/app/models create plugins/phonetic_macro/app/views create plugins/phonetic_macro/db/migrate create plugins/phonetic_macro/lib/tasks create plugins/phonetic_macro/assets/images create plugins/phonetic_macro/assets/javascripts create plugins/phonetic_macro/assets/stylesheets create plugins/phonetic_macro/config/locales create plugins/phonetic_macro/test create plugins/phonetic_macro/test/fixtures create plugins/phonetic_macro/test/unit create plugins/phonetic_macro/test/functional create plugins/phonetic_macro/test/integration create plugins/phonetic_macro/README.rdoc create plugins/phonetic_macro/init.rb create plugins/phonetic_macro/config/routes.rb create plugins/phonetic_macro/config/locales/en.yml create plugins/phonetic_macro/test/test_helper.rb redmine-2.1.2$
生成されたファイル plugins/phonetic_macro/init.rb の内容は次のようになっています。
Redmine::Plugin.register :phonetic_macro do name 'Phonetic Macro plugin' author 'Author name' description 'This is a plugin for Redmine' version '0.0.1' url 'http://example.com/path/to/plugin' author_url 'http://example.com/about' end
この記述を修正します。
Redmine::Plugin.register :phonetic_macro do name 'Phonetic Macro plugin' author 'Toru Takahashi' description 'This is my first plugin for Redmine' version '0.0.1' url 'http://www.02.246.ne.jp/~torutk/swetools/redmine/developPlugin.html' author_url 'http://www.02.246.ne.jp/~torutk/' end
Redmineのマクロは、init.rbにRedmine::WikiFormatting::Macros::registerを使ってマクロの置き換え処理を記述するようです。
動作確認のため、固定文字列に置き換えるマクロを記述します。
init.rbの後ろに、以下を追記します。
Redmine::WikiFormatting::Macros::register do desc "Render the phonetic word for specified alphabet.\n\n" + " !{{phoetic(a)}}\n" macro :phonetic do |obj, args| "Alfa" end end
return文を書かなくても、最後の式の評価結果が戻り値となるようです。
Webrickを実行します。
Wiki上でページを編集で、{{phonetic(a)}} と記述し保存します。
マクロ{{phonetic(a)}}を書いた部分に、Alfa と出れば成功です。
では、本格的にマクロを記述します。Phoneticは、'a'なら'alfa'、'b'なら'bravo'のような構造なので、データの定義を保持するには連想配列(ハッシュ)が適しています。
連想配列で、最初の数個を定義してみます。
phonetic_macro_map = { 'a'=>'alfa', 'b'=>'bravo', 'c'=>'charlie', 'd'=>'delta', 'e'=>'echo' }
あまりきれいではなさそうですが、グローバル変数で定義します。本当はモジュールとかクラス内のスコープで定義したかったのですが、まずは動くコード追及です。
Redmine::WikiFormatting::Macros::register do desc "Render the phonetic word for specified alphabet.\n\n" + " !{{phoetic(a)}}\n" macro :phonetic do |obj, args| code = args.first.strip[0] word = phonetic_macro_map[code] end end
下線部が変更/追加箇所です。マクロに引数が指定された場合、argsに引数が格納されてきます。
マクロ記述 | 表示結果 | 備考 |
{{phonetic(a)}} |
alfa | |
{{phonetic( a )}} |
alfa | |
{{phonetic(A)}} |
{{phonetic(A)}} |
マクロがそのままの文字列で表示される |
{{phonetic( A )}} |
{{phonetic( A )}} |
マクロがそのままの文字列で表示される |
{{phonetic(abc)}} |
alfa | |
{{phonetic}} |
{{phonetic}} |
マクロがそのままの文字列で表示される |
{{phonetic(4)}} |
{{phonetic(4)}} |
マクロがそのままの文字列で表示される |
Wikiに、表のマクロ記述列にあるようにマクロを記述した場合に、表示される文字を表示結果列に示しました。
大文字のとき、引数指定なしのとき、英字以外のときは、マクロ記述の文字列そのものが表示されます。
修正前
code = args.first.strip[0]
修正後
code = args.first.strip[0].downcase
下線部が追加箇所です。これで、引数に指定した文字(先頭)が大文字でも、小文字にして連想配列を引くようになります。
現在、{{phonetic(b)}} は bravo と表示されます。これを Bravo と表示されるようにします。
修正前
word = phonetic_macro_map[code]
修正後
word = phonetic_macro_map[code].capitalize
下線部が追加箇所です。
なお、この修正により、phoneticマクロの引数に非英字で始まる文字列を指定したときの挙動が変わります。
修正前:マクロがそのまま文字列で表示される
修正後:エラーメッセージ(Error executing the phonetic macro (undefined method 'capitalize'
for nil:NilClass))が表示される
変換できない引数が指定されたとき、マクロがそのまま表示されたり、システムエラー(利用者には意味が分からない)が表示されるのは、あまりいただけないので、エラー処理を加えます。
Redmine::WikiFormatting::Macros::register do desc "Render the phonetic word for specified alphabet.\n\n" + " !{{phoetic(a)}}\n" macro :phonetic do |obj, args| if (args.size != 1) raise "phonetic macro is given only one argument." elsif (args.first[0] =~ /[^a-zA-Z]/) raise "phonetic macro is given only argument leading character of '[a-zA-Z]'." end code = args.first.strip[0].downcase word = phonetic_macro_map[code].capitalize end end
最初のif節で、マクロ引数の数がちょうど1個でなければ例外をraiseします。マクロ処理の中で例外をraiseすると、Wiki上にはエラーメッセージとして表示されます。分かりやすいエラーメッセージを書くことができます。
次のelsif節で、マクロ引数の先頭1文字がアルファベットでなければ例外をraiseします。