20200330のRubyに関する記事は18件です。

Typeformのかっこいい入力フォームをWebhookを使って自作アプリに実装した話

概要

非エンジニアビジネス職(カスタマーサクセス職)の@yuta_maruyamaが、動的でイケてるかっこよい入力フォームを自力で作ろうと思ったけれど難易度が高すぎたので米国産SaaSのTypeformをWebhookで組み込んでフォームを実装した話です。
ちなみにカスタマーサクセスの話は一切なく、大好きなクラフトビールのテイスティングノートアプリを作ろうとしている中での話。

環境

  • Ruby 2.6.2
  • Rails 6.0
  • heroku

作ろうとしているもの

BEER BASH

  • ビールのテイスティングノートアプリ
  • 飲んだビールに関する評価やコメントをnoteとして残す
  • Rails練習用なので見てくれは勘弁
  • 「ノートを残す煩わしさ」がないことをサービスのユニークな価値にしたかったのでそこにはこだわりたい

Typeformとは

  • フォームやサーベイに特化した米国のサービス
  • 無料で使える
  • 有料版だとHidden Fields機能など色々使える
  • 日本語対応も(一応)している

やったこと

Typeform側

フォームの作成

マグマパスタばりのすっ飛ばし方だが、まずはTypeformにアカウント作成し、フォーム作成をする。Googleフォームを作るのとほぼ変わらない。

Webhookの設定

WebhookなのでPOSTリクエストが前提となる。なので勝手にアクションを指定しなくとも勝手にPOSTになる。今回はPOST /notesで設定。保存。
Connect___Tasting_note___Typeform.png

hidden_filedの設定(オプショナル)

Hidden Fieldsは有料オプションなので有料化するつもりなければ飛ばして良い。Hidden Fieldsを用いない場合フォーム内で必要な値を入力してもらうのが良さそう(ユーザーにとってはひと手間だが)

今回の場合は、どこのBrewery(醸造所)のなんていうBeerのnoteを書いているかの値を渡して、
- Typeformに遷移した際にどのビールに関してのnoteかをはっきり示す
- わざわざTypeform側でユーザーがなんのビールについてのnoteか入力させないようにする

を実現したい。なのでbeer_id brewery_id beer_name brewery_name のフィールドを用意しておく

Fullscreen_2020_03_30_21_51.png
Create___New_typeform___Typeform.png

サービス側

Controller

notes#create をいじる。

データのもらい方

Typeformのフォームへの回答をイベントとし、JSON形式でwebhookは送られる。
要素としては大きく
- Event information
- definition
- answers

だが階層がややこしい。コードはこんな感じ。

  def create
    @note = Note.new()
    @note.beer_id = params["form_response"]["hidden"]["beer_id"]

    params["form_response"]["answers"].each do |answer|
      if answer["field"]["ref"] == "rating"
        @note.rating = answer["rating"]
      end

      if answer["field"]["ref"] == "sweetness"
        @note.sweetness = answer["number"]
      end

      if answer["field"]["ref"] == "bitterness"
        @note.bitterness = answer["number"]
      end

      if answer["field"]["ref"] == "sourness"
        @note.sourness = answer["number"]
      end

      if answer["field"]["ref"] == "aroma"
        @note.aroma = answer["number"]
      end

      if answer["field"]["ref"] == "comment"
        @note.comment = answer["text"]
      end

      if answer["field"]["ref"] == "serving_type"
        @note.serving_type = answer["choice"]["label"]
      end
    end

View

beer#showからTypeformにパラメータを渡しながら遷移する。こんな感じで書けばOK。

<%= link_to("Add note", "https://beerbash.typeform.com/to/******?beer_id=#{@beer.id}&beer_name=#{@beer.name}&brewery_name=#{@brewery.name}") %>

最後に

感謝

自分はカスタマーサクセス職としてSQLを業務中に書くことは多いが、Railsに関してはまさに勉強中といった感じ。
そんな中メンターとして色々教えてくれる勤め先のCTO @showwin には本当に感謝が尽きない。

SaaSとの共存

showwin先生には #write-code-or-die というSlackチャンネルでご指導いただいている。そのチャンネルでこんなことを述べたが
Slack___write-code-or-die___LAPRAS.png
非エンジニアの自分でも、外部サービスをきちんと使えれば学習時間を大幅に節約してそれなりのアプリケーションを作れる実感がわいた。ZapierなどのノンコーディングでSaaSを連携できるSaaSのためのSaaSもどんどん進化を遂げており、非エンジニアでもプロトタイプレベルならガンガン自分で作ってみて欲しい

見た目

最後に、こんな感じの見た目になった。

beers#show
好みのビールが見つかるテイスティングノートサービス___BEER_BASH.png

Typeformに遷移。ちゃんとビール名とブルワリー名が出ている。
Tasting_note.png

元々Javascriptで自分でどうにかしようとおもってたがこれは無理
Tasting_note.png

そして、Yorroco Beerが好きな人はぜひ連絡ください!!

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

Typeformのかっこいい入力フォームを自作アプリに実装した話

概要

非エンジニアビジネス職(カスタマーサクセス職)の@yuta_maruyamaが、動的でイケてるかっこよい入力フォームを自力で作ろうと思ったけれど難易度が高すぎたので米国産SaaSのTypeformを組み込んでWebhookで連携しようとした話です。
ちなみにカスタマーサクセスの話は一切なく、大好きなクラフトビールのテイスティングノートアプリを作ろうとしている中での話。

環境

  • Ruby 2.6.2
  • Rails 6.0
  • heroku

作ろうとしているもの

BEER BASH

  • ビールのテイスティングノートアプリ
  • 飲んだビールに関する評価やコメントをnoteとして残す
  • Rails練習用なので見てくれは勘弁
  • 「ノートを残す煩わしさ」がないことをサービスのユニークな価値にしたかったのでそこにはこだわりたい

Typeformとは

  • フォームやサーベイに特化した米国のサービス
  • 無料で使える
  • 有料版だとHidden Fields機能など色々使える
  • 日本語対応も(一応)している

やったこと

Typeform側

フォームの作成

マグマパスタばりのすっ飛ばし方だが、まずはTypeformにアカウント作成し、フォーム作成をする。Googleフォームを作るのとほぼ変わらない。

Webhookの設定

WebhookなのでPOSTリクエストが前提となる。なので勝手にアクションを指定しなくとも勝手にPOSTになる。今回はPOST /notesで設定。保存。
Connect___Tasting_note___Typeform.png

hidden_filedの設定(オプショナル)

Hidden Fieldsは有料オプションなので有料化するつもりなければ飛ばして良い。Hidden Fieldsを用いない場合フォーム内で必要な値を入力してもらうのが良さそう(ユーザーにとってはひと手間だが)

今回の場合は、どこのBrewery(醸造所)のなんていうBeerのnoteを書いているかの値を渡して、

  • Typeformに遷移した際にどのビールに関してのnoteかをはっきり示す
  • わざわざTypeform側でユーザーがなんのビールについてのnoteか入力させないようにする

を実現したい。なのでbeer_id brewery_id beer_name brewery_name のフィールドを用意しておく

Fullscreen_2020_03_30_21_51.png
Create___New_typeform___Typeform.png

サービス側

Controller

notes#create をいじる。

データのもらい方

Typeformのフォームへの回答をイベントとし、JSON形式でwebhookは送られる。
要素としては大きく

  • Event information
  • definition
  • answers

だが階層がややこしい。コードはこんな感じ。

  def create
    @note = Note.new()
    @note.beer_id = params["form_response"]["hidden"]["beer_id"]

    params["form_response"]["answers"].each do |answer|
      if answer["field"]["ref"] == "rating"
        @note.rating = answer["rating"]
      end

      if answer["field"]["ref"] == "sweetness"
        @note.sweetness = answer["number"]
      end

      if answer["field"]["ref"] == "bitterness"
        @note.bitterness = answer["number"]
      end

      if answer["field"]["ref"] == "sourness"
        @note.sourness = answer["number"]
      end

      if answer["field"]["ref"] == "aroma"
        @note.aroma = answer["number"]
      end

      if answer["field"]["ref"] == "comment"
        @note.comment = answer["text"]
      end

      if answer["field"]["ref"] == "serving_type"
        @note.serving_type = answer["choice"]["label"]
      end
    end

