Railsの基本(+α)1
そもそも、一度しっかりRailsの基本についてメモしておこうと考え書き始めたのですが、やっているうちに、remote: trueを少し調べ、 興味がVuejsに移っていき、Axiosでajaxする方向で検討したり、だいぶ基本から逸脱することも調べながら書いているうちに(迷走したというのが正しいかもですw)、かなり文章の量も増えてしまったので、いつくつかに分割することになってしまいました。
なので、全体としては
- Rails5.1でのWebpackの使い方
- Vuejsの基本
- vee-validaeの基本(特にajaxによるユニーク確認ができるように)
- deviseの基本
- Blogの登録や閲覧部分、およびプラスα(markdownの組み込みなど)
あたりが、そもそものテーマなんですが、今回は3までとなります。
確認環境
筆者の環境は以下のようです。
- MacBook Pro (15-inch, Late 2016)
- MacOS Sierra 10.12.6
- ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin16]
- Rails 5.1.4
- mysql Ver 14.14 Distrib 5.7.18, for osx10.12 (x86_64) using EditLine wrapper
- node v8.1.0
前提
上記の環境でRailsはバージョン5.1、rubyは少なくとも2.3以上、mysqlは5.6以上とかは必要かもです。rails5.1からwebpackを利用できるようになったので、そのためnodeが、さらに、yarnもあると便利なので、これもインストールしておきます。
なので、あまりサーバ系のインストールとかに慣れていない方は、http://qiita.com/etsracas/items/53f1230e13cf5f9c40baで、brewとrbenv(rubyを色々なバージョンでインストールできるツール)とrubyをインストールします。
また、https://qiita.com/tabolog/items/da18143e70f40e356b5dを参考に、rbenv同様、nodebrewを利用してnodeをインストールし、yarnはターミナル上でnpm install -g yarn
を実行してインストールしてください。
さらに、次のように、先ほどインストールしたbrewを使ってmysqlをインストールし、そして最後に以下のようにして、mysqlのユーザを1人作成しておきます。
1
2
3
brew install mysql
brew tap homebrew/services
brew services start mysql
とこれで、mysqlがインストールされ、自動起動するようになっているはず。以下で確認。
1
2
3
4
5
6
brew services list
Name Status User Plist
...
mysql started chikkun /Users/chikkun/Library/LaunchAgents/homebrew.mxcl.mysql.plist
...
という行が出て入れば大丈夫です。
次にrootのパスワードを設定(パスワードまで真似しないでね)。
1
mysqladmin -u root password 'yourpassword'
次のようなコマンドを叩いて、mysqlクライアントを立ち上げます。パスワードが聞かれるので、上記のパスワードを入力してください。
1
mysql -u root -p
mysql上で
1
2
3
CREATE USER hoge IDENTIFIED BY 'hogechan';
GRANT ALL PRIVILEGES ON *.* TO 'hoge'@'localhost' IDENTIFIED BY 'hogechan' WITH GRANT OPTION;
FLUSH PRIVILEGES;
«««< HEAD を実行して、hogeというユーザ(もちろん名前やパスワードは任意)を作成します。 ======= を実行して、hogeというユーザ(もちろんユーザやパスワードは任意)を作成します。
b6d4789738b078c9e4e2f9298c0c62d2b83d6342
さて、開発開始
Railsの入門というとまずはScaffoldから、というのが多いのですが、これだと今ひとつ何をやるべきことなのか、ということがブラックボックスになってしまい「う〜ん・・・?」という中途半端な理解で終わってしまうことが多い気がします。
そこで、何かしらフォームを使ってのデータの登録を行う簡単なアプリを、Scaffold機能を(その他コマンドも)使わず行ってみようと思います。
しかも、若干基本よりはみ出して、実用性があることまで持って行きたいと考えております。
どんなアプリを作成するか
«««< HEAD usersテーブルを作成し、そこに入るデータは管理者のユーザ名、パスワードなどを登録します。そして、この管理者に登録された人でないと、その管理者自身を登録できないという仕様とします。
また、この管理者は簡易ブログをタグとカテゴリを伴って登録することができ、そのブログは不特定多数の人が見ることができる、極簡単なブログシステムです。一応プロジェクト名をbanshoとします(森羅万象を記す、的な安易な名前ですw)。
usersテーブルを作成し、そこに入るデータは管理者のユーザ名、パスワードなどを登録します。そして、この管理者に登録された人でないと、その管理者自身を登録できないという仕様とします(次回deviseで実装)。
また、この管理者は簡易ブログをタグとカテゴリを伴って登録することができ、そのブログは不特定多数の人が見ることができる、そんな極簡単なブログシステムです。一応プロジェクト名をshinraとします(森羅万象を記す、的な安易な名前ですw)。
b6d4789738b078c9e4e2f9298c0c62d2b83d6342
どんな作業をするかを外観
- Railsのプロジェクトを作成、database.ymlの設定とかを変更
テーブルのmigrationファイル作成とmigrate
要するに、テーブル(およびフィールド)の作成です。あとでDeviseを入れ込むときに、usersテーブルがぶつかりますが、Deviseが結構賢く、よしなに扱ってくれるらしい(これは次回)。
Controller «««< HEAD コントローラの作成です。今回はAjaxでのアクセスがメインになるかもです。
indexを定義します。
コントローラの作成です。今回はAjaxでのアクセスがメインになるので、api的な使い方がメインになります。index等を定義します。
b6d4789738b078c9e4e2f9298c0c62d2b83d6342
Model
上記1.で作成したテーブルに対応したモデルの作成です。バリデーションはクライアント側で行うので、何もないようなModelになってしまいそうです。
View
最終的にはブラウザにhtmlとして出力される、Rail標準のテンプレートの作成です。今回は、クライアントでバリデーションするので、このテンプレートの中にJavaScriptでガリガリ書くことになるかもしれません。
ただ、始めのページで読み込まれた後は、VuejsでAjax通信させるので、index.html.erbのみ書きます。その分Javascriptの量がさらに増えそうですが・・・。
- routes どんなURL(とメソッド—POSTやGET)だったら、どのコントローラのどのメソッドを実行するかを定義したものです。例えば、http://sample.rails.jp/ にGETでアクセするとusers_controller.rbのindexというメソッドを実行する、などという記述をすることになります。
Railsプロジェクトの作成
まずは、ターミナルを立ち上げて、プロジェクトを作成する1つ上のディレクトリを作成します(mustじゃありません)。
1
2
3
cd ~
mkdir projects
cd projects
«««< HEAD 次にRailsプロジェクトを作成します。-dオプションでmysqlを指定して、作成します(mysqlがインストールできていない場合は、この指定をやめます)。
さらにwebpackというオプションも付けて、Javascript等はWebpackに管理させます。
- ※
- *rails new appli_name -d database_name* というように-dでデータベースの種類を指定すると、少し幸せになります。
- ※
- *rails new appli_name -d database_name --webpack* というようにwebpackを利用する設定にします。
1
2
3
rails new bansho -d mysql --webpack
.....
cd bansho
色々、標準出力が出てきますが、とりあえずスルーして、banshoというプロジェクトディレクトリにcdします。
このwebpackを使うと言うことで(cssはそのままで、jsのみ置き換えます)、
app/assets/javascripts/application.js
は削除して
app/javascript/pack/application.js <-これはすでにあるので、後で中身を書き換えます。
また、今回はajaxメインにしようとしているので、turbolinksは必要ないかと思い、Gemfileから次の行を削除します。
1
gem 'turbolinks', '~> 5'
そして、
app/views/layout/application.html.erb
1
2
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
がありますが、この左の方になるturbokinks関係を削除します。
1
2
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
さらに、ヘルパーメソッドのjavascript_include_tagを次のように変更します。
1
2
<%= stylesheet_pack_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
database.ymlの変更
======= 次にRailsプロジェクトを作成します。–database=mysqlオプションでmysqlを指定して、作成します(mysqlがインストールできていない場合は、この指定をやめます→するとsqliteが使われ、これはインストール等が必要ありませんので、単に試すには便利です)。
さらにwebpackというオプションも付けて、Javascript等はWebpackに管理させます。
- ※
rails new appli_name –database=database_name というように-dでデータベースの種類を指定すると、少し幸せになります。
- ※
rails new appli_name -d database_name –webpack というようにwebpackを利用する設定にします。
1
2
3
$ rails new shinra --webpack --skip-sprockets --skip-javascript --database=mysql
.....
cd shinra
また、スプロケットやrails-ujs(以前はjquery-ujs)等も使わないため、上記のような–skip-sprockets –skip-javascriptという2つのオプションも付けました。
色々、標準出力が出てきますが、とりあえずスルーして、shinraというプロジェクトディレクトリにcdします。
Vuejsのインストール
インストールと言っても、railsコマンドで一発です。ついでにAjaxで利用するAxiosもついでにインストールしておきます。
1
2
$ rails webpacker:install:vue
$ yarn add axios
これで終了w
ただ、一応この後、vuejsでHello worldをしてみます。しかも、実用性の高そうな単一ファイルコンポーネント(Single File Components)を試してみます。
/app/views/layout/application.html.erb等の修正
さすがに–skip-sprockets –skip-javascriptを指定しているので、/app/views/layout/application.html.erb内に見慣れたapplication.js部分もありません(何故かCSSだけはありますね)。わーお、すっきりしたぁ。
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<title>Wakaran</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all' %>
</head>
<body>
<%= yield %>
</body>
</html>
さて、これを次のような変更を加えます。
- CSS関係
- stylesheet_link_tagをstylesheet_pack_tagに変更。
- /app/assets/stylesheets/application.cssの拡張子を変えて、/app/javascript/pack/application.scssに移動させます。
中の
1 2
*= require_tree . *= require_self
となっている、イコールをとって、単なるコメントにします(もしくは、削除してしまう)。そして、次のものを書き込みます(暫定)。
1 2 3
p { color: red; }
- javascript関係
/app/javascript/pack/application.jsをworldwide.jsにリネームします。そして、もともとのを少しだけ変えて、次のようにします(in worldswide.jsを加えただけ)。
1
console.log('Hello World from Webpacker in worldwide.js');
- ※
- application.jsだと、何故かうまくいかない・・・。なぜだかは追えていません・汗。
/app/views/layout/application.html.erbに以下を加えます(CSSの上あたり)。
1
<%= javascript_pack_tag 'worldwide' %>
/app/controllers/welcome_controller.rb
コントローラが1つもないと確認のしようもないので、ランディングページ的な/app/controllers/welcome_controller.rbを作成します。
1
2
3
4
5
6
class WelcomeController < ApplicationController
def index
end
end
何もないindexメソッドしかありませんが、次のようなルールで一応javascriptの確認はできそうです。
- ※
controller内のrenderのルールは「app/views/コントローラ名/メソッド名.html.erb」を描画します。
つまり、/app/views/welcome/index.html.erbを次で作成します。
/app/views/welcome/index.html.erb
/app/views/welcome/index.html.erbというファイルを作成して、次のような内容を書き込みます。
1
2
3
4
5
6
7
<%= stylesheet_pack_tag 'welcome', media: 'all' %>
<div id="page-header" class="page-header">
<h4>トップページ</h4>
</div>
<p>Hello Rails 5.1</p>
<div id="MyAppRoot"></div>
<%= javascript_pack_tag 'welcome' %>
- 1行目はwelcome.cssを読み込め(下にあるindex.vueファイルから自動作成される)、というERBへの指示
- 2〜4行はただのhtml
- 6行目は、Vue.jsと紐付けるところ
- 7行目はwelcome.jsを読み込め(後で作成)、というERBへの指示
ここで上記3のところに、Hello Vue!を表示させます。
/app/javascript/packs/index.vue
/app/javascript/packs/index.vueが少し触れた単一ファイルコンポーネントです。簡単に言ってしまえば、templateやjavascriptやstyleをコンポーネント毎に1つのファイルにまとめて書いてしまえ、という感じです。実際には次のように書き込んでおきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data: () => {
return {
message: "Hello Vue!"
}
}
}
</script>
<style scoped>
p {
font-size: 10em;
text-align: center;
color: blue;
}
</style>
- 1〜5行目がこのコンポーネントのテンプレート
- 6〜14行目までは、この中で動くJavascriptのスクリプト
- 17〜22行目までがstyle
です。
3に関しては少々コメントしますと、<style scoped>
のようにscopedが書いてあると、この場合だとCSSのpがp[data-v-071215b2]というように属性が書き込まれ、上記1にあるtemplateの中の全てのタグに同じ属性が書き込まれ、<div data-v-071215b2="" id="MyAppRoot"><p data-v-071215b2="">Hello Vue!</p></div>
というようになります。
つまり、これによって、他のコンポーネントはもとより、別のアプリと関係ないスタイルへの影響を避ける、というものです(これは意外に重宝しそうですね)。
/app/javascript/packs/welcome.js
/app/javascript/packs/welcome.jsは、/app/views/welcome/index.html.erb内のコンポーネントと/app/javascript/packs/index.vueの単一ファイルコンポーネントをマッピングするものになります。内容は次のようです。
1
2
3
4
5
6
7
8
9
import Vue from 'vue'
import App from './index.vue';
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({ // eslint-disable-line no-new
el: '#MyAppRoot',
render: h => h(App)
})
console.log(app)
})
上記の
1
document.addEventListener('DOMContentLoaded', () => {
の部分は、jQueryでは
1
$(document).ready
とやっていたところです。
また、/app/javascript/packs/index.vueをimportして、それをAppという変数に受け取り、
1
const app = new Vue(App).$mount('#MyAppRoot')
とうように、Vueのコンストラクターの引数に渡して、そして、$mountで、views/welcome/index.html.erbの<div id="MyAppRoot"></div>
へのマッピング(紐付)をします。
config/routes.rb
次の2行を書き込み、ブラウザで確認できるようにします。
1
2
get "welcome/index"
root :to => 'welcome#index'
ブラウザによる確認
カレントディレクトリをshinraプロジェクトのルートにしたターミナルを2つオープンしておきます。
片方では
1
bin/webpack-dev-server
を実行します。Webpackがごちゃごちゃ言ってきますが、無視します。これはserverという名前が付いているように、ファイルを変更すると自動でWebpackを実行してくれる優れものです。
次に、railsを立ち上げます。
1
rails s
これでhttp://localhost:3000/にアクセスすると›
という感じの画面が見えるはずです。とりあえず、Hello World!は見えたということでw
ここのVue.jsのポイント
- /app/javascript/pack/application.scsはcssに変換されて、/app/views/layout/application.html.erbで読み込まれ、そこではcolor: red;と書いてあるのですが、それを上に出てきたindex.vueファイルの中のscopedのところに書いたCSSで上書きしています。反対にviews/welcome/index.html.erb内のHello Rails 5.1は赤いままです。
- Vue.jsには書き方が色々ありますが(参考:https://aloerina01.github.io/javascript/vue/2017/03/08/1.html)、今回の単一ファイルコンポーネント(Single File Components)はおすすめです(異論はあるかもしれませんが)。
index.vueの中のdataプロパティは関数を仕込むことを推奨しているようですが、その返しているオブジェクトのキーをtemplateの中から参照できます(<p>{{ message }}</p>のように)。
1 2 3 4 5 6 7
export default { data: () => { return { message: "Hello Vue!" } } }
index.vueの内容をVueとして登録するのがタグのIDを通じてで今回の場合、/app/views/welcome/index.html.erb内の
<div id="MyAppRoot"></div>
と/app/javascript/packs/welcome.jsの1 2 3 4
const app = new Vue({ // eslint-disable-line no-new el: '#MyAppRoot', render: h => h(App) })
にあるelで指定した要素でマッピングします。
Database.ymlの変更
さて、今度はデータベースを作成します。まずは設定からです。今回はmysqlを指定したので、最初に作成したユーザやパスワードに変更します(productionのところとかは今回は変更しません)。
b6d4789738b078c9e4e2f9298c0c62d2b83d6342
config/database.ymlというファイルをエディタで開くと次のようになっていると思います(コメントは取っています。また、上記で-dオプションでmysqlを指定していない場合はsqliteを使う設定なので、変更等は必要はありません)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
socket: /tmp/mysql.sock
development:
<<: *default
database: bansho_development
test:
<<: *default
database: bansho_test
production:
<<: *default
database: bansho_production
username: bansho
password: <%= ENV['BANSHO_DATABASE_PASSWORD'] %>
上記のうち変更するのは唯一mysqlのユーザの作成で作成したユーザ名とパスワードを書き込みます(上では「hoge:hogechan」でしたが、もちろん、任意です)。
1
2
3
4
5
6
7
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: hoge
password: hogechan
socket: /tmp/mysql.sock
これでrailsからmysqlにアクセスできるようになったはずです。
テーブルのmigrationファイル作成とmigrate
- ※
- Railsでは、テーブル名は複数形にするのがルールです。
今回もそれに則っています。
さて、次の一覧のように8つのテーブルを作成します。id等はRailsが自動で作成してくれるので書いていません。
また、通常railsでは型を[string, text, integer, float, date, datetime, boolean, primary_key, time, timestamp, binary, decimal]あたりから選んで書くことになるのですが、このうち4種類しか利用していません(実際のDBには型やサイズ等がもっと色々ありますが、Railsではプログラミングでもなじみのあるもので代用できるようになっており、必要に応じてlimit等で長さを指定したりできます)。
- usersテーブル
- username:string
- email:string
- password:string
- rolesテーブル
- role_name:string
- user_rolesテーブル
- user_id:integer
- role_id:integer
- blogsテーブル
- user_id
- title:string
- content:text
- issue_date:datetime
- tagsテーブル
- tag_name:string . categoriesテーブル
- category_name:string
- blog_tagsテーブル
- blog_id:integer
- tag_id
- blog_categoriesテーブル
- blog_id:integer
- category_id
これらのテーブルは
- usersとrolesがuser_roles(中間テーブル)を介して多対多
- usersとblogsが1:多
- blogsとtagsがblog_tags(中間テーブル)を介して多対多
- blogsとcategoriesがblog_categories(中間テーブル)を介して多対多
という関係なっています。ER図にすると、下のような感じになっています。
もう少しだけ説明すると、具体的なレコードで説明をすると(さらに下の表参照)、blogsにuser_idというフィールドがあって、これが書いたユーザが誰なのかを表すもので、ユーザ1人で複数のブログを書けるので、「1:多」ということになります(このuser_idは通常外部キーとか言います)。
次に、usersとrolesの関係、つまり、userがどんなロール(役割)を持てるかというのを表す際、もしユーザ1人にロール1つなら、1:1の関係で「1:多」の「多」が常に1になるような関係になれば良いのです。ただ今回は、一人のユーザが複数のロールを持てるようにするので(方法的にはいくつかありますが)、上のER図のように中間テーブルを利用して、つまり、中間テーブルでユーザとロールの関係を表現します。
すなわち(下の表のように)、IDが1のユーザはadminとsuper_adminのロールを2つ持っているような場合、中間テーブルであるuser_rolesにuser_idが1でrole_idが1(これがadmin)のレコードとuser_idが1でrole_idが2(これがsuper_admin)の2レコードを作成することにより表現します。もちろん、user_idが2の人のようにロールが1つということも可能になります。これで中間テーブルを利用して、「多対多」を定義したということになります。
さて、具体的にmigrateしています。これもコマンドを叩いて作成することが可能ですが、今回は手作業で行います。
db/migrate/20170926153600_create_users_and_roles_and_user_roles.rb
というファイルに次のような内容で書き込みます。ここでファイル名の「20170925153600」は「年→月→日→時→分→秒」までの14桁(しっかりmigrateが順番になるようになっていれば大丈夫なんで、あまり正確じゃなくてもOKかも)、その後の名前はわかりやすいようにしただけです(ただし、createとかremoveとかある程度Railsは読み取っているらしいけれど、ここでは無視します)。今回の名前は、usersとrolesとuser_rolesを作成する、というような意味にしました(もしかしたら、テーブル1つ1つ分けた方が良いという人もいるかもしれませんが、面倒なのでまとめました)。
ただ、後半のわかりやすいようにした名前は(数字は無視)、クラス名ではその名前に合わせたルールがあって、それは最初は大文字、そして_は削除して_の後ろは大文字するというものなので、「create_users_and_role_and_user_roles」は「CreateUsersAndRolesAndUserRoles」というようなクラス名になります。
- ※
- migrationファイルは年〜秒までの14桁の数字+テーブル名やcreateとかaddとかわかりやすい言葉を_(半角アンスコ)でつなげて作成する。
- ※
- migrationファイル内のクラス名は数字は無視して、最初は大文字、そして_は削除して_の後ろは大文字にして作成する。
- ※
- *rails db:create*でデータベースができ、*rails db:migrate*で、テーブルの作成やフィールドの追加・変更・削除ができる。
- ※
- *rails db:drop*でデータベースを削除して、再度行うことができるが、Rails5からは*rails db:environment:set RAILS_ENV=development*を実行しないとダメになった。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class CreateUsersAndRolesAndUserRoles < ActiveRecord::Migration[4.2]
def change
create_table :users do |t|
t.string :username, :null => true
t.string :email, :null => false
t.string :password
t.timestamps
end
create_table :roles do |t|
t.string :role_name, :null => false
t.timestamps
end
create_table :user_roles do |t|
t.integer :user_id, :null => false
t.integer :role_id, :null => false
t.timestamps
end
add_index :user_roles, :user_id
add_index :user_roles, :role_id
end
end
※usersのパスワードは今後インストールするdeviseの時に使わなくなるので、今回は「not null」をはずしています。
次に
db/migrate/20170926153800_create_blogs_and_tags_and_categories_and_chukan_tables.rb
というファイルを次のような内容で書き込みます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class CreateBlogsAndTagsAndCategoriesAndChukanTables < ActiveRecord::Migration[4.2]
def change
create_table :blogs do |t|
t.integer :user_id, :null => false
t.string :title, :null => false
t.text :content, :null => false
t.datetime :issue_date
t.timestamps
end
create_table :tags do |t|
t.string :tag_name, :null => false
t.timestamps
end
create_table :categories do |t|
t.string :category_name, :null => false
t.timestamps
end
create_table :blog_tags do |t|
t.integer :blog_id, :null => false
t.integer :tag_id, :null => false
t.timestamps
end
create_table :blog_categories do |t|
t.integer :blog_id, :null => false
t.integer :category_id, :null => false
t.timestamps
end
add_index :blog_categories, :blog_id
add_index :blog_categories, :category_id
add_index :blog_tags, :blog_id
add_index :blog_tags, :tag_id
end
end
上記はchangeメソッドしか書いていませんが、フィールド名を変えたり、フィールドを削除したりするmigrationの場合はupメソッドとdownメソッドを書いて、migrateした場合はupメソッドが、roll back(元に戻)した場合はdownメソッドを書いたりします。
これは、テーブルを作ったり、フィールドを増やしたりした場合は、ロールバックするにはそれを単純に削除するということで実現できるのでrailsはchangeだけ書けば問題なく処理してくれますが、フィールドを削除したり、フィールド名を変えたりした場合は、どんなフィールドを元に戻すのか、どんなフィールド名に戻すのかがchangeメソッドだけだとさすがのRailsでもわからないので、upメソッドとdownメソッドを書くということになります。詳しくはhttp://tanihiro.hatenablog.com/entry/2014/01/10/182122あたりをご参考に。
また、migrationファイルの書き方等はhttp://www.rubylife.jp/rails/model/index9.htmlや本家のhttps://railsguides.jp/active_record_migrations.htmlをご参考ください。
さて、このファイルを元に次のようなコマンドを叩きます。
1
2
3
rails db:create
rails db:environment:set RAILS_ENV=development #←これをやる必要がある!
rails db:migrate
これでエラーが起こらなかったら、DBやテーブルが作成されたはずです。
controller
- ※
- controllerの名前は「テーブル名」+_+「controller.rb」というルールになっており、usersテーブルを扱うコントローラだったら*users_controller.rb*となる
というわけで、app/controllers/users_controller.rbを作成します(BlogsControllerは後回しとします)。
下以外のメソッドを使っていけないわけじゃありませんが、通常は次のようなメソッドを作成します。
controllerの中にすでに定義されているメソッド
- public
index
これはレコードの一覧を表示させるメソッド。
show
1つのレコードの値を表示させるメソッド。
new
新しいレコードを登録するためのフォームを表示するメソッド(登録するのは次の次のメソッド)。
edit
レコードの編集するためのフォームを表示するメソッド(値はフィールドにセットされています)。
create
newで開いたフォームの値を元にレコードを作成するメソッド。
update
editで表示されたフォームに対して何らかの値を修正して、それらの変更を更新するメソッド。
destroy
レコードをIDを指定して削除するメソッド。
- private
set_user
編集の時(edit)やデータの表示(show)等にidからレコードを拾ってBlogモデルクラスのインスタンスにレコードをセットするメソッド。
user_params
Strong Parameterといって、リクエストの値の中にシステムが要求している以外のフィールドとかないかチェックしているメソッド(今回は詳細は避けます)。
さて、このusersテーブルには管理者の情報を登録することになるわけですが、だいたい、scaffoldを使ってcontrollerを作成すると次のようになります。
ただし、Userというモデルがまだないし、Viewもないので、当然動きません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
@users = User.all
end
def show
end
def new
@user = User.new
end
def edit
end
def create
@user = User.new(user_params)
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: 'User was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to user_url, notice: 'User was successfully destroyed.' }
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
model
usersテーブルに対応したModelを作成します(多のモデルは必要に応じて順次作成します)。ただ、バリデーションもしない単純なものを作成します。
app/models/user.rbに下の内容(たったの2行)を書き込みます。
1
2
class User < ActiveRecord::Base
end
view
viewは少々面倒です(scaffoldで作成される程度のものなら、手作業で数は若干多いという程度ですが、その若干数が多いという点に加え、ある程度はしっかりデザインしたいし、クライアントサイドのバリデーションをもしたいとなると面倒だ、という感じです)。通常ですと、次のようなファイルを作成することになります。
_form.html.erb
edit.html.erbとnew.html.erbからインクルードされている、htmlのformが書かれている(後述)。
index.html.erb
レコード(Blog)の一覧表示するもの。
new.html.erb
ほとんど、_form.html.erbを読み込んでいるだけ。
edit.html.erb
ほとんど、_form.html.erbを読み込んでいるだけ。
show.html.erb
レコードの値の表示するもの。
ただし、とりあえず、今回はすべてをシングルページアプリケーションにするわけじゃありませんが、このユーザ部分はそうしようと考えているのでindex.html.erbのみを作成します。
app/views/users/index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="page-header" class="page-header">
<h4>管理者一覧ページ</h4>
</div>
<table class='table-striped table-condensed table-bordered' align="center" width="55%">
<thead>
<tr>
<th width="45%" class="center">メールアドレス</th>
<th width="55%" class="center">パスワード</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td class="left"><%= user.email %></td>
<td class="left"><%= Devise::Encryptable::Encryptors::Aes256.decrypt(user.password).gsub(/\w/, "*") %></td>
</tr>
<% end %>
</tbody>
</table>
ちょっと先回りして、暗号化
パスワードの暗号化は、後にDeviseをインストールするときに考えようと思っていたんですが、この後viewでパスワードとかを登録するときに必要かな、と思いここで暗号化するクラスをを作成します。
Deviseの標準のパスワードの暗号化はsha512とか使っていて、これは復号できない。つまり、暗号化してその暗号化されたものから元には戻せないものです。
これはユーザ本人がパスワードをすべて管理するような場合(つまり、本人が自分のユーザ登録を行い、忘れた場合は自分で再発行してメールで受け取るような場合)ではなく、管理者が管理者を登録したりするような場合、復号化できた方が良いようなケースも存在します(もちろん、リスクも伴うので色々意見もありそうですが、とりあえずそこはスルーしてもらって)。
というわけで、暗号化をDeviseの標準じゃなく、例えば、復号もできるけれど強度の高いAes256とかを使ってみようと考えているわけです。
そこで、
まずは、暗号化のライブラリーを導入
Gemfileに
gem 'aes'
を書き込んで、シェルで
bundle install
config/initializers/aes256.rbというファイルに次のような内容を書き込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# -*- coding: utf-8 -*- require 'aes' module Devise module Encryptable module Encryptors class Aes256# < Base class << self def digest(password, stretches = 10, salt = Rails.application.config.salt, pepper = Rails.application.config.pepper) begin ::AES.encrypt(password, pepper, {:iv => salt}) if password rescue nil end end def salt(stretches = 10) ::AES.iv(:base_64) end def decrypt(encrypted_password, pepper = Rails.application.config.pepper) if encrypted_password.blank? return "" end ::AES.decrypt(encrypted_password, pepper = Rails.application.config.pepper) end end end end end end
上記でsaltやらpepperの扱いは注意が必要です。というのもこれがわかってしまうと復号されてしまうわけで、可能なら環境変数等へ登録して、そこから読み取るような仕組みにした方が良いでしょう(今回はしていませんが)。
また、Rails.application.config.salt, Rails.application.config.pepperを書き込む必要があります(暗号化については詳細は省きます)。つまり、
config/appliction.rbの上の方に
1
require 'aes'
class宣言の中に
1
2
3
class Application < Rails::Application
config.pepper = 'fca0c29958bcddb613c9b94cbb537761536433d5da7c533486f003e367c72f6d1b280fbd2646d042170a4541bcb0127b0713786aaa23786a3031d564e2feaca0'
config.salt = ::AES.iv(:base_64)
を書き込みます。
また、libディレクトリへの検索パスを通すためにconfig/application.rbに次のものを書き込みます。
1
2
config.autoload_paths += %W(#{config.root}/lib)
config.eager_load_paths += %W(#{config.root}/lib)
サンプルデータの作成
ついでに当面必要なusersテーブルのレコードを作成してきます。そのためにGemfileに次の2行を書き込み(当面forgeryしか使っていませんが)
1
2
gem 'faker'
gem 'forgery'
そして、
1
bundle install
を実行します。
次にdb/seeds.rbに次のコードを書き込み
1
2
3
10.times do
User.create(email: Forgery('email').address, password: Devise::Encryptable::Encryptors::Aes256.digest(Forgery(:basic).password))
end
これで
1
rails db:seed
で、サンプルデータが投入されます(10人分)。
bootstrapの導入
Bootstrapを使ってデザインしていこうと考えているので、以下のことを行います。
プロジェクトのトップにあるGemfileの中に
1
gem 'bootstrap-sass'
を書き込み、ターミナルで
1
bunlde install
を実行します。
次に、app/javascript/src/application.cssというファイル(上でコピーしていたもの)をapp/javascript/src/application.scssにリネームします。これは拡張子をcssからscssに変えているだけです。
ただし、中身を少々変えます。
1
2
*= require_tree .
*= require_self
という2行しかないところを(それ以外はコメント)、2行自体もコメントにします。つまり、*イコールをとります}。
1
2
* require_tree .
* require_self
そして、次の2行をコメントが終わったあたりに書き込みます。
1
2
@import "bootstrap-sprockets";
@import "bootstrap";
また、JavaScriptも使えるように、app/javascript/pack/application.jsに以下の1行を書き込みます。
1
//= require bootstrap-sprockets
application.html.erb
app/views/layouts/application.html.erb
は、特に指定しない限り、通常すべてのページで読み込まれ、今後作成するnew.html.erbなどをこれから読み込む、という形になります。
つまり、
app/views/layouts/application.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<title>Shinra</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
</head>
<body>
<%= yield %>
</body>
</html>
の下の方にある<%= yield %>
のところにnew.html.erbとかの内容と置き換わる仕組みです。
さて、Bootstrapではcontainerクラスのdivで囲むのが一般的(僕の場合だけの可能性が・・・汗)なので、上記を
app/views/layouts/application.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<title>森羅万象を記す</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
</head>
<body>
<div class="container">
<%= yield %>
</div>
</html>
と書き換えます(12〜14行目)。タイトルがShinraではダメなので(と思うので)、森羅万象を記すと書き換えました。
config/routes.rb
config/routes.rbに次の1行を書き込みます。
1
resources :users, only: %i(index)
とりあえず、ここまでの確認
ターミナルで、
1
rails s
http://localhost:3000/usersにブラウザでアクセスすると、次のように表示されればOKです。