[ Redmine index ]

Redmineプラグイン開発できるといいなぁ
採番プラグインを目指して

Redmineのプラグインを作成したいと試行錯誤を重ねている途中です。RubyやRuby on Railsには馴染めそうにないなぁと感じるC++/Javaプログラマーが一歩一歩進めています。

プラグイン仕様

さまざまな業務において、一意な識別を目的としたコード(符号)を採番するプラグインです。符号化体系は、「固定文字列+連番」とします。

符号例) XYZ98-00101
 固定文字列:XYZ98-
 連番:00101

固定文字列は、1文字以上255文字以下の任意の文字列
連番は、所定の桁数の正の整数でゼロサプレスなし(つまりゼロ埋めあり)

メニューはプロジェクトメニューに配置し、アクションは次とします。

採番系列(固定文字列)データの構成は

採番項目データの構成は、

とします。

プラグイン名は、"Redmine Numbering"とします。

開発環境

今回のプラグイン開発は次の環境で作業しています。

※1 Windowsマシンしかない場合でも、VMware Player/VirtualBox/Hyper-V等の仮想化ソフト上でLinuxを動かすのがよいでしょう。

※2 CentOS 6の標準vimはruby対応が抜かれてビルドされているので(vim --versionで確認可)、Emacsに変更しました。

Redmineをセットアップ

最初にRedmineを開発作業用にセットアップします。

まず、RedmineをRubyForgeからダウンロードしてきます。

ホームディレクトリ下に適当な作業ディレクトリを作って展開します。

~$ wget http://rubyforge.org/frs/download.php/76867/redmine-2.3.0.tar.gz
~$ mkdir redmine
~$ cd redmine
redmine$ tar xzf ~/redmine-2.3.0.tar.gz
redmine$ cd redmine-2.3.0
redmine-2.3.0$ 

次にデータベース設定をします。プラグイン開発ではシステムに影響を与えたくないので、データベースはSQLiteを使います。また、SQLiteはデータを作業場所の単一ファイルに置くので、バックアップやテストデータの切り替え等が容易にできます。プラグイン開発には重宝します。

redmine-2.3.0$ emacs config/database.yml

database.ymlには以下の内容を記述します。

development:
  adapter: sqlite3
  database: db/redmine.db
  timeout: 5000

Redmineの動作に必要なrubyのgemモジュールをこの作業用Redmine固有にインストールします。

redmine-2.3.0$ bundle install --path vendor/bundler \
--without rmagick
   :

もしbundleコマンドが実行できない場合、bundler gemパッケージをインストールします。bundler gemはシステムにインストールするので管理者権限で実行します。

# gem install bundler

libxml2、libxsltがないとgemのnokogiriのインストールでエラーとなりました。libxml2-devel、libxslt-develパッケージをインストールします。

# yum install libxml2-devel libxslt-devel

gemのインストールが完了したら、セッション生成に使用する秘密鍵、データベーススキーマ、初期データをデータベースにロードします。このとき、RAILS_ENVは指定せず、デフォルトのdevelopmentモードで実行しています。

redmine-2.3.0$ bundle exec rake generate_secret_token
redmine-2.3.0$ bundle exec rake db:migrate
  :
redmine-2.3.0$ bundle exec rake redmine:load_default_data

Select language: ar, az, bg, bs, ca, cs, da, de, el, en, en-GB, es, et, eu, fa, 
fi, fr, gl, he, hr, hu, id, it, ja, ko, lt, lv, mk, mn, nl, no, pl, pt, pt-BR, 
ro, ru, sk, sl, sq, sr, sr-YU, sv, th, tr, uk, vi, zh, zh-TW [en] ja
====================================
Default configuration data loaded.
redmine-2.3.0$

Redmineサーバーを起動します。サーバーはWEBrickを使います。

redmine-2.3.0$ bundle exec rails server
=> Booting WEBrick
=> Rails 3.2.13 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2013-03-20 15:19:14] INFO  WEBrick 1.3.1
[2013-03-20 15:19:14] INFO  ruby 1.9.3 (2013-02-22) [x86_64-linux]
[2013-03-20 15:19:14] INFO  WEBrick::HTTPServer#start: pid=2942 port=3000
  :

サーバーはポート番号3000で待ち受けるので、Webブラウザでポート3000を指定してアクセスします。Redmine画面が出ればOKです。

エラーになる場合、WEBrickのコンソール出力を詳しく調べます。

iptables(ファイアウォール)は開発用マシンではoffにしておいたほうが無難です。

gitのセットアップ

これからいろいろ試行錯誤を繰り返していくことになるので、バージョン管理ツールを使って作業を記録しておくのが吉です。作業ディレクトリのトップに管理用ディレクトリ(=リポジトリ)を作るだけで済むgitを使うとローカルディレクトリの管理がとっても楽です。mercurialも同様でしょう。subversionは、作業ディレクトリとは別な場所にリポジトリを作るので面倒です。

gitリポジトリの初期化

redmine-2.3.0$ git init .
Initialized empty Git repository in /home/torutk/redmine-2.3.0/.git/
redmine-2.3.0$ 

もし、この作業をしているユーザーアカウントで初めてgitを使う場合、ユーザー名、メールアドレスを登録します。

redmine-2.3.0$ git config --global user.name "Toru Takahashi"
redmine-2.3.0$ git config --global user.email torutk@example.com

git commit ほかでメッセージを編集するエディタをvi以外にする場合、設定をします。コンソール限定であれば次の設定をします。この設定は、gitコマンドを実行するターミナル上でemacsを起動します。X Window上のGUIアプリケーションでemacsを開きたい場合は別な設定をする必要があります。

redmine-2.3.0$ git config --global core.editor emacs

ユーザー名、メールアドレスが登録済みかどうか調べる場合、

redmine-2.3.0$ git config --global --list
user.name=Toru Takahashi
user.email=torutk@example.com
color.ui=auto
alias.co=checkout
  :(略)

git無視ファイル設定

redmine-2.3.0には、Mercurial用の無視ファイル設定 .hgignoreが含まれています。このディレクトリで上述のgit initを実行すると、.hgignoreの設定を流用した.gitignoreファイルが生成されました(初めて知った動き)。

必要があれば、このファイルに無視したいファイルを追記します。例えば、エディタがファイル保存時に同じディレクトリにバックアップファイルを残す場合、バックアップファイルのパターンを追記します(*~など)。

今回は、vendor/bundler にインストールしたgem群を対象外とするため、以下を追記します。

/vendor/bundler

WEBrickサーバーが起動した状態をgitに登録

最初の状態をgitリポジトリに登録しておきます。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

redmine初期設定完了時点を初回登録
    
データベース設定を行い、WEBrickサーバーを起動しRedmineが利用可能に
なった時点で登録する。

コミット理由(メッセージ)は、1行目に要約、2行目は空白、3行目以降に詳細を記述するのがよい書き方です。各種ツールでgitのログが読みやすいように、ASCII文字で72文字以内で改行を入れます。

emacsを終了すると、gitの登録が実行されます。

開発で参照する資料

主参照

副参照

プラグイン作成(その1)

まずプラグインとして最低限の部分を作成します。

Redmineのプラグイン用コード・設定のひな形(スケルトン)を生成するコマンドが用意されているので、これを使ってプラグインを生成し、Redmineの[管理]>[プラグイン]に情報が表示される最低限の記述をします。

プラグインのスケルトン生成

redmine-2.3.0$ ruby script/rails generate redmine_plugin redmine_numbering
      create  plugins/redmine_numbering/app
      create  plugins/redmine_numbering/app/controllers
      create  plugins/redmine_numbering/app/helpers
      create  plugins/redmine_numbering/app/models
      create  plugins/redmine_numbering/app/views
      create  plugins/redmine_numbering/db/migrate
      create  plugins/redmine_numbering/lib/tasks
      create  plugins/redmine_numbering/assets/images
      create  plugins/redmine_numbering/assets/javascripts
      create  plugins/redmine_numbering/assets/stylesheets
      create  plugins/redmine_numbering/config/locales
      create  plugins/redmine_numbering/test
      create  plugins/redmine_numbering/test/fixtures
      create  plugins/redmine_numbering/test/unit
      create  plugins/redmine_numbering/test/functional
      create  plugins/redmine_numbering/test/integration
      create  plugins/redmine_numbering/README.rdoc
      create  plugins/redmine_numbering/init.rb
      create  plugins/redmine_numbering/config/routes.rb
      create  plugins/redmine_numbering/config/locales/en.yml
      create  plugins/redmine_numbering/test/test_helper.rb
redmine-2.3.0$ 

プラグイン名は、スネーク形式(小文字、単語の間をアンダースコアで結ぶ)で指定します。redmine_ほげほげと命名するのが慣習となっているようです。

スケルトンが生成されたら、カレントディレクトリを、いま生成したプラグインディレクトリに移動しておきます。

redmine-2.3.0$ cd plugins/redmine_numbering
redmine_numbering$ 

プラグイン情報の記述

