- 投稿日:2019-03-24T23:07:49+09:00
Ruby on Railsを体系的に学ぶためのRAILS GUIDESの使い方
始めに
この記事は、Railsアプリケーションがリクエストを受け取って、レスポンスを返すまでにどんな処理を行うかについて触れています。
個人的に「一度は読んでおいて損はない」と感じたRAILSGUIDEのリンクを集めました。
ここで紹介するリンクは、はじめは読んでも意味がわからないと思いますが、目を通す意味があると思います。取り急ぎ執筆したため、現時点では世界一投げやりなRails講座です。
今後少しずつ説明を書いていこうと思います。
こうした方がいいんじゃないかなど、意見がありましたら、ご教授願います。注意書き
初心者の人がここに書いてあることを全て理解するのは不可能だと思います。
目を通して頭の片隅に入れておくだけでいいです。
単語も覚えようとしないでください。「ふーん、こんなのがあるんだ」程度でいいです。対象
以下のような人が「ギリギリ理解できるかできないか」レベルの箇所を抜粋しています。
- Railsの勉強始めたけど腑に落ちない人
- コードの意味がわかってない人
- コピペコーダー
対象じゃない人
- Rails newしたことない人
- MVCという言葉を聞いたことすらない人
- 読むより実践派の人
- Railsできる人
0. リクエストとレスポンス
Webのアプリケーションはリクエスト(お願い)を受け取ることではじまります。
アプリ内のボタンをおされたり、URLが入力されたり、google検索に引っかかったりすることで、アプリケーションにリクエスト(お願い)が送られます。リクエストはパス(URL)とHTTPリクエストメソッド(GETやPOST)で構成されます。
Webアプリケーションは、このリクエスト(お願い) に対して、様々な処理をした後にレスポンス(返信) を返します。
HTTPリクエストについては、こちらの記事を見るといいと思います。
超絶初心者のためのサーバとクライアントの話1. Routing
Railsアプリケーションはリクエストを受け取ると、Railsのルーターがリクエストを識別します。
ルーターのカスタマイズは、config/routes.rbで行います。以下のリンクの2.4までは目を通すといいと思います。
Railsのルーティング | RAILS GUIDE2. Controller
MVCモデルのCです。
ルーターに指名されたコントローラは、何かしらの処理を行い、最終的にViewを用いてHTMLを表示します。この「何かしらの処理」を理解するために、リンクの以下の部分を目を通すといいと思います。
- 1 ~ 4.1 コントローラの役割〜ハッシュと配列パラメータ
- 4.3 ルーティングパラメータ
- 4.5 Strong Parameter
- 8 フィルタ
3. View
MVCモデルのVです。
前章で、コントローラは以下のように説明しました。
ルーターに指名されたコントローラは、何かしらの処理を行い、最終的にViewを用いてHTMLを表示します。
この章では、「Viewとは何か」と「コントローラはどうやってViewを用いるのか」について触れます。
3-1 Viewとは何か
リンクの以下の部分を目を通すといいと思います。
- 1 ~ 3.1.1 Action Viewについて 〜 ERB
- 3.2 ~ 4 パーシャル 〜 パーシャルレイアウト
- 6.1 Action Viewのヘルパーメソッドの概要(余裕があれば)
もし余裕があればこちらも読んでみましょう。(優先順位は3-2の方が高いです。)
ユーザに入力させる、フォームについてです。
- 1 ~ 2.2 基本的なフォームを作成する 〜 フォームとオブジェクトを結び付ける
- 7 パラメータの命名ルールを理解する
Action View フォームヘルパー | RAILS GUIDE
3-2 コントローラはどうやってViewを用いるのか
リンクの以下の部分を目を通すといいと思います。
- 1 ~ 2.2 概要: 部品を組み上げる 〜 レンダリングを使用する
- 2.3 redirect_toを使用する
- 3.4 パーシャルを使用する
4. モデル
MVCモデルのMです。
前章で、コントローラは以下のように説明しました。
ルーターに指名されたコントローラは、何かしらの処理を行い、最終的にViewを用いてHTMLを表示します。
この「何らかの処理」にデータベースが絡む場合、モデルが必要になります。
モデルは、基本的にはデータベース内のテーブルの数だけ存在します。モデルが複数存在するということは、テーブルのデータ同士が関連している可能性があります。この関連自体をアソシエーションといいます。この章では、以下について触れます。
- 4-1 Modelとは何か
- 4-2 データベースの編集方法
- 4-3 どうやってModelを通じてデータを取り出すか
- 4-4 アソシエーションとは何か
4-1 Modelとは何か
リンクの以下の部分を目を通すといいと思います。
- 1 ~ 3 Active Recordについて 〜 Active Recordのモデルを作成する
- 5 ~ 8 CRUD: データの読み書き 〜 マイグレーション
Active Record の基礎 | RAILS GUIDE
4-2 データベースの編集方法
リンクの以下の部分を目を通すといいと思います。
- 1 ~ 2 マイグレーションの概要 〜 マイグレーションを作成する
- 3 (余裕があったら)
- 4 ~ 8 CRUD: マイグレーションを実行する 〜 マイグレーションとシードデータ
Active Record マイグレーション | RAILS GUIDE
4-3 データベースの編集方法
リンクの以下の部分を目を通すといいと思います。
- 1 ~ 3 データベースからオブジェクトを取り出す 〜 並び順
Active Record クエリインターフェイス | RAILS GUIDE
4-3 データベースの編集方法
リンクの以下の部分を目を通すといいと思います。
- 1 ~ 2.3 関連付けを使用する理由 〜 has_many関連付け(余裕があったら、2章全体を読んでみましょう)
Active Record の関連付け (アソシエーション) | RAILS GUIDE
フィードバックについて
「こうした方がいいんじゃないか」
「この部分の説明はリンク先だけでは1mmも理解できない」
などの意見がありましたら、コメントにてご教授頂けると嬉しいです。説明、オススメリンクを随時足していく予定です。
- 投稿日:2019-03-24T20:32:05+09:00
プログラミング学習記録36〜Cloud9で動くだけでも進歩〜
今日やったこと
- Progate Railsの道場コース2までの内容をCloud9で再現する
ProgateでやっていたのをCloud9でもやっています。
まったく同じコードを書いても上手くいかないことがあるのには慣れてきました。
Progateで使われている画像はなしで再現しました。
道場コース2までは問題なくいけました。
ですが、道場コース2から3に進むと、いきなりUserモデルが出来上がってる状態から始まるので、ここからは学習コースの手順に従って進んでいこうと思います。
Progateの模写を終えた後はオリジナルアプリ作りに入りたいと思います。
ツイッターは投稿する欄が1つですが、これを3つにして、前田裕二さんの「メモの魔力」のようなメモができるアプリが作れたらいいなと思います。
ということで、明日からも引き続きプログラミング学習頑張ります。
おわり
- 投稿日:2019-03-24T20:14:43+09:00
質疑応答アプリ「ama」を動かしてみる
このまえのRailsdmで発表への質問を受け付けるのに使われてた「ama」というアプリが良さげだったので、試しに手元で動かしてみた。
- Railsdmのama: https://railsdm.herokuapp.com/
- Githubリポジトリ: https://github.com/yhirano55/ama
ほとんど
README.mdに書かれてるとおりにやればいいけど、自分のメモのために書いておく。ローカルで動かす
Ruby 2.6.1とPostgreSQLが必要なのであらかじめセットアップしておく。
rbenvを使っているなら、Ruby 2.6.1を使うように設定。
$ rbenv local 2.6.1DB接続情報を
.envに設定:.envDATABASE_HOST=127.0.0.1 DATABASE_USERNAME=postgres(好みで)gemをアプリ配下にインストールするように設定:
$ bundle config --local path .bundlesetupコマンド実行:
$ bin/setupGithub認証の設定。
- https://github.com/settings/developers にアクセス
- 「New OAuth App」をクリック
- 以下のように入力
- Application name
- 任意
- Authorization callback URL
- Application description
- 任意
- Authorization callback URL
- http://localhost:3000/auth/github/callback
config/routes.rbに記載されているのでそれに合わせる- 「Register Application」をクリック
アプリが登録されて、Client IDとClient Secretが発行されるので
.envに追記:.envGITHUB_OAUTH_CLIENT_KEY=xxx GITHUB_OAUTH_CLIENT_SECRET=xxx以上で設定完了。railsサーバを起動する:
$ bin/rails sブラウザで http://localhost:3000/ にアクセスするとトップページが表示される。
Dockerで動かす
イメージをビルド:
$ docker-compose buildsetupコマンド実行:
$ docker-compose run --rm app bin/setupローカル実行時と同じようにGithub認証を設定。
起動:
$ docker-compose up -dブラウザで http://localhost:3000/ にアクセスするとトップページが表示される。
管理者権限をつける
usersテーブルのrole列に1(admin)を設定すると管理者になる。
rails consoleで設定するならこんなかんじ:$ bin/rails c > user = User.find_by(nickname: 'okonomi') > user.role = 'admin' > user.save管理者になると、Issueの新規作成・編集・削除ができたり、他の人のコメントの編集・削除ができるようになる。
- 投稿日:2019-03-24T19:19:39+09:00
gemのあらゆるコマンドがAugumentErrorで実行できなかった件
この時のPC環境 ※要確認
OS:Windows10
Ruby:2.5.3p105 (2018-10-18 revision 65156) [x64-mingw32]
RubyGems:2.3.0Ruby on Railsのバージョンを確かめようとコマンド"rails -v"を実行したところ、以下のようなエラー表示が出ました。
C:\Ruby25-x64\rubygems-3.0.3>rails -v Traceback (most recent call last): 2: from C:/Ruby25-x64/bin/rails:23:in `<main>' 1: from C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/rubygems.rb:302:in `activate_bin_path' C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/rubygems.rb:283:in `find_spec_for_exe': can't find gem railties (>= 0.a) with executable rails (Gem::GemNotFoundException)エラー表示"ArgumentError"
Ruby on Railsをインストールしてみようと、コマンド"gem install rails"を実行したところ、以下のようなエラーが返ってきました。
ERROR: While executing gem ... (ArgumentError) wrong number of arguments (given 1, expected 0)ArgumentError・・・?引数がおかしい?
とりあえず、上のエラー表示についてググってみると、以下の記事を見つけました。
https://teratail.com/questions/156929
自分と同じようなエラーかなーと思いましたが、結局解決できていない模様。さらにググってみると、以下の記事を発見。
https://github.com/rubygems/rubygems/issues/2224この記事から、RubyとRubyGemsのバージョンによっては上手くいかないことがあると考え、RubyGemsをアップデートしてみることに。
コマンドプロンプトからRubyGemsをアップデートできない
gem update --systemを実行すると、以下のような表示が出ました。
C:\>gem update --system Updating rubygems-update ERROR: While executing gem ... (ArgumentError) wrong number of arguments (given 1, expected 0)またArgumentError・・・
どうにか別の方法でRubyGemsをアップデートできないものかとググりまくっていると、次の記事を発見。
http://d.hatena.ne.jp/c_mutoh/20100329/1269877259この記事にはZipファイルを使ってRubyGemsをアップデートする方法が書かれていました。
Zipファイルを使ってRubyGemsをアップデートする方法
※先の記事に書かれている手順通りに実行しましたが、念のため書いておきます。
この時、私がインストールしたRubyGemsのバージョンは3.0.3です。(この記事を書いている時点において最新)
まず、RubyGemsのサイトのダウンロードページからZIPを選択して、Zipファイルをダウンロードします。
C:\Ruby25-x64のディレクトリ(Rubyがインストールされているディレクトリ)にダウンロードしたZipファイルを解凍します。
解凍できたら、cdコマンドで解凍したディレクトリに移動します。(私の場合"cd C:\Ruby25-x64\rubygems-3.0.3"コマンドを実行)
ここで、setup.rbを以下のように実行します。
C:\Ruby25-x64\rubygems-3.0.3>ruby setup.rbこれでRubyGemsのバージョン3.0.3がインストールできたはず・・・
バージョンを確認すると、
C:\Ruby25-x64\rubygems-3.0.3>gem -v 3.0.3バージョンが2.3.0から3.0.3に更新されました!
Ruby on Railsをインストール
RubyGemsのバージョンアップが完了したところで、Ruby on Railsをインストールしてみます。
以下のコマンドを実行します。C:\Ruby25-x64\rubygems-3.0.3>gem install rails Fetching rails-5.2.2.1.gem Successfully installed railties-5.2.2.1 Successfully installed rails-5.2.2.1 Parsing documentation for railties-5.2.2.1 Installing ri documentation for railties-5.2.2.1 Parsing documentation for rails-5.2.2.1 Installing ri documentation for rails-5.2.2.1 Done installing documentation for railties, rails after 0 seconds 2 gems installed今度はエラーが返ってきませんでした。
Ruby on Railsが無事インストールされたか確認してみます。
C:\Ruby25-x64\rubygems-3.0.3>rails -v Rails 5.2.2.1インストールできたみたいです!
まとめ
Ruby on Railsをインストールしたいが、できない
↓
RubyGemsのバージョンを変更する必要がある(?)
↓
コマンドプロンプトでRubyGemsのバージョンを更新できない
↓
Zipファイルを使ってRubyGemsのバージョンを更新
↓
Ruby on Railsインストール成功という話の流れでした。
個人的な感想
Qiita初投稿の記事になります。
私はRuby初心者ですが、プログラミングする以前に環境構築の時点でつまづくのは結構辛いですよね・・・
私はこの問題で半日以上費やしてしまい、同じような思いを他の方が味わうのは何とも酷だなと思い、思い切って投稿しました。参考になれば幸いです。
あぁ・・・おとなしくMac買って使った方が良いんだろうな・・・
- 投稿日:2019-03-24T19:19:39+09:00
gemのあらゆるコマンドがAugument Errorで実行できなかった件
この時のPC環境
OS:Windows10
Ruby:2.5.3p105 (2018-10-18 revision 65156) [x64-mingw32]
RubyGems:2.3.0Ruby on Railsのバージョンを確かめようとコマンド"rails -v"を実行したところ、以下のような表示が出ました。
C:\Ruby25-x64\rubygems-3.0.3>rails -v Traceback (most recent call last): 2: from C:/Ruby25-x64/bin/rails:23:in `<main>' 1: from C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/rubygems.rb:302:in `activate_bin_path' C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/rubygems.rb:283:in `find_spec_for_exe': can't find gem railties (>= 0.a) with executable rails (Gem::GemNotFoundException)エラー表示"ArgumentError"
Ruby on Railsをインストールしてみようと、コマンド"gem install rails"を実行したところ、以下のようなエラーが返ってきました。
ERROR: While executing gem ... (ArgumentError) wrong number of arguments (given 1, expected 0)ArgumentError・・・?引数がおかしい?
とりあえず、上のエラー表示についてググってみると、以下の記事を見つけました。
https://teratail.com/questions/156929
自分と同じようなエラーかなーと思いましたが、結局解決できていない模様。さらにググってみると、以下の記事を発見。
https://github.com/rubygems/rubygems/issues/2224この記事から、RubyとRubyGemsのバージョンによっては上手くいかないことがあると考え、RubyGemsをアップデートしてみることに。
コマンドプロンプトからRubyGemsをアップデートできない
gem update --systemを実行すると、以下のような表示が出ました。
C:\>gem update --system Updating rubygems-update ERROR: While executing gem ... (ArgumentError) wrong number of arguments (given 1, expected 0)またArgumentError・・・
どうにか別の方法でRubyGemsをアップデートできないものかとググりまくっていると、次の記事を発見。
http://d.hatena.ne.jp/c_mutoh/20100329/1269877259この記事にはZipファイルを使ってRubyGemsをアップデートする方法が書かれていました。
Zipファイルを使ってRubyGemsをアップデートする方法
※先の記事に書かれている手順通りに実行しましたが、念のため書いておきます。
この時、私がインストールしたRubyGemsのバージョンは3.0.3です。(この記事を書いている時点において最新)
まず、RubyGemsのサイトのダウンロードページからZIPを選択して、Zipファイルをダウンロードします。
C:\Ruby25-x64のディレクトリ(Rubyがインストールされているディレクトリ)にダウンロードしたZipファイルを解凍します。
解凍できたら、cdコマンドで解凍したディレクトリに移動します。(私の場合"cd C:\Ruby25-x64\rubygems-3.0.3"コマンドを実行)
ここで、setup.rbを以下のように実行します。
C:\Ruby25-x64\rubygems-3.0.3>ruby setup.rbこれでRubyGemsのバージョン3.0.3がインストールできたはず・・・
バージョンを確認すると、
C:\Ruby25-x64\rubygems-3.0.3>gem -v 3.0.3バージョンが2.3.0から3.0.3に更新されました!
Ruby on Railsをインストール
RubyGemsのバージョンアップが完了したところで、Ruby on Railsをインストールしてみます。
以下のコマンドを実行します。C:\Ruby25-x64\rubygems-3.0.3>gem install rails Fetching rails-5.2.2.1.gem Successfully installed railties-5.2.2.1 Successfully installed rails-5.2.2.1 Parsing documentation for railties-5.2.2.1 Installing ri documentation for railties-5.2.2.1 Parsing documentation for rails-5.2.2.1 Installing ri documentation for rails-5.2.2.1 Done installing documentation for railties, rails after 0 seconds 2 gems installed今度はエラーが返ってきませんでした。
Ruby on Railsが無事インストールされたか確認してみます。
C:\Ruby25-x64\rubygems-3.0.3>rails -v Rails 5.2.2.1インストールできたみたいです!
まとめ
Ruby on Railsをインストールしたいが、できない
↓
RubyGemsのバージョンを変更する必要がある(?)
↓
コマンドプロンプトでRubyGemsのバージョンを更新できない
↓
Zipファイルを使ってRubyGemsのバージョンを更新
↓
Ruby on Railsインストール成功という話の流れでした。
個人的な感想
Qiita初投稿の記事になります。
私はRuby初心者ですが、プログラミングする以前に環境構築の時点でつまづくのは結構辛いですよね・・・
私はこの問題で半日以上費やしてしまい、同じような思いを他の方が味わうのは何とも酷だなと思い、思い切って投稿しました。参考になれば幸いです。
あぁ・・・おとなしくMac買って使った方が良いんだろうな・・・
- 投稿日:2019-03-24T19:18:55+09:00
Rails I18nでcreated_atなど各モデル共通の属性を一発で日本語化する
ユースケース
created_atやupdated_atなど、各モデル共通のattributesを日本語化したいときに。
コード
models.ymlja: attributes: created_at: 作成日時 updated_at: 更新日時一番上の階層に記述すれば最初に読み込まれる。
参考
http://alfa.hatenablog.jp/entry/2013/12/03/221308
https://qiita.com/Kta-M/items/bd4ba36a58ad602a9d8b
- 投稿日:2019-03-24T18:50:47+09:00
railsチュートリアル 第九章
はじめに
railsチュートリアルで理解しにくいところや、詰まったところを書いていく記事になります。
なので、手順を示す記事とはなっていません。Remember me 機能
[remember me] 機能を利用すると、ユーザーが明示的にログアウトを実行しない限り、ログイン状態を維持することができるようになる。
この機能を利用するには、記憶トークン (remember token) を生成し、cookiesメソッドによる永続的cookiesの作成や、安全性の高い記憶ダイジェスト (remember digest) によるトークン認証にこの記憶トークンを活用することが必要となる。
(トークンとは、パスワードと似たようなものであるが、違いとしてはトークンの場合はコンピュータが作成した情報である)
永続的セッションを作成するにあたって、以下のようなことを留意点とする。
・記憶トークンにはランダムな文字列を生成して用いる。
・ブラウザのcookiesにトークンを保存するときには、有効期限を設定する。
・トークンはハッシュ値に変換してからデータベースに保存する。
・ブラウザのcookiesに保存するユーザーIDは暗号化しておく。
・永続ユーザーIDを含むcookiesを受け取ったら、そのIDでデータベースを検索し、記憶トークンのcookiesがデータベース内のハッシュ値と一致することを確認する。rememberダイジェストを追加
最初の手順として、マイグレーションファイルを生成し、remember_digest属性(string)をUserモデルに追加する。
また、remember_digestはユーザーが直接生み出すことはないので、インデックスを追加する必要がない。
記憶トークンの作成
記憶トークンには基本的に長くてランダムな文字列が求められるので今回はSecureRandomモジュールにあるurlsafe_base64メソッドを使用する。
このメソッドは、A–Z、a–z、0–9、"-"、"_"のいずれかの文字 (64種類) からなる長さ22のランダムな文字列を返す。
例:$ rails console >> SecureRandom.urlsafe_base64 => "q5lt38hQDc_959PVoo6b7A"ユーザーを記憶するには、記憶トークンを作成して、そのトークンをダイジェストに変換したものをデータベースに保存すればいい。
ここで、トークン生成用のメソッドを追加する。
app/models/user.rb# ランダムなトークンを返す def User.new_token SecureRandom.urlsafe_base64 end次に有効なトークンとそれに関連するダイジェストを作成する。
具体的には、最初にUser.new_tokenで記憶トークンを作成し、続いてUser.digestを適用した結果で記憶ダイジェストを更新する。app/models/user.rbattr_accessor :remember_token . . . # 永続セッションのためにユーザーをデータベースに記憶する def remember self.remember_token = User.new_token update_attribute(:remember_digest, User.digest(remember_token)) endrememberメソッドの1行目の代入の箇所では、selfというキーワードを使わないと、Rubyによってremember_tokenという名前のローカル変数が作成されてしまう。
selfキーワードを与えると、この代入によってユーザーのremember_token属性が期待どおりに設定される。
rememberメソッドの2行目では、update_attributeメソッドを使って記憶ダイジェストを更新している。ログイン状態の保持
user.rememberメソッドが動作するようになったので、ユーザーの暗号化済みIDと記憶トークンをブラウザの永続cookiesに保存して、永続セッションを作成する準備ができた。
これを実行するにはcookiesメソッドを使う。以下に手順を示す。
app/models/user.rb. . . # 渡されたトークンがダイジェストと一致したらtrueを返す def authenticated?(remember_token) BCrypt::Password.new(remember_digest).is_password?(remember_token) endrememberヘルパーメソッドを追加して、log_inと連携させる。
app/controllers/sessions_controller.rbdef create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) log_in user #追加 remember user redirect_to user else flash.now[:danger] = 'Invalid email/password combination' render 'new' end end実際のSessionsヘルパーの動作は、rememberメソッド定義のuser.rememberを呼び出すまで遅延され、そこで記憶トークンを生成してトークンのダイジェストをデータベースに保存される。
続いて上と同様に、cookiesメソッドでユーザーIDと記憶トークンの永続cookiesを作成する。
app/helpers/sessions_helper.rbmodule SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id end # ユーザーのセッションを永続的にする #追加 def remember(user) user.remember cookies.permanent.signed[:user_id] = user.id cookies.permanent[:remember_token] = user.remember_token end # 現在ログインしているユーザーを返す (いる場合) def current_user if session[:user_id] @current_user ||= User.find_by(id: session[:user_id]) end end # ユーザーがログインしていればtrue、その他ならfalseを返す def logged_in? !current_user.nil? end # 現在のユーザーをログアウトする def log_out session.delete(:user_id) @current_user = nil end endこれにより、current_userヘルパーを追加修正する必要がある。
app/helpers/sessions_helper.rb# 記憶トークンcookieに対応するユーザーを返す def current_user if (user_id = session[:user_id]) @current_user ||= User.find_by(id: user_id) elsif (user_id = cookies.signed[:user_id]) user = User.find_by(id: user_id) if user && user.authenticated?(cookies[:remember_token]) log_in user @current_user = user end end endユーザーを忘れる
ユーザーがログアウトできるように、ユーザーを記憶するためのメソッドと同様の方法で、ユーザーを忘れるためのメソッドを定義する。
具体的には、記憶ダイジェストをnilで更新するメソッドを定義することとなる。
app/models/user.rb. . . # ユーザーのログイン情報を破棄する def forget update_attribute(:remember_digest, nil) end endforgetヘルパーメソッドを追加して、log_outヘルパーメソッドから呼び出す。
app/helpers/sessions_helper.rbmodule SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id end . . . # 永続的セッションを破棄する def forget(user) user.forget cookies.delete(:user_id) cookies.delete(:remember_token) end # 現在のユーザーをログアウトする def log_out forget(current_user) session.delete(:user_id) @current_user = nil end end[Remember me] チェックボックス
ログインフォームにチェックボックスを追加する。
チェックボックスは、他のラベル、テキストフィールド、パスワードフィールド、送信ボタンと同様にヘルパーメソッドで作成できる。app/views/sessions/new.html.erb<% provide(:title, "Log in") %> <h1>Log in</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(:session, url: login_path) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> #追加 <%= f.label :remember_me, class: "checkbox inline" do %> <%= f.check_box :remember_me %> <span>Remember me on this computer</span> <% end %> <%= f.submit "Log in", class: "btn btn-primary" %> <% end %> <p>New user? <%= link_to "Sign up now!", signup_path %></p> </div> </div>2つのCSSクラスcheckboxとinlineを使い、Bootstrapではこれらをチェックボックスとテキスト「Remember me on this computer」として同じ行に配置することができる。
最後に、チェックボックスがオンのときにユーザーを記憶し、オフのときには記憶しないようにする。
ログインフォームから送信されたparamsハッシュには既にチェックボックスの値が含まれていて、オンの時は1、オフの時は0になるので、三項演算子を使うことで、コンパクトに処理を書くことができる。
app/controllers/sessions_controller.rbdef create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) log_in user #追加 params[:session][:remember_me] == '1' ? remember(user) : forget(user) redirect_to user else flash.now[:danger] = 'Invalid email/password combination' render 'new' end endおわりに
普段、当たり前のようにお世話になっている機能がここまで難しとは、、という感じです、、、
徐々に理解していこうと思います!
- 投稿日:2019-03-24T18:24:33+09:00
【Rails】読み仮名でもオートコンプリート検索できるようにしよう
概要
- 検索にオートコンプリートをつけます
- 読み仮名(ひらがな)でもオートコンプリートできるようにします。
動作イメージ
環境
- rails 6.0.0beta3
- dbはsqliteなのでローカルで適当に試してください。
- gem
- fast_jsonapi オートコンプリート用の結果をjsonで返すときにつかう
- rubyfuri 読み仮名を取得
- dotenv-rails yahoo japanのapi keyを管理するため
- javascript libary
- selectize.js フロント側のオートコンプリート表示を制御
heroku
https://fuchu-nosanbutsu.herokuapp.com/
コード
https://github.com/junara/fuchu-nosanbutsu/releases/tag/1.1.1
ローカルで試す場合
gitから https://github.com/junara/fuchu-nosanbutsu/releases/tag/1.1.1 をクローンして下さい。
- bundle install
bundle install
- migarate
bin/rails db:migrate
- データを取り込む
bin/rails c > Store.import_csv('db/30chokubaijo_map.csv')
- Storeデータからキーワードを登録
> importer = ItemImporter.new(Store.pluck(:description)) > importer.execute
- ふりがなを登録
.envにyahoo japanのdeveloper apiを取得して書き込んでください。- その後以下を事項
bin/rake item:name_hiragana
- サーバーを起動
bin/rail s
- webpacker-dev-serverを起動
bin/webpack-dev-server下記にアクセス
http://localhost:3000手順
- サーバーサイド(オートコンプリートエンドポイント作成)
- データ取り込み
- ふりがなデータ取得
- レスポンスのjson出力
- エンドポイント
- 複数キーワードで検索できるように修正
- フロントエンド
- javascript環境のセットアップ
- jquery, selectizeのインストール
- オートコンプリートの実装
サーバーサイド(オートコンプリートエンドポイント作成)
データ取り込み
オートコンプリート対象となるテーブル
Itemを作成します。db/migrate/20190322115059_create_items.rbclass CreateItems < ActiveRecord::Migration[6.0] def change create_table :items do |t| t.string :name t.timestamps end end endbin/rails db:migrateインポート用の ItemImporterを実装します。これは、Store.descriptionをparseしてItem.nameに取り込むクラスです。
app/importers/item_importer.rbclass ItemImporter def initialize(descriptions) @descriptions = descriptions end def execute ActiveRecord::Base.transaction do Item.delete_all list = [] item_names.uniq.reject(&:blank?).each do |item_name| list << Item.new(name: item_name) end Item.import list end end private def item_names current_item_names = Item.pluck(:name) @descriptions.each_with_object(current_item_names) do |str, res| res.concat(custom_split(str)) end end def custom_split(str) return [] if str.nil? str.gsub(/ほか|など|あり/, '').split(/・|\(|(|)|、|:|\n| |\)| /) end endこれで取り込みを行います。
rails c > importer = ItemImporter.new(Store.pluck(:description)) > importer.executeこれで、item.nameにstore.descriptionのデータからパースされたキーワードが保存されています。
Item.pluck(:name)などで確認して下さい。ふりがなデータ取得
これだけだと
ナスや茄子をなすでオートコンプリートすることができません。そのために、カタカナをひらがなに、さらに茄子を なすに対応させます。まずふりがな(ひらがな)を格納するカラムをマイグレーションします。
db/migrate/20190324070509_add_name_furi_to_item.rbclass AddNameFuriToItem < ActiveRecord::Migration[6.0] def change add_column :items, :name_hiragana, :string end endbin/rails db:migrateフリガナを追加するためのメソッドを追加します。
Gemfileに下記のgemを追加します。rubyfuriはyahoo japanのapiを叩いて、読み仮名を取得するgemですdotenv-railsはapiキーを管理するためです。gem 'dotenv-rails' gem 'rubyfuri'で、yahoo japanのdeveloper apiキーを取得します。 https://developer.yahoo.co.jp/webapi/jlp/ このあたりから取得してください。(取得方法は、各自調べて下さい。)
取得したapiキーを
.envファイルに記載します。(gitignoreに記載しているので大丈夫だと思いますが、こちらのAPIキーは決してgithubにアップロードしないで下さい!)YAHOO_JAPAN_DEVELOPER_CLIENT_ID=あなたのAPIキー良みこむためのメソッドをItemに付け加えます。
app/models/item.rbclass Item < ApplicationRecord def update_name_hiragana(force: false) rubyfuri = Rubyfuri::Client.new(ENV['YAHOO_JAPAN_DEVELOPER_CLIENT_ID']) update(name_hiragana: rubyfuri.furu(name)) end end先ほど実装したメソッドを利用してitem.name_hiraganaに読み仮名を追加するためのrake taskを作ります。
lib/tasks/item.rakenamespace :item do desc 'item.nameに読み仮名をつける' # rake item:name_hiragana task name_hiragana: :environment do Item.all.each {|item| item.update_name_hiragana} end end上記rake taskを実行します。
> rake item:name_hiragana
Item.pluck(:name_hiragana)等で読み仮名が格納されたことを確認して下さい。レスポンスのjson出力
ユーザーが入力したキーワード(フロント側から送られてきたキーワード)に従って、オートコンプリート候補となるレスポンスを返します。レスポンスの形式はなんでもいいです。jbuilderで書いてもいいのですが、意外と面倒。
なので、簡単にいい感じのJSONを返してくれるgemをつかいます。
active_model_serializers が有名ですが、今回はactive_model_serializersと同様の使い勝手で、active_model_serializersよりも速い!とされる、 fast_jsonapi を使います。まずgemをインストールします。
Gemfileに記述してgem 'fast_jsonapi'ふつーにbundle install。
bundle isntallfast_jsonapi でJSONを返すためには、
include FastJsonapi::ObjectSerializerしたクラスを作り、出力したいカラムをattributes :id, :name, :name_hiraganaのように書きます。これは、一番シンプルな例ですが、has_manyなどの関連でもできます。そちらをやりたい場合は公式のREADME読んでくださいませ。app/serializers/item_serializer.rbclass ItemSerializer include FastJsonapi::ObjectSerializer attributes :id, :name, :name_hiragana end上記は単純に単純にid, name, name_hiraganaを返します。
なお、返ってくるjsonはこんなかんじ。(この構造はフロント側のコードで使うので頭の片隅に置いておいてください。)
{ data: [ { id: "167", type: "item", attributes: { id: 167, name: "トマト", name_hiragana: "とまと" } }, { id: "168", type: "item", attributes: { id: 168, name: "きゅうり", name_hiragana: "きゅうり" } } ] }このjsonを返すコントローラのアクションはこちらです。フロント側から
params[:keyword]として入力した文字列がやってきます。その文字列を検索して、結果をItemSerializerに渡してJSONで返します。render json: ItemSerializer.new(items)class AjaxController < ApplicationController def item_autocomplete items = Item.ransack(name_or_name_hiragana_cont: params[:keyword]).result render json: ItemSerializer.new(items) end endエンドポイント
オートコンプリートとは関係無いですが、複数のキーワードによる検索に対応できるようにします。
split_queries(str)がそれです。keywordが,で区切りだったら分割してransackに渡します。description_cont_allとすることで、複数のキーワード(and検索)に対応しています。ransackの細かなところは こちら を見ましょうapp/controllers/home_controller.rbclass HomeController < ApplicationController def index @stores = Store.ransack(description_cont_all: split_queries(params[:keyword])).result end private def split_queries(str) return nil unless str str.split(',') end endエンドポイントをroutes.rbに書きます。これで
ajax/item_autocompleteでアクセスできます。(js由来だけに制限したい!とかったら適宜コード書き換えましょう。今回は緩ーく書いています)config/routes.rbRails.application.routes.draw do root to: 'home#index' namespace :ajax do # このあたりを追加 get :item_autocomplete end endフロントエンド
javascript環境のセットアップ
(rails 6なので最初からwebpacker入っています。rails 5系だったら 先にwebpackerを先にインストールしておいて下さい。)
jquery, selectizeのインストール
yarn add selectize yarn add jqueryselectizeのcssを設定。( application.css だったら application.scss にリネームして下さい。)
ちなみに、bopotstrap3だと
node_moudles/selectize/dist/css/を見てbootstrap3用を選択するといいかも。app/assets/stylesheets/application.scss/* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's * vendor/assets/stylesheets directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * *= require_tree . *= require_self */ @import "selectize/dist/css/selectize.default";オートコンプリートの実装
ここら辺はrailsでseletize.jsの使うための手順。
sprocketにのっかって書いてもいいけど、ES6で書きたいのでwebpackerつかった実装にしました。
rails 5系で webpackerが入っていない場合はこちらのQiitaの記事 Rails & Webpackerでフロントエンド開発環境を整えるを参考に事前にインストールして下さい。view
selectizeのターゲットとなるフォームにidを付与します。今回は、
store_searchというidを追加しました。それぞれのプロジェクトであわせてください。
こちら後で説明しますが、javascriptを読み込むためのjavascript_pack_tagを書きます。今の段階でページを表示するとjavascriptがないのでエラーになります。app/views/home/index.html.erb<%= javascript_pack_tag 'home' %> <div> <%= form_with url: root_path, method: :get, local: true do |f| %> <%= f.text_field :keyword, value: params[:keyword], autocomplete: 'off', id: 'store_search' %> <%= f.submit '検索' %> <% end %> </div>javascript
ようやく最後。Javascriptを書きます!
webpackerのエントリーポイントはデフォルトは
app/javascript/packs。こちらにjavascript書きます。ファイル名は何でもいいのですが、viewとあわせるとわかりやすいのでhome.jsにしました。
で、実際のコードはapp/javascript/home以下に書くのでここでは読み込むだけのコードを書きます。app/javascript/packs/home.jsrequire('home')で、下記のコードが実行されます。ファイル名はindex.jsにしてください。(
require('home')だとデフォルトでindex.jsを読み込むので。)app/javascript/home/index.jsimport $ from 'jquery'; require('selectize'); const url = (query) => { return `/ajax/item_autocomplete?keyword=${encodeURIComponent(query)}` }; const items = (data) => { return data.map((item) => { return { text: item['attributes']['name'], sub_text: item['attributes']['name_hiragana'], // ひらがなでも検索結果を絞り込めるようにする label: item['attributes']['name'], value: item['attributes']['name'] } }) }; const renderOption = (item, escape) => { return `<div><span>${escape(item.text ? item.text : '')}</span></div>` }; const renderOptionCreate = (item, escape) => { return `<div><span>新規キーワード... </span><span>${escape(item.input) ? item.input : ''}</span></div>` }; const getAutocomplete = (query, callback) => { $.ajax({ url: url(query), type: 'GET', error: () => { callback() }, success: (res) => { callback(items(res.data)) } }) }; const addSelectize = () => { $('#store_search').selectize({ searchField: ['text', 'sub_text'], // 入力値のフィルター対象、ひらがなでも検索結果を絞り込めるようにする labelField: 'label', // 表示させるラベル valueField: 'value', // inputのvalue closeAfterSelect: true, create: true, // 新規単語を追加を許可 createOnBlur: true, // 画面外をタッチすると新規単語を追加 render: { option: (item, escape) => { return renderOption(item, escape) }, option_create: (item, escape) => { return renderOptionCreate(item, escape) } }, load: (query, callback) => { if (!query.length) return callback(); getAutocomplete(query, callback) } }) }; const initialize = function () { addSelectize(); }; window.onload = function () { initialize(); };細かく解説します。
import $ from 'jquery'; require('selectize');ここで、ライブラリを読み込みます。raisl 6は最初からjqueryがないので、 jqueyをimportして、selectizeも読み込みます。
const addSelectize = () => { $('#store_search').selectize({ searchField: ['text', 'sub_text'], // 入力値のフィルター対象、ひらがなでも検索結果を絞り込めるようにする labelField: 'label', // 表示させるラベル valueField: 'value', // inputのvalue closeAfterSelect: true, create: true, // 新規単語を追加を許可 createOnBlur: true, // 画面外をタッチすると新規単語を追加 render: { option: (item, escape) => { return renderOption(item, escape) }, option_create: (item, escape) => { return renderOptionCreate(item, escape) } }, load: (query, callback) => { if (!query.length) return callback(); getAutocomplete(query, callback) } }) };ここら辺がselectizeの中心部。
$('#store_search')はjqueryのいつものやつ。.selectizeでselectizeを付与。load: (query, callback) => { if (!query.length) return callback(); getAutocomplete(query, callback) }ここで、キーワードが入力されたときに実行されます。ここで、 ajaxリクエスト送ります。
const getAutocomplete = (query, callback) => { $.ajax({ url: url(query), type: 'GET', error: () => { callback() }, success: (res) => { callback(items(res.data)) } }) };みたままですが、queryがあったら下記のurlにリクエストを送ります。
const url = (query) => { return `/ajax/item_autocomplete?keyword=${encodeURIComponent(query)}` };レスポンスは下記で処理。
success: (res) => { callback(items(res.data)) }レスポンスで得られたJSONを使いやすいようにマッピングし直します。
const items = (data) => { return data.map((item) => { return { text: item['attributes']['name'], sub_text: item['attributes']['name_hiragana'], // ひらがなでも検索結果を絞り込めるようにする label: item['attributes']['name'], value: item['attributes']['name'] } }) };マッピングし直したkeyをここで指定します。意味の詳細は、公式のusage で調べましょう。簡易的には下記のようにコメント書きました。
searchField: ['text', 'sub_text'], // 入力値のフィルター対象、ひらがなでも検索結果を絞り込めるようにする labelField: 'label', // 表示させるラベル valueField: 'value', // inputのvalueオートコンプリートリストおよび、フォームに表示される内容は下記で指定します。
const renderOption = (item, escape) => { return `<div><span>${escape(item.text ? item.text : '')}</span></div>` }; const renderOptionCreate = (item, escape) => { return `<div><span>新規キーワード... </span><span>${escape(item.input) ? item.input : ''}</span></div>` };既に記述していますが、このjavascriptを使いたいところで読み込むために
<%= javascript_pack_tag 'home' %>をviewに記述します。
今回は、app/views/home/index.html.erb記述しました。最後、webpackerでjavascriptをアセットコンパイルしてもらいます。
bin/webpack-dev-serverhttp://localhost:3000 にアクセスするとOK
以上。
長文読んでくれてありがとうです。
- 投稿日:2019-03-24T17:41:42+09:00
Docker Image を runして、 bundle exec rails new する
自分用の備忘録です。
まず、ビルド
docker build . -t <IMAGE_NAME>Rails new
docker run --rm -v $PWD:$PWD -w $PWD <IMAGE_NAME> bundle exec rails new . --database=mysql --skip-test
docker runのオプションは下に説明参考にした記事
Dockerコンテナからホスト側カレントディレクトリにアクセス
--rmオプションでDockerコンテナのプロセス(コマンド)が終了した時点でコンテナを破棄します。コマンド終了後にDockerコンテナ上のファイルを色々とアクセスしたい場合は外しても構いません。--userオプションでホスト側のUIDを指定します。指定しない場合、生成したファイル等の所有者がroot:rootとなるためホスト側のユーザが編集したり削除したりできません。-v $PWD:$PWDオプションで、ホス ト側カレントディレクトリとコンテナ側カレントディレクトリのボリューム内容を一致させます。-w $PWDオプションで、ホスト側カレントディレクトリとコンテナ側カレントディレクトリのディレクトリパスを一致させます。-v $PWD:$PWDオプションと組み合わせて使います。
- 投稿日:2019-03-24T16:37:00+09:00
Railsチュートリアル-本番環境でメールが送れないとき
Railsチュートリアルの「11.4 本番環境でのメール送信」について。
https://railstutorial.jp/chapters/account_activation?version=5.1#sec-activation_email_in_productionチュートリアルの手順を実施してもメールが送れなかったので解決方法をメモ。
症状
メールが送れない。
SenderGridの状態を確認。
herokuのappのページ -> Resources -> Add-onsのSendGrid -> Activityを開く。
すると、sample_appからのメール送信リクエストは受け取っているが、
ステータスが"Processed"で止まっていた。原因
SenderGridのページの上部を見ると、
アカウントがSuspendされているというメッセージが表示されていた。
これが原因。解決方法
SenderGridのサポートに、アカウントのSuspendを解除してもらうように依頼。
それで解決。参考ページ
- 投稿日:2019-03-24T16:23:42+09:00
Railsdm2019の個人的なメモまとめ
2019.03.22-23で開催されたRailsdm2019に聞く側で参加してきました。
備忘録も兼ねて自分が面白いなーと思ったとこをまとめたいと思います。最初に運営の皆さんに謝辞
開催にあたって色々とご尽力されたカルパスさん、他スタッフの皆さん、ご提供いただいた各社に感謝します。
本当に素晴らしいカンファレンスだったと思います。
今後は形を変えての運営という事になるようですが、ぜひ次も参加したいです!
楽しみにしています。以下、個人的にささった順で、見てきたセッションについてのメモ。
ピクスタ 後藤さん: Ruby on Railsの正体と向き合い方
https://speakerdeck.com/yasaichi/what-is-ruby-on-rails-and-how-to-deal-with-it
- Railsは何を妥協することで何を獲得したのか?という鋭い考察
- 特に、RailsとHanami(クリーンアーキテクチャ)の比較が面白かった
- URLからテーブルまでが一直線とか、モデルの中で例外が出てくる時が破綻のタイミングなど全体的にとてもわかりやすい資料。すごい。
クックパッド 庄司さん: DevOps, Immutable Infrastructure, Microservices and Chaos Engineering
https://speakerdeck.com/yoshiori/devops-immutable-infrastructure-microservices-and-chaos-engineering
- Dockerはinfrastructureが不変であることをアプリケーションコードに強制した、というのが腹落ち感すごかった
- Conwayの法則は最近どこに行っても聞く話
- チーム編成/教育/スクラム関連の需要の高まりを感じる
- ここまでやって(さらに言うならそれはMonolithの段階から)初めて「うちはMicroserviceやってます」って言えるんだなー、と
- 自分がやってる規模は全然しょぼいよなーと再認識
- カンファレンスはこういう普段の自分の環境と全然違う話を聞けるのが面白い
- サービスメッシュの話が面白い
- 各サービスは全部localhostを向いてて、あとは全部envoyがやってくれるそうな
- Chaos Enginieeringはサーバーを落とすことではない
- サーバーを落とすことだと認識していたのは私です。。。すいません
- SteadyState = 計測指標を持つ、というのがとても大事
- これは本当にそう思う。chaos engeneeringがどうとか関係なく
永和システムマネジメント 伊藤さん: rubyコミュニティの歩き方
松田さん: 毎日の開発に役立つRailsプラグインづくりの秘訣
https://speakerdeck.com/koic/somebody-to-ruby
https://speakerdeck.com/a_matsuda/extending-rails-for-real-world-app-development
- 両者とも、もっと「参加」しようということを呼びかけていて、今の自分に足りないものだなと思った
- とはいえまだまだ実力不足なので、まずはちゃんとお勉強から...
- 最後のJeremyのkeynoteで質疑応答があった時も、質問者に「ぜひあなたがPRを出してください」と言っていたのが印象的だった
永和システムマネジメント 三村さん: Railsな受託の会社で僕がやっていること
https://speakerdeck.com/takkanm/what-i-do-in-a-rails-consulting-company
- コードレビューで厳しめになるのは、そもそもその前提となる設計/実装ポリシー等の共有が足りてないのでそこを改善していくという考え方はなるほどなと
- 最後に紹介していたRuby-道の話は資料見たらマジで感動ものだった
マチマチ 藤村さん: 入門 名前
https://speakerdeck.com/fujimura/ru-men-ming-qian
- 良い名前ってなに?という問いに対するすごく明快な回答
- 後藤さんのRailsの話でもあったけど、そもそも命名の時点で正しく分割できるように実装してかないとダメっていう方針で行きたいなと思った
- 名前重要
- 個人的に、すぐ使えて応用範囲が広いという意味ではこの発表が一番だったかなと思っています
トクバイ 前田さん: サービスを成長させる仮説検証
https://speakerdeck.com/takatoshimaeda/railsdm2019
- ビジネスの要件でどのようなパスを見つけ、対処していくかという話
- 前提として要素を列挙・計測しているというのがあって、やっぱそこ大事ですよねと思う
- プロダクトに閉じない視野というのは抜けがちなので気をつけたい
kaizen platform 池田さん: ログを解析し続けてわかった、会社で眠っているアクセスログを活用する5つのプラクティス
https://blog.ikedaosushi.com/entry/2019/03/23/150125
- なぜそのデータを見えるようにすべきか?という観点をたくさん得られた
- 特に、必ずフィードバックをもらうというのは自分に欠けてる視点。ありがたい
- 先日Metabaseを使えるようにしたばかりだったので色々ためになった
- 開発者もデータをどう見て分析するのかという視点を持つの大事だと思う
- 場合によってはDB設計にも絡む話だしSTORES.jp 勝亦さん 自分たちを信じられるチームが作りたくて私はこうした
https://speakerdeck.com/katsumataryo/railsdm-katsumata
- 内容自体は特に目新しいことはないスクラムの話なんだけど、めっちゃ共感多かった
- 小さな改善の積み重ねが本当に大事だよなと改めて思う
- 「どこも似たような苦労があるんだな」って実感するのもカンファレンスのいいとこだと思う
- しんどかった話を堂々とできるのは普通に尊敬 (個人だけでなくそれを許可できる組織も)Wovn Technologies 久田さん: red-black-tree
https://speakerdeck.com/kyuden/red-black-tree-for-ruby
- 最近業務でパフォーマンスを気にしているところあったので参考になった
- 即応用は難しいかもしれんけどHashやArrayばっかじゃなくてSetも色々試してみたいなと思った
- こういうアルゴリズムとか実装の中の話聞くの楽しい
SmartHR 会社説明
- ランチセッションで会社説明をしてくれた
- SmartHR社の社内制度が色々充実してていいなあと思った
- あと、それが導入されるに至った風通しの良さとかも
- 残念ながら直近は転職できない事情があるんだが、タイミングが合えば詳しい話聞いてみたい
他にも参加したセッションあるのですが、とりあえずこの辺で。
感想
- 総じてめっちゃいいイベントだったと思う
- 個人的にしんどかったのが、座りっぱなしで尻が痛くなった件
- もう少しセッションの間の休憩をとってもよかったのかも?
- 英語で資料作ってる人多かったなー
- 国際対応していかねばという危機感が自分の中で上がった
- esaのトリさんかわいい
- 2日目のサンドイッチうまかった
- 事情で両日とも懇親会に出れなかったのが残念だった
- 次は最後まで入れるように調整したい
- 投稿日:2019-03-24T16:12:54+09:00
学習記録8 rails(6)
2019/03/24 Ruby on rails の学習記録(6)です。
昨日書き忘れてしまったのですが、3/23がプログラミングスクール初日でした。
午前中にオリエンテーションや自己紹介等々ありまして、午後からそのまま学習に入っています。
年齢も境遇も様々で、いい刺激になりそうです!
【デバッグツール】
binding.pry
- Rails向けに開発されたデバッグツールである「pry-rails」の機能の中で最も使用するもの。
binding.pryという文字列をソースコードの中に記述することで、binding.pryという文字列が存在する部分でRailsの処理を止めることができる- 仮に実装を行なった際にエラーが出たばあいは複数個の
binding.pryを記述することで処理が正しく行われているのかを確認することができる。binding.pryで処理を止めた際にはコンソールとほぼ同じことができるので、変数を出力したい際などには変数を入力することで内容を出力できる。- 処理を再開させたい場合には
exitというコマンドを入力する。- ローカルサーバーを終了させたい場合は
exit!と入力する。
【ルートパス】
- ルートパスとはパスをつけないホスト名だけのURLのこと
- config/routes.rbにルートパス専用のルーティングを設定する
config/routes.rb(例) Rails.application.routes.draw do root 'samples#index' # ルートパスの指定 end
【並び替え】
orderメソッド
- orderメソッドはテーブルから取得してきたインスタンスたちを並び替えるメソッド
- 引数として
("テーブルのカラム名 並び替える順序")という形で指定する。- 並び替える順序には
ASC(昇順)とDESC(降順)があるapp/controllers/contents_controller.rb(例) def index @contents = Content.all.order("id DESC") end
【ページネーション】
- ページネーションとは長い文章を複数のページに分割して、各ページへのリンクを並べアクセスしやすくすること。
- 「kaminari」というGemをインストールすることで簡単にページネーションを実装することができる。
- kaminariを導入するとモデルクラスにpageメソッドというページネーションにおけるページ数を指定するメソッドが定義される。
- ビューのリクエストの際
paramsの中にpageというキーが追加されて、その値がビューで指定したページ番号となるので、pageの引数はparams[:page]となる- perメソッドは1ページあたりに表示する件数を指定できる。
app/controllers/contents_controller.rb(例) def index @contents = Content.all.order("id DESC").page(params[:page]).per(5) # pageキーで取得した値のページに表示する # 1ページに5件まで表示する end
- ページネーションのリンクを表示したいときに使用するのがpaginateメソッド。
- paginateメソッドの引数はコントローラで定義した変数を指定する。
app/views/tweets/index.html.erb(例) <div class="contents row" > 〜中略〜 <%= paginate(@samples) %> <!-- ページネーションを表示する --> </div>
【ログイン機能の実装】
- Railsの場合、「devise」というGemを使用することで簡単に実装が可能。
$ rails g devise:install # deviseの設定ファイルを作成 $ rails g devise user # userモデルを作成Rubyタグ
<%=と%>で囲まれた部分をRubyタグという。- 上記の書き方では処理の返り値をビューに出力する。
- 閉じタグを
-%>とすると余計な改行を取り除くことができる。- 開始タグを
<%とすると処理の返り値をビューに出力しない。link_toメソッド
- 引数を指定することで様々なリンクを生成するメソッド。
- HTMLコード内でリンクを生成するには通常
aタグを使用するが、link_toメソッドを使って記述するとHTMLコードが読み込まれる際にaタグに変換される。sample.html.erb(例) <%= link_to 'トップページへ','/top', class: 'sample' %> # 作成したaタグに`class="sample"`属性を付与するuser_signed_in?メソッド
- deviseでログイン機能を実装すると
user_signed_in?メソッドを使用することができる。- ユーザーがサインインしている場合には
trueを返し、サインインしていない場合はfalseを返す。sample.html.erb(例) <% if user_signed_in? %> # ユーザーがサインインしている場合に実行する処理 <% end %>prefix
- prefixとは、ルーティングのパスが入る変数のことで、コントローラやビューなどで呼び出すことでprefixに入っているパスやURL情報を取得できるようになる。
- 確認するにはターミナルから
rake routesコマンドを実行する。unless文
- ifの反対で、条件式がfalseの場合の処理を記述するのに使われる。
sample.rb(例) puts 'ログインをしてください' unless user_signed_in? # 上下の記述は同義 unless user_signed_in? puts 'ログインをしてください' endridirect_toメソッド
- Railsでは通常、アクション内の処理が終了すると自動的にアクション名と同名のビューが表示されるが、ridirect_toメソッドをアクション内で利用するとそこからさらに別のアクションを実行したり、ビューに遷移させたりできる。
- 引数には
action: :indexという形で、キーがaction:、バリューが:indexであるハッシュを指定する。- このようにバリューにはアクションの名前のシンボル型を利用する。
- 丁寧に書くと
{action: :index}となるが、Railsの内部では特別にはっしゅの括弧{}を省略できる。before_action
- コントローラで
before_action :メソッド名と記述することで、コントローラのアクションが実行される前にそのメソッドを実行することができる。
【devise用のビューファイルの作成】
- deviseでログイン機能を実装すると、ログイン・サインアップ画面は自動的に生成されるがビューファイルは生成されないため、deviseのコマンドを利用してビューファイルを生成する必要がある。
$ rails g devise:views【サインアップ時に新たな項目を登録する】
ユーザーテーブルにカラムを追加する
- カラムを追加するマイグレーションファイルの作成には
rails g migration Addカラム名To追加先テーブル名 追加するカラム名:型と実行する$ rails g migration AddIntroductionToUsers introduction:text # usersテーブルにintroductionカラムをtext型で追加するマイグレーションファイルを作成するスネークケースとキャメルケース
- プログラムが認識する単語の区切り方が2通りある。
- 「スネークケース」… add_to_introduction
- 「キャメルケース」… AddToIntroduction
text_field:maxlengthオプション
- maxlengthは、text_fieldというinputタグを生成するヘルパーメソッドにつけることができるオプション。
sample.html.erb<div class="field"> <%= f.label :nickname %> <em>(6 characters maximum)</em><br /> <%= f.text_field :nickname, autofocus: true, maxlength: "6" %> <!-- text_fieldタグのヘルパーメソッドとしてmaxlength: "6"とすることで 最大6文字と設定する --> </div>configure_permitted_parametersメソッド
- 初期状態で受け取れる項目をストロングパラメーターで設定してある場合において、追加のパラメーターを許可したい場合は
application_controllerにおいてbefore_actionにconfigure_permitted_parametersメソッドを設定するapp/controllers/application_controller.rbclass ApplicationController < ActionController::Base 〜中略〜 before_action :configure_permitted_parameters, if: :devise_controller? def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname]) end end
- 投稿日:2019-03-24T16:12:54+09:00
学習記録8 rails(6) 投稿機能の実装,他
2019/03/24 Ruby on rails の学習記録(6)です。
昨日書き忘れてしまったのですが、3/23がプログラミングスクール初日でした。
午前中にオリエンテーションや自己紹介等々ありまして、午後からそのまま学習に入っています。
年齢も境遇も様々で、いい刺激になりそうです!
【デバッグツール】
binding.pry
- Rails向けに開発されたデバッグツールである「pry-rails」の機能の中で最も使用するもの。
binding.pryという文字列をソースコードの中に記述することで、binding.pryという文字列が存在する部分でRailsの処理を止めることができる- 仮に実装を行なった際にエラーが出たばあいは複数個の
binding.pryを記述することで処理が正しく行われているのかを確認することができる。binding.pryで処理を止めた際にはコンソールとほぼ同じことができるので、変数を出力したい際などには変数を入力することで内容を出力できる。- 処理を再開させたい場合には
exitというコマンドを入力する。- ローカルサーバーを終了させたい場合は
exit!と入力する。
【ルートパス】
- ルートパスとはパスをつけないホスト名だけのURLのこと
- config/routes.rbにルートパス専用のルーティングを設定する
config/routes.rb(例) Rails.application.routes.draw do root 'samples#index' # ルートパスの指定 end
【並び替え】
orderメソッド
- orderメソッドはテーブルから取得してきたインスタンスたちを並び替えるメソッド
- 引数として
("テーブルのカラム名 並び替える順序")という形で指定する。- 並び替える順序には
ASC(昇順)とDESC(降順)があるapp/controllers/contents_controller.rb(例) def index @contents = Content.all.order("id DESC") end
【ページネーション】
- ページネーションとは長い文章を複数のページに分割して、各ページへのリンクを並べアクセスしやすくすること。
- 「kaminari」というGemをインストールすることで簡単にページネーションを実装することができる。
- kaminariを導入するとモデルクラスにpageメソッドというページネーションにおけるページ数を指定するメソッドが定義される。
- ビューのリクエストの際
paramsの中にpageというキーが追加されて、その値がビューで指定したページ番号となるので、pageの引数はparams[:page]となる- perメソッドは1ページあたりに表示する件数を指定できる。
app/controllers/contents_controller.rb(例) def index @contents = Content.all.order("id DESC").page(params[:page]).per(5) # pageキーで取得した値のページに表示する # 1ページに5件まで表示する end
- ページネーションのリンクを表示したいときに使用するのがpaginateメソッド。
- paginateメソッドの引数はコントローラで定義した変数を指定する。
app/views/tweets/index.html.erb(例) <div class="contents row" > 〜中略〜 <%= paginate(@samples) %> <!-- ページネーションを表示する --> </div>
【ログイン機能の実装】
- Railsの場合、「devise」というGemを使用することで簡単に実装が可能。
$ rails g devise:install # deviseの設定ファイルを作成 $ rails g devise user # userモデルを作成Rubyタグ
<%=と%>で囲まれた部分をRubyタグという。- 上記の書き方では処理の返り値をビューに出力する。
- 閉じタグを
-%>とすると余計な改行を取り除くことができる。- 開始タグを
<%とすると処理の返り値をビューに出力しない。link_toメソッド
- 引数を指定することで様々なリンクを生成するメソッド。
- HTMLコード内でリンクを生成するには通常
aタグを使用するが、link_toメソッドを使って記述するとHTMLコードが読み込まれる際にaタグに変換される。sample.html.erb(例) <%= link_to 'トップページへ','/top', class: 'sample' %> # 作成したaタグに`class="sample"`属性を付与するuser_signed_in?メソッド
- deviseでログイン機能を実装すると
user_signed_in?メソッドを使用することができる。- ユーザーがサインインしている場合には
trueを返し、サインインしていない場合はfalseを返す。sample.html.erb(例) <% if user_signed_in? %> # ユーザーがサインインしている場合に実行する処理 <% end %>prefix
- prefixとは、ルーティングのパスが入る変数のことで、コントローラやビューなどで呼び出すことでprefixに入っているパスやURL情報を取得できるようになる。
- 確認するにはターミナルから
rake routesコマンドを実行する。unless文
- ifの反対で、条件式がfalseの場合の処理を記述するのに使われる。
sample.rb(例) puts 'ログインをしてください' unless user_signed_in? # 上下の記述は同義 unless user_signed_in? puts 'ログインをしてください' endridirect_toメソッド
- Railsでは通常、アクション内の処理が終了すると自動的にアクション名と同名のビューが表示されるが、ridirect_toメソッドをアクション内で利用するとそこからさらに別のアクションを実行したり、ビューに遷移させたりできる。
- 引数には
action: :indexという形で、キーがaction:、バリューが:indexであるハッシュを指定する。- このようにバリューにはアクションの名前のシンボル型を利用する。
- 丁寧に書くと
{action: :index}となるが、Railsの内部では特別にはっしゅの括弧{}を省略できる。before_action
- コントローラで
before_action :メソッド名と記述することで、コントローラのアクションが実行される前にそのメソッドを実行することができる。
【devise用のビューファイルの作成】
- deviseでログイン機能を実装すると、ログイン・サインアップ画面は自動的に生成されるがビューファイルは生成されないため、deviseのコマンドを利用してビューファイルを生成する必要がある。
$ rails g devise:views
【サインアップ時に新たな項目を登録する】
ユーザーテーブルにカラムを追加する
- カラムを追加するマイグレーションファイルの作成には
rails g migration Addカラム名To追加先テーブル名 追加するカラム名:型と実行する$ rails g migration AddIntroductionToUsers introduction:text # usersテーブルにintroductionカラムをtext型で追加するマイグレーションファイルを作成するスネークケースとキャメルケース
- プログラムが認識する単語の区切り方が2通りある。
- 「スネークケース」… add_to_introduction
- 「キャメルケース」… AddToIntroduction
text_field:maxlengthオプション
- maxlengthは、text_fieldというinputタグを生成するヘルパーメソッドにつけることができるオプション。
sample.html.erb<div class="field"> <%= f.label :nickname %> <em>(6 characters maximum)</em><br /> <%= f.text_field :nickname, autofocus: true, maxlength: "6" %> <!-- text_fieldタグのヘルパーメソッドとしてmaxlength: "6"とすることで 最大6文字と設定する --> </div>configure_permitted_parametersメソッド
- 初期状態で受け取れる項目をストロングパラメーターで設定してある場合において、追加のパラメーターを許可したい場合は
application_controllerにおいてbefore_actionにconfigure_permitted_parametersメソッドを設定するapp/controllers/application_controller.rbclass ApplicationController < ActionController::Base 〜中略〜 before_action :configure_permitted_parameters, if: :devise_controller? def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname]) end end
- 投稿日:2019-03-24T15:42:21+09:00
Railsチュートリアル(AWSのS3のアクセス権設定)
はじめに
Ruby on Railsチュートリアルの13.4.4の本番環境での画像アップロードについて。
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-image_upload_in_productionAWSのS3を画像アップロードの場所として使うが、チュートリアルではS3の権限設定の手順が省略されている。ここでは、S3の権限設定の手順を記述する。
チュートリアルの手順に沿って、AWSにサインアップ、IAMユーザ作成、S3バケット作成までは実施している前提。
細かいことは置いておいて、とりあえずチュートリアルがこなせれば良い人向け。AWSのS3の権限の設定手順
S3バケットへのパブリックアクセスを許可する
S3バケットの一覧ページを開く -> チュートリアルに沿って作成したバケットを選択。
「パブリックアクセスコントロールリスト (ACL) を管理する」の項目を両方共Offにする。
IAMユーザにS3へのアクセス権を付与する
既存のポリシーを直接アタッチを選択 -> ポリシーのフィルタに'S3'を入力 -> AmazonS3FullAccessを選択して、権限付与
- 投稿日:2019-03-24T15:30:31+09:00
13日目(1):Deviseによるログイン機能付きサイトの作成
12日目:12日目:PostgreSQLを用いたログイン機能付きサイトの続き
環境
- ホストOS: Windows10 Home
- 仮想環境OS: Ubuntu Bento/Bionic
- Ruby:2.51
- Rails: 5.2.2 -主使用gem : devise(参照)
- DB: PostgreSQL
前回やったこと
- nodejsとpostgresqlのインストール
- rails new self_univ3 -d postgresql とbundle install (devise)
- PostgreSQLのパス設定、DB作成
- rails g devise:install
- modelsにdeviseを追加。rails g devise Student
今回
- controllersとviewsを以前の大学データの方から流用
- migrationファイル作成
- rooting変更
実作業
controllersフォルダとviewsフォルダのコピー
# cp -r コピーしたいフォルダの場所 ペースト先migrationファイル作成
rails g migration AddNameToStudents name:string gender:integer age:integer opinion:text # 実行 create db/migrate/20190324043018_add_name_to_students.rbDBに反映
rails db:migraterooting変更
app/confing/routes.rb# 追加 resources :students root to: 'students#index'viewsの変更
app/views/student.html.erb# 今回不要なExamResultNewのリンク削除 # ログアウトリンクの作成 <% @students.each do |student| %> <tr> <td><%= student.try(:name) %></td> <td><%= student.email %></td> <td><%= student.try(:gender) %></td> <td><%= student.try(:age) %></td> <td><%= student.try(:opinion) %></td> <td><%= link_to 'Show', student %></td> <td><%= link_to 'Edit', edit_student_path(student) %></td> <td><%= link_to 'Destroy', student, method: :delete, data: { confirm: 'Are you sure?' } %></td> <%= link_to 'Log Out', destroy_student_session_path, method: :delete %> </tr> <% end %>コントローラ変更
app/controllers/student_controller.rbclass StudentsController < ApplicationController before_action :authenticate_student!次からは、このページに、以前の大学データを組み合わせる
- 投稿日:2019-03-24T14:43:11+09:00
railsチュートリアル 第八章
はじめに
railsチュートリアルで理解しにくいところや、詰まったところを書いていく記事になります。
なので、手順を示す記事とはなっていません。セッション
HTTPはステートレス (Stateless) なプロトコルであり、ブラウザのあるページから別のページに移動したときに、ユーザーのIDを保持しておく手段がHTTPプロトコル内にはまったくない。
よって、ユーザーログインの必要なwebアプリケーションではセッション (Session) と呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザとRailsサーバーなど) に別途設定する必要がある。
セッションコントローラ
まずはログインのフォームをnewアクションで処理するような、コントローラを作成する。
$ rails generate controller Sessions newcreateやdestroyといったアクションも扱うが、ビューファイルが必要ないため、ここではnewアクションだけを一緒に生成しておく。
routes.rbでは、Usersリソースのようにresourcesメソッドを使ってRESTfulなルーティングを自動的にフルセットで利用することはないので、「名前付きルーティング」だけを使う。
routes.rbRails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' get '/signup', to: 'users#new' #追加 get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' resources :users endこのように名前付きルーティングが増えてきた場合は、以下のコマンドを打つことで現状のルーティングを確認することができる。
$ rails routesログインフォーム
コントローラとルーティングを定義した次はビューファイルを整えていく。
ログインフォームでは、会員登録をまだしていない人のために、登録のリンクも作っておく。
app/views/sessions/new.html.erb<% provide(:title, "Log in") %> <h1>Log in</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(:session, url: login_path) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.submit "Log in", class: "btn btn-primary" %> <% end %> <p>New user? <%= link_to "Sign up now!", signup_path %></p> </div> </div>このソースではform_forタグを使用しているが、form_tagも同じく使用可能
セッションフォームとユーザー登録フォームの違いとして、セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない。
したがって、新しいセッションフォームを作成するときには、form_forヘルパーに追加の情報を独自に渡す必要がある。↓
<%= form_for(:session, url: login_path) do |f| %>以下に、 HTMLフォームを示しておく。
login.form<form accept-charset="UTF-8" action="/login" method="post"> <input name="utf8" type="hidden" value="✓" /> <input name="authenticity_token" type="hidden" value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" /> <label for="session_email">Email</label> <input class="form-control" id="session_email" name="session[email]" type="text" /> <label for="session_password">Password</label> <input id="session_password" name="session[password]" type="password" /> <input class="btn btn-primary" name="commit" type="submit" value="Log in" /> </form>コントローラで、それぞれのアクションを定義する。
app/controllers/sessions_controller.rbclass SessionsController < ApplicationController def new end def create render 'new' end def destroy end endまた、createアクションでユーザーを検証する処理を書く。
app/controllers/sessions_controller.rbdef create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) # ユーザーログイン後にユーザー情報のページにリダイレクトする else # エラーメッセージを作成する render 'new' end endセッションでは、ユーザー登録のようにActive Recordのモデルを使っていないため、自分でエラーメッセージを作る必要がある。
app/controllers/sessions_controller.rbdef create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) # ユーザーログイン後にユーザー情報のページにリダイレクトする else #追加 flash.now[:danger] = 'Invalid email/password combination' render 'new' end end↑では、flash[:danger]としてしまうと、一度表示されたメッセージが消えなくなってしまうため
flash.now[:danger]
を使い、一度だけ表示されるようにする。ログイン
まず、SessionsHelperをコントローラに読みこますことで、セッションに必要なメソッドを使えるようにしておく。
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper endlog_inメソッド
railsで定義済みのsessionメソッドを使って、Sessionsヘルパーにlog_inメソッドを定義する。
app/helpers/sessions_helper.rbmodule SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id end endこれによりsessionコントローラのcreateアクションを整えることができる。
app/controllers/sessions_controller.rbdef create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) #追加 log_in user redirect_to user else flash.now[:danger] = 'Invalid email/password combination' render 'new' end end
redirect_to user
はrailsでは自動的に以下のルーティングに変換される。
redirect_to user現在のユーザー
セッションIDに対応するユーザーネームを取り出す、current_userメソッドを定義する。
if @current_user.nil? @current_user = User.find_by(id: session[:user_id]) else @current_user end↑のようにすれば、current_userを定義することができるが、もっとコンパクトに定義することができる。
「||=」を使うことで以下のようになる。@current_user ||= User.find_by(id: session[:user_id])「||=」は左辺がnilあれば、右辺を代入するという使い方ができる。
よって、current_userメソッドは以下のように定義することができる。
app/helpers/sessions_helper.rb# 現在ログイン中のユーザーを返す (いる場合) def current_user if session[:user_id] @current_user ||= User.find_by(id: session[:user_id]) end endレイアウトリンクの変更
ユーザーがログインしているかしていないかで、レイアウトを変更する必要がある。
下記のようにlogged_in?のメソッドを使えば、レイアウトを変更する必要がある。
<% if logged_in? %> # ログインユーザー用のリンク <% else %> # ログインしていないユーザー用のリンク <% end %>よって、logged_in?を定義する。
current_userがnilでなければログインしている、という風に解釈できるので↓のように定義できる。app/helpers/sessions_helper.rbmodule SessionsHelper # 渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id end # 現在ログイン中のユーザーを返す (いる場合) def current_user if session[:user_id] @current_user ||= User.find_by(id: session[:user_id]) end end # ユーザーがログインしていればtrue、その他ならfalseを返す #追加 def logged_in? !current_user.nil? end endユーザー登録時にログイン状態
登録が終われば自動的にログインがすんでいる状態になるようにusersコントローラのcreateアクションを追加修正しておく。
app/controllers/users_controller.rbdef create @user = User.new(user_params) if @user.save #追加 log_in @user flash[:success] = "Welcome to the Sample App!" redirect_to @user else render 'new' end endログアウト
session.delete(:user_id)
↑で、現在のユーザーをnilにすることができるので、logoutメソッドを以下のように定義する。app/helpers/sessions_helper.rbmodule SessionsHelper # 現在のユーザーをログアウトする def log_out session.delete(:user_id) #セキュリティ上、念の為次の一行も追加する @current_user = nil end end次に、Sessionsコントローラのdestroyアクションを定義する。
app/controllers/sessions_controller.rbclass SessionsController < ApplicationController . . . def destroy log_out redirect_to root_url end end終わりに
第八章では、Sessionコントローラでログイン・ログアウトを実装した。
webアプリケーションには必要な機能のためしっかり理解しておこうと思う。
- 投稿日:2019-03-24T13:42:42+09:00
rails s 時のエラー (Gem::GemNotFoundException)
Railsアプリ作成中に、rails sでサーバー起動しようとしたら、Gem::GemNotFoundException エラー。
とくになにかしたわけではないが、突如発生。
stack overflowで似たようなエラー事例があったので、参考にし、解決。
参考:stack overflow
rails s 時のエラー。Traceback (most recent call last): 4: from /home/ec2-user/.rvm/gems/ruby-2.6.0/bin/ruby_executable_hooks:24:in `<main>' 3: from /home/ec2-user/.rvm/gems/ruby-2.6.0/bin/ruby_executable_hooks:24:in `eval' 2: from /home/ec2-user/.rvm/gems/ruby-2.6.0/bin/rails:23:in `<main>' 1: from /home/ec2-user/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path' /home/ec2-user/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': can't find gem railties (>= 0.a) with executable rails (Gem::GemNotFoundException)以下で解決。
gem install bundler bundle install
- 投稿日:2019-03-24T12:48:49+09:00
kubernetes内で実行中のrails(puma)の実行された場所を知る
まずはpodを調べる
$kubectl get pod NAME READY STATUS RESTARTS AGE mysql-76c755cc88-bv9wf 1/1 Running 5 12m rails-679bf9b89c-rcl26 1/1 Running 0 2m26s
kubectl exe -it <pod> /bin/bashでログイン$kubectl exec -it rails-679bf9b89c-rcl26 /bin/bashpumaのpidを調べる。この例だと、
pid=1root@rails-679bf9b89c-rcl26:/myapp# ps aux | grep puma root 1 0.3 2.9 1142908 59432 ? Ssl 03:27 0:02 puma 3.12.1 (tcp://0.0.0.0:3000) [myapp] root 77 0.0 0.0 11112 852 pts/0 S+ 03:41 0:00 grep puma実行されている場所を調べる
root@rails-679bf9b89c-rcl26:/myapp# ls -l /proc/1/cwd lrwxrwxrwx. 1 root root 0 Mar 24 03:41 /proc/1/cwd -> /myapp root@rails-679bf9b89c-rcl26:/myapp# cat /proc/1/cmdline puma 3.12.1 (tcp://0.0.0.0:3000) [myapp]root@rails-679bf9b89c-rcl26:/myapp#
- 投稿日:2019-03-24T12:34:27+09:00
VScodeでRails開発をしていたらerbファイルでEmmet(エメット)が使えなかったので使えるようにした
VScodeでRails開発してるんだけど、erbファイルでEmmet使えない!という人や
そもそもHTMLファイルでもEmemt 使えないよ! という人は以下を試してみてください。VScodeの設定
まずVSCodeの左下の歯車マークをクリック
次に、settingsをクリック検索窓で、
"Trigger Expansion On Tab" と検索
Emmet:Trigger Expansion on tab の左のチェックマークを入れるこれでHtmlファイルではEmmetが使えるようになったはず。
セッティングファイルの記述
さらに続けて、検索窓で
”edit in settings.json”と検索
少し小さい文字の edit in settings.jsonをクリックファイルが開くので以下を追記
settings.json{ "workbench.iconTheme": "vscode-icons", "window.zoomLevel": 0, "emmet.triggerExpansionOnTab": true, # ここから記述 "emmet.includeLanguages": { "erb": "html" } }これで試しに erbファイルで
h1 と打ってからTabキーを叩いてください。それでうまくいかなければ,
拡張機能のインストール
VScode画面上の左上側にあるアイコンの一番下の四角い拡張機能をクリック
Rails と検索してRailsをインストールコレです⬇︎
https://marketplace.visualstudio.com/items?itemName=bung87.rails
上記全て終えたら、晴れてEmmetが使用できるはずです。
※EmmetはVScodeじゃなくても使えます。
たくさんコードを書いてみんなで爆速になりましょう
ごあいさつ
実はこの記事がQiitaでの初投稿になります。
1人でも多くの人のお役に立てたらと願っていますが、至らない所あるかもしれません。
その際は遠慮なく教えていただけると幸いです。参考
- 投稿日:2019-03-24T12:12:49+09:00
deviseメモ
deviseとは、認証機能を追加できるgem
参考サイト:https://web-camp.io/magazine/archives/16811必要なファイルの生成
rails g devise:installdeviseで呼び出されるビューの生成
rails g devise:views認証処理を行うためにユーザー情報を登録するためのテーブルを生成
rails g devise:users生成されるファイル
invoke active_record
create db/migrate/20180909134145_devise_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/users_test.rb
create test/fixtures/users.yml
insert app/models/user.rb
route devise_for :usersオーバーライドとは?
参考サイト:https://rails-study.net/extend/deviseのコントローラーのカスタマイズ
参考サイト:https://madogiwa0124.hatenablog.com/entry/2017/11/26/221657rails g devise:controllers users
Devise::SessionsControllerを継承したコントローラーが生成される。
元のdeviseのユーザー作成のコントローラーを継承したファイルが生成されるらしい。satounoMacBook-Pro:freemarket_47nagoya satou$ rake routes Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit user_registration PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy POST /users(.:format) devise/registrations#create rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#createroutes.rbRails.application.routes.draw do devise_for :users, :controllers => { :registrations => 'users/registrations', :sessions => 'users/sessions' } endsatounoMacBook-Pro:freemarket_47nagoya satou$ rake routes Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) users/sessions#new user_session POST /users/sign_in(.:format) users/sessions#create destroy_user_session DELETE /users/sign_out(.:format) users/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) users/registrations#cancel new_user_registration GET /users/sign_up(.:format) users/registrations#new edit_user_registration GET /users/edit(.:format) users/registrations#edit user_registration PATCH /users(.:format) users/registrations#update PUT /users(.:format) users/registrations#update DELETE /users(.:format) users/registrations#destroy POST /users(.:format) users/registrations#create rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#createroutes.rbに記述することで、今まで見えなかった(いじれなかった)deviseのコントローラーをカスタマイズすることができる!!
devise/sessions#new => users/sessions#new devise/sessions#create => users/sessions#create devise/sessions#destroy => users/sessions#destroy---ここから下はどうでもいいので無視してください---
application.html.erb#これと <%= render partial: 'layouts/header' %> <% flash.each do |key, value| %> <%= content_tag(:div, value, class: "flash flash__#{key}")%> <% end %> #これはどちらも同じかも? <% if flash[:notice] %> <p class="notice"><%= flash[:notice] %></p> <% end %> <% if flash[:alert] %> <p class="alert"><%= flash[:alert] %></p> <% end %>
- 投稿日:2019-03-24T10:54:23+09:00
railsチュートリアル 第七章
はじめに
railsチュートリアルで理解しにくいところや、詰まったところを書いていく記事になります。
なので、手順を示す記事とはなっていません。デバッグ
共通のビューファイルにdebugメソッドを書くことで、各ページでデバッグ用の情報が表示されるようになる。
デバッグを出力することで、ページの状態が把握しやすくなる。
app/views/layouts/application.html.erb<!DOCTYPE html> <html> . . . <body> . . <%= debug(params) if Rails.env.development? %> </body> </html>
if Rails.env.development?
としておくことで、開発環境だけで表示されるようになる。Usersリソース
config/routes.rbresources :usersroutesファイルに↑の一行を追加するだけで、ユーザーのURLを生成するための多数の名前付きルート (5.3.3) と共に、RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになる。
HTTPリクエスト URL アクション 名前付きルート 用途 GET /users index users_path すべてのユーザーを一覧するページ GET /users/1 show user_path(user) 特定のユーザーを表示するページ GET /users/new new new_user_path ユーザーを新規作成するページ (ユーザー登録) POST /users create users_path ユーザーを作成するアクション GET /users/1/edit edit edit_user_path(user) id=1のユーザーを編集するページ PATCH /users/1 update user_path(user) ユーザーを更新するアクション DELETE /users/1 destroy user_path(user) ユーザーを削除するアクション ただ、このルーティング先には表示するページとアクションが用意されていないから、各々準備する必要がある。
ユーザー登録フォーム
ユーザー情報を入力するために、
form_forヘルパーメソッドを使う。
このメソッドはActive Recordのオブジェクトを取り込み、そのオブジェクトの属性を使ってフォームを構築する。まず、新規ユーザーのためのユーザー登録フォーム
は下のようなものになる。app/controllers/users_controller.rbdef new @user = User.newend endapp/views/users/new.html.erb% provide(:title, 'Sign up') %> <h1>Sign up</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(@user) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %> </div> </div>例えば
<%= f.label :name %>
<%= f.text_field :name %>
という埋め込みruby部分では、
以下のようなHTMLを生成している。
<label for="user_name">Name</label>
<input id="user_name" name="user[name]" type="text" />
ブラウザからソースを表示することもできるがフォーム部分をHTMLで表すと以下のようになる。
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post"> <input name="utf8" type="hidden" value="✓" /> <input name="authenticity_token" type="hidden" value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" /> <label for="user_name">Name</label> <input id="user_name" name="user[name]" type="text" /> <label for="user_email">Email</label> <input id="user_email" name="user[email]" type="email" /> <label for="user_password">Password</label> <input id="user_password" name="user[password]" type="password" /> <label for="user_password_confirmation">Confirmation</label> <input id="user_password_confirmation" name="user[password_confirmation]" type="password" /> <input class="btn btn-primary" name="commit" type="submit" value="Create my account" /> </form>
<form action="/users" class="new_user" id="new_user" method="post">
の部分では、/users に対してHTTPのPOSTリクエスト送信する、といった指示をしてる。このフォームの値はcreateアクションに送られるようになる。
createアクション内では、
@user = User.new(params[:user])のように@userを定義すればいいが、paramsハッシュ全体を初期化するという行為はセキュリティ上よろしくない。
この場合、user_paramsという外部メソッドを使ういことが慣習とされている。
よってcreateアクションは以下のようなものとなる。app/controllers/users_controller.rbclass UsersController < ApplicationController . . . def create @user = User.new(user_params) if @user.save # 保存の成功をここで扱う。 else render 'new' end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end end↑privateキーワードを使って外部からはprivate内のメソッドを使えないようにする。
user_paramsを定義することにより、
paramsハッシュでは:user属性を必須とし、名前、メールアドレス、パスワード、パスワードの確認の属性をそれぞれ許可し、それ以外を許可しないようになる。flash
ユーザー登録が成功すれば、登録完了後に表示されるページにメッセージを表示し、二度目以降には表示しないというものがよく見かける。
そういったメッセージ(フラッシュメッセージ)を表示したい場合は、flash変数を使う。
例:
app/controllers/users_controller.rbdef create @user = User.new(user_params) if @user.save flash[:success] = "Welcome to the Sample App!" redirect_to @user else render 'new' end end成功時はflash[:success]の:sucessというキーを使うことが慣習とされている。
また、このフラッシュメッセージをサイト全体で表示したい場合はapplication.html.erbは次のようなコードとなる。
app/views/layouts/application.html.erb<!DOCTYPE html> <html> . . . <body> <%= render 'layouts/header' %> <div class="container"> <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> . . . </body> </html>
<div class="alert alert-<%= message_type %>"><%= message %></div>
の部分では、メッセージの種類を適応するCSSによって、変更するようにしている。例えば、
<div class="alert alert-<%= message_type %>"><%= message %></div>
というコードをHTMLで表示すると
<div class="alert alert-success">Welcome to the Sample App!</div>というふうになる。
おわり
この章では、主にユーザー登録、フラッシュメッセージを見てきた。
もう一度、7章をするときはエラーメッセージやSSLについてもう少し深く見ていこうと思う。
- 投稿日:2019-03-24T04:57:53+09:00
Rails開発をVScodeしていたらerbファイルでEmmet(エメット)が使えなかったので使えるようにした
VScodeでRails開発してるんだけど、erbファイルでEmmet使えない!という人や
そもそもHTMLファイルでもEmemt 使えないよ! という人は以下を試してみてください。VScodeの設定
まずVSCodeの左下の歯車マークをクリック
次に、settingsをクリック検索窓で、
"Trigger Expansion On Tab" と検索
Emmet:Trigger Expansion on tab の左のチェックマークを入れるこれでHtmlファイルではEmmetが使えるようになったはず。
セッティングファイルの記述
さらに続けて、検索窓で
”edit in settings.json”と検索
少し小さい文字の edit in settings.jsonをクリックファイルが開くので以下を追記
settings.json{ "workbench.iconTheme": "vscode-icons", "window.zoomLevel": 0, "emmet.triggerExpansionOnTab": true, # ここから記述 "emmet.includeLanguages": { "erb": "html" } }これで試しに erbファイルで
h1 と打ってからTabキーを叩いてください。それでうまくいかなければ,
拡張機能のインストール
VScode画面上の左上側にあるアイコンの一番下の四角い拡張機能をクリック
Rails と検索してRailsをインストールコレです⬇︎
https://marketplace.visualstudio.com/items?itemName=bung87.rails
上記全て終えたら、晴れてEmmetが使用できるはずです。
※EmmetはVScodeじゃなくても使えます。
たくさんコードを書いてみんなで爆速になりましょう
ごあいさつ
実はこの記事がQiitaでの初投稿になります。
1人でも多くの人のお役に立てたらと願っていますが、至らない所あるかもしれません。
その際は遠慮なく教えていただけると幸いです。参考
- 投稿日:2019-03-24T00:55:25+09:00
バックエンド:railsAPI(heroku),フロントエンド:Vue(netlify)で連携
概要
バックエンドでrails、フロントではVueを使いたい。
連携させるにはrailsでAPIサーバを作成してVueで呼ぶのがいいかと考えました。
しかしまとまったやり方が調べても出てこなかったので自分なりのやり方をまとめました。
rails,vue-cliでプロジェクト作成可能な状態であり
heroku,netlifyのアカウントは登録済みとします。環境
Rails: 5.1.6.2
vue-cli: 3.3.0
heroku: 7.22.7 darwin-x64 node-v11.10.1手順
railsプロジェクトの準備
railsアプリを作成する
まずはAPI用のrailsプロジェクトを作成$ rails new ***-api --api $ cd ***-apigemファイルの以下を変更し bundle install します。
生成時からの修正点は以下
- コメントアウト部分を削除
- gemファイルの本番環境にpostgreを追加
- sqliteを開発環境に限定
- herokuはsqliteでなくpostgreを使用するため
- rack-corsを追加
- クロスドメイン対策(後述)で使用
Gemfilesource 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end gem 'rails', '~> 5.1.6', '>= 5.1.6.2' gem 'puma', '~> 3.7' gem 'rack-cors' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'sqlite3' gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end group :production do gem 'pg' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]config/initializers/cors.rbのコメントアウトを解除し
originsを'*'に修正します。
先ほどのrack-corsの追加と本設定をしないとvueからAPIを呼び出した際にエラーが発生します。
(参考)【Ruby on Rails】「No 'Access-Control-Allow-Origin' header is present on the requested resource」を回避する.config/initializers/cors.rbRails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' #コメントアウトを解除し、ここを'*'に修正 resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end endjsonで渡す用のデータを作成 今回はサンプルとしてUserテーブルを作ります
$ rails generate scaffold user name:string email:string password_digest:string $ rails db:migrate適当なデータを入れておきましょう
$ rails console >user1 = User.new(name:"hoge_name",email:"hoge_email",password_digest:"hoge_pass") >user1.save >user2 = User.new(name:"fuga_name",email:"fuga_email",password_digest:"fuga_pass") >user2.save >User.all >exit取得できるか確認します。
$ curl -X GET -H 'Content-Type:application/json' http://0.0.0.0:3000/users #結果表示以上ができたらherokuにアップロードしておきましょう。
#commit,herokuログイン後 $ git push heroku master $ heroku run rake db:migrate #herokuのdb準備herokuにアップロードされたアドレス https://***.herokuapp.com/users
にアクセスしjsonデータが表示されればOKです。Vueプロジェクトの準備.
Vueプロジェクトを作成します。作成後はとりあえず動くか確認しましょう。
プリセット設定はなんでも大丈夫だと思います。$ vue create *** ~~プリセット設定~~ $ cd *** $ yarn serve #動作確認今回はサンプルとして初期表示ページでAPIからjsonを取得して表示したいと思います。
json取得のためaxiosをインストールします$ npm install axios --save次にsrc/components/HelloWorld.vueを以下のように編集します。
src/components/HelloWorld.vue<template> <div class="hello"> <h1>{{ msg }}</h1> <h1>{{ info }}</h1> </div> </template> <script> import axios from 'axios' export default { name: 'HelloWorld', props: { msg: String, }, data() { return { info: "" } }, mounted () { axios.get("http://localhost:8080/users").then(response => (this.info = response)) } } </script>上記を保存し yarn serve すると、ページ上にuserのデータが表示されます。
ここまでできたらvueプロジェクトもgithub等にpushしnetlifyで公開してしまいましょう。
(参考)vue-cliでwebアプリケーションを作って、Netlifyを使って無料で爆速でリリースした話(リリースまででOK)本番環境の連携
さて、ここまででローカルでの連携は終わりました。
しかしnetlifyで公開したページを見るとjsonデータは表示されずエラーを吐いています。
axios.getでlocalhostを参照しているので当然ですね。
ここからは本番環境(heroku-netlify間)でも連携するように修正していきます。vueプロジェクトのフォルダに環境変数を設定するフォルダを作成します。
$ touch .env $ touch .env.development内容に以下を追記します。
.envVUE_APP_BASE_API=https://(作成したherokuアプリの名前).herokuapp.com/.env.developmentVUE_APP_BASE_API=http://localhost:3000/HelloWorld.vueのaxios.getについて、参照先を環境変数から取得するよう修正します
src/components/HelloWorld.vuemounted () { axios.get(process.env.VUE_APP_BASE_API + "users").then(response => (this.info = response)) }process.env.VUE_APP_*** は先ほど作成したenvファイルから値を取得します
yarn serveで起動した時は.env.development、
yarn buildで作成したファイルは.envから値を取得します。
これによりローカルではlocalhost,netlify上ではheroku-appを参照するようになります。
(参考)vue-cli 3.0 で作成したプロジェクトの環境変数(.env)の設定後述しますが、環境変数には他の人に見られたくないものを書いたりするので
gitignoeに忘れずに登録しておきましょう。gitignore.env .env.*API認証の設定
現在の状況ではURLを知っていれば誰でもuserの中身がわかってしまいます。
なので認証をつけてVueのプロジェクトからしか見られないようにします。application_controllers.rbに認証機能を追記します。
これによりいずれのアクションが起動する際も認証が必要となります。app/controllers/application_controllers.rbclass ApplicationController < ActionController::API include ActionController::HttpAuthentication::Token::ControllerMethods before_action :authenticate def authenticate #環境変数API_TOKENがrailsとvueで一致しないと認証されない authenticate_or_request_with_http_token do |token,options| token == ENV.fetch('API_TOKEN') end end endAPI_TOKENは環境変数で設定します。
こんかいは先ほどと違い、自身の端末に設定します。$ export API_TOKEN=xxxxx(任意の文字列 予測できないランダムなものが望ましい)本番環境(heroku)でも同様の文字列を環境変数に設定します。
$ heroku config:set API_TOKEN=xxxxx次にvueプロジェクトで同様のトークンキーを設定します。
.envVUE_APP_BASE_API=https://(作成したherokuアプリの名前).herokuapp.com/ VUE_APP_API_TOKEN=xxxxx.env.developmentVUE_APP_BASE_API=http://localhost:3000/ VUE_APP_API_TOKEN=xxxxx最後にHelloWorld.vueのmountedを編集します
headersにトークンを追加します。src/components/HelloWorld.vuemounted () { axios .get(process.env.VUE_APP_BASE_API + "users",{ headers : { "Authorization": "Token " + process.env.VUE_APP_API_TOKEN } }).then(response => (this.info = response)) }上記で本番環境でもAPI連携が可能になりました。
おわりに
APIサーバ作るのも初めてだったのでなにか不備があればご指摘お願いします。
テスト環境とかも作らなければ…そのたお世話になった記事
- 投稿日:2019-03-24T00:55:25+09:00
railsAPI+Vue.jsの開発環境構築(heroku,netlify使用)
概要
バックエンドでrails、フロントではVueを使いたい。
連携させるにはrailsでAPIサーバを作成してVueで呼ぶのがいいかと考えました。
しかしまとまったやり方が調べても出てこなかったので自分なりのやり方をまとめました。
rails,vue-cliでプロジェクト作成可能な状態であり
heroku,netlifyのアカウントは登録済みとします。環境
Rails: 5.1.6.2
vue-cli: 3.3.0
heroku: 7.22.7 darwin-x64 node-v11.10.1手順
railsプロジェクトの準備
railsアプリを作成する
まずはAPI用のrailsプロジェクトを作成$ rails new ***-api --api $ cd ***-apigemファイルの以下を変更し bundle install します。
生成時からの修正点は以下
- コメントアウト部分を削除
- gemファイルの本番環境にpostgreを追加
- sqliteを開発環境に限定
- herokuはsqliteでなくpostgreを使用するため
- rack-corsを追加
- クロスドメイン対策(後述)で使用
Gemfilesource 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end gem 'rails', '~> 5.1.6', '>= 5.1.6.2' gem 'puma', '~> 3.7' gem 'rack-cors' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'sqlite3' gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end group :production do gem 'pg' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]config/initializers/cors.rbのコメントアウトを解除し
originsを'*'に修正します。
先ほどのrack-corsの追加と本設定をしないとvueからAPIを呼び出した際にエラーが発生します。
(参考)【Ruby on Rails】「No 'Access-Control-Allow-Origin' header is present on the requested resource」を回避する.config/initializers/cors.rbRails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' #コメントアウトを解除し、ここを'*'に修正 resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end endjsonで渡す用のデータを作成 今回はサンプルとしてUserテーブルを作ります
$ rails generate scaffold user name:string email:string password_digest:string $ rails db:migrate適当なデータを入れておきましょう
$ rails console >user1 = User.new(name:"hoge_name",email:"hoge_email",password_digest:"hoge_pass") >user1.save >user2 = User.new(name:"fuga_name",email:"fuga_email",password_digest:"fuga_pass") >user2.save >User.all >exit取得できるか確認します。
$ curl -X GET -H 'Content-Type:application/json' http://0.0.0.0:3000/users #結果表示以上ができたらherokuにアップロードしておきましょう。
#commit,herokuログイン後 $ git push heroku master $ heroku run rake db:migrate #herokuのdb準備herokuにアップロードされたアドレス https://***.herokuapp.com/users
にアクセスしjsonデータが表示されればOKです。Vueプロジェクトの準備.
Vueプロジェクトを作成します。作成後はとりあえず動くか確認しましょう。
プリセット設定はなんでも大丈夫だと思います。$ vue create *** ~~プリセット設定~~ $ cd *** $ yarn serve #動作確認今回はサンプルとして初期表示ページでAPIからjsonを取得して表示したいと思います。
json取得のためaxiosをインストールします$ npm install axios --save次にsrc/components/HelloWorld.vueを以下のように編集します。
src/components/HelloWorld.vue<template> <div class="hello"> <h1>{{ msg }}</h1> <h1>{{ info }}</h1> </div> </template> <script> import axios from 'axios' export default { name: 'HelloWorld', props: { msg: String, }, data() { return { info: "" } }, mounted () { axios.get("http://localhost:8080/users").then(response => (this.info = response)) } } </script>上記を保存し yarn serve すると、ページ上にuserのデータが表示されます。
ここまでできたらvueプロジェクトもgithub等にpushしnetlifyで公開してしまいましょう。
(参考)vue-cliでwebアプリケーションを作って、Netlifyを使って無料で爆速でリリースした話(リリースまででOK)本番環境の連携
さて、ここまででローカルでの連携は終わりました。
しかしnetlifyで公開したページを見るとjsonデータは表示されずエラーを吐いています。
axios.getでlocalhostを参照しているので当然ですね。
ここからは本番環境(heroku-netlify間)でも連携するように修正していきます。vueプロジェクトのフォルダに環境変数を設定するフォルダを作成します。
$ touch .env $ touch .env.development内容に以下を追記します。
.envVUE_APP_BASE_API=https://(作成したherokuアプリの名前).herokuapp.com/.env.developmentVUE_APP_BASE_API=http://localhost:3000/HelloWorld.vueのaxios.getについて、参照先を環境変数から取得するよう修正します
src/components/HelloWorld.vuemounted () { axios.get(process.env.VUE_APP_BASE_API + "users").then(response => (this.info = response)) }process.env.VUE_APP_*** は先ほど作成したenvファイルから値を取得します
yarn serveで起動した時は.env.development、
yarn buildで作成したファイルは.envから値を取得します。
これによりローカルではlocalhost,netlify上ではheroku-appを参照するようになります。
(参考)vue-cli 3.0 で作成したプロジェクトの環境変数(.env)の設定後述しますが、環境変数には他の人に見られたくないものを書いたりするので
gitignoeに忘れずに登録しておきましょう。gitignore.env .env.*API認証の設定
現在の状況ではURLを知っていれば誰でもuserの中身がわかってしまいます。
なので認証をつけてVueのプロジェクトからしか見られないようにします。application_controllers.rbに認証機能を追記します。
これによりいずれのアクションが起動する際も認証が必要となります。app/controllers/application_controllers.rbclass ApplicationController < ActionController::API include ActionController::HttpAuthentication::Token::ControllerMethods before_action :authenticate def authenticate #環境変数API_TOKENがrailsとvueで一致しないと認証されない authenticate_or_request_with_http_token do |token,options| token == ENV.fetch('API_TOKEN') end end endAPI_TOKENは環境変数で設定します。
こんかいは先ほどと違い、自身の端末に設定します。$ export API_TOKEN=xxxxx(任意の文字列 予測できないランダムなものが望ましい)本番環境(heroku)でも同様の文字列を環境変数に設定します。
$ heroku config:set API_TOKEN=xxxxx次にvueプロジェクトで同様のトークンキーを設定します。
.envVUE_APP_BASE_API=https://(作成したherokuアプリの名前).herokuapp.com/ VUE_APP_API_TOKEN=xxxxx.env.developmentVUE_APP_BASE_API=http://localhost:3000/ VUE_APP_API_TOKEN=xxxxx最後にHelloWorld.vueのmountedを編集します
headersにトークンを追加します。src/components/HelloWorld.vuemounted () { axios .get(process.env.VUE_APP_BASE_API + "users",{ headers : { "Authorization": "Token " + process.env.VUE_APP_API_TOKEN } }).then(response => (this.info = response)) }上記で本番環境でもAPI連携が可能になりました。
おわりに
APIサーバ作るのも初めてだったのでなにか不備があればご指摘お願いします。
テスト環境とかも作らなければ…そのたお世話になった記事
- 投稿日:2019-03-24T00:13:14+09:00
railsで複合主キーをやめるmigration
概要
railsのシステムでは複合主キーは認められてません。全てテーブルがidという自動採番の主キーを持つのが決まりです。
それはわかっていたのですが、多対多の中間テーブル、例えば、モデルで言えば下記ようなものです。
class User < ApplicationRecord has_many :user_skills has_many :skills, inverse_of: :users, through: :user_skills end class Skill < ApplicationRecord has_many :user_skills has_many :users, inverse_of: :skills, through: :user_skills end # これが私のいうところの中間テーブルです。 class UserSkill < ApplicationRecord belongs_to :skill belongs_to :user endこれだけは自分の経験上納得がいかないというか、先入観も大きかったと思いますが、下記の理由で複合主キーを使っていました。
- 必要のないIDの分の無駄な容量が増える
- 頻繁にすげ替えが行われるとIDが枯渇するんじゃないか?
ただ、それで運用してみていくつかの問題がわかっています。
- UserSkillをレシーバーにして削除ができない。
- UserSkillをレシーバーにして更新ができない。
- UserSkillをeager_loadできない。
他にもあるかもしれませんが、今の所私が把握してるのは上記です。
問題点に関して言えば容量はだいぶ低価格になってきてるし、アクセス速度の早いステレージもだいぶ低価格になってきてます。IDの枯渇に関してはbigintであれば一般的なシステムでは多分問題にならないので気にしなくてもいいかも。
こういうGemもあるのですが、いつまでメンテナンスされるかわからないし、メンテナンスが止まったからといって自分でどうにかならないくらい深いところに手を入れてるGEMだと思いました。
そこでRailsの恩恵を受けた方がいいと思うテーブルに関しては複合主キーをやめてみようと思い、そのmigarationを書いてみました。
class UserSkillToSinglePKey < ActiveRecord::Migration[5.2] def change remove_foreign_key :user_skill, :skill remove_foreign_key :user_skill, :user reversible do |change| change.up do execute 'ALTER TABLE user_skill DROP PRIMARY KEY' end change.down do execute 'ALTER TABLE user_skill ADD PRIMARY KEY (skill_id, user_id)' end end add_column :user_skill, :id, :primary_key, unsigned: true, first: true add_foreign_key :user_skill, :skill add_foreign_key :user_skill, :user end endポイントとしては生SQLのところで
reversibleを使ってる点、foreign_keyを外さないと主キーをDROPできないことと、add_column :user_skill, :id, :primary_keyとすると、勝手にAUTOINCREMENTになるところくらいですかね。もし、運用しているサービスにやるのであれば、テーブルロックがかかるのでレコード数によっては問題になるかもしれません。また、一度外部制約を外すので不整合が起きる可能性があるとは思いますのでその点のも考慮に入れてください。







