20190523のRailsに関する記事は19件です。

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 を入れたら

1. 環境を最新にしよう

Ubuntu のターミナルから次のコマンドを実行して環境を更新する。
sudo はスーパーユーザでの実行権限を付与するコマンド。apt-get は管理者権限での実行が必要なので、sudoをつけて実行する。

環境を更新する
$ sudo apt-get update
$ sudo apt-get upgrade

Rails 環境を構築しよう

注意

ご使用の環境で 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-dev

2. 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.13

8. bundler を入れよう

Rails では一旦プロジェクトを作ると、そのプロジェクトで扱っているライブラリは Gemfile で管理され、それらは bundler でインストールすることになる。
ということで、 bundler をインストールしておく。

bundlerのインストール
$ gem install bundler

Rails プロジェクトを作ろう

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 にアクセスして、次のページが見れれば環境構築は完了。

rails_app.png

Windows のフォルダにシンボリックリンクを貼って Rails プロジェクトを Windows から編集しよう

上記までで Ubuntu on WSL の環境に Rails を導入できたが、このままだと Windows 環境から Rails プロジェクトの編集ができない。

Ubuntu から Windows のフォルダは /mnt/c/ から参照できるので、Rails のプロジェクトを Windows 側のフォルダに作成してそこを Ubuntu 側で参照しに行っても良いが、毎回 /mnt/c から参照しにくのは流石に面倒臭い。

ということで、シンボリックリンクである。
ここでやることの目的は

  • Ubuntu から Windows への参照の手間を減らすこと

で、やることは以下の2つ。

  1. あらかじめ Windows のユーザホームフォルダ直下に Rails フォルダを作成しておく
  2. 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 の開発ができるようになったはずなので、これから遊んでみようと思う。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【jQuery】【Ajax】iTunes APIを使って音楽情報の検索フォームを作り、Ajaxを使って表示させる

iTunesAPIを使って音楽情報を検索したい!

はじめまして。記念すべき初めてのQiita投稿です。
私はRailsチュートリアルから勉強を初めて2〜3ヶ月の:hatching_chick:エンジニア転職志望者でございます。
ポートフォリオ制作として現在音楽のSNSアプリを作っています。

その過程で、このwebアプリになくてはならない
音楽CDのアーティスト名とかタイトルとかジャケット情報をフォームから検索して表示
させる部分をド初心者なりに必死こいて作りなんとか動く形になったため、はじめてのアウトプットとしてまとめます!!

動けば万々歳精神で作ったものなので、もっとこうすれば綺麗にできるよ!
というアドバイスありましたらバシバシご指摘いただけると嬉しいです:bow_tone4:

使ったもの

当初、音楽系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とか全くわからーん:sob:」という人は以下を参考にするとなんとなくやっていることが分かると思います。

まずこの記事のコードを基本としてアレンジしました。

その他大いに参考にしたもの

最後に個人的感想

初めて記事を書いたので、
節目としてここまで勉強してきた中で個人的な感想を記録しておきます:walking_tone3:

初ポートフォリオ制作はインスタの音楽ジャケ版的なアプリを作りたいなぁ〜と思い(そういうサービス既にありますが)、
作る前はなんとなく「音楽系のAPIあるだろうし、理想形はそこからデータ取ってきてそれをそのまま投稿できたらいいなぁ」とぼんやりイメージしていた程度でした。

いざ作り始めてからは、必死に調べては参考記事のコードをアレンジし、試行錯誤して
あれよあれよと言う間に気づいたら結果的に理想と同じ機能を作ることができました。

どのAPIを使うか?の段階でそもそもAPIの知識が足りず、若干壁にぶち当たりましたが
粘り強くググっていたら初心者にも簡易に使えるものを発見できたのが、今回の超ラッキーでした:metal:そしてAPIの使い方の丁寧なまとめがあったことも:metal:ありがたや。
とりあえず今回学んだことは粘り強くググることの大切さでした

まだアプリは完成していませんが、この機能を実装できたことがちょっとした自信とモチベーションにつながりました。今後もがんばるぞ!!
そして私と同じくエンジニア目指して勉強に勤んでいる方々、一緒に頑張りましょう:octopus:

ここまで読んでいただいてありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

_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>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.enc
msb4Uzo8vfnzbP65VQ58Hmu4o5WzNDTVqv9EFRnR5.....