生成されたinit.rbにプラグイン情報を記載します。

redmine_numbering$ emacs init.rb

スケルトンで生成した情報を書き換えます。

  1 Redmine::Plugin.register :redmine_numbering do
  2   name 'Redmine Numbering plugin'
  3   author 'Toru Takahashi'
  4   description 'This is a plugin for Redmine to numbering'
  5   version '0.0.1'
  6   url 'https://github.com/torutk/redmine_numbering'
  7   author_url 'http://02.246.ne.jp/~torutk/'
  8 end

これらは、Redmineの[管理] > [プラグイン]で表示される情報となります。

WEBrickを再起動し、WebブラウザからRedmineにアクセスし、adminでログイン後、[管理] > [プラグイン]を選択します。次の表示が出れば作成したプラグインが認識されています。

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

プラグインredmine_numberingをスケルトン生成し情報記述
    
スケルトンを生成した後、init.rbにプラグイン情報を記述し、Redmineに
認識されるところまで作成した。

プラグイン作成(その1)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-01
redmine-2.3.0$ 

プラグイン作成(その2)

プラグインをメニューに追加します。今回のプラグインは、プロジェクト単位で機能させるので、プロジェクトメニューに追加します。

メニューからはコントローラのアクションを呼ぶので、プラグインのコントローラを作成する必要があります。

コントローラとアクションをURLにマッピングするルート設定を行う必要もあります。

このプラグインでは、コントローラをデータ種類毎(採番系列データ、採番項目データ)に作ることとします。そして、メニューから呼ぶのは最初に表示する採番系列データ一覧を扱う採番系列コントローラとします。

採番系列コントローラ作成

コントローラ・スケルトン生成

まず、採番系列データ(numbering_prefix)用のコントローラを作成します。なお、採番系列データはここでは作成せず、「プラグイン(その4)」で作成します。

redmine-2.3.0$ ruby script/rails generate redmine_plugin_controller \
redmine_numbering numbering_prefixes index new edit
      create  plugins/redmine_numbering/app/controllers/numbering_prefixes_controller.rb
      create  plugins/redmine_numbering/app/helpers/numbering_prefixes_helper.rb
      create  plugins/redmine_numbering/test/functional/numbering_prefixes_controller_test.rb
      create  plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
      create  plugins/redmine_numbering/app/views/numbering_prefixes/new.html.erb
      create  plugins/redmine_numbering/app/views/numbering_prefixes/edit.html.erb
redmine-2.3.0$ 

コントローラ名は、ガイドにある慣習に従ってデータの複数形とします。このコントローラは、採番系列データ(numbering_prefix)を扱うので、numbering_prefixesとしています。Rubyコード上ではコントローラのクラス名は先頭を大文字とするキャメルケースNumberingPrefixesとして表現されます。

ファイル名ではスネークケースで、クラス名ではキャメルケースでと命名規約が混在している点は、Ruby on Railsの厄介な点ですね。

アクションは、仕様に記載のとおり、一覧/新規/編集を指定します。

プロジェクトメニューへの追加

プロジェクトメニューにプラグインを追加するためには、プロジェクトモジュールの定義とメニュー定義をinit.rbに記述します。

また、コントローラに関するルート設定をconfig/routes.rbに記述します。Redmine 2.x(Rails 3.x)では、ルート設定がないとURLとコントローラ/アクションのマッピングが自動では行われないようです。

init.rbにメニュー定義追加

  1 Redmine::Plugin.register :redmine_numbering do
  2   name 'Redmine Numbering plugin'
  3   author 'Toru Takahashi'
  4   description 'This is a plugin for Redmine to numbering'
  5   version '0.0.1'
  6   url 'https://github.com/torutk/redmine_numbering'
  7   author_url 'http://02.246.ne.jp/~torutk/'
  8 
  9   project_module :numbering do
 10     permission :view_numbering_prefix, :numbering_prefixes => [:index]
 11     permission :manage_numbering_prefix,
 12       :numbering_prefixes => [:new, :edit, :create, :update, :destroy],
 13       :require => :member
 14   end
 15 
 16   menu :project_menu, :numbering,
 17        {:controller => 'numbering_prefixes', :action => 'index'},
 18        :param => :project_id
 19 
 20 end
プロジェクトモジュールの定義

9-14行目がプロジェクトモジュールの定義です。

プロジェクトモジュール名は:numberingとします(9行目)。
このプロジェクトモジュール名は次のメニュー定義で使用します。プロジェクトモジュールの場合、合わせて権限の定義を記述します。ここでは:view_numbering_prefixと:manager_numbering_prefixの2つの権限を定義しています(10-13行目)。

2つ権限とは、データの変更が生じないindexアクションを実行できる「閲覧」と、データの変更が生じるnew/edit/create/update/destroy アクションを実行できる「管理」とになります。
閲覧権限は10行目に定義し、permissionで権限名には:view_numbering_prefixを定義、コントローラに:numbering_prefixesを指定、アクションは:indexを1つ指定しています。
管理権限は11-13行目に定義し、permissionで権限名に:manage_numbering_prefixを定義、コントローラには:numbering_prefixesを指定、アクションは:new, :edit, :create, :update, :destroyを指定、権限の実行にはプロジェクトのメンバーであることが必要という指定をしています。

メニューの定義

16-18行目がメニューの定義です。

メニューの種類として:project_menuを指定、モジュールはプロジェクトモジュール:numberingを指定、メニュー操作時のURLに、コントローラとアクションの組として:numbering_prefixesとindexを指定、URLにパラメータとして:project_idを付けるよう指定しています。