View

beer#showからTypeformにパラメータを渡しながら遷移する。こんな感じで書けばOK。

<%= link_to("Add note", "https://beerbash.typeform.com/to/******?beer_id=#{@beer.id}&beer_name=#{@beer.name}&brewery_name=#{@brewery.name}") %>

最後に

感謝

自分はカスタマーサクセス職としてSQLを業務中に書くことは多いが、Railsに関してはまさに勉強中といった感じ。
そんな中メンターとして色々教えてくれる勤め先のCTO @showwin には本当に感謝が尽きない。

SaaSとの共存

showwin先生には #write-code-or-die というSlackチャンネルでご指導いただいている。そのチャンネルでこんなことを述べたが
Slack___write-code-or-die___LAPRAS.png
非エンジニアの自分でも、外部サービスをきちんと使えれば学習時間を大幅に節約してそれなりのアプリケーションを作れる実感がわいた。ZapierなどのノンコーディングでSaaSを連携できるSaaSのためのSaaSもどんどん進化を遂げており、非エンジニアでもプロトタイプレベルならガンガン自分で作ってみて欲しい

見た目

最後に、こんな感じの見た目になった。

beers#show
好みのビールが見つかるテイスティングノートサービス___BEER_BASH.png

Typeformに遷移。ちゃんとビール名とブルワリー名が出ている。
Tasting_note.png

元々Javascriptで自分でどうにかしようとおもってたがこれは無理
Tasting_note.png

そして、Yorroco Beerが好きな人はぜひ連絡ください!!

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

renderの処理について

本記事を書いた経緯

私はエンジニアになるために、プログラミングスクールにて学習を行なっています。
そこで、renderの処理について学び、備忘録として残したい使い方がありましたので本記事を書くに至りました。

renderの使い方

下記の[A]と[B]は書き方に違いがあれど、どちらも描画されるビューは全く同じです。
しかしながら、ある理由により、[A][B]いずれかの記述の方が好まれます。より良い記述は[A][B]はどちらでしょうか...?

[A]の場合
@users = User.all
@users.each do |user|
  = render 'user', user: user

[B]の場合
@users = User.all
  = render 'user', users: @users

良い記述は[B]となります。
[B]の方が好まれます。
理由は、[B]の方が処理が速いからです。

[A]では、@usersにeachを使用して、user一人一人に対してrenderを実行しています。
この書き方だと、userが100人いると100回、50000人いると50000回、どの部分テンプレートを利用するのか探しにいく処理が実行されるため、ユーザーが多ければ多くなるほどパフォーマンスが低下していきます。

一方、[B]では、renderする際の変数に、@usersを渡しています。
この書き方だと、どの部分テンプレートを利用するのか探しにいく処理は一度しか実行されないため、[A]より高速にビューを描画することができます。

なお、render @users と省略して記述することも可能です。

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

『Q22 百マス計算で最小のマスをたどると?』もっとプログラマ脳を鍛える数学パズル

はじめに

アルゴリズムの勉強に、もっとプログラマ脳を鍛える数学パズル アルゴリズムが脳にしみ込む70問 を解いています。
経路問題がありましたので、Perl で実装してみました。

苦労した所

最小値のマスを取り出すため、2次元配列 cost[r][c] の値でソートし 配列 que に格納された (r, c) 座標を返します。

ruby.rb
  que.sort_by!{|r, c| cost[r][c]}
  r, c = que.shift