このように暗号化されて直接編集することはできない。

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]のように入力する。(以下コード参照)
尚、初期値はコメントアウトされているため、読み取る際には外しておくこと。

Terminal
aws:
  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.rb
config.require_master_key = true

上記のように設定することで、本番環境におけるマスターキーが指定されていない場合にエラー表示を出すことができる。

基本的にcredential.yml.encの用途は「本番環境での秘匿情報の管理」だが、各環境で別々に秘匿情報を管理するためのrails-env-credentialsというgemもあるらしい。

参考

Railsガイド セキュリティ10.1-独自のcredential
Rails5.2から追加された credentials.yml.enc のキホン

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】コピペで使えるdevise備忘録

はじめに

deviseでユーザーログイン機能を追加するにあたって、普段よく使っているものの備忘録です。

rootをログイン画面に設定

deviseのcontrollerを作成し、routes.rbで以下のように設定。

$ rails g devise:controllers users
routes.rb
Rails.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.rb
class ApplicationController < ActionController::Base
  def after_sign_in_path_for(resource)
    tasks_path
  end

このようにログイン後最初のページを条件分岐させることも可能です↓

application_controller.rb
class 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.rb
class 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

おわり

他にも備忘録しておきたい事ありそうなので、思い出し次第追加したいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

1日1問解いてSQLをマスター!SQLの学習サービス Qdash を作った話

目次

  • はじめに
  • なぜ作ろうと思ったか
  • 使ってる技術
  • 同じようなサービスを作りたい人のために実装を紹介
    • 1日に1回、指定の時間に問題を切り替えて表示する
    • 質問箱風なOGP画像を生成
  • これから
  • 最後に

はじめに

Qdash という1日1問形式の SQL 学習サービスを作りました。

https://q-dash.jp

問題画面

スクリーンショット 2019-05-21 12.18.08.png

回答画面

スクリーンショット 2019-05-21 12.18.37.png

なぜ作ろうと思ったか

とあるデータを見たいけど、いつも SQL を書ける人にお願いしている。
勉強はしたいが、中々勉強する時間が取れない。

と思ってるディレクター、営業、マーケターの方!!
でも1日の中でも隙間時間はありますよね?

その隙間時間に問題を解き、SQL は任せて!と自信を持って言えるようになりませんか?
というのがサービスのコンセプトです。

使っている技術

・ Ruby
・ Rails
・ ServiceWorker / Webpush
・ Heroku

Rails / 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
end

Heroku 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
end
class 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

スクリーンショット 2019-05-21 12.17.04.png

問題を作成すると、このような OGP 画像が自動で生成されます。

これから

ログイン機能とコース機能を作って問題を解けるようにしたいと思っています。
ログインしていなくても問題を解くことができる一方で、熱量の高い人はログインしてまとまった時間で勉強できたら良いですね。

実は一番大変なのは機能の実装より SQL の問題を作ることだったりしますw

最後に

詳細な使い方については note に書いているので見ていただけたら幸いです。
https://note.mu/usabdelah/n/n0b7f8fd30ed5

Qdash で学んだことを活かして実務で SQL を書き、
データの可視化をやっていく人が増えたらいいなと思っています。

ぜひ、Qdash で SQL の問題を解いてみてください。

https://q-dashjp

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails hands on

rails で Web アプリを作ってみよう!

Web アプリを作って、動かして、触って、デプロイしてみよう!

事前準備

docker , bundler はインストールしておいてね

docker

bundler

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:migrate

hands 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.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 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:install

add

    %p.notice= notice
    %p.alert= alert

in 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:migrate

add before_action :authenticate_user! in app/controllers/posts_controller.rb and app/controllers/users_controller.rb

add root to: 'post#show' route.rb

access 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

コピー用

Dockerfile
FROM 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.0
docker-compose.yml
version: '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_password
docker/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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【自分用】railsでhello, world!するまでの備忘録

Railsインストールからhello, world!出力までのもくじ

  1. rbenvを使ってRubyとRuby on Railsをインストール ※前提として、Command line tools をターミナルからインストール済 xcode-select --install
  2. コードを編集するのに必要なテキストエディタをインストール
  3. hello, world!を出力するだけの単純アプリの作成

1.rbenvを使ってRubyとRuby on Railsをインストール

1-1.Homebrewをインストール

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

ターミナルのスクショ
スクリーンショット 2019-05-23 14.14.50.png

1-2.rbenvをインストール