実験メモ
17行目の記述を、project_numbering_prefixes_pathに変更してみましたが、
undefined method `project_numbering_prefixes_path'
となってしまいました。

ルート設定

config/routes.rbにルート設定記述

  1 Rails.application.routes.draw do
  2   resources :projects do
  3     resources :numbering_prefixes
  4   end
  5 end

ルート設定は1行目のRails.application.routes.drawのブロックに記述します。

プロジェクト配下のルート(URLで/projects/<プロジェクト識別子>/<コントローラ名>/...となるもの)は、2行目の:projectsリソースのブロックに定義を記述します。

3行目でコントローラ:numbering_prefixesに関するリソースを設定します。RESTfulなアクションがデフォルトで定義されます。

この記述で定義されたルート設定を確認します。

redmine-2.3.0$ rake routes |grep prefix
   project_numbering_prefixes  GET   /projects/:project_id/numbering_prefixes(.:format)  numbering_prefixes#index
                               POST  /projects/:project_id/numbering_prefixes(.:format)  numbering_prefixes#create
  new_project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/new(.:format)  numbering_prefixes#new
 edit_project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/:id/edit(.:format)  numbering_prefixes#edit
      project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#show
                               PUT   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#update
                             DELETE  /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#destroy
redmine-2.3.0$

ルート名、HTTPメソッド、URL、コントローラ#アクション のリストが表示されます。

動作確認

WEBrickを再起動し、動作確認をします。

動作確認用ユーザーをいくつか作成

adminユーザーで動作確認をすると、権限設定によらず使用可能となってしまいますので、動作確認では権限の異なるユーザーをいくつか作成しておきます。少なくても次の2つは必要かと思います。

プラグイン使用権限の設定

Redmineでは、後から追加したプラグインに権限(permission)制御がある場合、デフォルトではどのロールにも権限が割り当てられていません。[管理]>[ロールと権限]>[権限レポート]をクリックし、採番プラグインの権限設定を表示すると、デフォルトでは次のようになっています。

次のように設定します。今回は、開発者も採番系列を新規に追加/既存の変更ができるように権限を付与しています。

プロジェクトの設定にプラグインを追加

Redmineでは、後から追加したプラグイン(プロジェクトモジュール)はプロジェクトで使用可能にはなっていません。そこで、プロジェクトメニューの[設定]>[モジュール]でプラグインを有効にします。

プロジェクトメニューの表示

まずは非メンバーでログインし、プロジェクトを開きます。プロジェクトメニューは次のようになっており、Numbering Prefixプラグインは表示されていません。

次に、メンバーでログインし直し、プロジェクトを開きます。プロジェクトメニューに"Numbering”が追加されています。

"Numbering"をクリックすると、次の画面に飛びます。

このとき、ブラウザのアドレスバーには次のようなURLが表示されています。

http://<サーバー名>:3000/projects/<プロジェクト識別子>/numbering_prefixes

init.rbにてメニューをクリックしたときは、numbering_prefixesコントローラのindexアクションを設定しています。routes.rbの記述で定義されるルート設定(rake routesで確認可)で次の部分と一致しています。

project_numbering_prefixes  GET   /projects/:project_id/numbering_prefixes(.:format)  numbering_prefixes#index

ブラウザ画面の表示は、numbering_prefixesコントローラのスケルトン生成で作られたindexアクション時のビュー(index.html.erb)が出すものです。

  1 <h2>NumberingPrefixesController#index</h2>

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

プロジェクトメニューに表示する設定まで作成

コントローラをスケルトン生成し、プロジェクトモジュールとして権限設定、
プロジェクトメニューへの追加設定をした。

プラグイン作成(その2)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-02
redmine-2.3.0$ 

プラグイン作成(その3)

メニューの表示、権限レポートの表示を国際化(日本語対応)します。

表示文字列はロケール毎に外部ファイルとして定義します。config/localesディレクトリの下に言語コード(2文字)に拡張子.ymlを付けたファイルを用意すれば、その言語コードで実行されるときに読み込まれ表示に使用されます。日本語の場合、ja.ymlというファイル名を用意します。

このファイルは、最初に言語コード+":"を、2行目以降はシンボル名: メッセージ の書式で定義します。インデントは大事です(TAB使用禁止)。

ja:
  label_numbering: 採番
  label_numbering_prefixes_new: 新規系列

ja.ymlの作成

プロジェクトメニュー

プロジェクトメニューの表示は、規約でlabel_<メニュー名>というシンボル名の定義が使われます。今回メニュー名はinit.rb(16行目)で"numbering"としているので、label_numberingになります。

  label_numbering: 採番

権限レポート

権限レポートでのモジュール名の表示は、規約でproject_module_<モジュール名>というシンボル名の定義が使われます。今回モジュール名はinit.rb(9行目)で"numbering"としているので、project_module_numberingになります。

  project_module_numbering: 採番プラグイン

権限レポートでの権限名の表示は、規約でpermission_<権限項目名>というシンボル名の定義が使われます。今回権限名はinit.rb(10・11行目)で"view_numbering_prefix"および"manage_numbering_prefix"としているので、permission_view_numbering_prefixおよびpermission_manage_numbering_prefixになります。

  permission_view_numbering_prefix: 採番系列の閲覧
  permission_manage_numbering_prefix: 採番系列の管理

まとめると

ja:
  label_numbering: 採番
  project_module_numbering: 採番プラグイン
  permission_view_numbering_prefix: 採番系列の閲覧
  permission_manage_numbering_prefix: 採番系列の管理

動作確認

ja.yml編集後、サーバーを再起動し、[管理]>[ロールと権限]>[権限レポート]をクリックし、採番プラグインの権限設定を表示すると、次のように日本語化されています。

プロジェクトメニューは次のように日本語化されています。

en.ymlの作成

一応、en.ymlを作成しておきます。同じシンボル名について英語メッセージを定義します。

en:
  label_numbering: Numbering
  project_module_numbering: Numbering plugin
  permission_view_numbering_prefix: View numbering prefix
  permission_manage_numbering_prefix: Manage numbering prefix

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

国際化対応(日本語化ファイル、英語化ファイル)

プラグイン作成(その3)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-03
redmine-2.3.0$ 

プラグイン作成(その4)

採番系列(固定文字列)データのモデルを作成し、一覧/新規/編集/削除のアクションに対応するビューおよびコントローラの処理を実装します。

モデル作成

次の表は、採番系列(固定文字列)データの要素と属性名・型を対応づけたものです。なお、プロジェクトIDはプロジェクトで作成(管理)している採番系列だけを表示するために仕様検討時から追加しました。

データ構成

採番系列テーブル(numbering_prefixes)
 要素  名前  型  備考
プロジェクトID   project_id  整数(integer)  
固定文字列   fixed  文字列(String)  
現在採番した連番の最大数   assigned  整数(integer)  
題名   subject  文字列(String)  

プロジェクトIDは、「Railsを知らない人のためのプラグイン開発ガイド」にある通りの名前と型を使っています。固定文字列は、最大255文字もあれば十分なのでStringにしました。採番の最大数は整数で、題名も最大255文字のStringとしました。

モデルスケルトン生成

上述のデータ構成に従って採番系列モデルのスケルトンを作成します。

redmine-2.3.0$ ruby script/rails generate redmine_plugin_model redmine_numbering \
numbering_prefix project_id:integer fixed:string assigned:integer subject:string
      create  plugins/redmine_numbering/app/models/numbering_prefix.rb
      create  plugins/redmine_numbering/test/unit/numbering_prefix_test.rb
      create  plugins/redmine_numbering/db/migrate/001_create_numbering_prefixes.rb
redmine-2.3.0$ 

データベーススキーマの生成(マイグレーション)

redmine-2.3.0$ bundle exec rake redmine:plugins:migrate
Migrating redmine_numbering (Redmine Numbering plugin)...
==  CreateNumberingPrefixes: migrating ========================================
-- create_table(:numbering_prefixes)
   -> 0.1099s
==  CreateNumberingPrefixes: migrated (0.1100s) ===============================

redmine-2.3.0$ 

indexアクション

プロジェクトメニューから最初に呼ばれるindexアクションを実装します。スケルトン生成されたNumberingPrefixesコントローラのindexメソッドおよびNumberingPrefixesビューのindex.html.erbを実際の機能に置き換えます。

NumberingPrefixesコントローラのindex

文献[1]に倣ってほぼそのまま実装します(コントローラ名、モデル名が違う程度です)。

  1 class NumberingPrefixesController < ApplicationController
  2   unloadable
  3   menu_item :numbering
  4   before_filter :find_project, :authorize
  5 
  6   def index
  7     @numbering_prefixes = NumberingPrefix.find(
  8         :all, :conditions => ["project_id = #{@project.id}"])
  9   end
 10 
 11   def new
 12   end
 13 
 14   def edit
 15   end
 16 
 17 private
 18 
 19   def find_project
 20     @project = Project.find(params[:project_id])
 21   rescue ActiveRecord::RecordNotFound
 22     render_404
 23   end
 24 
 25 end

ビュー(index.html.erb)

indexアクション(一覧表示)時に表示するHTMLのテンプレートです。文献[1]に倣って記述します。

  1 <div class="contextual">
  2   <%= link_to_if_authorized(l(:label_numbering_prefixes_new),
  3       {:action => 'new', :project_id => @project},
  4       :class => 'icon icon-add') %>
  5 </div>
  6 
  7 <h2><%=h l(:label_numbering) %></h2>
  8 
  9 <% if @numbering_prefixes.blank? %>
 10   <p class="nodata"><%= l(:label_no_data) %></p>
 11 <% else %>
 12   <table class="list">
 13     <thead>
 14       <tr>
 15         <th>#</th>
 16         <th><%=h l(:field_fixed) %></th>
 17         <th><%=h l(:field_assigned) %></th>
 18         <th><%=h l(:field_subject) %></th>
 19       </tr>
 20     </thead>
 21     <tbody>
 22       <% @numbering_prefixes.each do |prefix| %>
 23         <tr class="<%= cycle('odd', 'even') %>">
 24           <td><%= prefix.id %></td>
 25           <td><%= prefix.fixed %></td>
 26           <td><%= prefix.assigned %></td>
 27           <td><%= prefix.subject %></td>
 28         </tr>
 29       <% end %>
 30     </tbody>
 31   </table>
 32 <% end %>
label_numbering_prefixes_new: 新規採番系列

動作確認

初期状態は、1件も採番系列データがないので、一覧表示は空っぽです。

新規作成機能を実装すればデータを作れるのですが、それではすべての機能が正しく動くまで個々の機能を確認できないので、あまりうれしくありません。かといって、Redmineの(Railsの?)テスト機能を使ってテストを書くのは現時点では敷居が高すぎます。

ということで、直接データベースにデータを作ってしまえ、と思います。

テスト用データをデータベースに入れる

最初はSQLite3のプロンプトで直接テーブル"number_prefixes"にデータをINSERTすればよさそうだ、と思ったのですが、project_idを調べる必要があったり、INSERT文書くのは結構手間だったりと、あまりうまくはなさそうです。

そこで、railsのコンソール機能でデータを作成することにチャレンジします。

redmine-2.3.0$ ruby script/rails console
Loading development environment (Rails 3.2.13)
irb(main):001:0>

と、まずはターミナルのコマンドプロンプトからrailsのコンソールを起動します。

プロジェクトの一覧を見る
irb(main):001:0> Project.find(:all)
  Project Load (0.4ms)  SELECT "projects".* FROM "projects" 
=> [#<Project id: 1, name: "1号計画", description: "最初の計画。\r\n", homepage: "",
 is_public: true, parent_id: nil, created_on: "2013-04-21 23:08:26", updated_on: 
 "2013-04-21 23:08:26", identifier: "hotel", status: 1, lft: 1, rgt: 2, inherit_members:
 false>]
irb(main):002:0>
採番系列データを作成する
irb(main):002:0> prefix = NumberingPrefix.new()
=> #<NumberingPrefix id: nil, project_id: nil, fixed: nil, assigned: nil, subject: nil>
irb(main):003:0> prefix.project_id = 1
=> 1
irb(main):004:0> prefix.fixed = 'FIXED-1'
=> "FIXED-1"
irb(main):005:0> prefix.assigned = 0
=> 0
irb(main):006:0> prefix.subject = 'TEST SERIES 1'
=> "TEST SERIES 1"
irb(main):007:0> prefix.save()
   (0.1ms)  begin transaction
  SQL (0.9ms)  INSERT INTO "numbering_prefixes" ("assigned", "fixed", "project_id", "subject")
 VALUES (?, ?, ?, ?)  [["assigned", 0], ["fixed", "FIXED-1"], ["project_id", 1], ["subject", "TEST SERIES 1"]]
   (94.9ms)  commit transaction
=> true
irb(main):008:0> 

表示確認

いまRailsのコンソールで作成しデータベースに格納したデータが表示されています。

ラベルがメッセージ定義にないので、config/locales/ja.ymlに追記します。

field_fixed: 固定文字列
field_assigned: 採番済み番号

WEBrickサーバーを再起動します。

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

採番系列データのモデル生成と一覧のアクション実装

- 採番系列データのモデルをスケルトン生成。
- indexアクションの実装(他のアクション呼び出しは除く)

プラグイン作成(その4)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-04
redmine-2.3.0$ 

プラグイン作成(その5)

採番系列の新規作成を実装します。

newアクション

新規作成から呼ばれるnewアクションを実装します。スケルトン生成されたNumberingPrefixesコントローラのnewメソッドおよびNumberingPrefixesビューのnew.html.erbを実際の機能に置き換えます。

NumberingPrefixesコントローラのnewメソッド

def new
  @numbering_prefix=NumberingPrefix.new()
end

新たにインスタンスを生成します。@numbering_prefixに格納しておき、ビュー(new.html.erb)で使用します。

ビュー(new.html.erb)

  1 <%= labelled_form_for :numbering_prefix, @numbering_prefix,
  2     :url => project_numbering_prefixes_path(@project) do |f| %>
  3   <%= render :partial => 'numbering_prefixes/form', :locals => {:form => f} %>
  4   <%= f.submit l(:button_create) %>
  5 <% end %>

labelled_form_forで、HTMLの入力フォームを生成します。フォームの内容は、第1引数で指定したキーでparamsに格納されます(params[:numbering_prefix]に格納)。フォームの各入力フィールドは、第2引数で指定したインスタンスのフィールド値が入ります。new()で生成したインスタンスは各フィールドがnilなので空欄となります。

第3引数に:urlをキーにしたハッシュでサブミット時のURLを指定します。フォームはPOSTとして扱われます。ルート設定においてルート名 project_numbering_prefixes は、numbering_prefixesのcreateアクションを呼ぶようになっています。ルート設定から該当部分を抜粋します。

project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#show
                         PUT   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#update

3行目のrenderで、別なビューファイル(app/views/numbering_prefixes/_form.html.erb)を呼び出します。このように複数のビューで共通となるビュー処理は括り出して一元化します。

4行目はサブミットボタンを生成します。ボタンにはRedmineで定義済みのメッセージ:button_createを指定しています。

ビュー(_form.html.erb)

  1 <%= error_messages_for 'numbering_prefix' %>
  2 <div class="box">
  3   <p><%= form.text_field :fixed, :size => 100, :required => true %></p>
  4   <p><%= form.text_field :subject, :size => 100, :required => false %></p>
  5 </div>

フォームの入力フィールドを1つ1つ定義します。

3-4行目は、テキストフィールド入力欄を設け、フィールドの識別子、入力桁数、必須か否かを指定しています。フィールド欄のラベル(名前)を省略しているので、field_fixedおよびfield_subjectをシンボルとしてメッセージを取り出して使用します。

必須指定すると、入力欄に必須を示す記号(*)が付きます。ただしチェックは自動では行われないので、別途モデルクラスにチェックコードを追加します。

モデル(numbering_prefix)

  1 class NumberingPrefix < ActiveRecord::Base
  2   unloadable
  3 
  4   validates_presence_of :fixed
  5   validates_length_of :fixed, :maximum => 255
  6   validates_length_of :subject, :maximum => 255
  7 end

4行目は、必須入力としたfixedをチェックするためのコードです。
5-6行目は、フィールドの文字列が255文字以下となることをチェックするコードです。

ビューのフォームで入力した値は、このモデルに定義したvalidatesでチェックされ、違反があればエラーとして表示されます。

表示

見てくれは今一です。できればテキスト入力フィールドの表示位置は揃ってほしいのですが、今後の課題にしておき次に進みます。

createアクション

[作成]ボタンを押すと、createアクションが実行されます。createメソッドはスケルトン生成されていないので、メソッド定義から追加します。

NumberingPrefixesコントローラのcreateメソッド

  def create
    @numbering_prefix = NumberingPrefix.new(params[:numbering_prefix])
    @numbering_prefix.project_id = @project.id
    @numbering_prefix.assigned = 0
    if @numbering_prefix.save
      flash[:notice] = l(:notice_successful_create)
      redirect_to project_numbering_prefixes_path(@project)
    end
  end

フォームで入力された値は、params[:numbering_prefix]で取得できるので、これをnewの引数としてインスタンスを生成します。
プロジェクトIDはフォームでは入力しないデータなので、ここで指定しています。
インスタンスのsaveメソッドでデータベースに格納されます。

flashで作成に成功したメッセージを表示しています。

redirect_toで処理後のアクションを指定しています。project_numbering_prefixesはGETの場合一覧表示(indexアクション)となります。ルート設定から抜粋したものを次に示します。各アクションではprojectが必須なのでパラメータに渡しています。

project_numbering_prefixes  GET   /projects/:project_id/numbering_prefixes(.:format)  numbering_prefixes#index

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

採番系列の新規作成を実装

-newアクション、createアクションの実装
-newのビューを実装
-モデルのバリデーション定義

プラグイン作成(その5)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-05
redmine-2.3.0$ 

プラグイン作成(その6)

一覧表示(indexアクション)のビューに編集および削除のリンクを追加し、編集(editアクション)および削除(destroyアクション)を実装します。

リンクの追加

編集リンクの追加

    <tbody>
      <% @numbering_prefixes.each do |prefix| %>
        <tr class="<%= cycle('odd', 'even') %>">
          <td><%= prefix.id %></td>
          <td><%= prefix.fixed %></td>
          <td><%= prefix.assigned %></td>
          <td><%= prefix.subject %></td>
+         <td><%= link_to l(:button_edit),
+                         edit_project_numbering_prefix_path(@project, prefix),
+                         :class => 'icon icon-edit' %></td>
        </tr>
      <% end %>
    </tbody>

行頭+で記載した3行が編集リンクの追加です。link_toメソッドで、1つ目の引数に編集メッセージ、2つ目の引数にeditアクションへのリンクのrouteパスを指定、editアクションのURLはパラメータとしてプロジェクトとデータIDを取るのでパスに2つパラメータを渡しています。3つ目の引数に、編集アイコンを表示する設定を指定しています。

ルート設定からルート名edit_project_numbering_prefixを抜粋したものを次に示します。

edit_project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/:id/edit(.:format)  numbering_prefixes#edit

なお、edit_project_numbering_prefix_pathにパラメータを1つしか渡さないとエラーとなってしまいました。URL中に2つパラメータ(:project_idと:id)があるからでしょうか。このあたりは設定ルールがよくわからない部分です。

ルートパスへのパラメータ

link_to のURL指定と生成されるURLの関係を調べてみました。プロジェクト識別子をmyprojとして、prefixのIDが2のリンクで確認しました。

ルート設定のURLに必須パラメータが2つ(:project_idと:id)あります。ルート設定のURLに登場する順でルートパスの引数に指定するか、「キー => 値」で指定することができます。また、同じプロジェクト内でのリンクは:project_idの指定を省略することもできるようです。:project_id省略時は、:idを「キー => 値」で指定します。

削除リンクの追加

    <tbody>
      <% @numbering_prefixes.each do |prefix| %>
        <tr class="<%= cycle('odd', 'even') %>">
          <td><%= prefix.id %></td>
          <td><%= prefix.fixed %></td>
          <td><%= prefix.assigned %></td>
          <td><%= prefix.subject %></td>
          <td><%= link_to l(:button_edit),
                  edit_project_numbering_prefix_path(@project, prefix),
                  :class => 'icon icon-edit' %></td>
+         <td><%=delete_link project_numbering_prefix_path(@project, prefix) %></td>
        </tr>
      <% end %>
    </tbody>

行頭+で記載した行が削除リンクの追加です。delete_linkはURLを引数にとり、deleteアクションへのリンクのrouteパスを指定しています。delete_linkメソッドはHTTP DELETEメソッドを使用するのでmethodの指定は不要です。また、アイコンとメッセージを自動で付けてくれます。

ルート設定でDELETEに対応するのがルート名project_numbering_prefixです。HTTPメソッドがGETかPUTかDELETEかで対応付くコントローラのアクションが異なります。ルート設定からルート名project_numbering_prefixを抜粋したものを次に示します。

project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#show
                         PUT   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#update
                       DELETE  /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#destroy
delete_linkではなくlink_toを使う場合

編集のリンクと同様link_toで削除を実現することもできます。

<%= link_to l(:label_delete),
            {:action => 'destroy', :project_id => @project, :id => prefix.id},
            :confirm => l(:text_are_you_sure),
            :method => :delete,
            :class => 'icon icon-del' %>

ラベルの文字列、削除のアイコン、HTTPのメソッド種別(DELETE)、削除操作時の確認ダイアログの表示などを逐一指定する必要があります。

この例では、リンク先URLをルート名に基づくパスではなく、コントローラ(上述例では省略)、アクション、オプションをハッシュで指定する方法で書いています。ルート名で書くことは可能かと思います(単に未確認)。

表示確認

見栄えはともかく、編集と削除のリンクが表示されました。

コントローラの実装

editビューの中で採番系列データにアクセスするので、editメソッド呼び出し前に選択された採番系列データをインスタンス変数@numbering_prefixに保持します。

editビューのフォームをサブミットするとupdateアクションが呼ばれるので、updateメソッドを定義し実装します。

削除のアクション(destory)が呼ばれた時のdestoryメソッドを定義し実装します。

編集アクションに関するコントローラの実装

   4   menu_item :numbering
   5   before_filter :find_project, :authorize
 + 6   before_filter :find_numbering_prefix, :except => [:index, :new, :create]
 
 +38   def find_numbering_prefix
 +39     @numbering_prefix = NumberingPrefix.find_by_id(params[:id])
 +40     render_404 unless @numbering_prefix
 +41   end
  42 
  43 end

38-41行目:find_numbering_prefixメソッドを追加し、HTTPパラメータでidが渡されるので、idから採番系列データのインスタンスを取得し、インスタンス変数@numbering_prefixに格納します。

6行目:アクションメソッド実施前にこのメソッドが実行されるようbefore_filter定義を追加します。なお、idを扱わないアクション(一覧、新規、作成)は除外するようexceptで指定します。

updateメソッドの実装

 +26   def update
 +27     @numbering_prefix.attributes = params[:numbering_prefix]
 +28     if @numbering_prefix.save
 +29       flash[:notice] = l(:notice_successful_update)
 +30       redirect_to project_numbering_prefixes_path(@project)
 +31     end
 +32   end

26-27行目:updateメソッドを定義し、フォームで入力したフィールドをHTTPパラメータから取得します。

28行目:採番系列データをデータベースに保存します。
30行目:保存に成功したら、一覧表示アクションを呼びます。route設定でコントローラのindexアクションに対応付いているのは以下のルート名です。そこで、redirect_to でこのルート名に基づくパスを指定します。

project_numbering_prefixes  GET   /projects/:project_id/numbering_prefixes(.:format)  numbering_prefixes#index

削除アクションに関するコントローラの実装

 +38   def destroy
 +39     @numbering_prefix.destroy
 +40     redirect_to project_numbering_prefixes_path(@project)
 +41   end

38行目:destroyメソッドを定義
39行目:採番系列データを削除
40行目:一覧表示アクションを呼ぶ

editビューの実装

  1 <h2><%=h l(:label_numbering) %>#<%= @numbering_prefix.id %></h2>
  2 
  3 <%= labelled_form_for :numbering_prefix, @numbering_prefix,
  4     {:url => project_numbering_prefix_path(@project),
  5      :html => {:multipart => true, :id => 'numbering_prefix_form'}} do |p| %>
  6   <%= render :partial => 'numbering_prefixes/form', :locals => {:form => p} %>
  7   <%= p.submit l(:button_edit) %>
  8 <% end %>

4行目:フォームのサブミット時はコントローラのupdateが呼ばれるよう、ルート名project_numbering_prefixのパスを指定しています。サブミットはHTTPのPUTメソッドとなります。

以下にroutes設定からルート名がproject_numbering_prefixのURLと対応するコントローラ#アクションを抜粋します。

project_numbering_prefix GET   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#show
                         PUT   /projects/:project_id/numbering_prefixes/:id(.:format)  numbering_prefixes#update

PUTのときは、updateアクションが対応する設定なので、editビューのサブミットによりコントローラのupdateメソッドが呼ばれることになります。

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

採番系列の編集・削除を実装

- 編集・削除のリンクを一覧表示に追加
- 編集の実装
- 削除の実装

プラグイン作成(その6)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-06
redmine-2.3.0$ 

プラグイン作成(その7)

ここからいよいよ採番項目データ側の実装に入ります。

採番項目(NumberingItem)のコントローラをスケルトン生成し、採番系列一覧表示から採番項目一覧表示へのリンクを用意するところまでを(その7)で実装します。

採番項目コントローラ作成(1)

コントローラ・スケルトン作成

採番項目データ(numbering_item)用のコントローラのスケルトンを生成します。

redmine-2.3.0$ ruby script/rails generate redmine_plugin_controller redmine_numbering \
 numbering_items index new show edit
      create  plugins/redmine_numbering/app/controllers/numbering_items_controller.rb
      create  plugins/redmine_numbering/app/helpers/numbering_items_helper.rb
      create  plugins/redmine_numbering/test/functional/numbering_items_controller_test.rb
      create  plugins/redmine_numbering/app/views/numbering_items/index.html.erb
      create  plugins/redmine_numbering/app/views/numbering_items/new.html.erb
      create  plugins/redmine_numbering/app/views/numbering_items/show.html.erb
      create  plugins/redmine_numbering/app/views/numbering_items/edit.html.erb
redmine-2.3.0$

ルート設定追加

コントローラを作成するだけではアクセスできません。そこで、ルート設定に採番項目コントローラに対応するルールを追加します。

  1 Rails.application.routes.draw do
  2   resources :projects do
  3     resources :numbering_prefixes
+ 4     resources :numbering_items
  5   end
  6 end

4行目に追加しました。

numbering_itemsに関するルート設定を確認します。

redmine-2.3.0 rake routes|grep numbering_item
     project_numbering_items GET     /projects/:project_id/numbering_items(.:format)            numbering_items#index
                             POST    /projects/:project_id/numbering_items(.:format)            numbering_items#create
  new_project_numbering_item GET     /projects/:project_id/numbering_items/new(.:format)        numbering_items#new
 edit_project_numbering_item GET     /projects/:project_id/numbering_items/:id/edit(.:format)   numbering_items#edit
      project_numbering_item GET     /projects/:project_id/numbering_items/:id(.:format)        numbering_items#show
                             PUT     /projects/:project_id/numbering_items/:id(.:format)        numbering_items#update
                             DELETE  /projects/:project_id/numbering_items/:id(.:format)        numbering_items#destroy
redmine-2.3.0$

採番系列の一覧表示から採番項目一覧へのリンク

採番系列のindexビューに採番項目indexへのリンク作成

採番項目データの一覧表示(index)は、採番項目データのすべてを表示するのではなく、指定された採番系列に属するものだけを表示します。

そのためには、採番項目のindexアクションでは何らかの方法で採番系列を受け取る必要があります。

今回は、URLのリクエストパラメータに指定することとします。

      <% @numbering_prefixes.each do |prefix| %>
        <tr class="<%= cycle('odd', 'even') %>">
          <td><%= prefix.id %></td>
+         <td><%= link_to prefix.fixed,
+                 project_numbering_items_path(:numbering_prefix_id => prefix) \%></td>
          <td><%= prefix.assigned %></td>

link_toのURL指定でproject_numbering_items_pathを指定し、そのオプションにルート設定では指定しないキーと値の組を指定すると、URLのリクエストパラメータ(URL末尾の?name=valueと示されるもの)としてリンクを生成します。ルート設定ではパラメータとして:project_idが含まれますが、省略すると現在のプロジェクトが設定されるので、ここではリクエストパラメータだけ指定しています。

次に生成されたリンクのURLを示します。

http://localhost:3000/projects/myproj/numbering_items?numbering_prefix_id=2

採番項目コントローラ作成(2)

indexアクションが呼ばれた際の実装を追加します。

  1 class NumberingItemsController < ApplicationController
  2   unloadable
  3   before_filter :find_project
  4   before_filter :find_numbering_prefix
  5 
  6   def index
  7   end
  8 
  9   def new
 10   end
 11 
 12   def show
 13   end
 14 
 15   def edit
 16   end
 17 
 18 private
 19 
 20   def find_project
 21     @project = Project.find(params[:project_id])
 22   end
 23 
 24   def find_numbering_prefix
 25     @numbering_prefix = NumberingPrefix.find(params[:numbering_prefix_id])
 26   end
 27 
 28 end

採番項目の一覧表示では、検索の条件で採番系列のIDを必要とします。採番系列のIDはHTTPのリクエストパラメータで渡されてくるので、24-26行目で採番系列IDのインスタンスを取得します。4行目でbefore_filterで各アクションメソッドが実行されるまえに呼ばれるようにします。

プロジェクトIDについても同様に20-22行目でインスタンスを取得します。

採番項目のindexビュー

ここでは実験コードとして、採番項目コントローラのindexメソッドでセットしたインスタンス変数@projectと@numbering_prefixを表示する実装を記述します。実際の処理は、モデルを生成してからとなります。

  1 <h2>NumberingItemsController#index</h2>
  2 <p><%= "project_id = #{@project.id}" %></p>
  3 <p><%= "numbering_prefix_id = #{@numbering_prefix.id}" %></p>
 

動作確認

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

採番項目コントローラの生成
    
動作確認用のindexアクションとビューの実装

プラグイン作成(その7)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-07
redmine-2.3.0$ 

プラグイン作成(その8)

採番項目データを作成し、採番項目ビューのindex(一覧表示)を実装します。

採番項目モデル作成

データ構成

次の表は、採番項目データの要素と属性名・型を対応づけたものです。採番は採番系列単位で連番になります。そして採番された各番号ごとに説明を持ちます。

次の表は、採番した番号とその内容を保持するテーブルです。

採番記述テーブル(numbering_items)
 要素  名前  型  備考
固定文字列ID  numbering_prefix_id  整数(integer)   
連番  number  整数(integer)   
題名  subject  文字列(string)   
詳細  description  文字列(text)   

モデル・スケルトン作成

redmine-2.3.0$ ruby script/rails generate redmine_plugin_model redmine_numbering \
numbering_item numbering_prefix_id:integer number:integer subject:string \
description:text
      create  plugins/redmine_numbering/app/models/numbering_item.rb
      create  plugins/redmine_numbering/test/unit/numbering_item_test.rb
      create  plugins/redmine_numbering/db/migrate/002_create_numbering_items.rb
redmine-2.3.0$ 

プラグインのデータベース・マイグレーション実行

redmine-2.3.0$ bundle exec rake redmine:plugins:migrate
Migrating redmine_numbering (Redmine Numbering plugin)...
==  CreateNumberingItems: migrating ===========================================
-- create_table(:numbering_items)
   -> 0.0343s
==  CreateNumberingItems: migrated (0.0344s) ==================================

redmine-2.3.0$ 

採番項目の実装

一覧表示(index)

採番項目コントローラのindexアクションを実装します。

  1 class NumberingItemsController < ApplicationController
  2   unloadable
  3   before_filter :find_project
+ 4   before_filter :find_numbering_prefix, :authorize
  5 
  6   def index
+ 7     @numbering_items = NumberingItem.find(
+ 8        :all, :conditions => ["numbering_prefix_id = #{@numbering_prefix.id}"])
  9   end

  :(中略)
 
22   def find_project
 23     @project = Project.find(params[:project_id])
+24   rescue ActiveRecord::RecordNotFound
+25     render_404
 26   end
 27 
 28   def find_numbering_prefix
 29     @numbering_prefix = NumberingPrefix.find(params[:numbering_prefix_id])
+30   rescue ActiveRecord::RecordNotFound
+31     render_404
 32   end

行頭の+の箇所を追記しました。4行目はプロジェクトに権限を持つユーザーのみ利用可能とする制限を付けました。(採番系列コントローラと同様)
7,8行目は、一覧表示のときに渡された採番系列IDに対応する採番項目を検索しています。
24,25および30,31はエラー処理を追加しました(採番系列コントローラと同様)。

採番項目ビューのindexを実装します。

  1 <div class="contextual">
  2   <%= link_to_if_authorized(l(:label_numbering_items_new),
  3       {:action => 'new', :project_id => @project,
  4        :numbering_prefix_id => @numbering_prefix},
  5       :class => 'icon icon-add') %>
  6 </div>
  7 
  8 <h2><%=h l(:label_numbering) %></h2>
  9 
 10 <% if @numbering_items.blank? %>
 11   <p class="nodata"><%= l(:label_no_data) %></p>
 12 <% else %>
 13   <table class="list">
 14     <thead>
 15       <tr>
 16         <th>#</th>
 17         <th><%=h l(:field_subject) %></th>
 18       </tr>
 19     </thead>
 20     <tbody>
 21       <% @numbering_items.each do |item| %>
 22         <tr class="<%= cycle('odd', 'even') %>">
 23           <td><%= item.id %></td>
 24           <td><%= link_to(item.subject,
 25                           project_numbering_item_path(:id => item,
 26                           :numbering_prefix_id => @numbering_prefix)) %></td>
 27         </tr>
 28       <% end %>
 29     </tbody>
 30   </table>
 31 <% end %>

1-6行目は、権限があるユーザーの場合、採番項目の新規作成リンクを表示するものです。
全体は、採番系列ビューの一覧表示とほぼ類似しています。

ここで動作確認すると、採番項目の新規作成リンクが表示されません。権限の設定でまだ採番項目について定義していないからです。

  9   project_module :numbering do
 10     permission :view_numbering_prefix, :numbering_prefixes => [:index]
 11     permission :manage_numbering_prefix,
 12         :numbering_prefixes => [:new, :edit, :create, :update, :destroy],
 13         :require => :member
+14     permission :view_numbering_item, :numbering_items => [:index, :show]
+15     permission :manage_numbering_item,
+16         :numbering_items => [:new, :edit, :create, :update, :destroy, :preview],
+17         :require => :member
 18   end

行頭の+箇所が追加分です。コントローラに管理権限を定義しています。この定義をしておかないと、コントローラ(とその先のビュー)で権限を必要とする記述をしても権限なしと扱われます。

  1 ja:
  2   label_numbering: 採番
  3   project_module_numbering: 採番プラグイン
  4   permission_view_numbering_prefix: 採番系列の閲覧
  5   permission_manage_numbering_prefix: 採番系列の管理
+ 6   permission_view_numbering_item: 採番項目の閲覧
+ 7   permission_manage_numbering_item: 採番項目の管理
  8   label_numbering_prefixes_new: 新規採番系列
+ 9   label_numbering_items_new: 新規採番
 10 
 11   field_fixed: 固定文字列
 12   field_assigned: 採番済み番号
+13   field_subject: 題名

6,7行目は、権限レポート([管理]>[ロールと権限]>[権限レポート])で表示される権限名のメッセージです。
9行目は、indexビューで右上に表示する新規作成のリンクのラベルメッセージです。

サーバーを再起動し、権限レポートを開き、採番プラグインを表示した画面を次に示します。

デフォルトではすべて権限なしなので、必要な権限を追加します。

[保存]ボタンを押すのをわすれないように。つい忘れて「あれ、チェック付けたのに表示されない」といらぬデバッグに入り込むと無駄な時間を費やしてしまいます。

テストデータの作成

採番系列の時と同様に、テスト用データをデータベースに入れます。

redmine-2.3.0$ ruby script/rails console
Loading development environment (Rails 3.2.13)
irb(main):001:0> NumberingPrefix.find(:all)
  NumberingPrefix Load (0.3ms)  SELECT "numbering_prefixes".* FROM "numbering_prefixes" 
=> [#<NumberingPrefix id: 1, project_id: 1, fixed: "DOG-ONE-", assigned: 0, subject: 
"SERIES ONE">]
irb(main):002:0> item = NumberingItem.new()
=> #<NumberingItem id: nil, numbering_prefix_id: nil, number: nil, subject: nil, description: nil>
irb(main):003:0> item.numbering_prefix_id = 1
=> 0
irb(main):004:0> item.number = 1
=> 1
irb(main):005:0> item.subject = 'Item number 1'
=> "Item number 1"
irb(main):006:0> item.description = 'This is a first assigned number item'
=> "This is a first assigned number item"
irb(main):007:0> item.save()
   (0.2ms)  begin transaction
  SQL (10.9ms)  INSERT INTO "numbering_items" ("description", "number", "numbering_prefix_id", "subject") VALUES (?, ?, ?, ?)  [["description", "This is a first assigned number item"], ["number", 1], ["numbering_prefix_id", 0], ["subject", "Item number 1"]]
   (115.6ms)  commit transaction
=> true
irb(main):008:0> 

動作確認

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

採番項目データ作成と一覧表示の実装

プラグイン作成(その8)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-08
redmine-2.3.0$ 

プラグイン作成(その9)

採番項目ビュー/コントローラの残りのアクションを実装します。

新規作成

新規作成は、newアクション→newビューでフォーム入力とサブミット→createアクション の流れで行われます。実装方法は、採番系列コントローラ(numbering_prefixes_controller.rb)とほぼ同様です。

コントローラにnewとcreateアクションを実装

 12   def new
 13     logger.debug("[DEBUG]NumberingItemsController#new")
 14     @numbering_item = NumberingItem.new()
 15   end
 16 
 17   def create
 18     logger.debug("[DEBUG]NumberingItemsController#create")
 19     numbering_item = NumberingItem.new(params[:numbering_item])
 20     numbering_item.numbering_prefix_id = @numbering_prefix.id
 21     @numbering_prefix.assigned = @numbering_prefix.assigned + 1
 22     numbering_item.number = @numbering_prefix.assigned
 23 
 24     numbering_item.save() and @numbering_prefix.save()
 25     flash[:notice] = l(:notice_successful_create)
 26     redirect_to project_numbering_item_path(:id => numbering_item, :numbering_prefix_id = @numbering_prefix)
 27   rescue ActiveRecord::StaleObjectError
 28     flash.now[:error] = l(:notce_locking_conflict)
 29   end

13,18行目はログ出力文なので記述不要です。

newアクションでは空のインスタンスを作ってnewのビューに渡します(14行目)。

createアクションでは、フォームに入力された内容をもとにインスタンスを生成(19行目)、フォームで入力しない属性に値を補って(20-22行目)からデータを保存し、詳細表示アクションを呼びます(26行目)。

newビューの実装

  1 <%= labelled_form_for :numbering_item, @numbering_item,
  2     :url => project_numbering_items_path(
  3  :numbering_prefix_id => @numbering_prefix.id) do |f| %>
  4   <h2><%= "固定文字列: #{@numbering_prefix.fixed}" %></h2>
  5   <%= render :partial => 'numbering_items/form', :locals => {:form => f} %>
  6   <%= f.submit l(:button_create) %>
  7 <% end %>
  1 <%= error_messages_for 'numbering_item' %>
  2 <div class="box">
  3   <p><%= form.text_field :subject, :size => 100, :required => true %></p>
  4   <p><%= form.text_field :description, :size => 100, :required => false %></p>
  5 </div>

詳細表示

コントローラへの実装追加

詳細表示用に、リンクURL中の:idからnumbering_itemインスタンスを取得するprivateメソッドfind_numbering_itemを定義し、:idをURL中に含むアクションメソッド(edit, show, update, destroy)を指定したbefore_filterを記述します。

  before_filter :find_numbering_item, :except => [:index, :new, :create, :preview]
    :
  def find_numbering_item
    @numbering_item = NumberingItem.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end

showビューの実装

  1 <div class="contextual">
  2 <%= link_to_if_authorized(l(:button_edit),
  3     {:action => 'edit', :project_id => @project, :id => @numbering_item.id,
  4      :numbering_prefix_id => @numbering_prefix.id},
  5     :class => 'icon icon-edit') %>
  6 <%= link_to_if_authorized(l(:button_delete),
  7     {:action => 'destroy', :project_id => @project, :id => @numbering_item.id,
  8      :numbering_prefix_id => @numbering_prefix.id},
  9     :comfirm => l(:text_are_you_sure), :method => :delete,
 10     :class => 'icon icon-del') %>
 11 </div>
 12 
 13 <h2><%=h l(:label_numbering_item) %>#<%= @numbering_item.id %></h2>
 14 
 15 <div class="issue">
 16 
 17   <div>
 18     <h3><%= @numbering_prefix.fixed %>
 19       <%= sprintf('%05d', @numbering_item.number) %></h3>
 20   </div>
 21 
 22   <div class="subject">
 23     <h3><%= @numbering_item.subject %></h3>
 24   </div>
 25 
 26 <% unless @numbering_item.description.blank? %>
 27   <p><strong><%=l(:field_description) %></strong></p>
 28   <div class="wiki">
 29     <%= textilizable @numbering_item.description %>
 30   </div>
 31 <% end %>
 32 
 33 </div>
 

1-11行目は右上に編集および削除リンクを表示します。削除時は削除後に一覧表示画面に遷移させたいので、一覧表示画面に必要なリクエストパラメータnumbering_prefix_idを付加しています。編集時は編集画面の表示に採番系列の固定文字列を使うかもしれないので、同じくリクエストパラメータnumbering_prefix_idを付加しています。

18-19行目は、採番系列の固定文字列と採番項目の一連番号(5桁でゼロサプレスなし)を結合した「採番」を表示します。

削除

コントローラへの実装追加(destroyメソッド)

def destroy
  @numbering_item.destroy
  redirect_to project_numbering_items_path(:numbering_prefix_id => @numbering_prefix)
end

インスタンスを削除後、一覧表示に移ります。

編集

コントローラへの実装追加(updateメソッド)

  4   before_filter :find_numbering_prefix, :authorize, :except => :update
        (中略)
 40   def update
 41     logger.debug("[DEBUG]NumberingItemsController#update")
 42     @numbering_item.attributes = params[:numbering_item]
 43     if @numbering_item.save
 44       flash[:notice] = l(:notice_successful_update)
 45       redirect_to project_numbering_item_path(@project, @numbering_item.id,
 46           :numbering_prefix_id => @numbering_item.numbering_prefix_id)
 47     end
 48   rescue ActiveRecord::StaleObjectError
 49     flash.now[:error] = l(:notice_locking_conflict)
 50   end

updateアクションは、editビューのフォームのサブミットから呼ばれますが、フォームのサブミット時にHTTPリクエストパラメータとして numbering_prefix_idを指定する方法が分からなかったので、before_filterでfind_numbering_prefixの適用除外にupdateメソッドを指定し(4行目)、@numbering_itemの属性からnumbering_prefix_idを取得しています。

ビューへの実装追加(edit.html.erb)

  1 <h2><%=h l(:label_numbering_item) %>#<%= @numbering_item.id %></h2>
  2 
  3 <%= labelled_form_for :numbering_item, @numbering_item,
  4     :url => project_numbering_item_path(@project),
  5     :html => {:multipart => true, :id => 'numbering_item_form'} do |f| %>
  6   <%= render :partial => 'numbering_items/form', :locals => {:form => f} %>
  7   <%= f.submit l(:button_edit) %>
  8 <% end %>

動作確認

ざっと一通り操作してみたところ、おおむね機能はしています。表示(見栄え)、操作性にはところどころ難点があるもののまずは区切りとしてよさそうです。

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

 採番項目の残りのアクションを実装

プラグイン作成(その9)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-09
redmine-2.3.0$ 

プラグイン作成(その10)

見栄えの改修に取り組んでみます。

採番系列ビュー

一覧表示画面の課題

とりあえず気になる箇所は、見出し行の欠落箇所と、数値が左寄せになっている点です。

1. 見出し欠如の対応

diff --git a/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb b/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
index 296b4c3..b6ef329 100644
--- a/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
+++ b/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
@@ -16,6 +16,7 @@
        <th><%=h l(:field_fixed) %></th>
        <th><%=h l(:field_assigned) %></th>
        <th><%=h l(:field_subject) %></th>
+        <th colspan="2"><%=h l(:field_manage) %></th>
       </tr>
     </thead>
     <tbody>
diff --git a/plugins/redmine_numbering/config/locales/en.yml b/plugins/redmine_numbering/config/locales/en.yml
index f1310c8..73c90be 100644
--- a/plugins/redmine_numbering/config/locales/en.yml
+++ b/plugins/redmine_numbering/config/locales/en.yml
@@ -11,4 +11,5 @@ en:
 
   field_fixed: Fixed text
   field_assigned: Latest number assigned
   field_subject: Subject
+  field_manage: Management
diff --git a/plugins/redmine_numbering/config/locales/ja.yml b/plugins/redmine_numbering/config/locales/ja.yml
index be5943c..696bb40 100644
--- a/plugins/redmine_numbering/config/locales/ja.yml
+++ b/plugins/redmine_numbering/config/locales/ja.yml
@@ -11,4 +11,5 @@ ja:
 
   field_fixed: 固定文字列
   field_assigned: 採番済み番号
   field_subject: 題名
+  field_manage: 管理

2. 数値が左寄せ対応

最初右寄せにしたのですが、隣の題名とくっつきすぎて今一なので中央寄せにしてみました。これなら左寄せのままでもいいかもしれません。

diff --git a/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb b/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
index b6ef329..f6ec2e2 100644
--- a/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
+++ b/plugins/redmine_numbering/app/views/numbering_prefixes/index.html.erb
@@ -25,7 +25,7 @@
   <td><%= prefix.id %></td>
   <td><%= link_to prefix.fixed,
           project_numbering_items_path(:numbering_prefix_id => prefix) %></td>
-  <td><%= prefix.assigned %></td>
+  <td align="center"><%= prefix.assigned %></td>
   <td><%= prefix.subject %></td>
   <td><%= link_to l(:button_edit),
           edit_project_numbering_prefix_path(@project, prefix),

1.および2.の処置後の画面

新規/編集画面の課題

入力フィールドが縦方向に揃っていない対応

diff --git a/plugins/redmine_numbering/app/views/numbering_prefixes/_form.html.erb b/plugins/redmine_numbering/app/views/numbering_prefixes/_form.html.erb
index a90bd11..fd8e6cd 100644
--- a/plugins/redmine_numbering/app/views/numbering_prefixes/_form.html.erb
+++ b/plugins/redmine_numbering/app/views/numbering_prefixes/_form.html.erb
@@ -1,5 +1,5 @@
 <%= error_messages_for 'numbering_prefix' %>
-<div class="box">
+<div class="box tabular">
   <p><%= form.text_field :fixed, :size => 100, :required => true %></p>
   <p><%= form.text_field :subject, :size => 100, :required => false %></p>
 </div>

処置結果の画面

採番項目ビュー

一覧表示画面の課題

1. 採番番号は一覧表示で見たいの対応

現在は、詳細表示で初めて「固定文字列-連番」を表示しているので、これを一覧表示ビューにも表示します。

diff --git a/plugins/redmine_numbering/app/views/numbering_items/index.html.erb b/plugins/redmine_numbering/app/views/numbering_items/index.html.erb
index 581af75..e2098e2 100644
--- a/plugins/redmine_numbering/app/views/numbering_items/index.html.erb
+++ b/plugins/redmine_numbering/app/views/numbering_items/index.html.erb
@@ -14,6 +14,7 @@
     <thead>
       <tr>
         <th>#</th>
+        <th><%=h l(:label_numbering) %></th>
         <th><%=h l(:field_subject) %></th>
       </tr>
     </thead>
@@ -21,6 +22,7 @@
       <% @numbering_items.each do |item| %>
         <tr class="<%= cycle('odd', 'even') %>">
           <td><%= item.id %></td>
+          <td><%= @numbering_prefix.fixed %><%= sprintf('%05d', item.number) %></td>
           <td><%= link_to(item.subject, 
                   project_numbering_item_path(:id => item, :numbering_prefix_id => @numbering_prefix)) %></td>
         </tr>

列を1つ増やし、そこに採番の固定文字列・連番(5桁ゼロサプレスなし)を表示しただけのものです。

1.の処置結果画面

2. 作成者情報の対応

これはモデルに新たな属性の追加を伴う(=データベース・マイグレーションが必要)ため、今後の対応とします。

詳細表示画面の課題

1. 各項目の記述が判然としない対応

「項目名 項目」のように表形式で表示します。基本<table>タグで表示しますが、そのままでは見栄えが今一です。<Redmineインストールディレクトリ>/public/application.css をさっと眺めて使えそうな定義を調べます。 

<div class="box">
<table class="attributes">
  <tbody>
    <tr>
      <th><%=h l(:label_numbering) %></th>
      <td><strong><%= @numbering_prefix.fixed %><%= sprintf('%05d', @numbering_item.number) %></strong></td>
    </tr>
    <tr>
      <th><%=h l(:field_subject) %></th><td><%= @numbering_item.subject %></td>
    </tr>
    <% unless @numbering_item.description.blank? %>
    <tr>
      <th><%=h l(:field_description) %></th><td><%= textilizable @numbering_item.description %></td>
    </tr>
    <% end %>
  </tbody>
</table>
</div>

div.boxを使うと、背景がグレーで囲われるので何となくこれを使いました。
table.attributesは、列と列とが適度に間隔をあけて表示されたので、これを使いました。

2. 一覧表示に移るリンクの対応

見出しの「採番項目#22」の採番項目の文字列部分をindexへのリンクとします。

<h2><%=link_to_if_authorized(l(:label_numbering_item),
       {:action => 'index', :project_id => @project,
        :numbering_prefix_id => @numbering_prefix.id}) %>#<%= @numbering_item.id %></h2>

1.および2.の対応結果の画面を次に示します。

コミット

ここまでの成果をgitリポジトリに登録します。

redmine-2.3.0$ git add .
redmine-2.3.0$ git commit

emacsエディタが立ち上がるので、コミット理由を記述します。

 採番項目の残りのアクションを実装

プラグイン作成(その10)はここで完了なので、タグを打っておきます。

redmine-2.3.0$ git tag milestone-10
redmine-2.3.0$ 

プラグイン作成(その11)

プラグイン作成(その12)

開発作業メモ

モデル

モデルの変更

開発の途上では、モデルの属性(データベースのカラム)を変更することが時折発生します。その際は、いったん次のコマンドでプラグインが使用するデータベースのテーブルを破棄します。

redmine-2.3.0$ bundle exec rake redmine:plugins:migrate NAME=redmine_numbering \
VERSION=0
(in /home/torutk/develop/redmine/redmine-2.3.0)
Migrating redmine_numbering (Redmine Numbering plugin)...
==  CreateNumberingItems: reverting ===========================================
-- drop_table("numbering_items")
   -> 0.0014s
==  CreateNumberingItems: reverted (0.0015s) ==================================

==  CreateNumberingPrefixes: reverting ========================================
-- drop_table("numbering_prefixes")
   -> 0.0011s
==  CreateNumberingPrefixes: reverted (0.0012s) ===============================

データベース上から2つのテーブル(numbering_itemsとnumbering_prefixes)が削除されました。

次に、変更するモデルのスケルトンを生成し直します(または既存のモデルの実装を変更します)。

redmine-2.3.0$ ruby script/rails generate redmine_plugin_model redmine_numbering \
 numbering_prefix project_id:integer fixed:string assigned:integer subject:string
    conflict  plugins/redmine_numbering/app/models/numbering_prefix.rb
Overwrite /home/toru/redmine/1.redmine-2.3.0/plugins/redmine_numbering/app/models/
numbering_prefix.rb? (enter "h" for help) [Ynaqdh] Y
       force  plugins/redmine_numbering/app/models/numbering_prefix.rb
   identical  plugins/redmine_numbering/test/unit/numbering_prefix_test.rb
      create  plugins/redmine_numbering/db/migrate/007_create_numbering_prefixes.rb
redmine-2.3.0$

ここでは、既存の(変更前の)モデルクラスを上書きするため[Y]を入力しています。migrate用のデータベース作成スクリプトは、既存のスクリプトの番号より1つ大きい番号(上述の例では007)で生成されます。古いスクリプトは削除します。

データベース上に新しいテーブルを作成するため、migrateを実行します。

redmine-2.3.0$ bundle exec rake redmine:plugins:migrate
Migrating redmine_numbering (Redmine Numbering plugin)...
==  CreateNumberingItems: migrating ===========================================
-- create_table(:numbering_items)
   -> 0.0889s
==  CreateNumberingItems: migrated (0.0891s) ==================================

==  CreateNumberingPrefixes: migrating ========================================
-- create_table(:numbering_prefixes)
   -> 0.0024s
==  CreateNumberingPrefixes: migrated (0.0025s) ===============================

データベース

現在のデータベースを破棄し、再度スキーマを構築したい

データベースの破棄をrakeコマンドで実行します。

redmine-2.3.0$ bundle exec rake db:drop
redmine-2.3.0$ bundle exec rake db:migrate
   :
redmine-2.3.0$ bundle exec rake redmine:plugins:migrate
  :

SQLite3操作

SQLite3対話環境での操作

対話環境に入る
redmine-2.3.0$ sqlite3 db/redmine.db
sqlite>
テーブル一覧
sqlite> .table
attachments                          news                               
auth_sources                         numbering_items                    
  :
slite>
対話環境を抜ける
sqlite> .q
redmine-2.3.0$ 
ヘルプを見る
sqlite> .h
.backup ?DB? FILE      Backup DB (default "main") to FILE
.bail ON|OFF           Stop after hitting an error.  Default OFF
  :
sqlite>
テーブルの定義(スキーマ)を調べる
sqlite> .schema numbering_items
CREATE TABLE "numbering_items" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 "project_id" integer, "number" integer, "subject" varchar(255), "description" text);
sqlite> 
現在のパラメータを調べる
sqlite> .show
     echo: off
  explain: off
  headers: off
     mode: list
nullvalue: ""
   output: stdout
separator: "|"
    width:
sqlite> 
sqlite> 
sqlite> 

ビュー

link_to_if_authorized

権限がないユーザー(あるいは非ログイン)では表示されないリンクですが、権限があるユーザーにもかかわらず表示されないことがありました。このメソッドは、プロジェクトの権限を見ているため

ことで表示されず、四苦八苦しました。

デバッグ

putsデバッグ

developmentモード・WEBRick限定なデバッグ(稚拙なデバッグ技術)としてputs(p)メソッドを使用した方法があります。

puts "[DEBUG]@prefix=#{@prefix.attributes}"
rubyでは、文字列中に変数の評価を展開するための#{変数名}という記法があります。

WEBRickのコンソール出力

[DEBUG]@prefix={"id"=>4, "project_id"=>1, "fixed"=>"ALFA-試験-", "assigned"=>nil, 
"subject"=>"試験関係での採番"}

ロギング

Redmine(Rails)にはロギングが定義されているので、putsよりはログを使う方がよいです。

 logger.debug("[DEBUG]NumberingPrefixesController#find_numbering_prefix")

リンク

Webアプリケーションは、とにかくページ遷移が面倒です。本ページのプラグインづくりをしていて一番はまったことは、ページリンクでエラーになることです。呼ばれるはずのアクションメソッドが呼ばれない、リンクの指定(主にビュー側の記述)がエラーとなる、パラメータが渡ってこないのでnilでエラーになる、など様々です。原因がなかなか判明しなくて、コントローラの全メソッドにロギングを埋め込んでやっとわかったということもありました。

ということで、リンクをしっかり押さえるとRails力が上がると感じました。