タスクの分解
管理画面には一般のユーザーには出来ない「他のユーザーや、ユーザーが作成したコンテンツ情報を編集、削除」できるような機能があります。
どのユーザーもその機能を使えるのではなく、専用のログイン画面を設けて管理者アカウントだけが機能を利用できるように制限する必要があります。
ユーザーに「権限」というカラムを新たに追加して「一般」ならログイン不可、「管理者」ならログインして管理画面を利用できるようにします。
この時点で「管理者用のログイン画面作成」や「ユーザーに権限判定用のカラム追加」と言ったタスクが見えてきたと思います。
また管理者用のアカウントは rails console などで毎回作成するのは面倒なので、Seedデータを用意しておきます。
それらに関連して「一般ユーザーがログインした場合はリダイレクトさせる」や「ログイン画面の見た目を整える」と言ったタスクも必要です。
ここでは、AdminLTE という公開ライブラリを利用して、管理画面用のCSSやJavascriptを適用します。
ライブラリはcloneしてローカルにCSSなどのファイルを配置しても適用できますが、今回は yarn というパッケージマネージャーで取得する形式を取ります。
これまでの内容を整理すると、以下のようなタスクがイメージできると思います。
管理者用のログイン画面作成
・一般ユーザーがログインした場合はリダイレクトさせる
ユーザーに権限判定用のカラム追加
・管理者アカウント登録用のSeed作成
ログイン画面の見た目を整える
・AdminLTEをyarnで取得
・取得したCSSなどのファイルを適用
パッケージ管理ツール
rubyのライブラリ管理を行うGemやMacOSに導入するライブラリ管理ツールであるHomebrew以外にも、様々なパッケージ管理ツールが存在します。
特にフロントエンド関連のパッケージ管理ツールは様々混在していますが、大枠 Bower → npm → yarn と言った流れを経ているのではないでしょうか。
今回は管理画面用のテンプレートとして有名なAdminLTE3をRailsのアプリケーションに導入します。
またAdminLTEのデフォルトテンプレートを使用したい場合、インストールしたファイルの中にあるstarter.html内に読み込むべきCSSファイル・JSファイルが記載されています。
これを参照しながら admin.scss ・ admin.js 2つのマニュフェストファイルを作成し、適切な記述をしていきます。
パッケージ管理ツールを使う理由
AdminLTEはadminlte-railsのようなgemから取得することもできます。
しかし確認するとメンテナンスが追いついておらず、取得できるAdminLTEのバージョンも古いことが分かります。
パッケージ管理ツールが使用できる場合、メンテナンスがされていないgemからの取得は、以下の理由から使用を控えましょう。
.メンテナンスが追いついていない分バージョンが古い
.何か問題などがあった際に、修正パッチなどが当てられる対応が遅い
admin.scss ・ admin.js 2つのマニュフェストファイルへの記述
「yarnでインストールしたファイルはどこにある?」
まずは参考にするべき雛形のファイルを探しましょう。
yarnでインストールしたので、ルートディレクトリ直下の、 node_modules/ 以下を確認しましょう。
AdminLTEをインストールしていれば、 node_modules/admin-lte/ というディレクトリがあると思います。
この中から必要なファイルをマニフェストファイルで読み込みたいですね。
ちなみに先述のstarter.htmlというサンプルのHTMLファイルもこの中にあります。
「何を読み込む?」
starter.htmlがAdminLTEを使った管理画面の完成図となりますので、このHTMLを参考にしてみましょう。
WEB上でも下記のリンクで管理画面のサイトイメージが公開されています。
https://adminlte.io/themes/v3/starter.html
この「サンプルのHTMLで使用されているJavaScriptとCSSと同じものを適用させれば、自分で作成したAdminLTEを使った管理画面もサンプルと同じような挙動をしてくれる」ということが予想できます。
サンプルで使用しているJavaScriptとCSSは、ブラウザの検証ツールからbodyタグとheadタグを開いて実際に確認してみましょう。
「どうやって読み込む?」
読み込むファイルの指定の仕方を説明します。
たとえば以下のように読み込みます。
//= require admin-lte/dist/js/adminlte.min
@import 'admin-lte/dist/css/adminlte.min.css';
どうやって admin-lte/dist/js/adminlte.min を探しているのか? node_modules/admin-lte/dist/js/adminlte.min ではないのか?
それには「 Rails.application.config.assets.paths 」という設定が関係しています。
Sprocketsはこの設定からファイルを検索します。
設定を確認したいので、Railsコンソールで Rails.application.config.assets.paths と打ってみましょう。
たくさん出力されたパスの内容の中に、node_modulesの設定もあるはずです。
これらの出力されたパスの先頭から順に検索していきます。
node_modules/の下に admin-lte/dist/js/adminlte.min というファイルがあるかといった具合にです。
なので、node_modules/admin-lte/dist/js/adminlte.min ではなく、admin-lte/dist/js/adminlte.min と書きましょう。
ちなみに、
config/initializers/assets.rbの
Rails.application.config.assets.paths << Rails.root.join('node_modules')
この部分で Rails.application.config.assets.paths に node_modules というパスを追加しています。
「//= require_tree . していると問題がある?」
app/assets/javascripts/application.js で、「 //= require_tree . 」していませんか?
require_tree には以下の特徴があります。
指定したディレクトリ以下のすべてのJSファイルを読み込む。
「require_tree . 」の場合は同じ階層以下すべてのJSファイルを読み込むということですね。
読み込む順序は指定できない。
そのため、利用しているJSファイルが特定の読み込み順に依存している場合に不都合があります。
今回は app/assets/javascripts/application.js と同じ階層に、 app/assets/javascripts/admin.js を作っていますね。
app/assets/javascripts/application.js で require_tree . してしまうと、
app/assets/javascripts/application.js で app/assets/javascripts/admin.js を読み込んでしまいます。
つまり管理画面でしか使わないはずのファイルまで、 app/assets/javascripts/application.js で読み込んでしまいます。
それを避けるために require_tree . は使わずに、必要なファイルを個別に必要な順序で読み込みましょう。
なぜレイアウトファイルを分けるのか?
現状レイアウトファイルは以下の2つがあります。
.views/layouts/admin_login.html.erb
.views/admin/layouts/application.html.erb
大きな違いは、
.stylesheet_link_tag で読み込むcssが違う。
.javascript_include_tag で読み込むjsが違う。
.body 内が違う。
一般の画面には必要だけど、管理画面には必要のないものやその逆もありますよね。
その場合は今回のようにレイアウトファイルを分けてみましょう!
enumについて
enumとは、列挙型とも呼ばれ、一連の整数値に対して複数の変数名をつけられる仕組みを指します。
例えばUserモデルに status というカラムが存在したとしましょう。
この status カラムで判定したいのは「承認されているか、非承認か」ということで、値としてinteger型の0と1が保存されるものとします。
ここで、Userモデルに下記のような定義を施したとします。
enum status: {
unpermitted: 0,
permitted: 1
}
これで status カラムをenumとして定義することができました。
enumを定義して、具体的にどのようなことが可能になるのでしょうか?
例えば、ユーザーが承認されているかどうかを判定したい場合、
enum未定義の場合
(実行したい処理) if @user.status == 1
と言った記述を行わねばならず、記述が長い上に、「1」という数字が何を表しているかわからないコードになってしまいます。
一方で、enumが定義されていると、
enum定義済の場合
(実行したい処理) if @user.permitted?
と言った具合に、コードを読んだ段階でどのような処理を施しているのかの予測が立ちやすく、且つ簡潔なコードで記述することができます。
enumはこの他にも様々なメソッドが用意されていますので、積極的に活用していけるようにしましょう。
実際の手順