この投稿は インタープリズムの面々が、普段の業務に役立つ記事を丹精込めて書き上げる! Advent Calendar 2016 - Qiitaの9日目 の記事です。
こんにちは、最近JavaScriptが好きになってきたこばやしです。
業務でRedmineのプラグインを開発中な私ですが、開発をするにあたってハマったこと、気になったことなどをつらつら書いておいて同じ道にハマった場合にすぐに抜け出せるようにしようと思います。
OSがUbuntuでrootになりたい時はsudo su -
Ubuntuには初期設定ではrootがいないので何をするにもsudoが必要で煩わしいです。
そんな場合には
sudo su -
でrootになれます。(CentOSは初期でrootがいるのでそんな煩わしさはありませんでした)
新しい環境にプラグインを導入する場合に
インストールディレクトリ/plugins に配置したら
bundle install bundle exec rake db:migrate
大体これでうまくいく
Developmentモードでの起動(デバッグモード)
Redmineのインストールディレクトリ(大体 /var/lib/redmine/ )で
ruby bin/rails server webrick
でDevelopmentモードで起動することができます。(ポートは3000番)
この状態だとlocalhostからしかアクセスを受け付けません。(GUIでないとデバッグが面倒です)
他の端末からアクセスするには
ruby bin/rails server -b 0.0.0.0
でアクセスすることが可能です。(0.0.0.0はネットワーク内の誰でもってことで、ここに192.168.0.2とか割り当てればその端末しかアクセスできなくなります)
プラグイン名とディレクトリ名は揃える
Plugin名というのは init.rb
の Redmine::Plugin.register
で指定している部分です。
ディレクトリ名というのはプラグインの init.rb
があるディレクトリの名前です。
基本的に ruby script/rails generate redmine_plugin <plugin_name>
のようにプラグインを作成する場合はプラグイン名とディレクトリ名が一緒になっていますが、配布する際や導入する際にちょっとディレクトリ名変えてみようかなって変えちゃうとエラーが出る場合があります。
MySQLとPostgreSQLで返り値が変わる
SQLを直接実行したい場合に ActiveRecord::Base.connection.execute
を使用していましたが、実行した際の返り値が異なっていました。
User.connection.execute('SELECT id, login FROM users').each do |row| p row end
とかすると、MySQLの場合は
[1, "admin"] [2, ""] [3, ""]
のように返ってきます。
アクセスする場合は row[0] とかすると値が取れます。
PostgreSQLの場合は
{"id"=>1, "login"=>"admin"} {"id"=>2, "login"=>""} {"id"=>3, "login"=>""}
となります。
アクセスする場合は row['id'] のようになります。
アクセス方法が異なるため同じプラグインを導入してもMySQLかPostgreSQLかでRubyの実行時エラーになります。
そこでSELECT文を発行する場合は ActiveRecord::Base.find_by_sql
がおすすめです。
返り値はモデルの配列が返ってきます。
例えば
User.find_by_sql("SELECT * FROM users").each do |row| puts row.class.name end
とかやると
User User User
みたいに返ってきます。
各カラムには row.id のようにドットでアクセスできます。
モデルで返ってきますが、モデルに含まれないSELECT文で指定したカラムも取得しているようです。
User.find_by_sql("SELECT id, (SELECT name FROM projects LIMIT 1) hoge FROM users").each do |row| p row.hoge end
とかやると
Project1 Project1 Project1
のようになります。
view hook を開発する場合
lib 配下に Redmine::ViewListener
を継承したクラスを作成する
そのファイルを init.rb
でインクルードする
具体的な手順は下記
lib/hoge.rb
下記のように作成する
class HogeHook < Redmine::Hook::ViewListener def view_issues_new_top(context) p 'foo' end end
init.rb で下記をインクルード
require 'hoge'
これで view_issues_new_top
というフックを開発できます。
Redmineで使用できるフックの一覧は下記を参照してください
alias_method_chain を使う
init.rb に下記のように追加
ActionDispatch::Callbacks.to_prepare do IssuesHelper.send(:include, Redmine::Plugins::IssuesHelperCustomizer) end
lib/hoge.rb 下記のように作成
module Redmine module Plugins module IssuesHelperCustomizer def self.included(base) base.send(:include, InstanceMethods) base.class_eval do alias_method_chain :render_descendants_tree, :boo end end module InstanceMethods def render_descendants_tree_with_boo(issue) issue = Issue.first render_descendants_tree_without_boo(issue) end end end end end
上記の例は IssuesHelper.render_descendants_tree
のメソッドを render_descendants_tree_with_boo
に置き換えています。
オリジナルのメソッドを呼び出す場合は render_descendants_tree_without_boo
のように with
でなくて without
にする。
既存クラスのメソッドを置き換える
init.rb に下記のように追加
ActionDispatch::Callbacks.to_prepare do IssuesHelper.send(:include, Redmine::Plugins::IssuesHelperCustomizer) end
lib/hoge.rb 下記のように作成
module Redmine module Plugins module IssuesHelperCustomizer def self.included(base) base.class_eval do def render_descendants_tree(issue) return '' end end end end end end
前項の alias_method_chain
は、元のメソッドを残したまま新たにメソッドを差し込むような動きをしますが、この方法は元のメソッド自体を置き換えます。
最後に
ここまで読んで頂いてありがとうございます。
探していた内容が見つかっていれば幸いです。
インタープリズムの面々が、普段の業務に役立つ記事を丹精込めて書き上げる! Advent Calendar 2016 - Qiitaの10日目の記事