まずはrbenvをインストール
brew update
brew install rbenv

ターミナルのスクショ
スクリーンショット 2019-05-23 14.22.37.png

rbenvのインストールが完了したら、rbenvにパスを通します
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

ターミナルのスクショ
スクリーンショット 2019-05-23 14.40.38.png

1-3.rbenvを使ってRubyをインストール

下記コマンドでrbenvでインストール可能なRubyのバージョンを確認
rbenv install -l

実際にコマンド打つとこんな感じ↓
スクリーンショット 2019-05-23 14.47.30.png
※実際にはもっと表示されます

現在の安定版を確認するならwww.ruby-lang.orgにアクセスすると良いです。
スクリーンショット 2019-05-23 14.55.12.png
※スクショは2019年5月23日時点での安定板なので最新の安定板は↑のリンクに実際にアクセスして確認してください。

現在の安定板の確認をしたらrbenvを使ってRubyをインストール
rbenv install 2.6.3

ターミナルのスクショ
スクリーンショット 2019-05-23 14.49.35.png

1-4.デフォルトのRubyを設定

rbenv global 2.6.3

ターミナルのスクショ
スクリーンショット 2019-05-23 15.02.40.png

1-5.Railsのインストール

gem install rails

ターミナルのスクショ
スクリーンショット 2019-05-23 15.05.53.png

2.コードを編集するのに必要なテキストエディタのインストール

Atomエディタ使ってる人多い印象なのでAtom.ioからダウンロード。

3.hello, world!を出力するだけの単純アプリの作成

3-1.プロジェクトで使うディレクトリenvironmentを作成

今回のプロジェクトで使うディレクトリenvironmentを作成し、environmentディレクトリに移動。

mkdir environment
cd environment

ターミナルのスクショ
スクリーンショット 2019-05-23 16.15.01.png

3-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.6

3-3.rails sコマンドでhello, world!を出力

作成したhello_appのディレクトリに移動し、rails sコマンドを実行。

rails s

アプリケーションを表示するには、ローカルサーバーの場合http://localhost:3000/をブラウザで開きます。
※Ctrl+Cを押すとサーバーを終了できる
実際に開くとこんな感じ↓
スクショ
スクリーンショット 2019-05-23 17.21.18.png

用語まとめ

後日追記予定

参照リンク

Rails Girls インストール・レシピ
最初のアプリケーション
railsチュートリアルでのエラー

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】実務未経験がプルリクエスト時レビュアーに指摘されたこと-コミット・RSpec編-

はじめに

実務未経験の自分がエンジニアスクールでレビューを頂いた時に、
おそらく現場でも指摘されるであろう項目をまとめていきます。

前回は、コード・文法編をまとめました。
【初心者向け】独学では気づきにくい!実務未経験がプルリクエスト時レビュアーに指摘されたこと-コードお作法編-

まとめきれていない部分もありますので、随時追記していきます。
特に、独学でRails等を学んでいる方の参考になれば幸いです。

1. コミットでの注意

わかりやすく、綺麗なコードを書くことは心がけているに、雑なコミットで残してませんか?
私が独学で勉強していた頃は、正直疎かにしてました。
しかし、プルリクエストでレビューしてもらう際やチーム開発では、理解しやすいコミットを残す必要があります。
特にコレが正解!というのはありませんが、指摘されたことを踏まえてまとめていきます。

1-1. 1つのコミットで複数の機能を追加するのは避けよう。(意図を理解しづらくなる)

一気に複数の機能を実装して、1つのコミットにまとめると見る側が意図を理解しづらくなります。

$ git commit -m "ログイン機能、ユーザー編集機慧能、画像投稿機能およびRspecテストを追加"

のようにしてしまうと、
メッセージ的にもコードを確認するにしても分かりづらい状況になります。
コミットをどのように分けるかは、常に意識する必要があります。

1-2. 理解しやすいコミットメッセージ

コミットメッセージには、端的に理解しやすいメッセージで残す必要があります。

  • どういう目的で
  • 何を
  • どうしたのか(追加・修正・更新・削除など)

をコミットメッセージに残せると見る側も意図を理解しやすそうです。
とはいえ、チーム・プロジェクトによりルールがあると思われますので注意が必要。
個人的には以下のURLで紹介されていた書き方が分かりやすいかなと感じました。

参考URL: 誰にとってもわかりやすいGitのコミットメッセージを考える

