- 投稿日:2019-05-23T23:18:49+09:00
Windows10 で WSL を使って Rails 環境を構築したときのメモ
はじめに
本記事の趣旨はタイトルの通り。
WSL( Windows Subsystem for Linux ) 環境で Rails 環境を構築したときのメモ。本記事を実施した環境
ツール バージョン 確認方法 Windows 10 Home 64bit Windows Subsystem for Linux Ubuntu 18.04.2 LTS (Bionic Beaver) $ cat /etc/os-release rbenv 1.1.2-2 $ rbenv -v Ruby 2.6.1p33 $ ruby -v Rails 5.2.3 $ rails -v gem 3.0.1 $ gem -v Bundler 2.0.1 $ bundler -v n 4.1.0 $ n --version Node.js 10.15.3 $ node -v npm 6.4.1 $ npm -v WSL( Windows Subsystem for Linux ) を入れよう
WIndows 10 の Professional でも Home でも WSL は入れられる。
WSL の導入は次の記事に従って作業すればスムーズに行えた。
- WSL(Windows Subsystem for Linux)を使ってみた ( 画像ありの分かりやすい記事、ありがとうございました )
WSL を入れたら
1. 環境を最新にしよう
Ubuntu のターミナルから次のコマンドを実行して環境を更新する。
sudo
はスーパーユーザでの実行権限を付与するコマンド。apt-get
は管理者権限での実行が必要なので、sudo
をつけて実行する。環境を更新する$ sudo apt-get update $ sudo apt-get upgradeRails 環境を構築しよう
注意
ご使用の環境で Kaspersky が起動していると以降の作業で通信を行う際にエラーとなって各種ツールのインストールやRailsプロジェクトの作成に失敗する。
ここでは Kaspersky を停止して作業を継続した。
1. 各種ライブラリを入れよう
rbenv を入れた あとに rbenv 経由で Ruby を入れるのだが、その際に ruby-build という rbenv のプラグインを入れる 必要がある。
で、その ruby-build のインストールにあたって色々と必要なライブラリがある。
ここでは実際に Ruby を入れる際に発生する余計なエラーを回避すべく、それらを事前に入れておく。各種ライブラリのインストール$ sudo apt-get install make $ sudo apt-get install gcc $ sudo apt-get install -y libssl-dev libreadline-dev zlib1g-dev2. Node.js を入れよう
こちらも同じく事前にエラーを回避するための手順。
Rails を実行する際に JavaScript のエンジンが必要になるのだが、 Node.js を入れておくことでこれに対応できる。(注)
これはあくまで WSL 上の Ubuntu における環境構築手順なので、Windows 上ですでに Node.js を入れていてもこの手順は実施する必要がある。インストールは次のコマンドを実行する。
Node.jsのインストール$ sudo apt-get install npmこれで Node.js そのものはインストールされたのだが
n
というツールを入れることで Node.js のバージョン管理を行うことができる。
n
のインストールは次のコマンドを実行する。nのインストール$ sudo npm install -g n $ sudo n stable(注)
手前味噌で恐縮だが、n
による Node.js のバージョン管理については 別記事 で記載しているので、ご興味あればそちらも参照されたい。
そちらの記事では LinuxMint 18 が確認環境だが、同じ Debian ベースのデストリビューションなので違和感なく進められると思う。3. rbenv を入れよう
どうせやるなら rbenv で Ruby のバージョンを管理したい。
ということで、 rbenv を GitHub からgit clone
で取ってくる。rbenvをインストール$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
rbenv がインストールできたら次のコマンドを実行して rbenv の PATH を通す。
rbenvのPATHを通す$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrcそして次のコマンドを実行して rbenv の設定を行う。
rbenvの設定を行う$ ~/.rbenv/bin/rbenv init
と打つと
実行結果# Load rbenv automatically by appending # the following to ~/.bashrc: eval "$(rbenv init -)"とでるので、
eval "$(rbenv init -)"
を.bashrc
に追記する。
まとめると、.bashrc
には次の2行が追記されることになる。追記される2行export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)"最後にターミナルを再起動して環境変数の反映させる。
($ source ~/.bashrc
か、もしくは$ exec $SHELL -l
でも反映できる )
- 参考
4. ruby-build を入れよう
rbenv をインストールしたら次は Ruby を入れるためのプラグイン、 ruby-build を入れる。
ruby-buildをインストール$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-buildここで 先の手順 でインストールしたライブラリが効いてくる。
これらのライブラリを入れていない場合は、逐次エラーが発生してその都度ライブラリをインストールしなければならず、非常に鬱陶しい。5. Ruby を入れよう
ようやく Ruby のインストールである。
Ruby のバージョンは任意のもので良い。ここでは2.6.1
を入れてみる。
次のコマンドを実行して Ruby のバージョン 2.6.1 をインストールする。Rubyのバージョン2.6.1をインストール$ rbenv install 2.6.1 $ rbenv rehash $ rbenv globalで、Ruby のバージョンは。。。
Rubyのバージョンを確認$ ruby -v ruby 2.6.1p33と、指定したバージョン(
2.6.1
) がインストールされていることが確認できた。6. Rails を入れよう
Ruby のインストールまで終わったら、次は Rails を入れるための作業にはいる。
Rails は gem 経由でインストールする。バージョンは指定しない。Railsのインストール$ gem install rails Fetching railties-5.2.3.gem /home/hogehoge/.rbenv/rbenv.d/exec/gem-rehash/rubygems_plugin.rb:6: warning: Insecure world writable dir /home/hogehoge/.rbenv/versions in PATH, mode 040777 Successfully installed railties-5.2.3 Successfully installed rails-5.2.3 Parsing documentation for railties-5.2.3 Installing ri documentation for railties-5.2.3 Parsing documentation for rails-5.2.3 Installing ri documentation for rails-5.2.3 Done installing documentation for railties, rails after 0 seconds 2 gems installedということで、2019年05月現在、
5.2.3
のバージョンがインストールされた。
既存の Rails プロジェクトにあわせたバージョンを指定したい場合は次のように指定してやれば良い。
( ここでは5.2.0
を指定した )Railsのインストール(バージョン指定)$ gem install rails --version="5.2.0"7. sqlite を入れよう
Rails を使うにあたり SQLite3 のインストールが必要になる。SQLite そのものは gem 経由でインストールするのだが、ここでも依存ライブラリを先に入れておかなければならない。
まずは依存ライブラリのインストールから。
SQLite3の依存ライブラリのインストール$ sudo apt-get install libsqlite3-dev次に
SQLIte3
のインストール。SQLite3のインストール$ gem install sqlite3 -v 1.3.138. bundler を入れよう
Rails では一旦プロジェクトを作ると、そのプロジェクトで扱っているライブラリは Gemfile で管理され、それらは bundler でインストールすることになる。
ということで、 bundler をインストールしておく。bundlerのインストール$ gem install bundlerRails プロジェクトを作ろう
1. Rails プロジェクトを作ろう
ここまでで Ruby のインストールと Rails のインストール、およびそれらを使う際に必要な最低限のライブラリのインストールが完了した。
実際に Rails プロジェクトを作って起動してみる。プロジェクトの作成は次のコマンドで行う。
Railsのプロジェクト作成$ rails new sample-app
2. Rails プロジェクトを起動しよう
これで雛形ができたので、プロジェクト直下に移動してアプリケーションを起動する。
Railsアプリの起動$ cd sample-app/ $ rails s => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.1 (ruby 2.6.1-p33), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:3000 Use Ctrl-C to stopあとはブラウザから http://localhost:3000 にアクセスして、次のページが見れれば環境構築は完了。
Windows のフォルダにシンボリックリンクを貼って Rails プロジェクトを Windows から編集しよう
上記までで Ubuntu on WSL の環境に Rails を導入できたが、このままだと Windows 環境から Rails プロジェクトの編集ができない。
Ubuntu から Windows のフォルダは
/mnt/c/
から参照できるので、Rails のプロジェクトを Windows 側のフォルダに作成してそこを Ubuntu 側で参照しに行っても良いが、毎回/mnt/c
から参照しにくのは流石に面倒臭い。ということで、シンボリックリンクである。
ここでやることの目的は
- Ubuntu から Windows への参照の手間を減らすこと
で、やることは以下の2つ。
- あらかじめ Windows のユーザホームフォルダ直下に
Rails
フォルダを作成しておく- Ubuntu のユーザホームディレクトリから
Rails
フォルダに対してシンボリックリンクを貼る以下の作業で
lns -s
を実行し、Rails
をフォルダにシンボリックリンクを貼る。シンボリックリンクを貼る$ pwd /home/hoge # Ubuntu 上のホームディレクトリ $ ln -s /mnt/c/Users/hoge/Rails # シンボリックリンクを貼る
ls -l
を実行すると、シンボリックリンクを確認$ ls -l total 0 lrwxrwxrwx 1 hoge hoge 27 May 22 23:15 Rails -> /mnt/c/Users/hoge/Rails上記のとおり、Windows のフォルダに対してシンボリックリンクが貼れている。
あとはシンボリックリンクを貼ったフォルダに対して
- 既存の Rails プロジェクトをここに移動させる
- ここで Rails プロジェクトを新たに作成する
等を行うことで、Windows から Rails プロジェクトを編集することができる。
これで
- Rails アプリの管理は Ubuntu on WSL
- Rails アプリの編集は Windows
という構成ができた。
所感
気分によって今日は Mac、今日は Windows という感じで作業ができると嬉しいと思い、Windows での Rails 環境構築を実施してみた。
とりあえずこれで Windows で Rails の開発ができるようになったはずなので、これから遊んでみようと思う。参考
- 投稿日:2019-05-23T22:43:13+09:00
【jQuery】【Ajax】iTunes APIを使って音楽情報の検索フォームを作り、Ajaxを使って表示させる
iTunesAPIを使って音楽情報を検索したい!
はじめまして。記念すべき初めてのQiita投稿です。
私はRailsチュートリアルから勉強を初めて2〜3ヶ月のエンジニア転職志望者でございます。
ポートフォリオ制作として現在音楽のSNSアプリを作っています。その過程で、このwebアプリになくてはならない
音楽CDのアーティスト名とかタイトルとかジャケット情報をフォームから検索して表示
させる部分をド初心者なりに必死こいて作りなんとか動く形になったため、はじめてのアウトプットとしてまとめます!!動けば万々歳精神で作ったものなので、もっとこうすれば綺麗にできるよ!
というアドバイスありましたらバシバシご指摘いただけると嬉しいです使ったもの
当初、音楽系APIで使えるものを「音楽系API(主に音楽配信サービス)まとめ」等から探していました。
Apple Music APIを使おうとしたのですが、理解が全く追いつかず断念。いつかリベンジしたい...途方にくれていたところこんなものを発見。
なんと
https://itunes.apple.com/search
の後ろに色々検索パラメータをつけるだけでJSON形式でデータが取得できるとのこと!リクエストで返ってくるデータはこんな感じらしいです。
result{ "resultCount": 2, -"results": [ -{ "wrapperType": "track", "kind": "song", "artistId": 94949844, "collectionId": 317186212, "trackId": 317186341, "artistName": "the pillows", "collectionName": "Rock stock & too smoking the pillows", "trackName": "Funny Bunny", "collectionCensoredName": "Rock stock & too smoking the pillows", "trackCensoredName": "Funny Bunny (Rock Stock Version)", "artistViewUrl": "https://itunes.apple.com/jp/artist/the-pillows/id94949844?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/funny-bunny-rock-stock-version/id317186212?i=317186341&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/funny-bunny-rock-stock-version/id317186212?i=317186341&uo=4", "previewUrl": "http://a744.phobos.apple.com/us/r20/Music/v4/2f/53/63/2f536341-4614-7453-1275-d68fdc0df2e2/mzaf_7461742241849893593.aac.m4a", "artworkUrl30": "http://is2.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.30x30-50.jpg", "artworkUrl60": "http://is1.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.60x60-50.jpg", "artworkUrl100": "http://is4.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.100x100-75.jpg", "collectionPrice": -1, "trackPrice": 250, "releaseDate": "2009-06-03T07:00:00Z", "collectionExplicitness": "notExplicit", "trackExplicitness": "notExplicit", "discCount": 1, "discNumber": 1, "trackCount": 14, "trackNumber": 10, "trackTimeMillis": 206600, "country": "JPN", "currency": "JPY", "primaryGenreName": "Rock" }, (以下略)詳しい使い方は記事を参照してください。
ちなみにiTunesで扱っているコンテンツであれば音楽以外も取得できるようです。ということで、今回こちらのAPIを使って検索フォームを実装してみました。
こんな感じでできた
検索キーワードとして
- アーティスト名
- アルバムタイトル
の2つでCD情報を検索したいと思います。
search.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script> $(function () { $('#search_button').on('click', function () { //入力したアーティスト名とアルバム名を検索フォームから取得。 var artistname = $('#my-form [name=artistterm]').val(); var albumname = $('#my-form [name=albumterm]').val(); //変数termに検索キーワードをがっちゃんこしていれます var term = artistname + albumname; //ajax通信を行い、リクエストをAPI側に送信 $.ajax({ url: 'https://itunes.apple.com/search', type: 'GET', data: { term: term, country: 'jp', media: 'music', entity: 'album', lang: 'ja_jp', limit: '10' }, dataType: 'json', timespan: 1000 }).done(function (data) { //通信が成功したときの動き var result = data.results[0]; if (result == null) { //検索結果がなかった場合 $("#image-box").children("img").attr({ 'src': '' }); $('#searchresult').text("検索結果が見つかりません"); } else { //検索結果が存在した場合 $("#searchresult").remove(); $('#artistname').text(result.artistName); $('#albumtitle').text(result.collectionName); $('#artwork').text(result.artworkUrl100); var url = result.artworkUrl100; $("#image-box").children("img").attr({ 'src': url }); } }); }).fail(function (jqXHR, textStatus, errorThrown) { //通信失敗したときの動き $("#span1").text(jqXHR.status); //例:404 $("#span2").text(textStatus); //例:error $("#span3").text(errorThrown); //例:NOT FOUND }); }); </script> </head> <body> <!--以下、検索フォームなどの表示部分--> <p>アーティスト名:<span id="artistname"></span></p> <p>トラック名:<span id="albumtitle"></span></p> <p>アルバム画像URL:<p><span id="artwork"></span></p> <div id="image-box"><img /></div> <p id="searchresult"></p> <form id="my-form"> <input type="text" name="artistterm" value="" /> <input type="text" name="albumterm" value="" /> </form> <input type="button" id="search_button" value="検索"> <p>エラー原因</p> <p><span id="span1"></span></p> <p><span id="span2"></span></p> <p><span id="span3"></span></p> </body> </html>jQueryやAjaxの基本的な使い方の解説などは、
私が正直全然ここで説明できるほど身につけていないので他の記事におまかせいたします。。。自分のRailsアプリで使いたいとき
自身のアプリのapp/assets/javascript配下に
search.js$(function () { $('#search_button').on('click', function () { //入力したアーティスト名とアルバム名を検索フォームから取得。 var artistname = $('#my-form [name=artistterm]').val(); var albumname = $('#my-form [name=albumterm]').val(); //変数termに検索キーワードをがっちゃんこしていれます var term = artistname + albumname; //ajax通信を行い、クエリパラメーターをAPI側に送信 $.ajax({ url: 'https://itunes.apple.com/search', type: 'GET', data: { term: term, country: 'jp', media: 'music', entity: 'album', lang: 'ja_jp', limit: '10' }, dataType: 'json', timespan: 1000 }).done(function (data) { //通信が成功したときの動き var result = data.results[0]; if (result == null) { //検索結果がなかった場合 $("#image-box").children("img").attr({ 'src': '' }); $('#searchresult').text("検索結果が見つかりません"); } else { //検索結果が存在した場合 $("#searchresult").remove(); $('#artistname').text(result.artistName); $('#albumtitle').text(result.collectionName); $('#artwork').text(result.artworkUrl100); var url = result.artworkUrl100; $("#image-box").children("img").attr({ 'src': url }); } }); }).fail(function (jqXHR, textStatus, errorThrown) { //通信失敗したときの動き $("#span1").text(jqXHR.status); //例:404 $("#span2").text(textStatus); //例:error $("#span3").text(errorThrown); //例:NOT FOUND }); });上記を「○○.js」の名前でファイルを作って格納。
またRails.5.x以降からjQueryを使えるように設定しないといけないらしいので、以下の記事などを参考に設定。
Rails 5.x標準で Ajax+(jQuery+Partial) でHTML部分更新する世界一シンプルなサンプルあとは
<body>
以下の検索フォーム部分をお好きなビューファイルに入れるだけで動くと思います。参考記事
上記を作るにあたって参考にした記事を以下に挙げます。
私のように「Railsチュートリアルは一通りやったけど、jQueryとかAjaxとか全くわからーん」という人は以下を参考にするとなんとなくやっていることが分かると思います。
まずこの記事のコードを基本としてアレンジしました。
その他大いに参考にしたもの
- railsふかぼり01:railsでWebAPIを叩く
- Ajax(非同期通信)についてわかりやすさ重視でまとめてみた(Rails使用のデモ付)
- jQuery.ajax()のまとめ
- Ajaxで外部APIへ通信する方法
- jQueryでフォームの値を取得する方法をまとめておくので、コピペでご利用ください。
最後に個人的感想
初めて記事を書いたので、
節目としてここまで勉強してきた中で個人的な感想を記録しておきます初ポートフォリオ制作はインスタの音楽ジャケ版的なアプリを作りたいなぁ〜と思い(そういうサービス既にありますが)、
作る前はなんとなく「音楽系のAPIあるだろうし、理想形はそこからデータ取ってきてそれをそのまま投稿できたらいいなぁ」とぼんやりイメージしていた程度でした。いざ作り始めてからは、必死に調べては参考記事のコードをアレンジし、試行錯誤して
あれよあれよと言う間に気づいたら結果的に理想と同じ機能を作ることができました。どのAPIを使うか?の段階でそもそもAPIの知識が足りず、若干壁にぶち当たりましたが
粘り強くググっていたら初心者にも簡易に使えるものを発見できたのが、今回の超ラッキーでしたそしてAPIの使い方の丁寧なまとめがあったことも
ありがたや。
とりあえず今回学んだことは粘り強くググることの大切さでしたまだアプリは完成していませんが、この機能を実装できたことがちょっとした自信とモチベーションにつながりました。今後もがんばるぞ!!
そして私と同じくエンジニア目指して勉強に勤んでいる方々、一緒に頑張りましょう!
ここまで読んでいただいてありがとうございました。
- 投稿日:2019-05-23T22:39:45+09:00
_pathメソッドが使えず、文字列のpathでgetする場合に、link_toにクエリパラメータを追加する
目的
- こんなことができるのかと驚いた
- ググってもこれは出てこなかった(当たり前だからなのかも...)
やりたいこと
- link_toでパスパラメータ以外の情報も送りたい。
- クエリパラメータを送りたいが、_pathメソッドが使えず、以下の方法が使えない
<%= link_to "#{@user.nickname}", user_path(@user.id, testid: @test.id) %>方法
- 直接文字列でクエリパラメータを載せる
<li><%= link_to "#{@user.nickname}", "/users/test/#{user.id}?testid=#{@test.id}" %></li>
- 投稿日:2019-05-23T22:32:13+09:00
credentials.yml.encでシークレットキーを管理
はじめに
Rails5.2から
secrets.yml
が廃止され、credentials.yml.enc
に統合されたことをメンターの方に教えて頂いたため、API Keyなどのシークレットキーをcredentials.yml.enc
にまとめた。
恥ずかしい話、今まではENV["hogehoge.key"]
と書いてシークレットキーを~/.bash_profile
にまとめていた。
もちろん、アプリごとにシークレットキーを管理したほうが良い(当たり前だが)ので、今回行ったcredentials.yml.enc
の設定に関する一連の流れを備忘録として残しておく。編集方法
credentials.yml.enc
を開いてみると、config/credentials.yml.encmsb4Uzo8vfnzbP65VQ58Hmu4o5WzNDTVqv9EFRnR5.....
このように暗号化されて直接編集することはできない。
credential.yml.enc
を編集するにはbin/rails credentials:edit
を実行する。
エディタを指定する必要があるので、今回はコマンドでEDITOR="vi" bin/rails credentials:edit
と入力。初期値は以下の通り。
Terminal# aws: # access_key_id: 123 # secret_access_key: 345 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: o8vfnzbP65VQ58Hmu4o5WzNDTVqv9EFRnR5.....AWS等各種サービスで発行されたシークレットキーをここに入力する。
変更が完了すると、再びcredential.yml.enc
が暗号化されキーの値が変わる。読み取り方法
各種シークレットキーをアプリ内で読み取る場合、
Rails.application.credentials.aws[:access_key_id
]のように入力する。(以下コード参照)
尚、初期値はコメントアウトされているため、読み取る際には外しておくこと。Terminalaws: access_key_id: 65VQ58Hmu4o5WzNDT... #=> Rails.application.credentials.aws[:access_key_id] secret_access_key: DTVqv9EFRnR... #=> Rails.application.credentials.aws[:secret_access_key] secret_key_base: 8be8e637d755f79c799048bed8be0c... #=> Rails.application.credentials.secret_key_base本番環境(Heroku)でのシークレットキーの読み取り
暗号化されている
credential.yml.enc
を読み取る際にはmaster.key
が必要となる。
しかし、master.key
はRails5.2において.gitignore
に標準指定されているため、そのままでは本番環境でcredential.yml.enc
を読み取ることはできない。
よってHerokuで読み取る際にはheroku config:set RAILS_MASTER_KEY=マスターキーの値
をコマンドで入力しマスターキーの値を指定する。config/environments/production.rbconfig.require_master_key = true上記のように設定することで、本番環境におけるマスターキーが指定されていない場合にエラー表示を出すことができる。
基本的に
credential.yml.enc
の用途は「本番環境での秘匿情報の管理」だが、各環境で別々に秘匿情報を管理するためのrails-env-credentialsというgemもあるらしい。参考
Railsガイド セキュリティ10.1-独自のcredential
Rails5.2から追加された credentials.yml.enc のキホン
- 投稿日:2019-05-23T21:27:18+09:00
【Rails】コピペで使えるdevise備忘録
はじめに
deviseでユーザーログイン機能を追加するにあたって、普段よく使っているものの備忘録です。
rootをログイン画面に設定
deviseのcontrollerを作成し、
routes.rb
で以下のように設定。$ rails g devise:controllers usersroutes.rbRails.application.routes.draw do # rootをログイン画面に設定 devise_scope :user do root "users/sessions#new" end devise_for :users, :controllers => { sessions: 'users/sessions' }ログイン後のpathを指定
application_controller.rb
で以下を記載。application_controller.rbclass ApplicationController < ActionController::Base def after_sign_in_path_for(resource) tasks_path endこのようにログイン後最初のページを条件分岐させることも可能です↓
application_controller.rbclass ApplicationController < ActionController::Base def after_sign_in_path_for(resource) if admin_user? users_path else tasks_path end endストロングパラメーターのpermit
deviseではサインアップ時にemailとpassword系のみを受け取るようにストロングパラメーターが設けられているので、それ以外の
:name
などを許可したい場合↓application_controller.rbclass ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? . . . private def configure_permitted_parameters added_attrs = [ :name, :email, :password, :password_confirmation, ] devise_parameter_sanitizer.permit :sign_up, keys: added_attrs devise_parameter_sanitizer.permit :account_update, keys: added_attrs devise_parameter_sanitizer.permit :sign_in, keys: added_attrs endおわり
他にも備忘録しておきたい事ありそうなので、思い出し次第追加したいです。
- 投稿日:2019-05-23T20:27:52+09:00
1日1問解いてSQLをマスター!SQLの学習サービス Qdash を作った話
目次
- はじめに
- なぜ作ろうと思ったか
- 使ってる技術
- 同じようなサービスを作りたい人のために実装を紹介
- 1日に1回、指定の時間に問題を切り替えて表示する
- 質問箱風なOGP画像を生成
- これから
- 最後に
はじめに
Qdash という1日1問形式の SQL 学習サービスを作りました。
問題画面
回答画面
なぜ作ろうと思ったか
とあるデータを見たいけど、いつも SQL を書ける人にお願いしている。
勉強はしたいが、中々勉強する時間が取れない。と思ってるディレクター、営業、マーケターの方!!
でも1日の中でも隙間時間はありますよね?その隙間時間に問題を解き、SQL は任せて!と自信を持って言えるようになりませんか?
というのがサービスのコンセプトです。使っている技術
・ Ruby
・ Rails
・ ServiceWorker / Webpush
・ HerokuRails / Heroku で手早く作りました。
push 通知をするために ServiceWorker と Webpush にも挑戦しました。 (この記事では触れません)実装紹介
似たようなサービスを作りたい人のために実装をいくつか紹介します!
1日に1回、指定の時間に問題を切り替えて表示する
SQL を学びたい人にとって負担にならないように1日1問という仕様にしました。
あなたならどのように実装しますか?今回は Cookie と Heroku scheduler を使って実装してみました。
class HomeController < ApplicationController def index @question = today_question delete_cookie_if_question_changed @answer = Answer.find_by(id: answer_id) end private # flesh set by scheduler.rake def today_question today_question = TodayQuestion.last || TodayQuestion.create_randomly today_question.question end def delete_cookie_if_question_changed answer = @question.answers.find_by(id: answer_id) cookies.delete('answer_id') if answer.nil? end def answer_id cookies['answer_id'] end end回答をした際に cookie をセットします。
class AnswersController < ApplicationController def create answer = Answers::Factory.build(answer_choice_params) if answer.save cookies['answer_id'] = answer.id redirect_to root_path else redirect_to root_path, notice: '回答に失敗しました。' end end private def answer_choice_params params.permit(:answer_choice_id) end endHeroku scheduler で1日1回、今日の問題をDBに登録しています。
時間と実行するコマンドはアドオンを入れた後に Heroku の管理画面から設定することができます。# sheduler.rake task set_question_randomly: :environment do TodayQuestion.create_randomly end昨日と同じ問題が出たらテンションが下がるのでそれは避けるようにしています。
class TodayQuestion < ApplicationRecord belongs_to :question def self.create_randomly question = nil loop do last_question = TodayQuestion.last.question question = Question.pick_randomly break if last_question.id != question.id end create(question: question) end end質問箱風なOGP画像を生成
続いて、質問箱風な OGP の生成処理です。
問題の作成時に問題のタイトルを画像に書き込み、画像を S3 にアップロードします。 (active_storage を利用)そして、シェアした時に作成した問題文が書かれたOGP画像が表示されるようにします。
# Gemfile gem 'mini_magic'class OgpImageGenerator def initialize(question: question) @question = question end def generate image = load_base_image image.combine_options do |c| c.gravity 'West' c.pointsize 30 c.draw "text 55,0 '#{title}'" c.size "70x" c.fill '#000' c.font Rails.root.join('public', 'hiraginokakugoW6.ttc').to_s c.interline_spacing 10 c.kerning 4 end image end private def title ApplicationController.helpers .sanitize(@question.title) .chars.each_slice(15) .map(&:join) .join("\n") end def load_base_image MiniMagick::Image.read(Rails.root.join('public', 'images', 'ogp_base.png')) end endclass Question < ApplicationRecord .. before_save :generate_ogp_image before_update :generate_ogp_image has_one_attached :ogp_image def generate_ogp_image return unless self.will_save_change_to_title? ogp_image_path = OgpImageGenerator.new(question: self).generate.path file = File.open(ogp_image_path) self.ogp_image.attach( io: file, filename: "question_ogp_image_#{id}_#{SecureRandom.urlsafe_base64}.png", content_type: "image/png" ) file.close end end問題を作成すると、このような OGP 画像が自動で生成されます。
これから
ログイン機能とコース機能を作って問題を解けるようにしたいと思っています。
ログインしていなくても問題を解くことができる一方で、熱量の高い人はログインしてまとまった時間で勉強できたら良いですね。実は一番大変なのは機能の実装より SQL の問題を作ることだったりしますw
最後に
詳細な使い方については note に書いているので見ていただけたら幸いです。
https://note.mu/usabdelah/n/n0b7f8fd30ed5Qdash で学んだことを活かして実務で SQL を書き、
データの可視化をやっていく人が増えたらいいなと思っています。ぜひ、Qdash で SQL の問題を解いてみてください。
- 投稿日:2019-05-23T19:41:12+09:00
rails hands on
rails で Web アプリを作ってみよう!
Web アプリを作って、動かして、触って、デプロイしてみよう!
事前準備
docker
,bundler
はインストールしておいてねdocker
- 公式
- Mac の人は
bundler
- 公式
- Mac の人は
他
docker-compose.yml
Dockerfile
dockerをローカルにコピー
TODO: curl でダウンロードできるようにするやってみよう!
$ mkdir rails_sample && cd rails_sample $ cp -a ~/Download/Dockerfile ~/Download/docker-compose.yml ~/Download/docker . remove `Gemfile.lock` in Dockerfile $ bundle init $ docker-compose run --rm web bundle exec rails new . -d mysql --skip-testここで今日のゴールを
https://rails-sample-fot-lt.herokuapp.com/users/sign_in
(今日はログインやらんけど)ログインあり&デプロイありの、ハンズオンやりたいと思う人!
希望あれば日程調整しましょう
3時間ぐらい
うまくいけば30分edit database.yml
pool: <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %> username: <%= ENV.fetch('MYSQL_USERNAME') { 'root' } %> password: <%= ENV.fetch('MYSQL_PASSWORD') { 'password' } %> host: <%= ENV.fetch('MYSQL_HOST') { '127.0.0.1' } %> port: <%= ENV.fetch('MYSQL_PORT') { 3306 } %>add
Gemfile.lock
in Dockerfile$ docker-compose up $ docker-compose exec web bin/rails g scaffold post content $ docker-compose exec web bin/rails db:migratehands on メモ
$ mkcd rails_sample $ cp -a ~/Download/Dockerfile ~/Download/docker-compose.yml ~/Download/docker .remove
Gemfile.lock
in Dockerfile$ bundle init $ docker-compose run --rm web bundle exec rails new . -d mysql --skip-test create remote repository $ cp -a ~/Download/Gemfile .edit database.yml
database.ymlpool: <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %> username: <%= ENV.fetch('MYSQL_USERNAME') { 'root' } %> password: <%= ENV.fetch('MYSQL_PASSWORD') { 'password' } %> host: <%= ENV.fetch('MYSQL_HOST') { '127.0.0.1' } %> port: <%= ENV.fetch('MYSQL_PORT') { 3306 } %>add
Gemfile.lock
in Dockerfile$ docker-compose up $ docker-compose exec web bundle install $ docker-compose exec web rails generate simple_form:install $ docker-compose exec web rails haml:erb2haml $ docker-compose exec web rails g annotate:install $ docker-compose exec web bin/rails g scaffold post content $ docker-compose exec web bin/rails g scaffold user name $ docker-compose exec web bin/rails db:migrate $ docker-compose exec web bundle exec rails generate devise:installadd
%p.notice= notice %p.alert= alertin app/views/layouts/application.html.haml
$ docker-compose exec web bundle exec rails g devise:views $ docker-compose exec web rails generate devise user $ docker-compose exec web bin/rails db:migrateadd
before_action :authenticate_user!
in app/controllers/posts_controller.rb and app/controllers/users_controller.rbadd
root to: 'post#show'
route.rbaccess http://localhost:3000/users
heroku deploy
create app in gui
$ heroku login $ heroku git:remote -a rails-sample-fot-lt $ git push heroku master $ heroku addons:create cleardb:ignite $ heroku config | grep CLEARDB_DATABASE_URL mysql://[username]:[password]@[hostname]/[db_name]?reconnect=true $ heroku config:add DB_NAME="[db_name]" $ heroku config:add DB_USERNAME="[username]" $ heroku config:add DB_PASSWORD="[password]" $ heroku config:add DB_HOSTNAME="[hostname]" $ heroku config:add DB_PORT="3306" $ heroku run rake db:migrate
コピー用
DockerfileFROM ruby:2.6.2 RUN apt-get update --fix-missing \ && apt-get -y upgrade \ && apt-get install -qq -y \ nodejs \ mysql-client \ && apt-get autoremove -y \ && apt-get clean all \ && fc-cache -f -v # Add dumb init to process system events RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 -O /usr/local/bin/dumb-init RUN chmod +x /usr/local/bin/dumb-init RUN gem update --system RUN gem install bundler WORKDIR /rails_sample COPY Gemfile Gemfile.lock ./ RUN bundle install --jobs=4 --retry=3 COPY . . EXPOSE 3000 # Start the main process. CMD bin/rails server -b 0.0.0.0docker-compose.ymlversion: '3.7' services: db: restart: always image: mysql:latest environment: MYSQL_ROOT_PASSWORD: password ports: - 3306:3306 volumes: - ./docker/mysql/conf:/etc/mysql/conf.d:rw - dbdata:/var/lib/mysql web: build: . depends_on: - db volumes: - .:/rails_sample - "bundle:/usr/local/bundle" environment: - MYSQL_HOST=db command: > bash -c " bundle install --quiet && docker/bin/start rails" ports: - 3000:3000 volumes: bundle: dbdata:docker/mysql/conf/default_authentication.cnf[mysqld] default_authentication_plugin=mysql_native_passworddocker/bin/rails#!/bin/sh docker-compose run --rm backend bin/rails "$@"docker/bin/start#!/bin/sh if [ $# = 0 ]; then echo "usage:" echo " start rails: Start rails server" echo " start rails recreate db: Start rails server recreate db" echo " start sidekiq: Start sidekiq process" echo " start sidekiq-web: Start sidekiq web server" exit 1 fi case "$1" in "rails") bundle exec rails tmp:pids:clear bundle exec rails log:clear tmp:clear bundle exec rails server --port 3000 --binding "0.0.0.0" ;; "rails recreate db") bundle exec rails tmp:pids:clear bundle exec rails db:reset bundle exec rails db:migrate bundle exec rails log:clear tmp:clear bundle exec rails server --port 3000 --binding "0.0.0.0" ;; "rails migrate db") bundle exec rails db:migrate ;; "sidekiq") bundle exec sidekiq ;; "sidekiq-web") bundle exec rackup --host "0.0.0.0" --port 9292 lib/sidekiq_web/config.ru ;; esac
- 投稿日:2019-05-23T17:30:09+09:00
【自分用】railsでhello, world!するまでの備忘録
Railsインストールからhello, world!出力までのもくじ
- rbenvを使ってRubyとRuby on Railsをインストール ※前提として、Command line tools をターミナルからインストール済
xcode-select --install
- コードを編集するのに必要なテキストエディタをインストール
- hello, world!を出力するだけの単純アプリの作成
1.rbenvを使ってRubyとRuby on Railsをインストール
1-1.Homebrewをインストール
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"1-2.rbenvをインストール
まずはrbenvをインストール
brew update brew install rbenv
rbenvのインストールが完了したら、rbenvにパスを通します
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile1-3.rbenvを使ってRubyをインストール
下記コマンドでrbenvでインストール可能なRubyのバージョンを確認
rbenv install -l実際にコマンド打つとこんな感じ↓
※実際にはもっと表示されます現在の安定版を確認するならwww.ruby-lang.orgにアクセスすると良いです。
※スクショは2019年5月23日時点での安定板なので最新の安定板は↑のリンクに実際にアクセスして確認してください。現在の安定板の確認をしたらrbenvを使ってRubyをインストール
rbenv install 2.6.3
1-4.デフォルトのRubyを設定
rbenv global 2.6.31-5.Railsのインストール
gem install rails
2.コードを編集するのに必要なテキストエディタのインストール
Atomエディタ使ってる人多い印象なのでAtom.ioからダウンロード。
3.hello, world!を出力するだけの単純アプリの作成
3-1.プロジェクトで使うディレクトリ
environment
を作成今回のプロジェクトで使うディレクトリ
environment
を作成し、environmentディレクトリに移動。mkdir environment cd environment3-2.移動先のディレクトリ
environment
でアプリケーションの作成下記コマンドの_5.1.6_のところではrailsのバージョンを明示的に指定)。
rails _5.1.6_ new hello_app``` Traceback (most recent call last): 2: from /Users/masaki-sakamoto/.rbenv/versions/2.6.3/bin/rails:23:in `<main>' 1: from /Users/masaki-sakamoto/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path' /Users/masaki-sakamoto/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': can't find gem railties (= 5.1.6) with executable rails (Gem::GemNotFoundException)コマンド実行したらGemNotFoundExceptionというエラーが発生しましたが、下記コマンドを実行したら解消され無事
hello_app
の作成完了。gem install rails -v 5.1.63-3.rails sコマンドでhello, world!を出力
作成したhello_appのディレクトリに移動し、
rails s
コマンドを実行。rails sアプリケーションを表示するには、ローカルサーバーの場合http://localhost:3000/をブラウザで開きます。
※Ctrl+Cを押すとサーバーを終了できる
実際に開くとこんな感じ↓
スクショ
用語まとめ
後日追記予定
参照リンク
- 投稿日:2019-05-23T16:08:05+09:00
【初心者向け】実務未経験がプルリクエスト時レビュアーに指摘されたこと-コミット・RSpec編-
はじめに
実務未経験の自分がエンジニアスクールでレビューを頂いた時に、
おそらく現場でも指摘されるであろう項目をまとめていきます。前回は、コード・文法編をまとめました。
【初心者向け】独学では気づきにくい!実務未経験がプルリクエスト時レビュアーに指摘されたこと-コードお作法編-まとめきれていない部分もありますので、随時追記していきます。
特に、独学でRails等を学んでいる方の参考になれば幸いです。1. コミットでの注意
わかりやすく、綺麗なコードを書くことは心がけているに、雑なコミットで残してませんか?
私が独学で勉強していた頃は、正直疎かにしてました。
しかし、プルリクエストでレビューしてもらう際やチーム開発では、理解しやすいコミットを残す必要があります。
特にコレが正解!というのはありませんが、指摘されたことを踏まえてまとめていきます。1-1. 1つのコミットで複数の機能を追加するのは避けよう。(意図を理解しづらくなる)
一気に複数の機能を実装して、1つのコミットにまとめると見る側が意図を理解しづらくなります。
$ git commit -m "ログイン機能、ユーザー編集機慧能、画像投稿機能およびRspecテストを追加"のようにしてしまうと、
メッセージ的にもコードを確認するにしても分かりづらい状況になります。
コミットをどのように分けるかは、常に意識する必要があります。1-2. 理解しやすいコミットメッセージ
コミットメッセージには、端的に理解しやすいメッセージで残す必要があります。
- どういう目的で
- 何を
- どうしたのか(追加・修正・更新・削除など)
をコミットメッセージに残せると見る側も意図を理解しやすそうです。
とはいえ、チーム・プロジェクトによりルールがあると思われますので注意が必要。
個人的には以下のURLで紹介されていた書き方が分かりやすいかなと感じました。参考URL: 誰にとってもわかりやすいGitのコミットメッセージを考える
1-3. 不要なファイルは除外or削除しよう
自動生成されるけど不要なファイルは事前に必要かどうかを考えましょう。
そして、本番環境で不要な.env
(環境変数)などのファイルは、事前に.gitignore
に記述して除外しておきましょう。
不要なファイルがコミットされれば、自分・レビュアー共に見づらいプルリクエストになってしまう。2. RSpecの準備編<導入後>
Gemfilegroup :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' endで、
bundle install
後に準備すると良いものをまとめます。
基本設定などは、[Qiita] Ruby on Rails のテストフレームワーク RSpec 事始めなどがオススメです。2-1. 結果を見やすくする
通常何も設定せずにRSpecテストをすると
... Finished in xx seconds 3 examples, 0 failures
テストに失敗していないことは分かりますが、詳細が分かりにくい状態に。
そんな時は、
.rspec
にformat documentation
を入れて結果を見やすくすると良い。
※個人的にもレビューをして頂いている方にも見やすいテスト結果になります。.rspec--require spec_helper --format documentation例えば、このように出力されます。
Sample::HomeController Home Controller indexの機能テスト sample/に正常にアクセスできる Sample::ProductsController Prodcut Controller showの機能テスト products/showに正常にアクセスできる @productが取得できている Finished in xx seconds 3 examples, 0 failures
2-2. FactoryBotを設定しておく
RSpecでFactoryBotを使う場面があると思います。
何も設定をしない状態では、let!(:product) { FactoryBot.create(:product) }FactoryBotを毎回書く必要があります。
これでは、作業効率的にあまり良くないので、rails_helper.rb
定義しておくと良いです。spec/rails_helper.rbRSpec.configure do |config| config.include FactoryBot::Syntax::Methods end見た目もすっきりして、呼び出せるようになりました。
let!(:product) { create(:product) }詳しい設定方法などは、こちらの記事がオススメです。
参考URL: RailsアプリへのRspecとFactory_botの導入手順3. RSpecのお作法編
RSpecを書いていく上で指摘されたお作法についてまとめています。
3-1. テストの文言を考える
機能単体テスト(feature test)で考えれば、
- featureは「どのような機能」
- scenarioは「ユーザーから見たサービスの挙動」
ということを踏まえて文言を考える。
例)feature '商品の詳細ページを見ることが出来る' do scenario '詳細ページにアクセス時、商品名や価格などの情報が表示される' do end scenario "関連商品をクリック時、商品詳細ページへ移動する" do end end適当な文言設定をすると分かりにくいテストになり、確認する側も意図を理解するのに時間がかかります。
参考URL:
使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」3-2. 検査するDOM(HTML)の範囲を絞る
within
を使うことで、対象範囲を指定することができます。
これにより正確に意図したテストを実行することができます。
例は以下になります。# id="add_to_cart"で探せるようにする <div id="add_to_cart" class="btn-area"> <%= link_to cart_page_path do %> <div class="btn btn-primary btn-block"> カートへ入れる <i class="fa fa-angle-right" aria-hidden="true"></i> </div> <% end %> </div>scenario "カートを入れるをクリック時、買い物カゴページへ移動する" do # id="add_to_cart"の範囲を指定したテストをする within "#add_to_cart" do click_on "カートへ入れる" end #現在のページがカートページであることを確認 expect(page).to have_current_path cart_page_path end参考URL:
Capybaraチートシート#スコープを切る3-3. RSpecを設置するディレクトリを考える
分かりやすいディレクトリ構成にすると良いでしょう。
例えば、
app/controllers/products_controller.rb
に対応するRSpecは、
rspec/requests/products/products_controller_spec.rb
が分かりやすい。RSpecテストのファイルが多くなってくると、一目でファイルを探すのに苦労する場面も。
分かりやすいディレクトリ構成にすることで、ストレスを少しは軽減できると思います。まとめ
今回は、コミット・RSpecで注意された点をまとめました。
まだ、細かい点などもありますので、まとめ次第追記いたします。
RSpecについては、独学で学習している頃は苦手意識があり敬遠していましたが、
指摘されることで少しずつ理解が深まってきた印象です。現場等でエンジニアをやられている方で、本記事に間違い等ございましたら、
大変恐縮でありますがご指摘いただければと存じます。
よろしくお願いいたします。RSpecの勉強に使用した著書:
「Everyday Rails - RSpecによるRailsテスト入門」
- 投稿日:2019-05-23T13:11:52+09:00
実務未経験からRails開発に途中参加した際の環境構築
はじめに
実務未経験から内定をいただいた企業のrailsでの開発に途中参加することになり、環境構築を行う中で躓いた部分があったため振り返り用として記録します。
環境
- プログラミングスクールにてRuby、Railsの環境構築済み
- データベースはMySQLを使用
- GitHubアカウント登録済
- Homebrew、rbenvインストール済み
- mac OS使用
前提
- 与えられた情報はGitHubのリポジトリのみ
- ローカル環境で起動できることを目標とする
やったこと
GitHubリポジトリの共有
1.プライベートリポジトリを共有するためGitHubに登録しているアドレス又はアカウント名を伝える
2.GitHubのリポジトリへ招待され参加する
3.ローカルリポジトリの作成開発環境構築
1.Ruby、Rails等のバージョンを開発環境に合わせる
Ruby、Railsのバージョンなどは 「Gemfile」 で確認をします
Gemfileruby '2.5.0' gem 'rails', '~> 5.2.0' gem 'pg', '>= 0.18', '< 2.0' gem 'sass-rails', '~> 5.0'ここではRubyのバージョンを合わせます
自分のPCにインストールしているバージョンを確認$ rbenv install --list Available versions: 1.8.5-p52 1.8.5-p113 :下記コマンドで変更したいバージョンを指定
インストールしていた場合$ rbenv local 2.6.0インストールしていない場合
$ rbenv install 2.6.0バージョンも合わせ、いざ Gemをインストール
$ bundle installそしたらこんなエラーが…
何やらpgがインストールされてないとか?An error occurred while installing pg (1.1.3), and Bundler cannot continue. Make sure that `gem install pg -v '1.1.3' --source 'https://rubygems.org/'` succeeds before bundling.pgってなんだというところから検索し…データベースがPostgreSQLであることがは判明!!
こちらを参考にインストールターミナルでコマンドを打ち
$ sudo gem install pg -v '1.1.4' --source 'https://rubygems.org/'またしてもエラー…
エラー内容は忘れましたがこちらを参考にし解決(恐らく同じエラーでした)$ brew install postgresql2.PostgreSQLも無事インストールできたので再度Gemをインストール
$ bundle install3.データベース作成
$ bundle exec rake db:create4.マイグレーションファイルの実行
$ bundle exec rake db:migrate5.PostgreSQLを起動
$ postgres -D /usr/local/var/postgres6.サーバーを起動
$ rails sこれで無事にローカル環境構築完了!!
※手順としては前後している可能性がありこれが正しいとは限りませんのでご了承ください
おわりに
プログラミング初心者として検索していく中で、同じような状況の人の手助けになればと思い記事にしました。
やり方としてはエラーに遭遇しながら手探り状態で調べて進めたため進め方おかしいぞとなるかもしれません。参考
https://qiita.com/nasutaro211/items/d69a5e0b883293537b7e#_reference-a0cf0f3d167d682de2dc
https://qiita.com/youcune/items/5b783f7fde45d0fd4b35
https://qiita.com/yh2020/items/8be3087004d100fe752b
- 投稿日:2019-05-23T12:48:19+09:00
capistranoでのデプロイ時に、assets:precompile でメモリエラーが発生する
エラー内容
capistranoでのデプロイ時に、デプロイ先サーバで
assets:precompile
をするのですが、これがメモリ不足で失敗しました。
以下はエラーログです。log/capistrano.logINFO [12ac274e] Running $HOME/.rbenv/bin/rbenv exec bundle exec rake assets:precompile as hoge@fuga.piyo DEBUG [12ac274e] Command: cd /var/www/hoge/releases/20190523032428 && ( export RBENV_ROOT="$HOME/.rbenv" RBENV_VERSION="2.5.1" RAILS_ENV="staging" RAILS_GROUPS="" ; $HOME/.rbenv/bin/rbenv exec bundle exec rake assets:precompile ) ... FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory対処法
1. サーバにSwap領域を追加する。
Ubuntu 16.04だったので、以下のサイトを参考にSwap領域を2GB追加しました。
2. 環境変数 NODE_OPTIONS に max-old-space-size を設定する
デプロイ先のサーバにssh接続し、以下の環境変数を追加します。
$ sudo vim /etc/environment # 以下の行を追加してください export NODE_OPTIONS="--max-old-space-size=2048"
- 投稿日:2019-05-23T10:59:47+09:00
Rails Tutorial(2週目)-4-
4章はチェリー本に書いてあることがほとんどだったので、また別でまとめようと思います。
アクセサーの作成
attr_accessorは内部的に次のような処理を行っています。
(1)引数のシンボルすべてに対して以下の処理を繰り返す。
(2)シンボルで指定された名前のメソッドを定義する。そのメソッドはシンボルの名前の先頭に「@」を付加したインスタンス変数の値を取り出す。
(3)シンボルで指定されたメソッド名の後ろに「=」を付加した名前のメソッドを定義する。そのメソッドは引数を1つ取り,その値をシンボルの名前の先頭に「@」を付加したインスタンス変数に設定する
attr_accessor によってクラス外からも、インスタンス変数を読み込んだり、変更したり操作できるようになる。
ユーザー名とメールアドレス (属性: attribute) に対応するアクセサー (accessor) をそれぞれ作成します。アクセサーを作成すると、そのデータを取り出すメソッド (getter) と、データに代入するメソッド (setter) をそれぞれ定義してくれます。具体的には、この行を実行したことにより、インスタンス変数@nameとインスタンス変数@emailにアクセスするためのメソッドが用意されます。
- 投稿日:2019-05-23T10:19:48+09:00
Rails hidden_fieldの渡し方、受けとり方
渡し方
値を指定する場合はvalueを使う。
第一引数にオブジェクト名、第二引数部分にvalueで受け渡す値を指定する。text.html.erb<%= f.hidden_field :unique_key, :value => params[:unique_key] %>受け取り方
valueで渡した値をモデルのインスタンスの属性に格納している場合、以下のようになる。
text_controller.rb@introduce = Follow.find_by(unique_key: params[:follow][:unique_key])Followモデルのunique_keyカラムの値を受け取る場合。
参考
Ruby on Rails - hidden_fieldの使い方
https://qiita.com/azusanakano/items/5849a7ca74be58d195b7
f.hidden_fieldとhidden_field_tagの使い方【Ruby on Rails】
https://sakurawi.hateblo.jp/entry/hidden_field
- 投稿日:2019-05-23T09:57:43+09:00
Ruby on Rails Tutorial 13章
13.1.1
演習
RailsコンソールでMicropost.newを実行し、インスタンスを変数micropostに代入してください。その後、user_idに最初のユーザーのidを、contentに "Lorem ipsum" をそれぞれ代入してみてください。この時点では、 micropostオブジェクトのマジックカラム (created_atとupdated_at) には何が入っているでしょうか?
先ほど作ったオブジェクトを使って、micropost.userを実行してみましょう。どのような結果が返ってくるでしょうか? また、micropost.user.nameを実行した場合の結果はどうなるでしょうか?
先ほど作ったmicropostオブジェクトをデータベースに保存してみましょう。この時点でもう一度マジックカラムの内容を調べてみましょう。今度はどのような値が入っているでしょうか?1
>> micropost = Micropost.new => #<Micropost id: nil, content: nil, user_id: nil, created_at: nil, updated_at: nil> >> micropost.user_id = User.first.id User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => 1 >> micropost.content = "Lorem ipsum" => "Lorem ipsum" >> micropost.created_at => nil >> micropost.updated_at => nil >2
>> micropost.user User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:47:38", password_digest: "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzu...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31"> >> micropost.user.name => "Example User"3
>> micropost.save (0.1ms) begin transaction SQL (2.1ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2019-05-06 11:17:52.944180"], ["updated_at", "2019-05-06 11:17:52.944180"]] (6.0ms) commit transaction => true >> micropost.created_at => Mon, 06 May 2019 11:17:52 UTC +00:00 >> micropost.updated_at => Mon, 06 May 2019 11:17:52 UTC +00:0013.1.2
演習
Railsコンソールを開き、user_idとcontentが空になっているmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?
コンソールを開き、今度はuser_idが空でcontentが141文字以上のmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?1 >> micropost =Micropost.new(user_id: "",content: "") => #<Micropost id: nil, content: "", user_id: nil, created_at: nil, updated_at: nil> >> micropost.valid? => false >> micropost.errors.full_messages => ["User must exist", "User can't be blank", "Content can't be blank"] >>2
>> micropost =Micropost.new(user_id: "",content: "a" * 141) => #<Micropost id: nil, content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", user_id: nil, created_at: nil, updated_at: nil> >> micropost.valid?=> false >> micropost.errors.full_messages=> ["User must exist", "User can't be blank", "Content is too long (maximum is 140 characters)"] >>13.1.3
演習
データベースにいる最初のユーザーを変数userに代入してください。そのuserオブジェクトを使ってmicropost = user.microposts.create(content: "Lorem ipsum")を実行すると、どのような結果が得られるでしょうか?
先ほどの演習課題で、データベース上に新しいマイクロポストが追加されたはずです。user.microposts.find(micropost.id)を実行して、本当に追加されたのかを確かめてみましょう。また、先ほど実行したmicropost.idの部分をmicropostに変更すると、結果はどうなるでしょうか?
user == micropost.userを実行した結果はどうなるでしょうか? また、user.microposts.first == micropost を実行した結果はどうなるでしょうか? それぞれ確認してみてください。1
>> user= User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:47:38", password_digest: "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzu...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31"> >> micropost = user.microposts.create(content: "Lorem ipsum") (0.1ms) begin transaction SQL (5.5ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2019-05-07 02:03:41.026572"], ["updated_at", "2019-05-07 02:03:41.026572"]] (5.3ms) commit transaction => #<Micropost id: 2, content: "Lorem ipsum", user_id: 1, created_at: "2019-05-07 02:03:41", updated_at: "2019-05-07 02:03:41">2
>> user.microposts.find(2) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? AND "microposts"."id" = ? LIMIT ? [["user_id", 1], ["id", 2], ["LIMIT", 1]] => #<Micropost id: 2, content: "Lorem ipsum", user_id: 1, created_at: "2019-05-07 02:03:41", updated_at: "2019-05-07 02:03:41">3
>> user == micropost.user => true >> user.microposts.first == micropost Micropost Load (0.6ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ? [["user_id", 1], ["LIMIT", 1]] => false13.1.4
演習
Micropost.first.created_atの実行結果と、Micropost.last.created_atの実行結果を比べてみましょう。
Micropost.firstを実行したときに発行されるSQL文はどうなっているでしょうか? 同様にして、Micropost.lastの場合はどうなっているでしょうか? ヒント: それぞれをコンソール上で実行したときに表示される文字列が、SQL文になります。
データベース上の最初のユーザーを変数userに代入してください。そのuserオブジェクトが最初に投稿したマイクロポストのidはいくつでしょうか? 次に、destroyメソッドを使ってそのuserオブジェクトを削除してみてください。削除すると、そのuserに紐付いていたマイクロポストも削除されていることをMicropost.findで確認してみましょう。1
>> Micropost.first.created_at Micropost Load (1.3ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]] => Tue, 07 May 2019 02:03:41 UTC +00:00 >> Micropost.last.created_at Micropost Load (0.3ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]] => Mon, 06 May 2019 11:17:52 UTC +00:00 >>2
>> Micropost.first Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]] => #<Micropost id: 2, content: "Lorem ipsum", user_id: 1, created_at: "2019-05-07 02:03:41", updated_at: "2019-05-07 02:03:41"> >> Micropost.last Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]] => #<Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2019-05-06 11:17:52", updated_at: "2019-05-06 11:17:52">DESC LIMITとASC LIMITでそれぞれソートしている
3
>> user = User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:47:38", password_digest: "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzu...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31"> >> user.microposts.find_by(id: 1) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? AND "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["user_id", 1], ["id", 1], ["LIMIT", 1]] => #<Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2019-05-06 11:17:52", updated_at: "2019-05-06 11:17:52"> >> user.destroy (0.1ms) begin transaction Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]] SQL (2.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 2]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 1]] SQL (0.5ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 1]] (7.4ms) commit transaction => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:47:38", password_digest: "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzu...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31"> >> Micropost.find(1) Micropost Load (1.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["id", 1], ["LIMIT", 1]] Traceback (most recent call last): 1: from (irb):8 ActiveRecord::RecordNotFound (Couldn't find Micropost with 'id'=1) >>13.2.1
演習
7.3.3で軽く説明したように、今回ヘルパーメソッドとして使ったtime_ago_in_wordsメソッドは、Railsコンソールのhelperオブジェクトから呼び出すことができます。このhelperオブジェクトのtime_ago_in_wordsメソッドを使って、3.weeks.agoや6.months.agoを実行してみましょう。helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるでしょうか?
micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードにあるように、まずはpaginateメソッド (引数はpage: nil) でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。
1,2
>> helper.time_ago_in_words(3.weeks.ago) => "21 days" >> helper.time_ago_in_words(6.months.ago) => "6 months" >> helper.time_ago_in_words(1.year.ago) => "about 1 year"3
>> user = User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-05-07 05:47:25", updated_at: "2019-05-07 05:47:25", password_digest: "$2a$10$QRkKx2KrqTkrHRMLVH.e5egPoaBdgtwmXQ6WGa1U1jF...", remember_digest: nil, admin: true, activation_digest: "$2a$10$NesRhlkJ.qhEx4jne8HfduqzO8zqxa611pyZQq76/2M...", activated: true, activated_at: "2019-05-07 05:47:25", reset_digest: nil, reset_sent_at: nil> >> microposts = user.microposts.paginate(page: nil) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ? [["user_id", 1], ["LIMIT", 11], ["OFFSET", 0]] => #<ActiveRecord::AssociationRelation []> >> microposts.class => Micropost::ActiveRecord_AssociationRelation >>13.2.2
演習
(1..10).to_a.take(6)というコードの実行結果を推測できますか? 推測した値が合っているかどうか、実際にコンソールを使って確認してみましょう。
先ほどの演習にあったto_aメソッドの部分は本当に必要でしょうか? 確かめてみてください。
Fakerはlorem ipsum以外にも、非常に多種多様の事例に対応しています。Fakerのドキュメント (英語) を眺めながら画面に出力する方法を学び、実際に大学名や電話番号、Hipster IpsumやChuck Norris facts (参考: チャック・ノリスの真実) を画面に出力してみましょう。(訳注: もちろん日本語にも対応していて、例えば沖縄らしい用語を出力するfaker-okinawaもあります。ぜひ遊んでみてください。)1
>> (1..10).to_a.take(6) => [1, 2, 3, 4, 5, 6]2
>> (1..10).take(6) => [1, 2, 3, 4, 5, 6]13.2.2
演習
1. リスト 13.28にある2つの’h1’のテストが正しいか確かめるため、該当するアプリケーション側のコードをコメントアウトしてみましょう。テストが green から redに変わることを確認してみてください。
2. リスト 13.28にあるテストを変更して、will_paginateが1度のみ表示されていることをテストしてみましょう。ヒント: 表 5.2を参考にしてください。1
確認
2
assert_select 'div.pagination', count: 113.3.1
演習
1
なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイのでしょうか? 考えてみてください。1同じメソッドが重複して予期せぬ挙動の原因になるから
13.3.2
演習
1. Homeページをリファクタリングして、if-else文の分岐のそれぞれに対してパーシャルを作ってみましょう。元の
home.html.erb<% if logged_in? %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> </div> <% else %> <div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.png", alt: "Rails logo"), 'http://rubyonrails.org/' %> <% end %> <% if logged_in? %> <%= render 'static_pages/_logged_in'%> <% else %> <%= render 'static_pages/_not_logged_in'%> <% end %>static_pages/_logged_in.html.erb<div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> </div>static_pages/_not_logged_in.hetml.eb<div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.png", alt: "Rails logo"), 'http://rubyonrails.org/' %>13.3.3.
演習
1. 新しく実装したマイクロポストの投稿フォームを使って、実際にマイクロポストを投稿してみましょう。Railsサーバーのログ内にあるINSERT文では、どういった内容をデータベースに送っているでしょうか? 確認してみてください。
2. コンソールを開き、user変数にデータベース上の最初のユーザーを代入してみましょう。その後、Micropost.where("user_id = ?", user.id)とuser.microposts、そしてuser.feedをそれぞれ実行してみて、実行結果がすべて同じであることを確認してみてください。ヒント: ==で比較すると結果が同じかどうか簡単に判別できます。1
>{"content"=>"123456testtest"}, "commit"=>"Post"} User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] (0.0ms) begin transaction SQL (4.1ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "123456testtest"], ["user_id", 1], ["created_at", "2019-05-08 05:30:37.728696"], ["updated_at", "2019-05-08 05:30:37.728696"]] (6.5ms) commit transaction2
>> user = User.first >> Micropost.where("user_id = ?", user.id) == user.microposts Micropost Load (367.6ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]] Micropost Load (19.2ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC => true >> Micropost.where("user_id = ?", user.id) == user.feed => true >> user.microposts == user.feed Micropost Load (20.8ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC => true13.3.5演習
1. リスト 13.55で示した4つのコメント (「無効な送信」など) のそれぞれに対して、テストが正しく動いているか確認してみましょう。具体的には、対応するアプリケーション側のコードをコメントアウトし、テストが redになることを確認し、元に戻すと greenになることを確認してみましょう。
2. サイドバーにあるマイクロポストの合計投稿数をテストしてみましょう。このとき、単数形 (micropost) と複数形 (microposts) が正しく表示されているかどうかもテストしてください。ヒント: リスト 13.57を参考にしてみてください。1
無効なマイクロポストを投稿、有効なマイクロポストを投稿、マイクロポストの削除、そして他のユーザーのマイクロポストには [delete] リンクが表示されないことを確認、無効な送信
Microposts_contoroller.rbdef create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else @feed_items = [] render 'static_pages/home' end endをコメントアウトすると
ERROR["test_should_redirect_create_when_not_logged_in", MicropostsControllerTest, 1.996017354002106] test_should_redirect_create_when_not_logged_in#MicropostsControllerTest (2.00s) AbstractController::ActionNotFound: AbstractController::ActionNotFound: The action 'create' could not be found for MicropostsController test/controllers/microposts_controller_test.rb:11:in `block (2 levels) in <class:MicropostsControllerTest>' test/controllers/microposts_controller_test.rb:10:in `block in <class:MicropostsControllerTest>' ERROR["test_micropost_interface", MicropostsInterfaceTest, 2.111486043999321] test_micropost_interface#MicropostsInterfaceTest (2.11s) AbstractController::ActionNotFound: AbstractController::ActionNotFound: The action 'create' could not be found for MicropostsController test/integration/microposts_interface_test.rb:15:in `block (2 levels) in <class:MicropostsInterfaceTest>' test/integration/microposts_interface_test.rb:14:in `block in <class:MicropostsInterfaceTest>'est_should_redirect_create_when_not_logged_inも一緒にテストではじかれてしまったがこの部分であっている?
投稿を削除するのテストは以下をコメントアウトすると
def destroy @micropost.destroy flash[:success] = "Micropost deleted" redirect_back(fallback_location: root_url) end結果
ERROR["test_should_redirect_destroy_for_wrong_micropost", MicropostsControllerTest, 1.2442943940041005] test_should_redirect_destroy_for_wrong_micropost#MicropostsControllerTest (1.24s) AbstractController::ActionNotFound: AbstractController::ActionNotFound: The action 'destroy' could not be found for MicropostsController test/controllers/microposts_controller_test.rb:27:in `block (2 levels) in <class:MicropostsControllerTest>' test/controllers/microposts_controller_test.rb:26:in `block in <class:MicropostsControllerTest>' ERROR["test_should_redirect_destroy_when_not_logged_in", MicropostsControllerTest, 1.2528192390018376] test_should_redirect_destroy_when_not_logged_in#MicropostsControllerTest (1.25s) AbstractController::ActionNotFound: AbstractController::ActionNotFound: The action 'destroy' could not be found for MicropostsController test/controllers/microposts_controller_test.rb:18:in `block (2 levels) in <class:MicropostsControllerTest>' test/controllers/microposts_controller_test.rb:17:in `block in <class:MicropostsControllerTest>' ERROR["test_micropost_interface", MicropostsInterfaceTest, 1.9133356579986867] test_micropost_interface#MicropostsInterfaceTest (1.91s) AbstractController::ActionNotFound: AbstractController::ActionNotFound: The action 'destroy' could not be found for MicropostsController test/integration/microposts_interface_test.rb:30:in `block (2 levels) in <class:MicropostsInterfaceTest>' test/integration/microposts_interface_test.rb:29:in `block in <class:MicropostsInterfaceTest>' 55/55: [==========] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.21534s 55 tests, 288 assertions, 0 failures, 3 errors, 0 skips3つもエラーがでるのでやりすぎらしい
@micropost.destroyだけをコメントアウトするとFAIL["test_micropost_interface", MicropostsInterfaceTest, 2.2194056610023836] test_micropost_interface#MicropostsInterfaceTest (2.22s) "Micropost.count" didn't change by -1. Expected: 38 Actual: 39 test/integration/microposts_interface_test.rb:29:in `block in <class:MicropostsInterfaceTest>' 55/55: [==========] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.24183s 55 tests, 293 assertions, 1 failures, 0 errors, 0 skipsこれで
assert_difference 'Micropost.count', -1 doの部分がうまくいっていると確認?def create内の
@feed_items = []をコメントアウトして無効な送信テストの確認結果
ERROR["test_micropost_interface", MicropostsInterfaceTest, 2.127671581998584] test_micropost_interface#MicropostsInterfaceTest (2.13s) ActionView::Template::Error: ActionView::Template::Error: undefined method `any?' for nil:NilClass app/views/shared/_feed.html.erb:1:in `_app_views_shared__feed_html_erb___2635249087422250435_66072860' app/views/static_pages/_logged_in.html.erb:12:in `_app_views_static_pages__logged_in_html_erb__2347269424197631278_65986000' app/views/static_pages/home.html.erb:2:in `_app_views_static_pages_home_html_erb__234919273053642744_70042497772920' app/controllers/microposts_controller.rb:12:in `create' test/integration/microposts_interface_test.rb:15:in `block (2 levels) in <class:MicropostsInterfaceTest>' test/integration/microposts_interface_test.rb:14:in `block in <class:MicropostsInterfaceTest>' 55/55: [==========] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.13492s 55 tests, 285 assertions, 0 failures, 1 errors, 0 skips2
test/integration/microposts_interface_test.rbtest "micropost sidebar count" do log_in_as(@user) get root_path assert_match "#{@user.microposts.count} microposts", response.body # まだマイクロポストを投稿していないユーザー other_user = users(:malory) log_in_as(other_user) get root_path assert_match "0 microposts", response.body other_user.microposts.create!(content: "A micropost") get root_path assert_match "1 micropost", response.body End13.4.1
演習
画像付きのマイクロポストを投稿してみましょう。もしかして、大きすぎる画像が表示されてしまいましたか? (心配しないでください、次の13.4.3でこの問題を直します)。
リスト 13.63に示すテンプレートを参考に、13.4で実装した画像アップローダーをテストしてください。テストの準備として、まずはサンプル画像をfixtureディレクトリに追加してください (コマンド例: cp app/assets/images/rails.png test/fixtures/)。リスト 13.63で追加したテストでは、Homeページにあるファイルアップロードと、投稿に成功した時に画像が表示されているかどうかをチェックしています。なお、テスト内にあるfixture_file_uploadというメソッドは、fixtureで定義されたファイルをアップロードする特別なメソッドです18。ヒント: picture属性が有効かどうかを確かめるときは、11.3.3で紹介したassignsメソッドを使ってください。このメソッドを使うと、投稿に成功した後にcreateアクション内のマイクロポストにアクセスするようになります。1
確認
2
test/integration/microposts_interface_test.rbtest "micropost interface" do log_in_as(@user) get root_path assert_select 'div.pagination' assert_select 'input[type=file]' # 無効な送信 assert_no_difference 'Micropost.count' do post microposts_path, params: { micropost: { content: "" } } end assert_select 'div#error_explanation' # 有効な送信 content = "This micropost really ties the room together" picture = fixture_file_upload('test/fixtures/rails.png', 'image/png') assert_difference 'Micropost.count', 1 do post microposts_path, params: { micropost: { content: content, picture: picture } } end assert assigns(:micropost).picture? follow_redirect! assert_match content, response.body # 投稿を削除する assert_select "a", text: 'delete' first_micropost = @user.microposts.paginate(page: 1).first assert_difference 'Micropost.count', -1 do delete micropost_path(first_micropost) end get user_path(users(:archer)) assert_select 'a', text: "delete", count: 0 end13.4.2
演習
5MB以上の画像ファイルを送信しようとした場合、どうなりますか?無効な拡張子のファイルを送信しようとした場合、どうなりますか?
2
エラーが出る
- 投稿日:2019-05-23T09:16:49+09:00
Rails5.2のrails credentials:editを好きなエディタで編集する(macOS)
この記事なに?
![]()
Rails5.2で導入された秘蔵情報を編集するコマンド
rails credentials:edit
というのがあります。この記事では、
rails credentials:edit
を自分の好きなエディタでやる場合のコマンドを載せておきます。
credentials
について、詳しく知りたいという方は以下の記事からどうぞ。どうやるの?
vi / vimの場合
vi系が得意な方はこちらで十分かなと思います。
EDITOR=vi bin/rails credentials:edit EDITOR=vim bin/rails credentials:edit他のエディタの場合
自分はvi系コマンドがいまだに苦手です。少しの編集だったら全然OKなのですが、十数行とかになると、慣れ親しんだエディタ(自分の場合はVSCode)で編集したくなります。
vi / vim以外の他エディタで編集したい場合は、
wait
オプションを付加し、以下のようなコマンドを打ちます。# EDITOR="好きなエディタ --wait" bin/rails credentials:edit # 好きなエディタ=VSCodeの場合 EDITOR="code --wait" bin/rails credentials:edit # 好きなエディタ=Atomの場合 EDITOR="atom --wait" bin/rails credentials:editコマンドを打つと、対象のエディタで編集画面となりますので、APIKeyなどの設定が済んだら保存、エディタを閉じると以下が生成されます。
config/credentials.yml.enc
config/master.key
注意事項
EDITOR="好きなエディタ --wait" bin/rails credentials:edit
の好きなエディタについては、別途Pathを通しておく必要がありますので、ご注意ください。VSCodeを
code
コマンドで起動させる場合は、以下の記事を参考にしてみてください。P.S.
いろいろ記事を探していたら、Windowsで
Sublime Text
でrails credentials:edit
しようとされている記事を発見しました。Windowsで好きなエディタで
rails credentials:edit
したい方は、以下の方の記事を参考にされるとよいかもです。おわり
誤記などありましたら、FBいただけると助かります!?
- 投稿日:2019-05-23T09:07:25+09:00
Railsアプリケーションをデプロイ&ロールバックするCapistranoコマンド
環境
$ cat /etc/system-release Amazon Linux AMI release 2018.03 # プロジェクトのディレクトリ内にて $ vim Gemfile.lock capistrano (3.11.0) unicorn (5.5.0) $ rbenv versions * 2.6.2 (set by /usr/local/rbenv/version) $ bin/rails -v Rails 5.2.3はじめに
開発環境やステージング環境などデプロイ先によって、設定ファイル名はそれぞれ
staging.rb
やdevelopment.rb
になると思います。
今回はproduction.rb
という本番環境の設定ファイルを使用して、デプロイ・ロールバックするという前提で話を進めます。production.rbserver 'localhost', user: 'banri', roles: %w{ app web db } set :rails_env, :production set :unicorn_rack_env, "#{fetch(:rails_env)}" set :deploy_to, '/var/www/hogehoge(アプリケーション名)/production' set :repo_url, 'git@github.com:banri/hogehoge.git' set :branch, :masterデプロイ?
デプロイの疎通確認?
$ bundle exec cap production deploy:check以下のような結果になれば、デプロイ疎通OKです。
$ bundle exec cap production deploy:check 00:00 git:wrapper 01 mkdir -p /tmp ✔ 01 banri@localhost 0.751s Uploading /tmp/git-ssh-Hogehoge-local-banri.sh 100.0% 02 chmod 700 /tmp/git-ssh-Hogehoge-local-banri.sh ✔ 02 banri@localhost 0.754s 00:02 git:check 01 git ls-remote git@github.com:banri/hogehoge.git HEAD 01 Warning: Permanently added 'github.com,XX.XX.XXX.XX' (RSA) to the list of known hosts. 01 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HEAD ✔ 01 banri@localhost 2.833s 00:05 deploy:check:directories 01 mkdir -p /var/www/hogehoge/production/shared /var/www/hogehoge/production/releases ✔ 01 banri@localhost 0.706s 00:05 deploy:check:linked_dirs 01 mkdir -p /var/www/hogehoge/production/shared/log /var/www/hogehoge/production/shared/tmp/pids /var/www/hogehoge/production/shared/tmp/cache /var/www/hogehoge/production/shared/tmp/sockets /var/www/hoge… ✔ 01 banri@localhost 0.745s 00:06 deploy:check:make_linked_dirs 01 mkdir -p /var/www/hogehoge/production/shared/config ✔ 01 banri@localhost 0.760sCapistranoのデプロイはSSHを使用します。接続ができずデプロイの疎通に失敗した場合は、以下のようなエラーを吐きます。
cap aborted! SSHKit::Runner::ExecuteError: Exception while executing as banri@localhost: Authentication failed for user banri@localhost Caused by: Net::SSH::AuthenticationFailed: Authentication failed for user banri@localhostSSHのセットアップなどに関しては、Connecting to GitHub with SSHを参考にしてください。
デプロイの実行???
$ bundle exec cap production deploy:checkCapistranoの公式ドキュメントによると、上記コマンド1発で以下のコマンドが順番に実行されているそうです。
$ bundle exec cap production deploy:starting - デプロイスタート+全てのデプロイ準備ができているかの確認 $ bundle exec cap production deploy:started - custom tasksへのデプロイフックの作成開始 $ bundle exec cap production deploy:updating - 新リリースに伴うサーバーのアップデート → ここではcurrentディレクトリに同期されていない状態 $ bundle exec cap production deploy:updated - デプロイフックのアップデート $ bundle exec cap production deploy:publishing - 新リリースの公開 $ bundle exec cap production deploy:published - デプロイフックの公開 $ bundle exec cap production deploy:finishing - デプロイ完了 $ bundle exec cap production deploy:finished - デプロイフック作成完了
deploy:updating
のコマンド入力後、/releases
以下を確認するとアプリケーションのバージョンが変わっていないことが分かります。
変わっていない場合は、本日の日付が入ったソースコードのディレクトリが存在しません。$ cd /var/www/hogehoge/production/releases $ ls -tl total 20 drwxrwxr-x 16 banri banri 4096 Mar 15 05:57 20190315033559 - 最終更新のファイル名が本日の日付になっていないことを確認 drwxrwxr-x 15 banri banri 4096 Jan 12 03:58 20190112061458 drwxrwxr-x 14 banri banri 4096 Aug 3 2018 20180803054816 drwxrwxr-x 15 banri banri 4096 Jul 4 2018 20180704091354 drwxrwxr-x 14 banri banri 4096 Jul 4 2018 20180704082634ロールバック?
1つ前のリリース状態へロールバック?
$ bundle exec cap production deploy:rollbackロールバックも同様に、上記コマンド1発で以下のコマンドが順番に実行されているそうです。
$ bundle exec cap production deploy:starting $ bundle exec cap production deploy:started $ bundle exec cap production deploy:reverting - 旧リリースへサーバーの巻き戻し $ bundle exec cap production deploy:reverted - デプロイフックの巻き戻し $ bundle exec cap production deploy:publishing $ bundle exec cap production deploy:published $ bundle exec cap production deploy:finishing_rollback - ロールバック完了 $ bundle exec cap production deploy:finished特定のリリース状態にロールバック???
ROLLBACK_RELEASE=
の後に、リリースのディレクトリ名を指定するだけです。$ bundle exec cap production deploy:rollback ROLLBACK_RELEASE=リリースのディレクトリ名(日付) 例えば以下のような感じです。 $ bundle exec cap production deploy:rollback ROLLBACK_RELEASE=20190112061458参考
- 投稿日:2019-05-23T08:09:40+09:00
Rails6 のちょい足しな新機能を試す22(filter_attributes 編)
はじめに
Rails 6 に追加されそうな新機能を試す第22段。 今回のちょい足し機能は、
filter_attributes
(filter_attributes=
メソッド)編です。
Rails 6.0 では、 filter_attributes で指定された model の attribute は[FILTERED]
と表示されるようになります。Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1Rails プロジェクトを作る
$ rails new rails6_0_0rc1 $ cd rails6_0_0rc1User model を作ります
$ bin/rails g model User name sensitive $ bin/rails db:create db:migrateconfig/initializers/filter_parameter_logging.rb を編集する
sensitive を
[FILTERED]
にするためにfilter_parameter_logging.rb
を編集します。config/initializers/filter_parameter_logging.rbRails.application.config.filter_parameters += [:password, :sensitive]irb で確認する
irb で User モデルを作成して、確認すると、 sensitive の値が
[FILTERED]
となっていることがわかります。bash-4.4# bin/rails c Running via Spring preloader in process 50 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> user = User.new(name: 'rails', sensitive: 'sensitive') => #<User id: nil, name: "rails", sensitive: [FILTERED], created_at: nil, updated_at: nil>
pp
を実行した場合も[FILTERED]
で表示されます。irb(main):002:0> pp user #<User:0x0000556be6e41c48 id: nil, name: "rails", sensitive: [FILTERED], created_at: nil, updated_at: nil> => #<User id: nil, name: "rails", sensitive: [FILTERED], created_at: nil, updated_at: nil>User model を編集する
filter_parameter_logging.rb
から:sensitive
を削除して、user.rb
でfilter_attributes=
メソッドを使います。config/initializers/filter_parameter_logging.rbRails.application.config.filter_parameters += [:password]app/models/user.rbclass User < ApplicationRecord self.filter_attributes += [:sensitive] end再度 irb で確認する
irb で User モデルを作成して、確認すると、 sensitive の値が
[FILTERED]
となっていることがわかります。bash-4.4# bin/rails c Running via Spring preloader in process 50 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> user = User.new(name: 'rails', sensitive: 'sensitive') => #<User id: nil, name: "rails", sensitive: [FILTERED], created_at: nil, updated_at: nil>
pp
を実行した場合も[FILTERED]
で表示されます。irb(main):002:0> pp user #<User:0x0000556be6e41c48 id: nil, name: "rails", sensitive: [FILTERED], created_at: nil, updated_at: nil> => #<User id: nil, name: "rails", sensitive: [FILTERED], created_at: nil, updated_at: nil>filter_parameters で指定した場合と model の filter_attributes で指定した場合の違い
Rails.application.config.filter_parameters
で指定した場合は、全てのモデルでその attribute が[FILTERED]
で表示されます。(Rails.application.config.filter_attributes
で設定されているものが、filter_attributes
に反映されます。)
filter_attributes
で指定した場合は、指定した model の属性だけが[FILTERED]
になります。基本的には、
Rails.application.config.filter_parameters
で指定した方が漏れがなくて良いと思います。試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try022_filter_parameters追記
正規表現による attribute の指定も可能になってます(
/sens/
を指定するとsensitive
が[FILTERED]
になる)。参考情報
- 投稿日:2019-05-23T06:32:23+09:00
アソシエーションについてまとめてみた
アソシエーション(関連付け)
アソシエーションとは、モデルとDB間をまたいだデータの呼び出しをより簡単に行うことが出来る。
アソシーションの定義
①モデルクラスにhas_manyやbelongs_toが定義されていること。
②所属するテーブル側に、クラス名_idというカラムがあること。用語の解説
※分かりやすくするために、ツイッターを例に解説します。
has_many → ユーザーは、自分の作成したツイートを複数個所持している状態である。
qiita.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :tweets endbelongs_to →全てのユーザーは、いずれかのツイートを保持している。
qiita.rbclass Tweet < ApplicationRecord belongs_to :user endアソシエーションのメリット
・モデルをまたいだ呼び出しが簡単になる。
→userモデルのインスタンス.tweetsで呼び出しが可能になる・アソシエーションを使用しない
ターミナル[2] pry(main)> Tweet.where(user_id: user.id)・アソシエーションを使用
ターミナル[2] pry(main)> user.tweets