javascript.js
  que.sort(function(a, b) {
    return cost[a[0]][a[1]] < cost[b[0]][b[1]];
  }
  var r, c;
  [r, c] = que.shift();

座標の受け渡しについて、後発の言語はスマートな感じがします。
特に、Ruby は、スッキリしていますね。

perl.pl
  @que = sort { $cost[$$_[0]][$$_[1]] } @que;
  my $in = shift @que;
  my ($r, $c) = ($$in[0], $$in[1]);

全ソース

q22_1.pl
use v5.18;
use warnings;

my @col = (8, 6, 8, 9, 3, 4, 1, 7, 6, 1);
my @row = (5, 1, 1, 9, 1, 6, 9, 0, 9, 6);

my (@board, @cost);
for my $r (0..@row-1) {
  for my $c (0..@col-1) {
    $board[$r][$c] = $row[$r] + $col[$c];
    $cost[$r][$c] = 2000;
  }
}

$cost[0][0] = $board[0][0];

my @que =[0, 0];
while (@que) {
#  @que = sort { $cost[$$b[0]][$$b[1]] <=> $cost[$$a[0]][$$a[1]] } @que; # 降順用
  @que = sort { $cost[$$_[0]][$$_[1]] } @que;
  my $in = shift @que;
  my ($r, $c) = ($$in[0], $$in[1]);
  if (0 <= $r - 1) {
    my ($x, $y) = ($r - 1, $c);
    if ($cost[$x][$y] > $cost[$r][$c] + $board[$x][$y]) {
      $cost[$x][$y] = $cost[$r][$c] + $board[$x][$y];
      push @que, [$x, $y];
    }
  }
  if (0 <= $c - 1) {
    my ($x, $y) = ($r, $c - 1);
    if ($cost[$x][$y] > $cost[$r][$c] + $board[$x][$y]) {
      $cost[$x][$y] = $cost[$r][$c] + $board[$x][$y];
      push @que, [$x, $y];
    }
  }
  if ($r + 1 < @row) {
    my ($x, $y) = ($r + 1, $c);
    if ($cost[$x][$y] > $cost[$r][$c] + $board[$x][$y]) {
      $cost[$x][$y] = $cost[$r][$c] + $board[$x][$y];
      push @que, [$x, $y];
    }
  }
  if ($c + 1 < @col) {
    my ($x, $y) = ($r, $c + 1);
    if ($cost[$x][$y] > $cost[$r][$c] + $board[$x][$y]) {
      $cost[$x][$y] = $cost[$r][$c] + $board[$x][$y];
      push @que, [$x, $y];
    }
  }
}
say $cost[@row - 1][@col - 1];

優先度付きキュー

Java では、java.util.PriorityQueue があります。
優先度付きキュー
計算量 O(1) + O(logN)
(上記 sortO(N) + O(N) )

Ruby で実装されている方もいらっしゃいます。
競プロの為にRubyでヒープ(優先度付きキュー)作った(ABC128E)
Ruby で Priority Queue を実装してみたい

まとめ

  • ダイクストラ法について少しだけ知見を得た

参照したサイト
Perlのdeleteでひっかかった話

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

bundle installのエラー解決法(can't find gem bundler (>= 0.a) with executable bundler)

エラー内容と状況

エラー内容はタイトルの通り。bundle installしたとき、can't find gem bundler (>= 0.a) with executable bundler (Gem::GemNotFoundException)と怒られてしまった。
そもそもこの時はgithubのリモートからpullして、rails sをした時に、bundle installしてくださ〜いってエラーから始まっています。チーム開発でよくある事象かと思い、自分も何回か直面してその都度調べていたので、そろそろ自分でまとめようと思います!

開発環境

  • ruby 2.5.1p57
  • Rails 5.2.4.1

bundlerとは

そもそもbundlerとは何だったのかを復習しましょう!
Railsアプリケーションにおいては非常に複雑なgemの管理をする必要があります。これらのgemを管理するのがbundlerというgemになります。
管理するものが無いのでbundle installできないってことだったのですね。

ターミナルでbundlerをインストールするコマンドを入力しましょう。

gem install bundler

解決するエラー内容の場合、上記コマンドで解決すると思います。(同様のエラーの記事を読むと)しかし今回は上手くbundlerをインストールできませんでした。

原因

今回の場合、gemfile.lockにbundlerのバージョンが記載されてえいるため、バージョンを指定しないとインストールができないことによるものでした。

gemfile.lock
BUNDLED WITH
   2.0.2
gem install bundler -v2.0.2

上記を実装することでエラーを解決することができます。

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

DBから取得したデータの中身を取得する方法 Ruby on Rails

MySQLからデータを取得してきた

whereメソッドを使って、特定条件下で検索に該当するクエリを発行させると、条件に当てはまったレコード全てを引っ張ってくる。

log_in.rb
yesterday_time = Time.now.yesterday
log_in_collection = LogIn.all #LogInというモデルを作成してあり、そこからデータを全て引っこ抜いている
log_in_list = log_in_collection.where("loged_in_at >= ?", yesterday_time)

そうすると、log_in_listをpメソッドで出力すると、

#<ActiveRecord::Relation [#<LogIn id: 456,  uid: "hogehoge", log_in_at: "2020-03-25 05:49:43", created_at: "2020-03-25 05:50:47", updated_at: "2020-03-25 05:50:47">]>

こんな感じのデータが取れる。パッと見、ハッシュに見える物が取ってこれているのが分かる。今回必要なのはこのuidにおけるhogehogeだけを引っ張ってくることがゴールです。

以下の自分の考え方に間違いがあったら、指摘をお願いします。

最初に犯したミス

欲しいデータを手に入れたければ、ハッシュのキーになっているものでメソッドチェーンすればいいってことだったので、安直に前述したコードの変数であるlog_in_listを使って、

hogehoge.rb
p log_in_list.uid

と書いて、hogehogeが取れているかを確認してみました。すると、ログはおろか、localhostでこの処理部分を走らせてみると、画面に真っ赤な文字でundefined uidと表示されてしまう。何でやねん。

ハッシュみたいなものからオブジェクトを取り出すような書き方はできん

ハッシュみたいなやつと書いているのは、ActiveRecordから取ってきているデータのことをなんて呼んでいいかわからないからですが、こう考えてみると、log_in_listを最初にpした時、これはデータの型がハッシュみたいなやつであって、オブジェクトになっていない。これが本当にハッシュで、ハッシュの値を取り出すには、

example.rb
log_in_list["uid"]

とかにせねばならない。今回のデータはActiveRecordから引っ張ってきているものなので、これでのアクセスもできない・・・ハズ。

また、whereは条件にあった全てのレコードを引っ張ってくるので、条件にあった全てのuidを抜き取る作業を必要とする。それじゃどうするか・・・一旦、このハッシュっぽい奴のデータをオブジェクトにしてやればいい。

hogehoge.rb
log_in_list.each do |data|
  log_in_users = data.uid
  p log_in_users
end

eachを使って、log_in_listの中身をdata変数へと入れ込むと、オブジェクト化してくれるので、オブジェクトへのアクセスする書き方が可能になる。これでhogehogeだけを引っ張ってこれる。

補足

後で知ったことですが、ハッシュ用のループ処理で使えるeachもある。この記事によると、やはりハッシュにはハッシュオブジェクトがちゃんと存在してそのオブジェクトへのアクセスの仕方も違うようだ・・・

ハッシュに対する繰り返し

考察

重要なのは2つあるかなと思いました。

書いているコードに内包されているデータの型を意識的に脳内で可視化すること。脳内で可視化って意味不明かもしれませんが、言わんとしていることは分かると思うんですよ。「今持ってきているデータは〇〇型で、まだ日オブジェクトだな」みたいな意識を持つイメージでしょうか。

また、それをオブジェクト化した時のアクセス方法を知っていること、もしくはそこから検索をかけて調べる足掛かりにする、ということでしょうか。

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

Ruby on Rails とかで docker 環境なのに #VSCode で ruby-rubocop 拡張を有効にするにはどうすれば良いのか? ( #Rails #Ruby )

ruby-rubocop

image

エラー

なんかいろいろと言われますけれど。

image
image

解決法

local に同じバージョンの ruby + rubocop 関連で必要なgemをインストーするれば動くっぽい

gem install rubocop
gem install rubocop-performance
gem install rubocop-rails

以上。

VsCodeがdockerまで見に行けないですもんね。いや、やり方はあるのかもしれないけど。やれたにしても激遅でしょ?たぶん。

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3055

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

vistaでkicad その2

概要

vistaでkicadやってみた。
sketchupで、dxf書いて、kicadで読み込んで見た。

環境

windows vista 32bit
kicad 4.0.4
sketchup 8

sketchupでbox書く。

image

skp_to_dxf.rbでdxfを出力。

 0
SECTION
 2
ENTITIES
  0
3DFACE
 8
GROUP0
10
2.54
20
2.54
30
0.0
11
0.0
21
0.0
31
0.0
12
0.0
22
2.54
32
0.0
13
0.0
23
2.54
33
0.0
70
1
  0
3DFACE
 8
GROUP0
10
0.0
20
0.0
30
0.0
11
2.54
21
2.54
31
0.0
12
2.54
22
0.0
32
0.0
13
2.54
23
0.0
33
0.0
70
1
  0
3DFACE
 8
GROUP0
10
2.54
20
0.0
30
2.54
11
0.0
21
2.54
31
2.54
12
0.0
22
0.0
32
2.54
13
0.0
23
0.0
33
2.54
70
1
  0
3DFACE
 8
GROUP0
10
0.0
20
2.54
30
2.54
11
2.54
21
0.0
31
2.54
12
2.54
22
2.54
32
2.54
13
2.54
23
2.54
33
2.54
70
1
  0
3DFACE
 8
GROUP0
10
2.54
20
0.0
30
2.54
11
0.0
21
0.0
31
0.0
12
2.54
22
0.0
32
0.0
13
2.54
23
0.0
33
0.0
70
1
  0
3DFACE
 8
GROUP0
10
0.0
20
0.0
30
0.0
11
2.54
21
0.0
31
2.54
12
0.0
22
0.0
32
2.54
13
0.0
23
0.0
33
2.54
70
1
  0
3DFACE
 8
GROUP0
10
0.0
20
2.54
30
2.54
11
0.0
21
0.0
31
0.0
12
0.0
22
0.0
32
2.54
13
0.0
23
0.0
33
2.54
70
1
  0
3DFACE
 8
GROUP0
10
0.0
20
0.0
30
0.0
11
0.0
21
2.54
31
2.54
12
0.0
22
2.54
32
0.0
13
0.0
23
2.54
33
0.0
70
1
  0
3DFACE
 8
GROUP0
10
0.0
20
2.54
30
2.54
11
2.54
21
2.54
31
0.0
12
0.0
22
2.54
32
0.0
13
0.0
23
2.54
33
0.0
70
1
  0
3DFACE
 8
GROUP0
10
2.54
20
2.54
30
0.0
11
0.0
21
2.54
31
2.54
12
2.54
22
2.54
32
2.54
13
2.54
23
2.54
33
2.54
70
1
  0
3DFACE
 8
GROUP0
10
2.54
20
2.54
30
0.0
11
2.54
21
0.0
31
2.54
12
2.54
22
0.0
32
0.0
13
2.54
23
0.0
33
0.0
70
1
  0
3DFACE
 8
GROUP0
10
2.54
20
0.0
30
2.54
11
2.54
21
2.54
31
0.0
12
2.54
22
2.54
32
2.54
13
2.54
23
2.54
33
2.54
70
1
 0
ENDSEC
 0
EOF

kicadでdxfインポートする。

うまくいかん。

image

以上。

:

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

初めてのBitbucket

rails チュートリアルを進めるにあたって、bitbucketを一番初めに使用するときにわからないことがあって苦しんだためメモ

・cloud9(aws)環境で実施
・アカウント作成は省略

公開鍵の作成

console
cat ~/.ssh/id_rsa.pub

上記のコマンド入力で公開鍵を所持しているか確認できます。所持していない場合にはNo such file or directoryと表示されます。
鍵を所持していない場合は

console
cd ~/.ssh #sshフォルダに移動
ssh-keygen -t rsa -C saber@example.com  # 自分のメールアドレス入力

Entキー押すと色々と出てくるが特にいじる必要ないのでそのままEntキー何回か押して鍵を作成。鍵を作成した後に先ほどの

console
cat ~/.ssh/id_rsa.pub

を実行して鍵を表示させます。

公開鍵の追加

bitbucketにログイン後、
ユーザーアイコン
  ↓
Bitbucket setting
  ↓
セキュリティー
  ↓
SSH鍵
  ↓
鍵追加
  ↓
さきほど表示した鍵をコピペ
  ↓
作成
これで完了です。

bitbucketへリポジトリ作成

+アイコンを押し、空のリポジトリを作成します。リポジトリ名はなんでも大丈夫せす(わかりやすくするなら自身のアプリケーション名が良いかと)。
bitbucketにリポジトリを作成するとバケツに何かを入れましょうというページに移動しgit remote add origin git@bitbucket.org:ユーザー名/先ほど作成したリポジトリ名.gitgit push -u origin masterいうコマンドが表示されます。このコードそのまま入力してもいいのですが、まだ自身の環境にリポジトリを作成していない場合は

console
git init (自身の環境の新しいリポジトリの初期化)

ちなみにawsのcloud9の場合はgit導入済みのためこのコマンドが最初から使用できます。

console
git add -A
git commit -m "メッセージ"

上記のコマンド実行し、自身のgitにリポジトリを作成したあとは先ほどのgit remote add origin git@bitbucket.org:ユーザー名/作成したリポジトリ名.gitgit push -u origin masterいうコマンドを実行します。

console
git remote add origin git@bitbucket.org:ユーザー名/先ほど作成したリポジトリ名.git
git push -u origin master

これでBitbucketに内容が反映されます。

あとがき

自分なりにわかりやすいよう書いてみましたがわかりにくかったらすみません。
参考になれば幸いです。

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

環境変数管理の便利ツール

環境変数管理

背景

秘密鍵を使うこともあり,
sample用の.env.sampleと開発用の.envファイルを分けている

.env.sample をコミットに残すため、こちらを正として.envを管理したい

インストール方法

go get github.com/locona/envdef/cmd/envdef

Usage

プロジェクトのルートにある.envファイルにアプリケーション構成を追加します。

S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE

プロジェクトのルートにある.env.sampleファイル

S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
REGION=REGION

次に実行します

envdef

その結果、.env.newファイルが作成されました

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

active_interaction でアプリケーション固有のビジネスロジックを管理する

はじめに

active_interaction というビジネスロジックの秩序を提供してくれる gem があります。
これは Rails における Fat Model や Fat Controller にならない為の一つの解になり得ると注目しています。

active_interaction をもっと上手く使いこなそうという考えから、使い方をまとめました。
README の部分的な翻訳 + 理解しやすいように加筆となっています。

Active Interactionは、アプリケーション固有のビジネスロジックを管理します。
これはRubyのコマンドパターンを実装したものです。

Active Interactionは、ビジネス・ロジックを配置する場所を提供します。
また、入力が期待通りのものであるかどうかを検証することで、より安全なコードを書くことができます。ActiveModel が名詞を扱う場合、Active Interaction は動詞を扱います。

なぜ gem を使うの?

Rails: Service Objectはもっと使われてもいい(翻訳)

よくある Service Object は書き手によって書き方が異なるケースが多いです。
gem のレールに乗る事で、誰が書いても同じようなコードになる事を期待しています。

Rails での使い方

インタラクションは app/interaction に入れることをお勧めします。

また、モデルごとにグループ化するのも非常に便利です。
例えば Account モデルに関する処理は app/interactions/accounts 配下で探すことができます。

- app/
  - controllers/
    - accounts_controller.rb
  - interactions/
    - accounts/
      - create_account.rb
      - destroy_account.rb
      - find_account.rb
      - list_accounts.rb
      - update_account.rb
  - models/
    - account.rb
  - views/
    - account/
      - edit.html.erb
      - index.html.erb
      - new.html.erb
      - show.html.erb

この構造を使うためには以下を application.rb に追加してください。

# config/application.rb
config.autoload_paths += Dir.glob("#{config.root}/app/interactions/*")

Index

# GET /accounts
def index
  @accounts = ListAccounts.run!
end
class ListAccounts < ActiveInteraction::Base
  def execute
    Account.not_deleted.order(last_name: :asc, first_name: :asc)
  end
end

ListAccounts には何も入力を渡さないので、.run の代わりに .run! を使うのは理にかなっています。
これが失敗した場合、インタラクションの記述に失敗したことになります。

Show

このアクションでは、正しいエラーを発生させるためのヘルパーメソッドを定義します。

.run! を呼び出した時、エラーが発生すると ActiveInteraction::InvalidInteractionError が発生します。つまり、ActiveRecord::RecordNotFound ではなくなるので、明示的に fail ActiveRecord::RecordNotFoundと書く必要があります。

# GET /accounts/:id
def show
  @account = find_account!
end

private

def find_account!
  outcome = FindAccount.run(params)

  if outcome.valid?
    outcome.result
  else
    fail ActiveRecord::RecordNotFound, outcome.errors.full_messages.to_sentence
  end
end

これはおそらく、あなたが慣れ親しんでいるものとは少し違っているように見えます。
Railsは一般的に before_action@account を定義します。

なぜこのようなインタラクションコードがすべて良いのでしょうか?
理由は2つあります。1つは、APIコントローラや Resque タスクなど、別の場所で FindAccount インタラクションを再利用できるからです。2つ目は、アカウントの検索方法を変更したい場合は、1つの場所 (今回の場合は FindAccount クラス) を変更するだけで済むということです。

インタラクションの内部では、#find_by_id の代わりに #find を使うことができます。そうすれば、コントローラの #find_account! ヘルパーメソッドは不要になります。しかし、インタラクションからエラーを発生させないようにしなければなりません。そうすると、結果の妥当性だけでなく例外が発生することにも対処しなければいけないからです。

class FindAccount < ActiveInteraction::Base
  integer :id

  def execute
    account = Account.not_deleted.find_by_id(id)

    if account
      account
    else
      errors.add(:id, 'does not exist')
    end
  end
end

実行中にエラーを追加しても全く問題ないことに注意してください。
すべてのエラーが型チェックやバリデーションに起因するものである必要はありません。

New

New は、これまで見てきたものとは少し異なります。
.run.run! を呼び出す代わりに、新しいインタラクションを初期化します。これはインタラクションが ActiveModels のように振る舞うためです。

# GET /accounts/new
def new
  @account = CreateAccount.new
end

インタラクションは ActiveModels のように動作するので、 ActiveModel のバリデーションを使用することができます。ここでは first_namelast_name が空白になっていないことを確認するためにバリデーションを使用します。

class CreateAccount < ActiveInteraction::Base
  string :first_name, :last_name

  validates :first_name, :last_name,
    presence: true

  def to_model
    Account.new
  end

  def execute
    account = Account.new(inputs)

    unless account.save
      errors.merge!(account.errors)
    end

    account
  end
end

ここではいくつかの高度な機能を使用しました。to_model メソッドは、ビューで使用する正しいフォームを決定するのに役立ちます。詳細はフォームのセクションをチェックしてください。executeの中では、エラーをマージします。これはエラーをあるオブジェクトから別のオブジェクトに移動させるのに便利な方法です。詳しくはエラーのセクションを参照してください。

Create

createアクションはnewアクションと多くの共通点があります。どちらも CreateAccount インタラクションを使用します。アカウントの作成に失敗した場合、このアクションは新しいアクションのレンダリングにフォールバックします。

# POST /accounts
def create
  outcome = CreateAccount.run(params.fetch(:account, {}))

  if outcome.valid?
    redirect_to(outcome.result)
  else
    @account = outcome
    render(:new)
  end
end

.run にハッシュを渡さなければならないことに注意してください。 nil を渡すとエラーになります。

インタラクションを使用しているので、Strong Parameter は必要ありません。
インタラクションはフィルタで定義されていない入力を無視します。つまり、 params.requireparams.permit は無くても大丈夫です。

Destroy

destroy アクションは、先ほど書いた #find_account! ヘルパーメソッドを再利用します。

# DELETE /accounts/:id
def destroy
  DestroyAccount.run!(account: find_account!)
  redirect_to(accounts_url)
end

この単純な例では、destroy インタラクションはあまり何もしていません。これをインタラクションに入れることで何かを得ることは明らかではありません。しかし、将来的に account.destroy 以上のことをする必要があるときには、DestrotAccount クラスを変更するだけで済むようになるでしょう。

Edit

destroy アクションと同様に、編集は #find_account! ヘルパーを使用します。
そして、フォームオブジェクトとして使用するための新しいインタラクションのインスタンスを作成します。

# GET /accounts/:id/edit
def edit
  account = find_account!
  @account = UpdateAccount.new(
    account: account,
    first_name: account.first_name,
    last_name: account.last_name)
end

アカウントを更新するインタラクションは、他のインタラクションよりも複雑です。更新にはアカウントが必要ですが、その他の入力は任意です。省略された場合、その属性は無視されます。属性が存在する場合は、それらを更新します。

ActiveInteractionは入力に対して述語メソッド(#first_name?のような)を生成します。これらのメソッドは入力が nil の場合はfalse を返し、そうでない場合は true を返します。述語についての詳細は述語のセクションを確認してください。

class UpdateAccount < ActiveInteraction::Base
  object :account

  string :first_name, :last_name,
    default: nil

  validates :first_name,
    presence: true,
    if: :first_name?
  validates :last_name,
    presence: true,
    if: :last_name?

  def execute
    account.first_name = first_name if first_name?
    account.last_name = last_name if last_name?

    unless account.save
      errors.merge!(account.errors)
    end

    account
  end
end

Update

もうコツがつかめたでしょう。アカウントを取得するために #find_account! を使い、 UpdateAccount のための入力を作成します。そしてインタラクションを実行し、更新されたアカウントにリダイレクトするか、編集ページに戻ります。

# PUT /accounts/:id
def update
  inputs = { account: find_account! }.reverse_merge(params[:account])
  outcome = UpdateAccount.run(inputs)

  if outcome.valid?
    redirect_to(outcome.result)
  else
    @account = outcome
    render(:edit)
  end
end

突っ込んだ使い方

Callbacks

ActiveModel はコールバックを定義するための強力なフレームワークを提供しています。Active Interaction はそのフレームワークにフックしてインタラクションのライフサイクルの様々な部分にフックすることができます。

class Increment < ActiveInteraction::Base
  set_callback :type_check, :before, -> { puts 'before type check' }

  integer :x

  set_callback :validate, :after, -> { puts 'after validate' }

  validates :x,
    numericality: { greater_than_or_equal_to: 0 }

  set_callback :execute, :around, lambda { |_interaction, block|
    puts '>>>'
    block.call
    puts '<<<'
  }

  def execute
    puts 'executing'
    x + 1
  end
end

Increment.run!(x: 1)
# before type check
# after validate
# >>>
# executing
# <<<
# => 2

利用可能なコールバックは、順に type_checkvalidateexecute です。
これらのコールバックには beforeafteraround のいずれかを設定することができます。

Composition

他のインタラクションの中から #compose を使ってインタラクションを実行することができます。
インタラクションが成功した場合は、結果を返します(.run で呼び出した場合と同じように)。
何か問題が発生した場合、実行はすぐに停止し、エラーは呼び出し元に移動します。

class Add < ActiveInteraction::Base
  integer :x, :y

  def execute
    x + y
  end
end

class AddThree < ActiveInteraction::Base
  integer :x

  def execute
    compose(Add, x: x, y: 3)
  end
end

AddThree.run!(x: 5)
# => 8

他のインタラクションからフィルタを導入するには、.import_filters を使用します。入力と組み合わせれば、他のインタラクションに委任するのも簡単です。

class AddAndDouble < ActiveInteraction::Base
  import_filters Add

  def execute
    compose(Add, inputs) * 2
  end
end

コンポジションされたインタラクションのエラーには、いくつかの厄介なケースがあることに注意してください。
それらについての詳細は、エラーのセクションを参照してください。

トランザクションを貼りたい場合は?

元々はトランザクションをサポートしていたが削除したとのこと。

Remove transaction support?

私たちが元々これらを追加したのは、インタラクションが複雑なビジネスロジックを管理するために使われると考えたからです。実際には、必ずしもそうとは限りません。 ActiveRecord トランザクションの薄いラッパーを提供するより、何も提供しない方が良いかもしれません。

# As it is currently:
class AutomaticTransactionInteraction < ActiveInteraction::Base
  def execute
    # ...
  end
end

# As it might be:
class ManualTransactionInteraction < ActiveInteraction::Base
  def execute
    ActiveRecord::Base.transaction do
      # ...
    end
  end
end

トランザクションを貼りたい場合は、後者の方で書けばよくない?という話。
具体例としては、以下のようになるだろうか。

def execute
  ActiveRecord::Base.transaction do
    compose(A)
    compose(B)
    raise ActiveRecord::Rollback if invalid?
  end
end

外部リンク

active_interaction

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

Rails6 のちょい足しな新機能を試す 126(upsert_all bug fix編)

はじめに

Rails 6 に追加された新機能を試す第126段。 今回は、upsert_all bug fix 編です。
Rails 6.0.0 では、 query のキャッシュが有効になっている状態で、 upsert_all を実行した後、キャッシュがクリアされないというバグがありました。

Rails 6.0.1 以降では修正されています。

Ruby 2.6.5, Rails 6.0.2.2, Rails 6.0.1, Rails 6.0.0 で確認しました。

$ rails --version
Rails 6.0.2.2

今回は、 スクリプトを作成して確認していきます。

Rails プロジェクトを作る

Rails プロジェクトを新たに作成します。

$ rails new rails_sandbox
$ cd rails_sandbox

User モデルを作成する

User モデルを作成します。

$ bin/rails g model User name

スクリプトを作成する

スクリプトを作成します。スクリプトは以下の処理を実行します。

  1. Andy というname を持つ User を1つ登録する。
  2. キャッシュを有効にする。
  3. 登録したUserを User.first で検索して、name を出力する。
  4. User.upsert_all を実行して、 Andy から Bob に name を更新する。
  5. User.first を使って name を出力する。
scripts/cache_clear.rb
User.delete_all
ActiveRecord::Base.connection.enable_query_cache!

User.create(name: 'Andy')
user = User.first
p user.name

User.upsert_all(
  [
    {
      id: user.id,
      name: 'Bob',
      created_at: user.created_at,
      updated_at: Time.now
    }
  ]
)

user = User.first
p user.name

Rails 6.0.0 では

Rails 6.0.0 では、 キャッシュがクリアされないため、名前が変わりません。、

$ bin/rails -v
Rails 6.0.0
$ bin/rails runner scripts/cache_clear.rb
Running via Spring preloader in process 369
"Andy"
"Andy"

Rails 6.0.1 以降では

キャッシュがクリアされるため、名前が期待通りに変化します。

$ bin/rails -v
Rails 6.0.1
$ bin/rails runner scripts/cache_clear.rb
Running via Spring preloader in process 447
"Andy"
"Bob"

試したソース

https://github.com/suketa/rails_sandbox/tree/try126_clear_cache_in_upsert

参考情報

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

has_secure_passwordについて

はじめに

パスワードを実装してみたのでメモ
userモデルにパスワードを実装していきます

1.has_secure_password

userモデルに

user.rb
class User < ApplicationRecord
has_secure_password
end

と追加することで
(1).セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。(条件あり
(2).2つのペアの仮想的な属性 (passwordpassword_confirmation) が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される。
(3).authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド) 。
この3つの機能が使えるようになります。順番に解説していきます。

(1)

ここでいうセキュアとは安全という意味です。設定したパスワードを暗号化(ハッシュ化)させて、password_dijestというカラムに保存できるようにします。すなわち、Userモデルにpassword_dijestカラムを追加する必要があります(先ほどの条件とはこのこと)。

console
rails generate migration add_password_digest_to_users password_digest:string

rails db:migrate

また、パスワードを暗号化するためのgemとして、bcrypt(最新ので大丈夫です)というgemをgemfailに追加してbundle installする必要があります。

(2)

passwordpassword_confirmationのセットというのはパスワードとパスワード(確認用)ということです。ちなみに、has_secure_passwordをUserモデルに追加したことで自動的にこの二つのバリデーションが追加されるようになっていますが、これは更新時には適用されないので

user.rb
validates :password, presence: true

と追加しておきましょう。パスワードの長さを制限したいときは

user.rb
validates :password, presence: true, length: {minimum:5}

とすれば5文字以上のパスワードに制限することができます。

(3)

authenticateメソッドではその説明の通り、引数の文字列がパスワードと一致しているか確認してくれます。

console
User.create(name: "saber",email: "saber@seihai.com",password: "shirou",password_confirmation: "shirou")
user = User.find_by(name:"saber")
user.authenticate("kotomine") => false
user.authenticate("kiritugu") => false
user.authenticate("shirou") => true

といった感じでそのユーザーのパスワードの正誤を確認してくれます。
autuenticateでは引数のパスワードをそのままデータベースの値と参照するわけではなく、一度ハッシュ化(暗号化)してからその値をデータベースにある値と参照してくれます。

最後に

has_secure_passwordを自分なりにかみくだいてみました。参考になれば幸いです。

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

dockerでカラムの変更ができないエラー

docker-composeで自作アプリのテーブルをいじっていたらこんなエラーに遭遇しました。

$ docker-compose run --rm app rails g migration rename_star_column_to_topics
Starting sakelog_db_1 ... done
Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "exec: \"rails\": executable file not found in $PATH": unknown

再起動してみたり、appをdbに書き換えてみたり、色々試しましたが解決できず。

でも解決法は意外と単純でした。

$ docker-compose run --rm app bundle exec rails g migration rename_star_column_to_topics
Starting sakelog_db_1 ... done
      invoke  active_record
      create    db/migrate/20200330013557_rename_star_column_to_topics.rb

railsの前に bundle execを付け足したらよかったんですね。

今までよくわからずに放置して使ってたbundle exec。
今回のエラーを経て改めて検索し直したら、
bundle execをつけずに実行すると、グローバルにインストールされているgemが動くのに対し、
bundle execをつけて実行するとプロジェクト内にあるgemが動くんだそうで。

なるほど、dockerのことはまだよくわかって無いですが、なんとな〜く理解できてきた気がするぞ。

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

shoulda-matchersが便利すぎる

はじめに

RSpecを書くときに、shoulda-matchersというGemを使うと非常に便利でした。

shoulda-matchersとは

Shoulda Matchers provides RSpec- and Minitest-compatible one-liners to test common Rails functionality that, if written by hand, would be much longer, more complex, and error-prone.

Shoulda Matchersは、手書きで書くと長くて、複雑で、エラーが起きやすいRailsのテストをワンライナーにします。(意訳)
ワンライナーって1行ってことでいいのでしょうか...

使用方法

このようなuserモデルに対し、

user.rb
class User < ApplicationRecord
  validates :name, presence: true
  has_many :topics
end

shoulda-matchersを使うとこのようなRSpecを書くことができます!

user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  it { is_expected.to validate_presence_of(:name) }
  it { is_expected.to have_many(:topics) }
end

バリデーションやアソシエーションのテストを一行で書けるのは便利すぎますね。
普通のRSpecの書き方で書いて、テストケースに抜けや漏れが出てしまうなんてことを防げます。

上記の例ではpresencehas_manyを取り上げましたが、他にも使えそうな書き方がたくさんあるので追記していきたいと思います。

参考:Shoulda-MatchersのREADME

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

USERにいろいろと機能をつけてく。〜Railsチュートリアル7章〜

いよいよ7章目に突入していきます。
大体10日前くらいからRailsチュートリアルはじめてそろそろ折り返し地点になるのかな。勉強時間はだいたいですけど60時間くらい。この調子でがんばりますよー!

さて、今回はユーザーのページ作ったりプロフィール写真載せたりしてきます。

ユーザーを表示する

初めて動的なページを入れていきます。ビューの中にApplicationのデータベースから情報を取り出して各プロフィールの情報をカスタマイズしていきます。Applicationに動的なページを追加する準備としてデバック情報を追加します。
デバック情報ではコントローラはなにを使っているのか?アクションはなに使ってるのかといった動的ページを動かす際に必要な情報を確認することができます。

デバック情報入れるには

 <%= debug(params) if Rails.env.development? %>

これを大元のApplication.htmlのフッターの下辺りに入れる。
Rails.env.developmentとは開発環境のこと。ここだけ表示される用にするよう指定している。

railsにはテスト環境 (test)、開発環境 (development)、そして本番環境 (production) の3つの環境がデフォルトで装備されています。
rails consoleは開発環境 (development)です。

Applicationを本番環境で実行する場合、本番のデータベースを利用できるよう実行する必要がある。

$ rails server --environment production
そのため、rails db:migrateを本番環境で実行して本番データベースを作成します。

 $ rails db:migrate RAILS_ENV=production
console、server、migrateの3つのコマンドでは、デフォルト以外の環境を指定する方法がそれぞれ異なっている

id=1のユーザーにアクセスするためのページのURIは/users/1となるが、現時点ではこのURLを使ってもエラーになります。
/users/1 のURLを有効にするために、routesファイルに以下を追加する。

Resources:users

これを入力すると、ユーザー情報を表示するURLを追加するだけでなく、ユーザーのURLを生成するための多数の名前付きルートと共に、RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになる。

| HTTPリクエスト | URL | アクション | 名前付きルート |   用途  |
---------------------------------------------------
|   GET    | /users | index |  users_path | すべてのユーザーを一覧するページ |
|   GET    |/users/1|  show |  user_path(user) | 特定のユーザーを表示するページ |
|   GET   |/users/new|  new |  new_user_path | ユーザーを新規作成するページ (ユーザー登録) |
|   POST   | /users  |create|  users_path | ユーザーを作成するアクション |
|   GET | /users/1/edit|edit|  edit_user_path(user) | id=1のユーザーを編集するページ |
|   PATCH  | /users/1|update|  user_path(user) | ユーザーを更新するアクション |
|   DELETE | /users/1|destroy| user_path(user) | ユーザーを削除するアクション |

この時点でコントローラとroutesは指定できたものの、まだプロフィール画面(ビュー)ができていないためこれを作成していく。
まずapp/views/users/show.html.erbを手動で作成。
ビューへは

<%= @user.name %>, <%= @user.email %>

を入力。ユーザー名、Emailアドレスを表示する。
コントローラへは

 def show
    @user = User.find(params[:id])
  end

を追加。ユーザーのid読み出しにはparamsを使う。Usersコントローラにリクエストが正常に送信されると、params[:id]の部分はユーザーidの1に置き換わります。(技術的な補足: params[:id]は文字列型の "1" ですが、findメソッドでは自動的に整数型に変換されます)。このIDを変数@userへ代入し、上のビューへ表示するようにする処理を作成した。

debuggerメソッドを使用する

byebug gemによるdebuggerメソッドを使用すればもっと直接的に確認ができる。

  def show
    @user = User.find(params[:id])
    debugger
  end

メソッドを差し込んだらブラウザから /users/1 へアクセスしRailsサーバーを立ち上げてターミナルを確認する。dyebugプロンプトが表示される。

(byebug) @user.name
"Example User"
(byebug) @user.email
"example@railstutorial.org"
(byebug) params[:id]
"1"

このプロンプトではRailsコンソールのようにコマンドを呼び出すことができてApplicationのdebuggerが呼び出された瞬間の状態を確認することができる。

debuggerを終了させる

ControlーDを押すとプロンプトから抜け出せる。デバッグが終わったらshowアクション内のdebuggerの行を削除する。

ユーザー登録フォームを作成する

form_forを使用

ユーザー登録ページにはフォーム(入力する空欄のこと)が必要となる。
Railsではform_forヘルパーメソッドを使用する。
まずform_forの引数で必要となるUserオブジェクトを作成することになります。必要となる@user変数の定義は

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end
end

と入力。
ビューは。。

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

と入力する。 f.label :name のfはオブジェクト。Labelは属性の設定。テキストフィールドは記載情報のこと。

正しいフォーム

/usersへのPOSTリクエスト(フォームからの入力情報)はcreateアクションに送られます。ここで、createアクションでフォーム送信を受け取り、User.newを使って新しいユーザーオブジェクトを作成し、ユーザーを保存 (または保存に失敗) し、再度の送信用のユーザー登録ページを表示するという方法で機能を実装する。

コントローラにはこんな入力となる。

  def create
    @user = User.new(params[:user])    # 実装は終わっていないことに注意!
    if @user.save
      # 保存の成功をここで扱う。
    else
      render 'new' #失敗したらnewへ戻る。
    end
  end

これは完璧な実装ではない。paramsハッシュ全体を初期化するという行為はセキュリティ上極めて危険だからです。
今のままだと、ユーザーが送信したデータをまるごとUser.newへ渡していることになります。(名前・アドレス・パスワード・再確認用パスワード)
これに追加で別の属性を一部に紛れ込ませて渡せてしまうという危険があります。(管理者権限等も渡せる危険があり非常に危険)

Strong Parameters

Rails 4.0では、先述している危険を防止するためコントローラ層でStrong Parametersというテクニックを使うことが推奨されています。

private

def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end

上記はparams[:user]の代わりとして使われる。これを追加することで許可された属性のみが含まれたparamsを使用できるように限定する。
このuser_paramsメソッドはUsersコントローラの内部でのみ実行され、Web経由で外部ユーザーにさらされる必要はない.
Rubyのprivateキーワードを使って外部から使えないようにしている。

エラーメッセージ

ユーザー登録に失敗した場合の最後の手順として、問題が生じたためにユーザー登録が行われなかったということをユーザーにわかりやすく伝えるエラーメッセージを追加する。
保存に失敗すると、@userオブジェクトの関連付けされたエラーメッセージの一覧が生成される。これをビューへ表示したい。このメッセージをブラウザで表示するには、ユーザーのnewページでエラーメッセージのパーシャル (partial) を出力します。このとき、form-controlというCSSクラスも一緒に追加することで、Bootstrapがうまく取り扱ってくれるようになります。

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

'shared/error_messages'というパーシャルをrender (描画) している
複数のビューで使われるパーシャルは専用のディレクトリ「shared」によく置かれます
よって

$ mkdir app/views/shared

にて新しくディレクトリを作成。ここにファイルも作成し、下記を入力。

<% if @user.errors.any? %> →#もし変数ユーザーにエラーが一つでもあったら
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

メソッドがcountとany?の2つ使用されている。countは上記の内容だとエラーの数を返してくれる。any?はempty?と逆の動作で要素が一つでもある場合はtrueを返す。
pluralizeは英語専用のテキストヘルパー単数形と複数形をcountの数に応じて変換し出力してくれる。

失敗時のテスト

railsではフォーム用のテストを自動的に書くことができる。

$ rails generate integration_test users_signup
      invoke  test_unit
      create    test/integration/users_signup_test.rb

ここではユーザー登録ボタンを押したときに (ユーザー情報が無効であるために) ユーザーが作成されないことを確認します。

test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    assert_template 'users/new'
  end
end

ユーザー登録成功

ここまでではまだ完成ではなく、デフォルトのアクションに対応するビューを表示しようとするもcreateアクションに対応するビューのテンプレートがないためエラーが表示されてしまう。

createアクションに対応するテンプレートを作成することもできますが、Railsの一般的な慣習に倣って、ユーザー登録に成功した場合はページを描画せずに別のページにリダイレクト (Redirect) するよう設定する。

具体的には、新しく作成されたユーザーのプロフィールページにリダイレクトする。

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user →ユーザーURLへ飛ばすを意味する
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
  end

さらにここに、登録完了後に表示されるページにメッセージを表示し (この場合は新規ユーザーへのウェルカムメッセージ)、2度目以降には表示しないようにするFrashという変数を使用する。

def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
   end

このとき、:successキーはシンボルでしたが、テンプレート内に反映させる際に埋め込みRubyが自動的に"success"という文字列に変換している点に注意してください。この性質を利用することで、キーの内容によって異なったCSSクラスを適用させることができ、メッセージの種類によってスタイルを動的に変更させることができる。

frash変数の内容をWebサイトのレイアウトに追加

<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
    .
    .
    .
  </body>
</html>

成功時のテスト

有効な送信に対するテストを書いていく。これによって、アプリケーションの振る舞いを検証し、もし今後バグが埋め込まれたらそれを検知できるようになります。今回の目的はデータベースの中身が正しいかどうか検証することです。すなわち、有効な情報を送信して、ユーザーが作成されたことを確認します。

require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect! →POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッド
    assert_template 'users/show'
  end
end

一応7章目終了できました。やっていて思ったことは内容自体はprogateやってれば理解できる内容なのかなと思います。しかし、とにかくテストの書き方が難しい。これちゃんと自分で考えてできるようになるんですかね。
とりあえず今日はここまで。

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

【MySQL版】プログラミング初心者と行うrails環境構築講座【Win版】

プログラミング初心者向けのRuby on Rails環境構築をまとめた記事です。
なかなか一つにまとまっている記事が見つからず、時間がかかったので忘備録的な意味も込めてここに記したいと思います。

動作環境

OS:Win10 (64bit)
Ruby2.4.9(x64)
Rails 5.0.7.2
db:MySQL

以上が僕のRailsを動かすにあたる環境です。
まず最初にこれらのものを順にインストールしていきましょう。

Rubyのインストール

インストーラのダウンロード

こちらのDEVKITの欄からRuby+Devkitをダウンロードしてください。
(64bitの方はx64、32bitの方はx86)
※バージョンはなんでも構いませんが、何もわからない!という方は推奨verの2.6.5-1をダウンロードするといいでしょう。

インストール

インストーラ(~.exe)がダウンロードされたら、実行してインストールを行いましょう。
何もわからない場合はデフォルトのまま進めちゃってokです。
finishしたら、大きくRubyInstallerと書かれた画面に遷移しますが、指示通りに進めて大丈夫です。

インストールできたか確認

ターミナルで下記のコマンドを実行して、無事インストールできたか確かめます。

$ ruby -v

自分がインストールしたrubyのバージョンが表示されればokです。

バージョン確認できなかった場合

PATHが通ってない可能性があるので、Windowsの設定を開き、検索ボックスで環境変数を検索、環境変数の編集に飛んでいただき、上部のユーザーの環境変数からpathを選択して、編集。
インストールしたrubyフォルダのbinディレクトリを新規として追加しましょう。
※pathが存在しなかった場合、下部のシステム環境変数からpathを新規として追加してください。

Railsのインストール

railsのインストールは簡単で下記コマンドを実行するだけです。

$ gem i rails #最新バージョンがインストールされる

$ gem i -v (バージョン番号) rails

上のコマンドだと最新のバージョンがインストールされてしまうので、それでは不都合が生じてしまう方は下のコマンドでバージョンを指定してください。

インストール確認

$ rails -v

これはお決まりです

MySQLのインストール

railsではsqlite3がデフォルトデータベースに設定されていますが、それだと僕は環境構築時に自力で解決できない不具合が発生したのでMySQLにしています。
また、現在自分自身データベースの知識が不足しているので、細かいことはよくわかりません。
ここには自分がうまくいった手順を載せています。

インストーラのダウンロード

こちらから下のサイズが大きいものをダウンロードしてください。
oracleアカウントにログインさせようとしてきますが、今回は無視して下部のNo thanks~~をクリックしてダウンロードしましょう。

インストール

インストーラ(~.msi)を実行しましょう。
これも基本的にデフォルトのまま適宜Executeボタンを押しつつ進めちゃってください。

Accounts and Rolesの設定

Accounts and Rolesと書かれた画面が出てきたら上部のボックスに希望のパスワードを入力してください。
これがrootアカウントのパスとなるため、メモを控えといてください。

次にMySQLアカウントを追加します。
Add Userボタンを押して希望のユーザー名、パスワードを入力(他はデフォルトのままで)し、ユーザーを追加します。
ここでのパスワードはrootアカウントのものと異なっていてもokです。

この作業が終了したら、あとは前と同じようにデフォルトのまま進めていけばインストールが完了します。

インストール確認

$ mysql --version

バージョン確認できなかった場合

Rubyの場合と同じくPATHが通ってない可能性があるので、設定からPATHにインストールしたMYSQLフォルダのbinディレクトリを新規として追加しましょう。

Railsアプリの作成、設定

ここまででrailsアプリの作成に必要な最低限のソフトのインストールが終了しました。
ここからは環境に合わせつつ、Railsで作成するアプリの設定を行っていきます。

Railsアプリの作成

下記コマンドを実行します。

$ cd (任意のディレクトリ名) 
#railsアプリを設置したいディレクトリに移動

$ rails new (任意のrailsアプリ名) --database=mysql 
#デフォルトのdbをmysqlに変更してアプリ作成

これでrailsアプリが作成されます。

railsアプリデータベースの作成

database.ymlの編集

作成されたrailsアプリフォルダ内のconfig内に存在するdatabase.ymlファイルを編集します。
上のほうにあるデフォルトのusernamepasswordだけ下記のように編集してください。

default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: (MySQLインストール時に作成した名前)
  password: (MySQLインストール時に作成したパスワード)
  host: localhost
データベース作成

下記のコマンドをrailsアプリ上で実行し、データベースを作成してください。

$ rails db:create

開発環境構築完了!!!

ひとまずRailsアプリを開発する環境はこれで整いました。
自分がこれまで学んできたことや、参考書、サイトなどを活かしてrailsアプリの開発を行いましょう。

※herokuにデプロイして公開する記事も後日執筆予定です。
※何か間違っている点、疑問点等ございましたらコメントお願いします。

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

【MySQL版】プログラミング初心者と行うRuby on Rails環境構築講座【Win版】

プログラミング初心者向けのRuby on Rails環境構築をまとめた記事です。
なかなか一つにまとまっている記事が見つからず、時間がかかったので忘備録的な意味も込めてここに記したいと思います。

注意

本記事では最低限の環境構築しか行わないため、手っ取り早くrailsアプリの開発が行いたい初心者さん向けの記事です。

様々な拡張機能や応用などには対応しきれていない部分もあるので、必要な方は個人で調べてください。

動作環境

OS:Win10 (64bit)
Ruby2.4.9(x64)
Rails 5.0.7.2
db:MySQL

以上が僕のRailsを動かすにあたる環境です。
まず最初にこれらのものを順にインストールしていきましょう。

※特にバージョンの指定などはありませんが、不安な場合は僕と同じバージョンをインストールしたほうが確実だと思います。

Rubyのインストール

インストーラのダウンロード

こちらのDEVKITの欄からRuby+Devkitをダウンロードしてください。
(64bitの方はx64、32bitの方はx86)
※バージョンはなんでも構いませんが、何もわからない!という方は推奨verの2.6.5-1をダウンロードするといいでしょう。

インストール

インストーラ(~.exe)がダウンロードされたら、実行してインストールを行いましょう。
何もわからない場合はデフォルトのまま進めちゃってokです。
finishしたら、大きくRubyInstallerと書かれた画面に遷移しますが、指示通りに進めて大丈夫です。

インストールできたか確認

ターミナルで下記のコマンドを実行して、無事インストールできたか確かめます。

$ ruby -v

自分がインストールしたrubyのバージョンが表示されればokです。

バージョン確認できなかった場合

PATHが通ってない可能性があるので、Windowsの設定を開き、検索ボックスで環境変数を検索、環境変数の編集に飛んでいただき、上部のユーザーの環境変数からpathを選択して、編集。
インストールしたrubyフォルダのbinディレクトリを新規として追加しましょう。
※pathが存在しなかった場合、下部のシステム環境変数からpathを新規として追加してください。

Railsのインストール

railsのインストールは簡単で下記コマンドを実行するだけです。

$ gem i rails #最新バージョンがインストールされる

$ gem i -v (バージョン番号) rails

上のコマンドだと最新のバージョンがインストールされてしまうので、それでは不都合が生じてしまう方は下のコマンドでバージョンを指定してください。

インストール確認

$ rails -v

これはお決まりです

MySQLのインストール

railsではsqlite3がデフォルトデータベースに設定されていますが、それだと僕は環境構築時に自力で解決できない原因不明の不具合が発生したのでMySQLにしています。
(友人も同じ現象が起こったため、この問題で悩んでる方、多いと思います)

また、現在自分自身データベースの知識が不足しているので、細かいことはよくわかりません。
ここには自分がうまくいった手順を載せています。

インストーラのダウンロード

こちらから下のサイズが大きいものをダウンロードしてください。
oracleアカウントにログインさせようとしてきますが、今回は無視して下部のNo thanks~~をクリックしてダウンロードしましょう。

インストール

インストーラ(~.msi)を実行しましょう。
これも基本的にデフォルトのまま適宜Executeボタンを押しつつ進めちゃってください。

Accounts and Rolesの設定

Accounts and Rolesと書かれた画面が出てきたら上部のボックスに希望のパスワードを入力してください。
これがrootアカウントのパスとなるため、メモを控えといてください。

次にMySQLアカウントを追加します。
Add Userボタンを押して希望のユーザー名、パスワードを入力(他はデフォルトのままで)し、ユーザーを追加します。
ここでのパスワードはrootアカウントのものと異なっていてもokです。

この作業が終了したら、あとは前と同じようにデフォルトのまま進めていけばインストールが完了します。

インストール確認

$ mysql --version

バージョン確認できなかった場合

Rubyの場合と同じくPATHが通ってない可能性があるので、設定からPATHにインストールしたMYSQLフォルダのbinディレクトリを新規として追加しましょう。

ターミナルがGit bashの場合

自分はgit bashをターミナルとして用いているのですが、MySQLコマンドを実行する場合は先頭に必ずwinptyをつけないと実行できないようです。(原因はわからず、、、)

Railsアプリの作成、設定

ここまででrailsアプリの作成に必要な最低限のソフトのインストールが終了しました。
ここからは環境に合わせつつ、Railsで作成するアプリの設定を行っていきます。

Railsアプリの作成

下記コマンドを実行します。

$ cd (任意のディレクトリ名) 
#railsアプリを設置したいディレクトリに移動

$ rails new (任意のrailsアプリ名) --database=mysql 
#デフォルトのdbをmysqlに変更してアプリ作成

これでrailsアプリが作成されます。

railsアプリデータベースの作成

database.ymlの編集

作成されたrailsアプリフォルダ内のconfig内に存在するdatabase.ymlファイルを編集します。
上のほうにあるデフォルトのusernamepasswordだけ下記のように編集してください。

default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: (MySQLインストール時に作成した名前)
  password: (MySQLインストール時に作成したパスワード)
  host: localhost
データベース作成

下記のコマンドをrailsアプリ上で実行し、データベースを作成してください。

$ rails db:create

開発環境構築完了!!!

ひとまずRailsアプリを開発する環境はこれで整いました。
自分がこれまで学んできたことや、参考書、サイトなどを活かしてrailsアプリの開発を行いましょう。

※herokuにデプロイして公開する記事も後日執筆予定です。
※何か間違っている点、疑問点等ございましたらコメントお願いします。

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