1-3. 不要なファイルは除外or削除しよう

自動生成されるけど不要なファイルは事前に必要かどうかを考えましょう。
そして、本番環境で不要な.env(環境変数)などのファイルは、事前に.gitignoreに記述して除外しておきましょう。
不要なファイルがコミットされれば、自分・レビュアー共に見づらいプルリクエストになってしまう。

2. RSpecの準備編<導入後>

Gemfile
group :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

テストに失敗していないことは分かりますが、詳細が分かりにくい状態に。
そんな時は、
.rspecformat 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.rb
RSpec.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テスト入門」

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

実務未経験からRails開発に途中参加した際の環境構築

 はじめに

実務未経験から内定をいただいた企業のrailsでの開発に途中参加することになり、環境構築を行う中で躓いた部分があったため振り返り用として記録します。

  環境

  • プログラミングスクールにてRuby、Railsの環境構築済み
  • データベースはMySQLを使用
  • GitHubアカウント登録済
  • Homebrew、rbenvインストール済み
  • mac OS使用

 前提

  • 与えられた情報はGitHubのリポジトリのみ
  • ローカル環境で起動できることを目標とする

やったこと

GitHubリポジトリの共有

1.プライベートリポジトリを共有するためGitHubに登録しているアドレス又はアカウント名を伝える
2.GitHubのリポジトリへ招待され参加する
3.ローカルリポジトリの作成

開発環境構築

1.Ruby、Rails等のバージョンを開発環境に合わせる

Ruby、Railsのバージョンなどは 「Gemfile」 で確認をします

Gemfile
ruby '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 postgresql

2.PostgreSQLも無事インストールできたので再度Gemをインストール

$ bundle install

3.データベース作成

$ bundle exec rake db:create

4.マイグレーションファイルの実行

$ bundle exec rake db:migrate

5.PostgreSQLを起動

$ postgres -D /usr/local/var/postgres

6.サーバーを起動

$ rails s

これで無事にローカル環境構築完了!!

 ※手順としては前後している可能性がありこれが正しいとは限りませんのでご了承ください

 おわりに

プログラミング初心者として検索していく中で、同じような状況の人の手助けになればと思い記事にしました。
やり方としてはエラーに遭遇しながら手探り状態で調べて進めたため進め方おかしいぞとなるかもしれません。

 参考

https://qiita.com/nasutaro211/items/d69a5e0b883293537b7e#_reference-a0cf0f3d167d682de2dc
https://qiita.com/youcune/items/5b783f7fde45d0fd4b35
https://qiita.com/yh2020/items/8be3087004d100fe752b

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

capistranoでのデプロイ時に、assets:precompile でメモリエラーが発生する

エラー内容

capistranoでのデプロイ時に、デプロイ先サーバでassets:precompileをするのですが、これがメモリ不足で失敗しました。
以下はエラーログです。

log/capistrano.log
 INFO [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追加しました。

参考: swap領域拡張手順(ファイル割当)

2. 環境変数 NODE_OPTIONS に max-old-space-size を設定する

デプロイ先のサーバにssh接続し、以下の環境変数を追加します。

$ sudo vim /etc/environment
# 以下の行を追加してください
export NODE_OPTIONS="--max-old-space-size=2048"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails Tutorial(2週目)-4-

4章はチェリー本に書いてあることがほとんどだったので、また別でまとめようと思います。

アクセサーの作成

attr_accessorは内部的に次のような処理を行っています。

(1)引数のシンボルすべてに対して以下の処理を繰り返す。

(2)シンボルで指定された名前のメソッドを定義する。そのメソッドはシンボルの名前の先頭に「@」を付加したインスタンス変数の値を取り出す。

(3)シンボルで指定されたメソッド名の後ろに「=」を付加した名前のメソッドを定義する。そのメソッドは引数を1つ取り,その値をシンボルの名前の先頭に「@」を付加したインスタンス変数に設定する

attr_accessor によってクラス外からも、インスタンス変数を読み込んだり、変更したり操作できるようになる。

ユーザー名とメールアドレス (属性: attribute) に対応するアクセサー (accessor) をそれぞれ作成します。アクセサーを作成すると、そのデータを取り出すメソッド (getter) と、データに代入するメソッド (setter) をそれぞれ定義してくれます。具体的には、この行を実行したことにより、インスタンス変数@nameとインスタンス変数@emailにアクセスするためのメソッドが用意されます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vuejsが反映されない

解決コマンド

npm install -g expo-cli
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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オブジェクトをデータベースに保存してみましょう。この時点でもう一度マジックカラムの内容を調べてみましょう。今度はどのような値が入っているでしょうか?

>> 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
>

>> 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"

>> 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:00

13.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 を実行した結果はどうなるでしょうか? それぞれ確認してみてください。

>> 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]]
=> false

13.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でそれぞれソートしている

>> 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..10).to_a.take(6)
=> [1, 2, 3, 4, 5, 6]

>> (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を参考にしてください。


確認

assert_select 'div.pagination', count: 1

13.3.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 transaction

2

>> 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
=> true

13.3.5演習
1. リスト 13.55で示した4つのコメント (「無効な送信」など) のそれぞれに対して、テストが正しく動いているか確認してみましょう。具体的には、対応するアプリケーション側のコードをコメントアウトし、テストが redになることを確認し、元に戻すと greenになることを確認してみましょう。
2. サイドバーにあるマイクロポストの合計投稿数をテストしてみましょう。このとき、単数形 (micropost) と複数形 (microposts) が正しく表示されているかどうかもテストしてください。ヒント: リスト 13.57を参考にしてみてください。


無効なマイクロポストを投稿、有効なマイクロポストを投稿、マイクロポストの削除、そして他のユーザーのマイクロポストには [delete] リンクが表示されないことを確認、

無効な送信

Microposts_contoroller.rb
  def 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 skips

3つもエラーがでるのでやりすぎらしい
@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 skips

test/integration/microposts_interface_test.rb

  test "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
End

13.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

確認

test/integration/microposts_interface_test.rb
  test "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
  end

13.4.2
演習
5MB以上の画像ファイルを送信しようとした場合、どうなりますか?

無効な拡張子のファイルを送信しようとした場合、どうなりますか?

1
railsttutorialcapter1301No-00.png

2
エラーが出る

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails5.2のrails credentials:editを好きなエディタで編集する(macOS)

この記事なに? :thinking:

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 Textrails credentials:editしようとされている記事を発見しました。

Windowsで好きなエディタでrails credentials:editしたい方は、以下の方の記事を参考にされるとよいかもです。

おわり

誤記などありましたら、FBいただけると助かります!?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rbdevelopment.rbになると思います。
今回はproduction.rbという本番環境の設定ファイルを使用して、デプロイ・ロールバックするという前提で話を進めます。

production.rb
server '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.760s

Capistranoのデプロイは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@localhost

SSHのセットアップなどに関しては、Connecting to GitHub with SSHを参考にしてください。

デプロイの実行???

$ bundle exec cap production deploy:check

Capistranoの公式ドキュメントによると、上記コマンド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

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rc1

Rails プロジェクトを作る

$ rails new rails6_0_0rc1
$ cd rails6_0_0rc1

User model を作ります

$ bin/rails g model User name sensitive
$ bin/rails db:create db:migrate

config/initializers/filter_parameter_logging.rb を編集する

sensitive を [FILTERED] にするために filter_parameter_logging.rb を編集します。

config/initializers/filter_parameter_logging.rb
Rails.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.rbfilter_attributes= メソッドを使います。

config/initializers/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [:password]
app/models/user.rb
class 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] になる)。

参考情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アソシエーションについてまとめてみた

アソシエーション(関連付け)

アソシエーションとは、モデルとDB間をまたいだデータの呼び出しをより簡単に行うことが出来る。

アソシーションの定義

①モデルクラスにhas_manyやbelongs_toが定義されていること。
②所属するテーブル側に、クラス名_idというカラムがあること。

用語の解説

※分かりやすくするために、ツイッターを例に解説します。

has_many → ユーザーは、自分の作成したツイートを複数個所持している状態である。

qiita.rb
class User < ApplicationRecord
    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :validatable
    has_many :tweets
  end

belongs_to →全てのユーザーは、いずれかのツイートを保持している。

qiita.rb
class Tweet < ApplicationRecord
    belongs_to :user
  end

アソシエーションのメリット

・モデルをまたいだ呼び出しが簡単になる。
 →userモデルのインスタンス.tweetsで呼び出しが可能になる

・アソシエーションを使用しない

ターミナル
[2] pry(main)> Tweet.where(user_id: user.id)

・アソシエーションを使用

ターミナル
[2] pry(main)> user.tweets
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む