20210303のRailsに関する記事は25件です。

Docker環境でのByebugの使い方

docker&railsの開発を学んでるとByebugを使うには一手間いるとのこと

以下docker-compose.ymlにコード追加

docker-compose.yml
services:
  web:
    stdin_open: true <= 追加する
    tty: true <= 追加する

続けてコードを反映するために以下をする

$ docker-compose up -d

エラーが特に出なければOK

Byebugの使い方

コンテナIDまたはコンテナ名を調べる

$ docker ps

コンテナのIDか名前をアタッチする

$ docker attach <container name or ID>

デバッグの準備完了

Byebug終わり方

byebugが終わり、標準出力に戻りたい場合は

Ctrl + P + Q

または

(byebug) quit

でデバッグから抜けます
Ctrl + C で抜けようとするとめんどくさい事になるらしいのでやらないように

参考記事

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

docker-compose up で突然エラーになる

備忘録的なもの

dockerでのrails開発中にいきなり docker-compose up したらエラーになり頭を抱えた。

ERROR: for <コンテナ名>  UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=60)

ERROR: for web  UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=60)
ERROR: An HTTP request took too long to complete. Retry with --verbose to obtain debug information.
If you encounter this issue regularly because of slow network conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher value (current value: 60).

長い時間解決法を探していたがDocker for Macを再起動すれば解決した。
Dockerがたまにおかしくなるらしいとのことで、、
理解するのにはまだ長い道のり

参考記事

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

【HTML/CSS】リアルタイムな正規表現検証を簡単にフォームに実装する方法【Rails】

初めに

 この記事は、上記記事で紹介したポートフォリオで使用した技術を切り出した記事になります。もし宜しかったらこちらもご覧ください。
また、この記事はGIF画像を挿入しておりますが、あまり滑らかに動かない可能性があります。GIF画像をクリックして頂けるとGyazoのURLへ移動することが出来ますので、そちらでしたらヌルヌル動くかと思います。

イメージ図

Image from Gyazo

 今回紹介する方法を使用すると、上記GIF画像のinput要素のように、

  • フォーカスされたら要素が大きくなる・背景が白っぽくなる
  • 正規表現にマッチするなら枠が緑色に、マッチしないなら赤色になる
  • 正規表現とマッチしない状態で送信ボタンを押すと、入力内容が送信される前に入力アシスタントを表示する

といった機能を割と簡単に実装出来るようになります。

HTMLで正規表現を記述する

/views/devise/registrations/new.html.erb
<div class="field font sign-up">
  <%= f.label :nickname %><br />
  <%= f.text_field :nickname, autofocus: true, autocomplete: "off", required: true, maxlength: 8, pattern: "^([a-zA-Z0-9]{1,8})$", placeholder: "^([a-zA-Z0-9]{1,8})$" %>
</div>

 上記コードはGIF画像でいうところの「Nickname」部分のフォームのソースコードです。form_withを使ったinput要素ですが、f.text_field :nicknameの後に何やらゴチャゴチャ書かれていますね。今回特に注目して頂きたいのは下記の部分です。

  • required: true
    • 必須項目になる
  • maxlength: 8
    • 8文字までしか入力出来なくなる
  • pattern: "^([a-zA-Z0-9]{1,8})$"
    • この正規表現(半角英数1~8文字)にマッチする入力内容しか通さなくなる

 そして、これらの条件を満たさないまま送信をしようとすると、
Image from Gyazo
以下のような入力アシスタントが表示される具合です。

裏でどのような処理がされているか

 詳しい内容は下記URLに全て書かれています。

 ここでは、今回の内容に関係する部分のみ引用します。

要素が妥当な場合は、次のようになります。

  • 要素が CSS の :valid 疑似クラスに一致します。これにより、妥当な要素に特定のスタイルを適用することができます。
  • ユーザーがデータを送信しようとすると、ブラウザーは止めるものが他になければ(JavaScript など)、フォームを送信します。

要素が不正なときは、次のようになります。

  • 要素が CSS の :invalid 疑似クラスに一致します。これにより、不正な要素に特定のスタイルを適用することができます。
  • ユーザーがデータを送信しようとすると、ブラウザーはフォームをブロックしてエラーメッセージを表示します。

 つまり、HTMLがフォームの入力内容を常に監視し、その検証結果を要素の擬似クラスとして出力するということです。
 擬似クラスがどのようなものかと言うと、簡単に言えば「そのクラスの状態」のことを指します。例えば<div class="test"></div>のような要素があるとして、この要素をCSSで装飾する時は

style.css
.test {
  display: flex;
}

のような記述になるかと思います。この要素がホバーされると裏では擬似クラス:hoverが要素に付与されるので、

style.css
.test:hover {
  background-color: red;
}

のような記述がCSSにあると、これが適用されるようになります。これを利用すれば、

style.css
.test {
~~省略~~
  background-color: blue;
  transition: all 0.3s ease;
}

.test:hover {
  background-color: red;
  transform: scale(1.1, 1.1);
}

のように記述をすると、testクラスを持った要素は、「ホバーされると0.3秒掛けて色が青から赤へ、大きさが縦横ともに1.1倍になる」といった装飾を適用することが出来る、という寸法です。ちなみに、transitionは要素の変化を滑らかに繋ぐためのプロパティで、transformは要素の形状を変化させるためのプロパティです。
 たったこれだけの記述を追加するだけでアプリに動きを出せる訳ですから、労力対効果は高そうですね。

擬似クラス別にCSSを記述してみる

 それでは、入力内容の検証が行われるフォームに装飾を追加してしてみましょう。

style.css
.input {
~~省略~~
  transition: all 0.3s ease;
}

 まずはいつも通りにinputクラスにCSSを記述し、フォームを作成します。そしてtransitionを追加することで、これから記述していく変化の内容が滑らかなアニメーションで表現されるようにしておきます。
 次に、要素がfocusされた時に起こしたい変化を記述していきます。

style.css
.input {
~~省略~~
  transition: all 0.3s ease;
}

.input:focus {
  transform: scale(1.1, 1.1);
  background-color: rgba(223, 223, 223, 0.5);
}

 更に今回は入力内容の検証も行いたいので、条件を満たさない時は枠を赤色に、条件を満たしたら緑色に変化するようにします。条件を満たさない時は擬似クラス:invalidが、満たすときは:validが適用されますので、

style.css
.input {
~~省略~~
  transition: all 0.3s ease;
}

.input:focus {
  transform: scale(1.1, 1.1);
  background-color: rgba(223, 223, 223, 0.5);
}

.input:focus:invalid {
  border: red 5px solid;
}

.input:valid {
  border: rgb(2, 175, 2) 6px solid;
  background-color: rgba(223, 223, 223, 0.5);
}

これで「フォーカスされている・入力内容が条件を満たさない」場合は枠が赤色に、「入力内容が条件を満たす」場合は緑色に変化させることが出来ます。

まとめ

 上記の記述だけでは、「入力したNicknameが既に使われているかどうか」といった「データベースと照合する検証」は行うことが出来ません。HTMLで行うことが出来るのは、あくまで入力の書式に関する検証のみになります。リアルタイムでデータベースとの照合を行う方法は発見することが出来ませんでした。
 その方法を発見し次第また記事にしようと思います。

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

Rails フォロー機能のリレーション

はじめに

フォロー機能について勉強している中で、リレーションが上手く理解できなかったため、
自分なりの言葉でまとめたものとなります。

フォロー機能のリレーションの前に

ウーバーイーツを例でまず考える。
ユーザーはたくさんの食べ物を選択することができる。
また、食べ物はたくさんのユーザーから選択される。
この関係はn対nの関係である。
image.png
この関係を2つのテーブルで表そうとすると、
ユーザーや食事が増えるたびお互いのカラムを増やし続けなければならない。
image.png
この場合、中間テーブルを置くことで、解決する。
image.png
これで、2つの1対多の関係で表すことができ、
UserやFoodが増えても関係性を足すだけで管理が可能になる
モデルには以下で表記する

class User < ApplicationRecord
  has_many :orders
end

class Order < ApplicationRecord
  belongs_to :user
  belongs_to :food
end

class Item < ApplicationRecord
  has_many :orders
end

ここで、ユーザーの頼んだ食事がなにかを直接知りたい場合、
現段階でもしれないことは無いが、@user.foodsと表記するだけで持ってこれるようにしたい。

class User < ApplicationRecord
  has_many :orders
  has_many :foods #間違い
end

上記のままでは、userとfoodの関係性が無いため、表すことができない。
そのため、一度orderを通してからfoodを確認したい。
それには、throughを用いることで可能になる。

class User < ApplicationRecord
  has_many :orders
  has_many :foods, through: :orders
end

class Order < ApplicationRecord
  belongs_to :user
  belongs_to :food
end

class Item < ApplicationRecord
  has_many :orders
  has_many :users, through: :orders
end

フォロー機能のリレーション

一度上のER図と形をあわせるため、userテーブルを複製し、user1、user2とした場合、フォロー関係は以下になる
image.png
しかし、実際には、ユーザーの更新の度に同じ情報を複製したりするのは、管理上非常にめんどくさい。
そのためuserテーブルを一つにして考える。
(この時、Relationshipのカラム名を変更している)
image.png

Relationshipモデルについて考える

class Relationship < ApplicationRecord
  belongs_to :user #間違い
  belongs_to :user #間違い
end

#class Order < ApplicationRecord
  #belongs_to :user
  #belongs_to :food
#end

先程の書き方に習うとこのようになるが、重複しており、Relationshipテーブルには”user_id”は無いため不可。
代わりにfollower_idとfollowed_idがあるためこれを用いて記載する。

class Relationship < ApplicationRecord
  belongs_to :follower #間違い
  belongs_to :followed #間違い
end

しかし、これではrailsがfollower、followedテーブルを参照しに行ってしまうので、
class_nameを指定することで回避する。

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

Userモデルについて考える

class User < ApplicationRecord
  has_many :relationships #間違い
  has_many :relationships #間違い
end

#class User < ApplicationRecord
  #has_many :orders
#end

#class Item < ApplicationRecord
  #has_many :orders
#end

先程の書き方に習うとこのようになるが、重複しており、Relationshipテーブルには”user_id”は無いため不可。
そのため、ひとまず名前をつける。
上記は、自分がフォローする側の関係性と自分がフォローされる側の関係性のため
active_relationships、passive_relationshipsとして表記する。
(テーブル名の複数形ではなくなるため、class_nameも追加)

class User < ApplicationRecord
  has_many :active_relationships, class_name: "Relationship" #間違い
  has_many :passive_relationships, class_name: "Relationship" #間違い
end

しかし、この状態ではrailsはRelationshipのuser_idを探しに行ってしまう。
そのため、参照するカラムをforeign_keyで指定する。

class User < ApplicationRecord
  has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id"
  has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id"
end

最後に、以下情報が取れるようにする
@userがフォローしているUser一覧” ⇒ @user.followings (followedsは英語的におかしいため)
@userがフォローされているUser一覧” ⇒ @user.followers

class User < ApplicationRecord
  has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id"
  has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id"
  has_many :followings, through: :active_relationships #間違い
  has_many :followers, through: :passive_relationships

#class User < ApplicationRecord
  #has_many :orders
  #has_many :foods, through: :orders
#end
end

ここでrailsはfollowings、followersの表記より、
(単数形)_idがRelationshipテーブルにあるかを探しに行くが、followingsの方は存在していない。
そのため、sourceを用いて、Relationshipテーブルの参照先カラムを指定する。

class User < ApplicationRecord
  has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id"
  has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id"
  has_many :followings, through: :active_relationships, source: :followed
  has_many :followers, through: :passive_relationships, source: :follower
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Formオブジェクトを使用した際の単体テスト(FactoryBotの記述)

Formオブジェクトを使用した場合のテストコードを記述した際に、
何度もエラーが出てしまった部分を、忘れないように記録します。

フリマアプリで商品購入機能を実装し、その時のテストコードの一部になります。
以下がその一部です。

qiita.rb
   before do
    @user = FactoryBot.create(:user)
    @item = FactoryBot.build(:item)
    @item.image = fixture_file_upload('app/assets/images/test_image.jpeg')
    @item.save
    @order_purchaser = FactoryBot.build(:order_purchaser, user_id: @user.id, item_id: @item.id)

  end

@itemの部分でbuildアクションを使用しているのは、その後の記述で画像情報を@itemに追加するためで、
createアクションで定義してしまうとデータが保存され、後からの記述で追加ができなかったためです。

qiita.rb
 @order_purchaser = FactoryBot.build(:order_purchaser, user_id: @user.id, item_id: @item.id)

今回のフリマアプリで扱った購入者情報(@order_purchaser)は、user、itemに紐づいているため、カッコ内の記述で、user_id、item_idのデータを使用できるようにしています。

今回のコードが必要になったのは、Formオブジェクトを使用しており、モデル間のアソシエーションが記述されておらず、FactoryBotでassociationを組めなかった為です。

不足箇所や間違えているところがあれば、随時更新します。

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

Formオブジェクトを使用した際の単体テスト(FactoryBot)

Formオブジェクトを使用した場合のテストコードを記述した際に、
何度もエラーが出てしまった部分を、忘れないように記録します。

フリマアプリで商品購入機能を実装し、その時のテストコードの一部になります。
以下がその一部です。

qiita.rb
   before do
    @user = FactoryBot.create(:user)
    @item = FactoryBot.build(:item)
    @item.image = fixture_file_upload('app/assets/images/test_image.jpeg')
    @item.save
    @order_purchaser = FactoryBot.build(:order_purchaser, user_id: @user.id, item_id: @item.id)

  end

@itemの部分でbuildアクションを使用しているのは、その後の記述で画像情報を@itemに追加するためで、
createアクションで定義してしまうとデータが保存され、後からの記述で追加ができなかったためです。

qiita.rb
 @order_purchaser = FactoryBot.build(:order_purchaser, user_id: @user.id, item_id: @item.id)

今回のフリマアプリで扱った購入者情報(@order_purchaser)は、user、itemに紐づいているため、カッコ内の記述で、user_id、item_idのデータを使用できるようにしています。

今回のコードが必要になったのは、Formオブジェクトを使用しており、モデル間のアソシエーションが記述されておらず、FactoryBotでassociationを組めなかった為です。

不足箇所や間違えているところがあれば、随時更新します。

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

検証ツールなどを使った情報の改竄を防ぐControllerの実装

なぜ必要か

商品を出品するフリマアプリのようなサービスを例に考えます。
あるユーザーが商品を出品後、その商品の情報を編集したり、商品を削除したりする場合、そうした操作をするのは当然、商品を出品した当事者であるべきです。
そこでビューファイルの実装においては、以下のようにif文を用いた条件分岐により、商品を出品したユーザーと現在ログイン中のユーザーが一致した場合に限り、「編集」や「削除」のボタンを表示するようにしています。

Viewファイルの実装

 <% if current_user.id == @item.user_id %>
      <%= link_to "商品の編集", edit_item_path(@item.id), method: :get, class: "item-red-btn" %>
      <p class="or-text">or</p>
      <%= link_to "削除", item_path(@item.id), method: :delete, class:"item-destroy" %>
   <% end %> 

しかし、これだけですと、検証ツールなどを使用して、第3者が勝手に商品の情報の編集をできてしまう状態です。
そこで、Controller側でも制限を設ける記述をすることで、そうしたリスクを排除します。

Controllerの実装

 @item = Item.find(params[:id])
  unless @item.user_id == current_user.id
   redirect_to root_path
  end

このように、unlessメソッドを使い、その商品を投稿したユーザーと現在ログインしているユーザーが一致しない場合、ログイン画面に遷移する実装をすれば検証ツールによる編集も防げます。
ちなみに、current_userメソッドを使っているのは、ユーザー管理機能を実装するにあたり事前にdeviseを導入しているためです。

まとめ

検証ツールを用いた悪意ある情報編集などを防ぐためにも、Controllerにおいて頻繁に使われる記述のようなので、これを機に覚えておきたいと思います。

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

子レコードの条件で親レコードを絞り込みたいときはEXISTS句を活用しよう

はじめに

Railsで次のような親子関連を持ったモデルがあったとする。

class Parent < ApplicationRecord
  has_many :children
end

class Child < ApplicationRecord
  belongs_to :parent
end

そして、DBのデータが次のように登録されていたとする。

  • parent = Namihei
    • child = Sazae
    • child = Katsuo
    • child = Wakame
  • parent = Misae
    • child = Shinnosuke
    • child = Himawari

この状況で以下のようなメソッドを作りたい。

# 引数で与えられた文字列を子どもの名前に含む親(Parent)を返す
# (子どもの名前の大文字・小文字は無視する)
Parent.children_name_with(str)

この要件を満たすためのテストコードは次のようになる。

require "test_helper"

class ParentTest < ActiveSupport::TestCase
  test "zで検索" do
    # Sazaeが該当するのでNamiheiが返る
    parents = Parent.children_name_with('z').order(:name)
    assert_equal [parents(:namihei)], parents
  end

  test "kで検索" do
    # ShinnosukeとKatsuoとWakameが該当するのでMisaeとNamiheiが返る
    parents = Parent.children_name_with('k').order(:name)
    assert_equal [parents(:misae), parents(:namihei)], parents
  end

  test "rで検索" do
    # Himawariが該当するのでMisaeが返る
    parents = Parent.children_name_with('r').order(:name)
    assert_equal [parents(:misae)], parents
  end
end

この要件を満たすchildren_name_withメソッドの実装方法を考えたい。

JOIN + DISTINCTを使う(あまり推奨しない)

この要件を満たすコードとして、以下のような実装をよく見かける。

scope :children_name_with, -> (str) do
  joins(:children)
    .where("LOWER(children.name) LIKE ?", "%#{str.downcase}%")
    .distinct
end

参考:発行されるSQL

SELECT DISTINCT "parents".* 
FROM "parents" 
INNER JOIN "children" 
  ON "children"."parent_id" = "parents"."id" 
WHERE (LOWER(children.name) LIKE '%k%') 
ORDER BY "parents"."name" ASC

たしかにこの実装でも要件は満たせる。
しかし、childrenテーブルをJOINすると大量のparentsレコードの重複行が発生してしまう恐れがある。
そのためにdistinctを呼んで重複行を排除する必要があるが、一般にこの処理はRDBMSにとってハイコストなものでであるため、パフォーマンスが悪化する恐れがある。

参考:DISTINCTを付けなかった場合

DISTINCTなしで子どもの名前に"k"が含まれる親を検索するSQLを実行すると以下のような結果になる。

id name created_at updated_at
2 Misae 2021-03-03 22:33:05 2021-03-03 22:33:05
1 Namihei 2021-03-03 22:33:05 2021-03-03 22:33:05
1 Namihei 2021-03-03 22:33:05 2021-03-03 22:33:05

Namiheiのレコードが重複する理由は、JOIN先のchildrenテーブルでKatsuoとWakameの2件が該当したためである。

EXISTS句を使う(個人的におすすめ)

上のようなコードは以下のようにEXISTS句を使ったクエリが発行されるように書き直すと、RDBMS上の処理効率が良くなる。

scope :children_name_with, -> (str) do
  sql = <<~SQL
    EXISTS (
      SELECT *
      FROM children c
      WHERE c.parent_id = parents.id
      AND LOWER(c.name) LIKE ?
    )
  SQL
  where(sql, "%#{str}%")
end

参考:発行されるSQL

SELECT "parents".*
FROM "parents"
WHERE (EXISTS (
  SELECT *
  FROM children c
  WHERE c.parent_id = parents.id
  AND LOWER(c.name) LIKE '%k%'
))
ORDER BY "parents"."name" ASC

このSQLにするとparentsレコードの重複行が発生しなくなり、DISTINCTの処理も不要になる。

この記事で示した程度の少量のレコード数であれば体感できる速度差はないが、何万、何十万という重複行が発生するような状況では無視できない違いが出てくる。

もしくはIN + サブクエリを使う

次のように書く方法もある。

scope :children_name_with, -> (str) do
  ids = Parent
    .joins(:children)
    .where("LOWER(children.name) LIKE ?", "%#{str.downcase}%")
    .select(:id)
  where(id: ids)
end

参考:発行されるSQL

SELECT "parents".* 
FROM "parents" 
WHERE "parents"."id" IN (
  SELECT "parents"."id" 
  FROM "parents" 
  INNER JOIN "children" 
    ON "children"."parent_id" = "parents"."id" 
  WHERE (LOWER(children.name) LIKE '%k%')
)
ORDER BY "parents"."name" ASC

このSQLでも重複行は発生しないのでDISTINCTは不要。
EXISTS句を使ったときとどちらが実行効率が良いのかは未検証。

ただ、個人的にはEXISTS句を使った方が「子レコードの存在有無(EXISTS OR NOT)で絞り込みたい」という意図が明確になるので、EXISTS句を使う方が好み。

まとめ

子レコード(has_manyで関連する関連先のレコード)の条件で親レコードを絞り込みたいときは、JOIN + DISTINCTよりもEXISTSを使って絞り込む。

実行環境

  • Ruby on Rails 6.1.3
  • SQLite3 (PostgreSQLやMySQLを使ったときも同じ議論になるはず)

サンプルコード

本記事のサンプルコードはこちら。

おまけ:子どもが一人もいないParentを検索する

次のように子どもが一人もいないParentレコードがあったとする。

  • parent = Namihei
    • child = Sazae
    • child = Katsuo
    • child = Wakame
  • parent = Misae
    • child = Shinnosuke
    • child = Himawari
  • parent = Golgo13
    • (no children)

子どもが一人もいないParentを探すwithout_childrenメソッドの実装を考える。
テストコードを書くと次のようになる。

test ".without_children" do
  parents = Parent.without_children.order(:name)
  assert_equal [parents(:golgo13)], parents
end

方法1:LEFT OUTER JOIN + id IS NULLを使う

scope :without_children, -> do
  left_outer_joins(:children).where(children: { id: nil })
end

参考:発行されるSQL

SELECT "parents".*
FROM "parents"
LEFT OUTER JOIN "children"
  ON "children"."parent_id" = "parents"."id"
WHERE "children"."id" IS NULL
ORDER BY "parents"."name" ASC

方法2:NOT EXISTS句を使う

scope :without_children, -> do
  sql = <<~SQL
    NOT EXISTS (
      SELECT *
      FROM children c
      WHERE c.parent_id = parents.id
    )
  SQL
  where(sql)
end

参考:発行されるSQL

SELECT "parents".*
FROM "parents"
WHERE (NOT EXISTS (
  SELECT *
  FROM children c
  WHERE c.parent_id = parents.id
))
ORDER BY "parents"."name" ASC

方法3:NOT IN + サブクエリを使う

scope :without_children, -> do
  ids = Parent.joins(:children).select(:id)
  where.not(id: ids)
end

参考:発行されるSQL

SELECT "parents".*
FROM "parents"
WHERE "parents"."id" NOT IN (
  SELECT "parents"."id"
  FROM "parents"
  INNER JOIN "children"
    ON "children"."parent_id" = "parents"."id"
)
ORDER BY "parents"."name" ASC

どれがいいか?

  • 方法1

    • メリット:SQLを書かずにActiveRecordの機能だけで済む
    • デメリット:RDBMSによっては内部的に大量に子レコードをJOINしてからidがNULLの行を絞り込む、というような処理が走りそう(未検証)
  • 方法2

    • メリット:書き手の意図が明確になる。RDBMSのクエリオプティマイザが効きやすそう(未検証)
    • デメリット:生SQLを書かなければいけない。状況によってはscopeの再利用性が下がる
  • 方法3

    • メリット:SQLを書かずにActiveRecordの機能だけで済む
    • デメリット:NOT INだとテーブルの全件走査が走るかも?(未検証。クエリオプティマイザが賢ければうまくindexが使われるかも?)

クエリオプティマイザの性能次第のところはあるが、個人的にはクエリの意図が明確で実行速度も速そうな方法2を使いたい。

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

Rails 6.1で発生する"Calling `<<` to an ActiveModel::Errors message array in order to add an error is deprecated"警告を修正する

Ruby on Rails 6.1に上げると以下のような警告が出ることがある。

DEPRECATION WARNING: Calling `<<` to an ActiveModel::Errors message array in order to add an error is deprecated. Please call `ActiveModel::Errors#add` instead.

この問題は次のように修正する。

-record.errors[:base] << "Something went wrong."
+record.errors.add :base, "Something went wrong."

応用:正規表現でプロジェクト全体のコードを一括置換する

が、数が多いと大変なので、エディタのGrep置換機能と正規表現を使ってこんなふうに一括置換すると便利かも。

  • 検索条件 = errors\[:(\w+)\] <<
  • 置換文字列 = errors.add :$1, (エディタによっては$1ではなく\1になることもある)

もちろん、これはプロジェクト全体でerrors[:hoge] <<が同じフォーマットで書かれていることが前提なので、フォーマットに一貫性がないときは置換対象から外れたり、おかしな置換結果になったりする場合があるので注意。

正規表現がわからん!という場合

僕が以前書いた正規表現の入門記事を見ればわかるはずです!

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

【Rails】通知機能をリファクタリングしてみた

プログラミングスクールに通っています。
先日ポートフォリオのフィードバックが返って来た際に、通知機能のリファクタリングをした方が良いとの指摘を受けたので、修正方法を記事にします。
通知機能に関しては以下の記事を参考にさせて頂きました。
https://qiita.com/nekojoker/items/80448944ec9aaae48d0a

修正方法

cook.rb
  def create_notification_like!(current_user)
    already_like_check = Notification.where(
      [
        "visitor_id = ? and visited_id = ? and cook_id = ? and action = ? ",
        current_user.id, user_id, id, 'like',
      ]
    )
    if already_like_check.blank?
      notification = current_user.active_notifications.new(
        cook_id: id,
        visited_id: user_id,
        action: 'like'
      )
      if notification.visitor_id == notification.visited_id
        notification.is_checked = true
      end
      notification.save if notification.valid?
    end
  end

投稿した料理に対して「いいね」がされた際に通知が行われるようにしています。
今回はモデルに定義したcreate_notification_like!メソッドをリファクタリングしていきます。

上記では
- already_like_check が blank だった場合
- notification.visitor_id と notification.visited_id が一緒だった場合
- notification が valid? だった場合
と条件が3つ出てきます。しかし、 already_like_checkblank じゃなかった場合の処理は出てきません。
つまり、already_like_checkblank じゃなかった場合 は例外的なパターンとして処理をその時点で中断してあげると良いということになります。
この考え方を適用すると下記のように実装できます。

cook.rb
class Cook < ApplicationRecord
  def create_notification_like!(current_user)
    already_like_check = Notification.where(
      [
        "visitor_id = ? and visited_id = ? and cook_id = ? and action = ? ",
        current_user.id, user_id, id, 'like',
      ]
    )
    return unless already_like_check.blank? #これを追加
    notification = current_user.active_notifications.new(
      cook_id: id,
      visited_id: user_id,
      action: 'like'
    )
    if notification.visitor_id == notification.visited_id
      notification.is_checked = true
    end
    notification.save if notification.valid?
  end
end

早期リターンという手法でifのネストを避けることができました。
(早期リターンとは簡単に言うと、「条件に合致しない場合は処理をそこでストップさせる」ことです。今回のケースではalready_like_checkblank じゃなかった場合、return unless already_like_check.blank?以下の処理を実行しないということです。)
次は メソッドの抽出 という方法を行い処理をみやすくしてみましょう。
メソッドの抽出 というのは処理の塊をメソッドに切り出してあげることです。
今回抽出できそうな処理は下記になります。

notification = current_user.active_notifications.new(
    cook_id: id,
    visited_id: user_id,
    action: 'like'
  )
if notification.visitor_id == notification.visited_id
  notification.is_checked = true
end
notification.save if notification.valid?

この処理は、
- notification.visitor_id と notification.visited_id が一緒だった場合
- notification が valid? だった場合
という2つの条件が含まれており、 create_notification_like! メソッドの中にあるとやや処理が複雑になってしまいます。
Notification クラスから作成したインスタンスに対する処理の実行ですので、 Notification クラスにインスタンスメソッドを作成してあげると良いでしょう。

notification.rb
def update_is_checked_and_save
  if visitor_id == visited_id
    is_checked = true
  end
  save if self.valid?
end

さらに、後置ifを活用して以下のようにします。

notification.rb
def update_is_checked_and_save
  is_checked = true if visitor_id == visited_id
  save if self.valid?
end

そして、このメソッドを create_notification_like! メソッド内で呼び出します。

cook.rb
def create_notification_like!(current_user)
  already_like_check = Notification.where(
    [
      "visitor_id = ? and visited_id = ? and cook_id = ? and action = ? ",
      current_user.id, user_id, id, 'like',
    ]
  )
  return unless already_like_check.blank?
  notification = current_user.active_notifications.new(
    cook_id: id,
    visited_id: user_id,
    action: 'like'
  )
  notification.update_is_checked_and_save
end

これで最初よりかはスッキリとしたと思います。

最後に

あくまで一例ですので、もっとわかり易い方法があれば教えて下さい!
以上です!ありがとうございました!

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

マイグレーションファイルを一括でロールバックする rails db:rollback

動機

何個か前のマイグレーションファイルまで遡りたいときにrails db:rollbackを何度も入力するのが面倒!
そう思ったので、解決策をサクッとまとめました。

STEP数を指定してロールバック

戻る数を指定してロールバックする方法

rails db:rollback STEP=ステップ数

drop、create、migrate全てやてくれる

rails db:migrate:reset

drop、create、schema.rbから復帰させる

rails db:reset

以上

短くなってしまいましたが、よかったら使ってみてください。

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

ActiveRecord::PendingMigrationErrorの対処法

ActiveRecord::PendingMigrationErrorの対処法

マイグレーションファイルを編集し、rails db:migrateでマイグレーションを実行したときに、このようなエラーが出た場合の対処法について記載します。

エラーメッセージ

error.png

開発環境

・データベース:MYSQL
・railsのバージョン:6.0.0
・使用PC:macbook pro

エラーの原因

Image from Gyazo
このように、usersテーブルが既に作ってあるにも関わらず、また作ろうとしてエラーになったようです。

エラーの解決方法

rails db:reset

このコマンドをターミナルに打つと、
①すでにあるテーブルを削除、
②再度マイグレーション実行
というタスクを一度に行ってくれます。

これを実行したあと、rails db:migrate:statusコマンドにてマイグレーションの状況を確認してみると、
Image from Gyazo

ちゃんとupの状態になっていました。

最後に

今回のエラーに直面し最初は困っていましたが、以下の記事を参考にして解決することができました。
同様のエラーに直面した方にはきっと参考になる記事ですので、ご一読下さい。
https://qiita.com/KONTA2019/items/0444ae3b8c8936a56ee0
https://qiita.com/ryota_ueda/items/fcf111ed0b56822408ed
https://qiita.com/ARTS_papa/items/64416fd4e05250941fb4

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

【Rails】ログイン画面にエラーメッセージを表示させる(devise使用)

環境

macOS: Big Sur Ver11.2.2
Rails: 6.0.0
Ruby: 2.6.5

やりたいこと

Railsアプリケーションでdeviseを使ってユーザー登録やらを実装している。
新規登録やパスワードリセット画面ではモデルに紐づくエラーメッセージが表示されるが、なぜかログイン画面では表示されない仕様になっている。
ログイン画面でもエラーメッセージを表示させてユーザーにわかりやすい仕様にしたい。

試したこと

新規登録のビューと同じパーシャルをログインのビューファイルにも記載した。
【結論】
表示されない…

app
<h2>ログイン</h2>

<%= form_with model: @user, url: new_user_session_path, local: true do |f| %>

<%# 以下にエラーメッセージのパーシャルを記載 %>
<%= render "devise/shared/error_messages", resource: resource %>

<div class="field">
  <%= f.label :email %><br />
  <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>

<div class="field">
  <%= f.label :password %><br />
  <%= f.password_field :password, autocomplete: "current-password" %>
</div>

<% if devise_mapping.rememberable? %>
<div class="field">
  <%= f.check_box :remember_me %>
  <%= f.label :remember_me %>
</div>
<% end %>

<div class="actions">
  <%= f.submit "ログインする" %>
</div>
<% end %>

<%= render "devise/shared/links" %>

解決方法

% rails g devise:install した時にターミナルの完了画面にそもそも指示が書いてありました!
指示に従って、以下のファイルを編集したら無事にエラーメッセージが表示されました(厳密に言うと、エラー用のフラッシュメッセージ)。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>

<head>
  <title>TakeOutApp</title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>

  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>

<body>

  <%# deivseのフラッシュメッセージを表示 %>
  <p class="notice"><%= notice %></p>
  <p class="alert"><%= alert %></p>

  <%= yield %>

</body>

</html>

</html>

ちなみに、上記の記述でログインしてルートページにリダイレクトしたときにも「ログインに成功しました!」というフラッシュメッセージが表示されるようになります。

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

単体テストコードの書き方(Rails)

はじめに

昨日、テストコードを書く意味と導入手順の記事を書いたので
本日は、単体テストコードの書き方を記事にしたいと思います。

テストコードの記事多くなっていますが、新人エンジニアが任される
ことの多い仕事と聞いたので、オリジナルアプリを使用し入念に学習しています。

テストコードの理解が浅い方はこちらを見てからだとわかりやすいかもしれません。

テストコードについて(Ruby on Rails)

Rspec、FactoryBot、Fakerは導入済みです。
導入の仕方は上記の記事に書いてあります。

コードを書く前の準備

1.ファイルを作成しよう
今回は、Userモデルの単体テストコードを書きたいと思います。
そこで、コードを書くファイルをを作りましょう。

ファイルの作り方は、ターミナルに以下のコマンドを打ち込みましょう。

% rails g rspec:model user

すると、

create  spec/models/user_spec.rb

これで、ファイルの作成は完了です。

2.バリデーションを確認しよう。
モデルの単体テストコードは、バリデーションの確認です。
自分の記述した、バリデーションが上手く行っているかを確認します。
なので、書く前に一度バリデーションを見直すと書きやすくなります。

models/user.rb

class User < ApplicationRecord

  PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze
  validates_format_of :password, on: :create, with: PASSWORD_REGEX, message: 'には半角の英字数字の両方を含めて設定してください'

  with_options presence: true do
    validates :nickname
    validates :profile
  end

end

今回私が組んだバリデーションは
・パスワードは半角英数混合
・ニックネームが空では登録できない
・プロフィールが空だと登録できない
上記3つです。

さらに、ユーザー管理機能にはdeviseを導入しています。
deviseのデフォルトのバリデーションが、
・パスワードは6字以上
・emailには@が必要
・同じemailは登録できない
・パスワードは確認用含め2回入力が必要

これらのバリデーションがしっかり組まれているかを記述します。

テストコードを書いてみよう

作成したファイルの中身を確認しましょう。

spec/models/user_spec.rb

require 'rails_helper'

RSpec.describe User, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

1番上に記述されているrails_helperについて。
これは、Rspecを用いてRailsの機能をテストするときに、共通の設定を書いておくファイルです。各テスト用ファイルでspec/rails_helper.rbを読み込むことで、共通の設定やメソッドを適用します。
rails gコマンドでテストファイルを生成すると、rails_helperを読み込む記述が、自動的追加されます。

FactoryBotを生成しインスタンス変数に代入しましょう。

spec/models/user_spec.rb

require 'rails_helper'

RSpec.describe User, type: :model do
  before do
    @user = FactoryBot.build(:user)
  end

これで、@userの中にFactoryBotで作成したユーザー情報が入りました。

exampleを整理しよう

exampleとは簡単に説明すると、確認すること整理しましょうということです。
先ほどバリデーションを確認したと思うのですがそのことですね。
・パスワードは半角英数混合
・ニックネームが空では登録できない
・プロフィールが空だと登録できない
・パスワードは6字以上
・emailには@が必要
・同じemailは登録できない
・パスワードは確認用含め2回入力が必要
これらが、exsampleとなります。

ここで、テストコードにでてくるメソッドを事前に説明させていただきます。

・describe
テストコードのグループ分けを行うメソッドです。
「どの機能に対してのテストを行うか」をdescribeでグループ分けし、その中に各テストコードを記述します。

・it
itメソッドは、describeメソッド同様に、グループ分けを行うメソッドです。
itの場合はより詳細に、「describeメソッドに記述した機能において、どのような状況のテストを行うか」を明記します。

・context
contextは、特定の条件を指定してグループを分けます。
使用方法はdescribeと同じですが、describeには何についてのテストなのかを指定するのに対し、contextには特定の条件を指定します。

正常系テストコード

それでは、正常系のテストコードから書いてみましょう

spec/models/user_spec.rb

describe "新規登録" do
    context "登録できる時" do
      it "フォームに情報が正しく入力されていれば登録できる" do
        expect(@user).to be_valid
      end

新規登録の登録ができる時についてコードを書いていきたいと思います。

・expect
検証で得られた挙動が想定通りなのかを確認する構文のことです。
expect().to matcher()を雛形に、テストの内容に応じて引数やmatcherを変えて記述します。

・matcher(マッチャ)
matcherは、「expectの引数」と「想定した挙動」が一致しているかどうかを判断します。
expectの引数には検証で得られた実際の挙動を指定し、マッチャには、どのような挙動を想定しているかを記述します。

@userがexpextで、be_validがマッチャになります。
つまり、@userの中身が意図したものになっているかを確認するものです。

ここで、be_validとvalidメソッドについて説明します。

・be_valid
be_validとは、valid?メソッドの返り値が、trueであることを期待するマッチャです。

・valid?
valid?は、バリデーションを実行させて、エラーがあるかどうかを判断するメソッドです。
エラーがない場合はtrueを、ある場合はfalseを返します。
また、エラーがあると判断された場合は、エラーの内容を示すエラーメッセージを生成します。

この場合、エラーがないのでtrueを返します。なので、マッチャがbe_validとなります。
すごく簡単に説明すると正常系の時は、be_validを記述します。

異常系のテストコード

今回は「新規登録できない時」のemailの部分の一部を抜粋し載せたいと思います。

spec/models/user_spec.rb

context "登録できない時" do
      it "emailの入力がないと登録できない" do
        @user.email = nil
        @user.valid?
        expect(@user.errors.full_messages).to include("Email can't be blank")
      end

まず、「emailがないと登録できない」について
実際にemailがないと登録できないのか確認してます。
なので、@userの中のemailを一度空にします。
そして、valid?メソッドを使用してエラーがあるかを確認します。

そして、

 expect(@user.errors.full_messages).to include("Email can't be blank")

デフォルトでemailは空だと登録できないバリデーションが組まれています。
valid?メソッドを使用したとき、falseが返ってくるはずです。
ここでの挙動の確認ですが、@userのエラーメッセージの中に意図したものが入っていれば
確認できます。
ちなみに、includeは「expectの引数」に「includeの引数」が含まれていることを確認するマッチャです。
つまり、@user.errors.full_messagesの中に"Email can't be blank"
のが含まれていれば、バリデーションが正しいかわかります。

どのように確認するのか?
デバッグをして確認します。

railsでは、デバックを行うにはpry-railsとういGemを導入して、
binding.pryという機能を使うことで確認できます。

pry-railsの導入の説明は省かせていただきます。
興味あるかたは調べてみてください。

 context "登録できない時" do
      it "emailが空だと登録できない" do
        @user.email = nil
        @user.valid?
        binding.pry
        expect(@user.errors.full_messages).to include("Email can't be blank")
      end

binding.pryの記述のところで、処理が一時停止します。

ターミナルより確認してみましょう。
以下のコマンドを実行します。

% bundle exec rspec spec/models/user_spec.rb 

Image from Gyazo

すると、処理が停止しコンソールが起動していることがわかります。
ここに、

@user.errors

こちらを打ち込むと、@userのエラーメッセージが確認できます。
しかし、これを全部打ち込むことはできないので、

@user.errors.full_messages

Image from Gyazo
これで、バリデーションを確認することができました。
ちなみにコンソールを終了する場合は、exitを入力します。

これで、ターミナルよりもう一度

% bundle exec rspec spec/models/user_spec.rb 

コマンドを入力し、確認します。
エラーがでなければ次のテストコードを書いていきます。

ここまでが、モデルの単体テストコードの書き方の説明です。
実際に書けても説明するのはとても難しかったです。

至らない点もあるかと思いますが、何かありましたらコメント
残してくだされば幸いです。

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

Railsで jQueryが動かない場合の対処法

目的

RailsでjQueryが動作するようにする。

記述

  $('button').on('click',function(){
    $('.popup').addClass('show').fadeIn();
  });

  $('#close').on('click',function(){
    $('.popup').fadeOut();
  });

なぜ動かなかったのか

Railsに標準でインストールされているturbolinksが原因でした。
以前JavaScriptでも同じ原因で動作しなかったことがありました。よく考えれば当然ですがjQueryにも影響があるようです。
JavaScriptと同様に'turbolinks:load'が必要なので下記の通り変更しました。

変更後の記述

$(document).on('turbolinks:load', function() {
  $('button').on('click',function(){
    $('.popup').addClass('show').fadeIn();
  });

  $('#close').on('click',function(){
    $('.popup').fadeOut();
  });
});

無事動作させることができました。

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

devise

deviseのインストール

マイグレーションファイルに必要なカラムを追加する

Image from Gyazo

アプリケーションコントローラーに以下を記述
・ストロングパラメーターの設定
・deviseを使用したときだけ、configure_permitted_parametersが動くようにする。

Image from Gyazo

ユーザー新規登録のビューにてnicknameのフォームを追加

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

deviseで複数のモデルを作成する

WHY

このことについて同じ記事はいくつもあるが、その記事の言っていることをきちんと理解しているか確認するため


今回はdeviseで複数のモデルを作成していきます。
どういうことかと言うと、deviseを使って、2つのテーブルを作成し、2つのユーザー管理機能を一つのアプリケーション内で管理しようということです。

deviseの導入

gemfile.
gem 'devise'
ターミナル.
bundle install
rails g devise:install

deviseの設定

主に2つの項目をコメントアウトを外し、変更します。

config/initializers/devise.rb.
#config.scoped_views = false
        ↓
config.scoped_views = true

#config.sign_out_all_scopes = true
     ↓
config.sign_out_all_scopes = false

これらの変更で
- それぞれのモデルで個別のログイン画面を使用することができる
- それぞれのモデルを扱う際に、一方をログアウトした時に、もう片方もログアウトすることを防ぐ

という設定が完了しました。
続いてMVCを作成していきます。

MVC作成

ターミナル.
#モデル
rails g devise user
rails g devise admin

#ビューファイル
rails g devise:views users
rails g devise:views admins

#コントローラー
rails g devise:controllers users
rails g devise:controllers admins

それぞれ最後にモデル名を記述することを忘れないでください。
忘れてしまって作成した場合は「rails d」で削除後、また行ましょう。

ルーティングの設定

現在のルーティングをみてみます

ターミナル.
%rails routes

                 Prefix Verb   URI Pattern                       Controller#Action
       new_admin_session GET    /admins/sign_in(.:format)         devise/sessions#new
           admin_session POST   /admins/sign_in(.:format)         devise/sessions#create
   destroy_admin_session DELETE /admins/sign_out(.:format)        devise/sessions#destroy
      new_admin_password GET    /admins/password/new(.:format)    devise/passwords#new
     edit_admin_password GET    /admins/password/edit(.:format)   devise/passwords#edit
          admin_password PATCH  /admins/password(.:format)        devise/passwords#update
                         PUT    /admins/password(.:format)        devise/passwords#update
                         POST   /admins/password(.:format)        devise/passwords#create
        new_user_session GET    /users/sign_in(.:format)          devise/sessions#new
            user_session POST   /users/sign_in(.:format)          devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)         devise/sessions#destroy
       new_user_password GET    /users/password/new(.:format)     devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)    devise/passwords#edit
           user_password PATCH  /users/password(.:format)         devise/passwords#update
                         PUT    /users/password(.:format)         devise/passwords#update
                         POST   /users/password(.:format)         devise/passwords#create
cancel_user_registration GET    /users/cancel(.:format)           devise/registrations#cancel
   new_user_registration GET    /users/sign_up(.:format)          devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)             devise/registrations#edit
       user_registration PATCH  /users(.:format)                  devise/registrations#update
                         PUT    /users(.:format)                  devise/registrations#update
                         DELETE /users(.:format)                  devise/registrations#destroy
                         POST   /users(.:format)                  devise/registrations#create

右側の「Controller#Action」に注目しましょう。
コントローラーがすべてdeviseになっています。

なのでルーティングをこのように設定してあげます。

routes.rb
devise_for :admins, controllers: {
    sessions:      'admins/sessions',
    passwords:     'admins/passwords',
    registrations: 'admins/registrations'
  }
devise_for :users, controllers: {
    sessions:      'users/sessions',
    passwords:     'users/passwords',
    registrations: 'users/registrations'
  }

もう一度確認すると

ターミナル.
rails routes

                 Prefix Verb   URI Pattern                          Controller#Action
       new_admin_session GET    /admins/sign_in(.:format)            admins/sessions#new
           admin_session POST   /admins/sign_in(.:format)            admins/sessions#create
   destroy_admin_session DELETE /admins/sign_out(.:format)           admins/sessions#destroy
      new_admin_password GET    /admins/password/new(.:format)       admins/passwords#new
     edit_admin_password GET    /admins/password/edit(.:format)      admins/passwords#edit
          admin_password PATCH  /admins/password(.:format)           admins/passwords#update
                         PUT    /admins/password(.:format)           admins/passwords#update
                         POST   /admins/password(.:format)           admins/passwords#create
        new_user_session GET    /users/sign_in(.:format)             users/sessions#new
            user_session POST   /users/sign_in(.:format)             users/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)            users/sessions#destroy
       new_user_password GET    /users/password/new(.:format)        users/passwords#new
      edit_user_password GET    /users/password/edit(.:format)       users/passwords#edit
           user_password PATCH  /users/password(.:format)            users/passwords#update
                         PUT    /users/password(.:format)            users/passwords#update
                         POST   /users/password(.:format)            users/passwords#create
cancel_user_registration GET    /users/cancel(.:format)              users/registrations#cancel
   new_user_registration GET    /users/sign_up(.:format)             users/registrations#new
  edit_user_registration GET    /users/edit(.:format)                users/registrations#edit
       user_registration PATCH  /users(.:format)                     users/registrations#update
                         PUT    /users(.:format)                     users/registrations#update
                         DELETE /users(.:format)                     users/registrations#destroy
                         POST   /users(.:format)                     users/registrations#create

きちんとコントローラーが割り振られました。
それぞれのコントローラーも先程作成していればうまく遷移してくれるはずです。

パスの指定

今回は
test.png

このようにページ遷移していきたいのでそれぞれにパスを指定していきます。
パスを指定するときには

def after_sign_in_path_for(resource)
    指定したいパス
end

をコントローラーに記述していきます。

controllers/admins/registrations_controller.rb
コメントアウトされているafter_sign_in_path_for(resource)を編集します。同じくusersディレクトリでも行ます。

registrations_controller.rb.
# The path used after sign up.
  def after_sign_in_path_for(resource)
    items_path # 指定したいパスに変更
  end

これで新規登録をそれぞれ行い、指定したパスへ遷移できると成功です。

続いてsession(ログイン後)の遷移の記述をしていきます。
先程と同じく次はそれぞれのsessions_controller.rbの一番下に記述していきます。

sessions_controller.rb.
def after_sign_in_path_for(resource)
    items_path # 指定したいパスに変更
end

ブラウザでログイン後うまく遷移できれば成功です。


sessionsにcreateアクションにredirect_toなどでもできるかもしれません、、(まだ試してませんし、調べ足りてません...)
挙動的には問題はないとおもいます、、

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

【Ruby on Rails】FactoryBotとFakerについてまとめ

初学者です。
テストコードがなぜか大好きです。

Ruby on Railsでテストコードを書く際にFactoryBotとFakerを使うと効率的にできます。
使い方をまとめます。

前提条件

Rspecを導入済みであること

FactoryBot

FactoryBotとは、Railsで利用できるGemです。
FactoryBot用のファイルであらかじめのインスタンスに定める値を設定しておき、各テストコードで使用します。
例えばユーザー登録のテストコードで、ユーザー名やメールアドレスやパスワードをFactoryBotにまとめておくことで効率的に記述をすることができます。

FactoryBot導入

下記のようにGemfilegroup :development, :test dogem 'factory_bot_rails'と記述します。

Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rspec-rails', '~> 4.0.0'

# 以下を追加する
  gem 'factory_bot_rails'

end

ターミナルで下記のコマンドを実行しGemを導入します。

ターミナル
bundle install

このあとにテストコードを記述するためのファイルを生成すると、specディレクトリ配下にfactoriesディレクトリFactoryBot用のファイル(例:users.rbなど)が生成されますが、既にテストコード用のファイルを生成している場合などは生成されないので手動で作成しても問題ありません。

specディレクトリ配下にfactoriesディレクトリFactoryBotを記述するためのファイルを作成します。

例:spec/factories/users.rb

そのファイル内に記述していきます。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name                  {'test'}
    email                 {'test@example.com'}
    password              {'123456'}
    password_confirmation {password}
  end
end

上記のように記述しておけば、テストコード内で下記のように記述してデータを使い回すことができます。

spec/models/user_spec.rb
user = FactoryBot.build(:user)

上記の例ではuserという変数にFactoryBotのデータを入れているので、例えばuser.nameのように呼び出して使うことができます。

このままでもいいのですが、実際のサービスはユーザーがどんなデータを入れてくるかわからないこともあるのでそれを想定していた方がいいと思います。
そのために使うのがFakerです。

Faker

Fakerとは、Railsで利用できるGemです。
ランダムな値を生成してくれて、人名や住所や電話番号など様々用意されています。
Faker公式Github
アニメの名前とか登場人物とかもあって見てるだけでも面白いです。

ちなみに日本語のデータがよければgimeiというGemもあります。
例えばバリデーションで日本語のカナじゃないとダメとかにしてる場合はFakerよりもgimeiが使いやすいと思いました。
gimei公式Github

Faker導入

下記のようにGemfilegroup :development, :test dogem 'faker'と記述します。

Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rspec-rails', '~> 4.0.0'
  gem 'factory_bot_rails'

# 以下を追加
  gem 'faker'
end

ターミナルで下記のコマンドを実行しGemを導入します。

ターミナル
 bundle install

先ほどのFactoryBotの内容をFakerを利用したものに変更します。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name                  {Faker::Name.initials(number: 2)}
    email                 {Faker::Internet.free_email}
    password              {Faker::Internet.password(min_length: 6)}
    password_confirmation {password}
  end
end

書き方は公式のGIthubにも書いてあるのですが
Faker::の後に指定したいものを選択します。

例えば名前だけでもたくさんあります。

Faker
Faker::Name.name                 #=>フルネームの名前
Faker::Name.first_name           #=>firstnameだけ
Faker::Name.initials             #=>イニシャル
Faker::Name.initials(number: 2) #=>2文字のイニシャル

基本的に公式に書いてある通りに記述すればOKなので簡単にできます。
コンソールでデータを見ると面白いですよ。

以上です。

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

【部分的SPA】初心者なりにRuby on Rails, Vue.jsでポートフォリオを作成してみた【タイピングアプリ 】

初めに

 プログラミング学習を初めて2ヶ月程度の初心者ですが、自分なりにあれこれ工夫をしながらrailsアプリに部分的にSPAを導入したポートフォリオを作成してみました。本記事ではアプリの概要について記述し、その後いくつかの記事に分けて工夫した箇所を投稿していきたいと思います。
 皆様のポートフォリオ作りに少しでもお役に立てると幸いです。
 また、所々GIF画像を挿入していますが、Qiita上だと動きが悪いかもしれません。画像をクリックして頂けるとGyazoのページに遷移致しますので、そちらでしたらヌルヌル動くかと思います。

関連記事

 このポートフォリオを作成するにあたって使用した技術を順次記事にしていきます。

アプリの概要

Image from Gyazo
 「エンジニア初学者用の英単語タイピングアプリ」です。
 日本語のタイピングならそれなりの速さで打てても、英単語のタイピングになった瞬間にガクッと速度が落ちますよね?また、コードは誤字脱字があるとエラーになってしまうので正確に打てているか確認が必要ですが、それもなかなか時間が掛かりますよね?(私だけ?)
 そんな悩みを解消すべく作ったのがこのアプリ「Typie」です。
 Typieはプログラミングに使用する英単語に絞ってタイピングの練習が出来るアプリです。

 URLは下記になります。PCで使用することが前提のアプリなので、レスポンシブ非対応になります。ご了承ください。
Typie_URL: http://54.95.229.185/

Github_URL:

開発環境

  • フロントエンド
    • HTML/CSS/JavaScript
    • Vue.js 2.6.12
    • Vuetify 2.4.4
  • バックエンド
    • Ruby 2.6.5
    • Ruby on Rails 6.0.3.5
  • インフラ
    • Mysql/MariaDB
    • AWS(EC2)/Nginx/Unicorn/Capistrano
  • その他
    • Visual Studio Code

使用イメージ

新規投稿

Image from Gyazo
 フォームに「タイトル」「言語名」「ソースコード」「色」を入力して送信すると、ソースコードから英単語を抽出して問題を作成する仕組みになっています。ソースコードに登場する英単語なら、それは「プログラミングに使用する言語」と言えるという発想です。

詳細表示・編集・削除など

Image from Gyazo
 マイページを弄り倒しているGIFです。詳細表示や編集・削除は全てダイアログを表示して行うようにしました。編集や削除結果は非同期通信で反映され、地味に詳細表示ページのヘッダーの色を問題集(今後"Book"と呼びます)の色と同期させています。(この辺は半分遊んでます)

絞り込み検索

Image from Gyazo
 Bookが増えてくると目当てのものが探し辛くなるので、絞り込み検索機能も実装しました。

機能一覧

機能 概要
アプリ使用方法紹介機能 ランディングページにてアプリの使用方法をカルーセル形式で紹介しています
ユーザー管理機能 新規登録・ログイン・ログアウトが出来ます
入力補佐機能 フォームは入力内容が条件を満たしていないと枠が赤色に、満たすと緑色に変化します
Book一覧表示機能 Bookのタイトル、言語名、単語数、ハイスコアが表示されます
Book詳細表示機能 Bookのタイトル、言語名、スコアTOP5が表示される他、タイピング・編集・削除もここから行えます
Book編集機能 Bookのタイトル、言語名、色を編集出来ます
Bookの削除機能 Bookを削除出来ます
Bookの絞り込み検索機能 言語名から絞り込み検索を行えます
カウントダウン機能 いきなりタイピングが始まらないよう、3秒数えます
タイピング機能 登録された英単語を出題し、正解・不正解文字数をカウントします
残りの単語数表示機能 あとどれくらいで終わるのかが分かる機能です
途中退出機能 途中で退出してもスコアが算出されます
スコア算出機能 タイピング技能検定が使用している計算式(下記リンク)を用いてスコアを算出します
  • タイピング技能検定URL

工夫した点

 とにかく「英単語のタイピングに慣れていないエンジニア初学者向け」であることを主軸に、以下の課題をどう解決するかを考えました。

既存のエンジニア向け英単語タイピングアプリで感じた課題

  • 投稿した範囲全てが問題として出題されるタイプ
    • 1つ1つの英単語のタイピングに時間が掛かると全然進んでいる気がしない
    • 終わりが見えないストレスがある
    • 途中で抜けるとスコアは出ない
    • 入力すべき文字がハイライトされていて見づらい
    • タイマーが気になる
  • 単語単位で出題されるタイプ
    • 出題される単語を、一度全て自力で設定するのはキツい

DB設計

db.png

各テーブルについて

テーブル名 説明
Users ユーザー情報
Books Bookの情報
Words 英単語情報、初出単語のみ保存
Book_words Book/Wordの中間テーブル
Languages 言語名情報、初出言語のみ保存
Book_languages Book/Languageの中間テーブル
Scores スコア情報

苦労した点

フロントエンド

  • 思った通りにならない
    「このプロパティを追加すればこうなるはず」…と思って記述しても想像と違う結果になる。何も変化していないことが最も多い。バックエンドと違ってエラーが出ないからどこがどう間違っているのか分かりづらい。あとセンスが無い。
  • Vue.jsとのデータの受け渡し
    当然ながらインスタンス変数が無い。JSON形式に変換しようとするも、狙った通りの連想配列にならない。本当に表示するデータに限定して受け渡しをしないと、表示されるまで時間が掛かる。

バックエンド

  • 6つのテーブルの値が変動するフォームオブジェクト
    BookとWord/Languageは多対多の関係。そしてWordは追加される単語の数だけ増え、Languageは増えるとしても1つだけなので処理が全く違う。そしてBook一覧表示ではベストスコアが表示されるので、スコアが空のままだとnilclassのエラーになってしまう。
  • フォームオブジェクトの編集
    レコードの編集は簡単だと思っていた。…それは全部railsがやってくれていたからで、1から自力で実装するのはなかなかの難易度だった。

参考にした学習教材・記事

本当に色んなものを参考にさせて頂きました。
ここでは特に助けていただいたものを一部抜粋して紹介します。

Vue.js

  • 【書籍】『Vue.jsのツボとコツがゼッタイにわかる本』(中田亨, 秀和システム, 2019/3/28)
     Vue.jsの基礎はこの書籍から学びました。ミニアプリの作成を通して解説を読んでいく形式なのでとても分かりやすく、これからVue.jsの学習を始める方におすすめの一冊です。

  • 【書籍】『Vue.js&Nuxt.js超入門』(掌田津耶乃, 秀和システム, 2019/2/11)
     こちらは「アプリを作る」ことを意識して書かれている一冊です。Nuxt.jsやFirebaseについても解説があるので、最低限の基本は既に抑えているという方にはこちらがおすすめです。

  • 【有料動画】『超Vue.js2完全パック』(よしぴー, Udemy, 最終更新 2020/12/20)
     Vue.js界隈では有名な動画です。Vue.jsの基礎から応用まで解説している動画になります。話し手の方がとても早口なのですが、ちゃんと当倍速で聞き続けているとだんだんクセになっていきます。 

Ruby on Railsへの組み込み

  • 【有料記事】『Rails × Vue.js でメモアプリを作成しながらモダンな開発を学ぼう!』(Daijiro, Techpit, 最終更新 2021/1)
     Vue.jsの基礎的な説明もされているのですが、どちらかというと「アプリ開発過程を追体験出来る」教材になっているので、Vue.js/Ruby on Railsの基礎は定着させた上で受講するのがよろしいかと思います。axiosやjbuilderを使って実際にアプリを作成する過程を学ぶことが出来ます。

  • 【有料記事】『【Rails / Vue.js】Snippet アプリを作ってみよう!』(kozzy, Techpit, 最終更新 2020/9)
     Vue.jsについての解説は少ないのですが、関数を使ったダイアログの表示/非表示切り替え、CRUDの実装、Vuetifyの使い方を学ぶことが出来ます。

開発に詰まった際の解決方法

 Vue.jsをRuby on Railsアプリに組み込む際に直面した課題の解決方法に関する記事です。詳細は今後違う記事にまとめようと思いますが、ここでは簡単な紹介をさせて頂きます。

  • 【無料記事】『【Rails】foremanでrails serverとwebpack-dev-serverを一度に起動する』(五十川洋平, 最終更新 2019/4/2)

  • 【無料記事】『【Rails】jbuilderの使い方辞典〜メソッドの文法と使い方がすぐわかる』(Pikawaka, 最終更新 2020/4/3)

  • 【無料記事】『【Vue】axiosで、デフォルトでCSRFトークンを設定できるようにする』(@ngron, Qiita, 最終更新 2019/10/14)

今後の課題

  • テストコードの記述
  • ドメイン取得
  • SSL取得
  • 完全なSPA化
  • ユーザー間交流
  • 絞り込み検索機能の充実化
  • dockerの学習と導入
  • CircleCIの学習と導入

 まだまだやることが沢山ありますね。今後の転職活動の状況も考えながら少しずつ進めていきたいと思います。

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

deviseによる[ ~~ must exist ] によるエラー

今回のエラーは強敵でした。
中々原因が分からずかなり苦戦しました。
とりあえず、真夜中ですが忘れないうちに備忘録書きます。


エラーに至った経緯

アプリ作成時すぐにdeviseでユーザー管理機能を作成し、
カラムを追加で足しました。
動物関連のアプリを作っていたので、追加で足したカラム名は
keep_petnumber_of_petsを足しました。
その時は何事もなくユーザーの新規登録ができました。
アプリ制作も順調に進み、編集と削除の機能を追加しました。

機能を確かめる為に、
ここで新しくid2新規ユーザーに登場してもらおうと思い、
ユーザー新規登録画面に行きました。

 

ここでエラー発生

全ての項目を埋めて、新規登録ボタンを押すと、

1 error prohibited this user from being saved:
1つのエラーにより、このユーザーの保存が禁止されました。
             Pet must exist

上記のエラーメッセージが出てきました。
Pet must existとは一体何ですか!?
カラムに間違いはなく、アソシエーションもdevise用のストロングパラメータの記述も完璧なはずだった。
なのに、どうしてか全く分からず完全に詰んだ..と思っていながら調べては試しを繰り返しやっと解決に至りました。

解決方法

結論、アソシエーションに問題がありました。
belongs_to :userだけでいいと思っていたら、間違っていたみたいです。
正解は↓

belongs_to :user, optional: true

optional: true

belongs_toの外部キーのnilを許可するというものだそうです。
外部キーに値がセットされていない場合はバリデーションが作動するせいでエラーが起きていたそうです。
optional: trueを設定しておくと、外部キーがnilであってもDBに保存できるようになります。

 

まとめ

これっぽっちのエラーでもまた一つ覚えることができたのはいい収穫。
新たなエラーに出くわしても挫けず頑張りたいと思った今日この頃。

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

The Rails Doctrine(日本語訳)

(訳者注: 原文は https://rubyonrails.org/doctrine/ です。しばらく寝かして問題なさそうであれば本家に投げようかと思っています。おかしいところがあればコメント・編集リクエストをお待ちしております。)

The Rails Doctrine

By David Heinemeier Hansson in January, 2016

Ruby on Railsの驚異的な台頭は、斬新な技術とタイミングによるところが少なからずあります。しかし、技術的な優位性は時間の経過とともに失われていきますし、タイミングの良さだけでは長期にわたってムーブメントを維持できません。そのため、Railsがどのようにして的確さを維持してきたのかだけでなく、どのようにしてそのインパクトとコミュニティを成長させてきたのかについて、より広範な説明が求められています。私が提唱するのは、永続的な実現要因は、今も昔も物議を醸しているドクトリン(信条)にあるということです。

このドクトリンは過去10年の間に進化してきましたが、その最も強力な柱のほとんどは開発当初からのものでもあります。私はこれらの考え方の根本的なオリジナリティを主張していません。Railsの主な成果は、プログラミングとプログラマーの本質についての異端的な考えを手広く集めたものを中心に、強力な部族を団結させ、育成したことです。

以上のことを踏まえて、Railsのドクトリンとして最も重要な9つの柱を以下に紹介します。

  • プログラマーの幸せへの最適化 Optimize for programmer happiness
  • 設定より規約 Convention over Configuration
  • メニューはおまかせ The menu is omakase
  • 唯一のパラダイムはない No one paradigm
  • 美しいコードの称賛 Exalt beautiful code
  • 鋭利なナイフの提供 Provide sharp knives
  • 価値の統合されたシステム Value integrated systems
  • 安定性よりも進歩 Progress over stability
  • 大きなテントの押し上げ Push up a big tent

プログラマーの幸せへの最適化 Optimize for programmer happiness

RubyなしではRailsは存在しません。そのため、最初の信条の柱がRubyを作る動機の核心から直接導き出されたものであることは当然のことです。

Rubyの独特な特徴は、プログラマーの幸せを台座に据えたことでした。それ以前のプログラミング言語やエコシステムを牽引してきた他の多くの競合する有効な懸念事項よりも優先して、です。

Pythonが「何かをするための方法は一つしかない」を長所として掲げるのに対し、Rubyは表現力と繊細さを追求してきました。Javaがプログラマーを自分たちから強制的に保護することを主張していたのに対し、Rubyは初学者向けキットに鋭利なナイフ一式を入れていました。Smalltalkがメッセージパッシングの純粋さを追求していたのに対し、Rubyはキーワードやコンストラクトを貪欲に蓄積していきました。

Rubyが他の言語と異なっていたのは、Rubyが異なるものを大切にしていたからです。そして、それらの多くはプログラマーの幸せへの憧れにつながるものでした。Rubyのポリシーは、他の多くのプログラミング環境だけでなく、プログラマーとは何か、どのように行動すべきかという主流の認識とも対立していました。

Rubyはプログラマーの感情を認識するだけでなく、それを受け入れ、高めようとしました。それが不完全であろうと、気まぐれであろうと、喜びであろうと。Matzは、驚くほど複雑な実装のハードルを飛び越えて、機械が人間の共犯者を見て笑っているように見えたり、お世辞を言っているように見えたりするようにしました。Rubyは、私たちの心の目にはシンプルで明確で美しく見えるものが、実際には床下配線のアクロバティックな混乱であるような錯覚に満ちています。これらの選択は容易に達成されたものではありませんでした(この魔法のオルゴールをリバースエンジニアリングしようとしたことについてはJRubyのクルーに尋ねてみましょう!)。それこそが、彼らが賞賛に値する理由です。

私のRubyへの恋心を決定づけたのは、プログラミングとプログラマーのための新たなビジョンへの真摯な姿勢でした。それは単に使いやすさだけではありません。ブロックの美学だけでもありません。また一つの技術的な成果でもありません。それはビジョンでした。カウンターカルチャーでした。既存のプロのプログラミングの型にはまってしまった人たちが、同じような心を持った人たちと仲間になるための場所でした。

私は以前、このRubyの発見を、自分の脳に完璧にフィットする魔法の手袋を見つけたと表現したことがあります。それまで自分に合った手袋として期待していたあらゆる想像を上回るものでした。しかし、それ以上のものでした。「プログラムが必要だったからプログラミングする」から、「知的なエクササイズと表現の手段としてのプログラミングが気に入ったからプログラミングする」へと、私自身の変化を示す出来事だったのです。それは、フローの源泉を見つけて、それを自由にオンにすることができるようになったことでした。チクセントミハイの仕事をよく知っている人にとっては、このインパクトは言い過ぎではないでしょう。

Rubyは私を変え、私のライフワークへの道を切り開いてくれたと言っても過言ではありません。それほど深い啓示でした。それは私にMatzの創造物に奉仕して宣教活動をするという召命を吹き込みました。この深遠な創造とその恩恵を広める手助けをするために。

皆さんの多くが信じられずに首を振っているのは想像できます。私はあなたを責めるつもりはありません。もし私がまだ「プログラミングはただの道具」というパラダイムの下で生きていたときに、誰かが上記の経験を説明してくれたら、私も首を横に振っていたでしょう。そして、宗教的な言葉を使った大げさな表現には笑っていたでしょう。しかし、これが真実であるためには、一部の人やほとんどの人が不快に思うようなことであっても、正直でなければなりません。

それはさておき、このことはRailsにとって何を意味し、この原則はどのように進化の指針となり続けるのでしょうか? その問いに答えるためには、初期の頃にRubyを説明するのによく使われていた別の原則、「驚き最小の原則」を見てみるのが有益だと思います。Rubyはあなたが期待するように振る舞うべきです。これはPythonとの対比で簡単に説明できます。

    $ irb
    irb(main):001:0> exit
    $ irb
    irb(main):001:0> quit

    $ python
    >>> exit
    Use exit() or Ctrl-D (i.e. EOF) to exit

Ruby は exitquit の両方を受け入れ、プログラマが対話型コンソールを終了したいという明らかな欲求に対応しています。一方、Python は、(エラーメッセージを表示しているため) 何を意味しているのか明らかに分かっているにもかかわらず、要求されたことを適切に行う方法をプログラマに指示します。これは、小さいとはいえ、「驚き最小の原則」のかなり明確な例です。

「驚き最小の原則」が Ruby コミュニティの支持を得られなくなった理由は、この原則が本質的に主観的だからです。誰にとって驚きが最小なのでしょうか? まあ、Matzにとってですね。そして、彼と同じように驚いている人たちです。Ruby コミュニティが成長し、Matz とは異なることに驚く人の割合が増えていくと、メーリングリストでは、この原則は実りのない自転車小屋の議論の源になっていきました。そこで、XさんがYという挙動に驚いたかどうかについて、どこにも行かないような議論をさらに招くことを避けるために、この原則は背景に消えていったのです。

では、繰り返しになりますが、これがRailsと何の関係があるのでしょうか。まあ、Railsは「(Matzにとっての)驚き最小の原則」と似たような原理で設計されています。「(DHHにとっての)微笑み最大の原則」という原則は、まさにブリキに書いてある通りです。私をもっともっと広く笑顔にしてくれるようなものに細心の注意を払って設計されたAPIです。このように書き出すと、ほとんどコミカルなナルシストのように聞こえますし、私でさえ、その第一印象に反論するのは難しいと思います。

しかし、RubyやRailsのようなものを作るということは、少なくともその最初の段階では、深く自己愛的な献身の賜物なのです。どちらのプロジェクトも、一人のクリエイターの頭の中から生まれたものです。しかし、おそらく私はここでMatzに自分の動機を投影しているのかもしれないので、私の宣言の範囲を私が知っていることに絞ってみます。私は私自身のためにRailsを作りました。まず第一に、私を笑顔にするために。Railsの実用性は、私の人生をより楽しいものとするためにRailsに持たせた、その能力に従属するものでした。Web情報システムへの要求やリクエストをこなす日々の労苦を豊かにするために。

Matzのように、私も時折、自分の主義主張を貫くためにふざけたことをしました。その一例が、PersonクラスをPeopleテーブルに、AnalysisクラスをAnalysesに、そしてCommentクラスをCommentsにマッピングするのに十分な英語のパターンと不規則性を理解しているクラスであるInflectorです。この動作は現在ではRailsの疑う余地のない要素として受け入れられていますが、まだドクトリンとその重要性がまとまっていなかった初期の頃には、論争の火種が猛威を振るっていました。

実装の労力はそれほどかからなかったものの、ほぼ同じくらいの騒動を引き起こした別の例を紹介します。配列#secondから#fifthまで(および#forty_twoは荒らし対策として)です。これらのエイリアスアクセサは、Array#[1]Array#[2](及びArray[41])のように書くことができるものの肥大化(及び文明の終焉に近いもの)として非難され、たいへん声高な支援者に深く不快感を与えました。

しかし、この二つの決定は今でも私を笑わせてくれます。テストケースやコンソールでpeople.thirdを書く楽しさを満喫しています。もちろん、この楽しさは論理的ではありません。効率的ではありません。病的かもしれません。しかし、それは私を笑顔にし続け、こうして原則を満たし、私の人生を豊かにし、12年間のサービスの後にRailsに関わり続けていることを正当化するのに役立っています。

パフォーマンスの最適化とは異なり、幸福度の最適化を測定するのは困難です。そのため、ほとんど本質的に非科学的な努力になっています。ある人にとっては重要性が低く、イライラさせてしまっているかもしれません。プログラマは、測定可能なものを議論し、征服するように教えられています。明確な結論があり、AがBよりも優れていると断言できるものを、です。

とはいえ、幸せの追求はミクロレベルでは測定が困難ですが、マクロレベルで観察するとはるかに明確になります。Ruby on Railsコミュニティには、まさにこの追求のためにここにいる人たちがたくさんいます。彼らは、より良い、より充実した仕事人生を自慢しています。このような感情の集合体の中で、勝利は明らかなのです。

というわけで、結論です。幸せのために最適化することは、おそらくRuby on Railsの最も形成的な鍵なのです。そして今後もそうあり続けるでしょう。

設定より規約 Convention over Configuration

Railsの初期の生産性のモットーの1つは、次のようなものでした。「あなたは美しくてユニークな雪の結晶ではない」というものです。空虚な個性を手放すことで、平凡な意思決定の煩わしさを跳ね除け、本当に重要な分野ではより速く進歩することができると仮定しています。

データベースの主キーがどのような形式で記述されているかなんて誰が気にするでしょうか? それが "id"、"postId"、"posts_id"、または "pid "であるかどうかは本当に重要でしょうか? これは繰り返し審議する価値のある決定なのでしょうか? いいえ、そうではありません。

Railsのミッションの一部は、Web用の情報システムを作成する開発者が直面する、分厚くて増え続けている反復的な意思決定のジャングルに鉈を振るうことです。このような意思決定は何千もありますが、本来は一度で済むことですし、誰かが代わりにやってくれるのであれば、それに越したことはありません。

設定を慣習に移行することで、私たちは審議から解放されるだけでなく、より深い抽象化を成長させるための青々としたフィールドを提供してくれます。
Person クラスの people テーブルへのマッピングに頼ることができれば、同じ屈折規則を使って has_many :people として宣言されたアソシエーションを Person クラスを探すためにマッピングすることができます。優れた規約の力は、幅広い使用範囲で利益を得ることができるということです。

しかし、エキスパートの生産性向上だけでなく、初心者の参入障壁を下げることもできます。Railsには、初心者が知らなくても、知らなくても恩恵を受けられるような規約がたくさんあります。すべてのものがなぜそのようになっているのかを知らなくても、素晴らしいアプリケーションを作ることは可能です。

フレームワークが分厚い教科書に過ぎず、新しいアプリケーションが白紙の紙切れに過ぎないのであれば、そんなことは不可能です。どこからどのように始めればいいのかを把握するのには、膨大な努力が必要です。始めるための戦いの半分は、引っ張るべきスレッドを見つけることです。

すべてのピースがどのように組み合わされているのかを理解している場合も、同じことが言えます。変更する際、変更後の状態がいつでも明確であれば、私たちは、その前のすべてのアプリケーションと同じか、非常に似ているアプリケーションの多くの部分を、駆け足で通過することができます。すべてのもののための場所、すべてが備わっている場所。制約は、最も有能な心をも解放します。

しかし、何事もそうですが、慣習の力には危険がないわけではありません。Railsがこれだけ多くのことをあまりにも取るに足らないようにみせていると、アプリケーションのあらゆる側面は既成のテンプレートで作れるのではないかと考えがちです。しかし、構築する価値のあるほとんどのアプリケーションには、何らかの方法でユニークな要素があります。それは5%や1%に過ぎないかもしれませんが、そこに存在してします。

難しいのは、いつ慣習から逸脱するかを把握することです。逸脱した特殊性は、いつ、遠出を正当化するのに十分なほど重大なものなのでしょうか? 私は、美しくユニークな雪片になりたいという衝動のほとんどは、十分に考慮されておらず、Railsから外れて行くことのコストが過小評価されていると主張しますが、慎重に検討するまでもないこともあるでしょう。

メニューは「おまかせ」 The menu is omakase

何が美味しいか分からないのに、レストランで何を注文すればいいのか、どうやってわかるのでしょうか? そうですね、シェフに選んでもらえば、何が「美味しいもの」かわからないうちから、たぶん「美味しいもの」にありつけます。それが「おまかせ」です。料理の達人でなくても、暗中模索の運任せでなくても、美味しいものを食べるための方法なのです。

プログラミングにとって、他の人にスタックの組み立てを任せるというこのプラクティスの利点は、「Convention over Configuration」から得られるものと似ていますが、さらに高いレベルでのものです。CoCが個々のフレームワークをどのように使うのがベストなのかということで占められているのに対し、おまかせはどのフレームワークをどのように組み合わせて使うのかということに関心があります。

これは、利用可能なツールを個々の選択肢として提示し、個々のプログラマに決定する特権(と負担)を与えるという崇高なプログラミングの伝統と対立しています。

「仕事に最適なツールを使え」という言葉を聞いたことがあるでしょうし、うなずいたこともあるでしょう。これは議論の余地がないほど基本的なことのように聞こえますが、「最高のツール」を選べるかどうかは、自信を持って「最高」を決められるほどの根拠にかかっています。これは見た目よりもずっと難しいことです。

これは、レストランでのディナーの問題に似ています。そして、8品の食事の各コースを選ぶように、個々のライブラリやフレームワークを選ぶことは、独立して行う仕事ではありません。どちらの場合も、目的はディナーやシステム全体を考慮することです。

そこでRailsでは、道具箱の中から好きなツールをそれぞれ選ぶというプログラマーの個人的な特権である善の一つを減らすことにしました。より大きな善、「何にでも使えるより良いツールボックス」のためにです。その結果、多くの利益を得ることができました:

  1. 数は安全性を生む: ほとんどの人が同じデフォルトの方法でRailsを使っているとき、私たちは同じ経験を共有しています。このような共通の基盤があると、人に教えたり助けたりするのが格段に容易になります。アプローチについての議論の基礎を築くことができます。昨日の夜7時にみんなで同じ番組を見たので、次の日にはその話をすることができます。それはより強いコミュニティの感覚を育みます。

  2. みんなで同じ基本ツールボックスを完成させる: フルスタックフレームワークであるRailsには多くの動作部品があり、それらがどのように連携して動作するかは、単体で何をするかと同じくらい重要です。ソフトウェアの痛みの多くは、個々のコンポーネントではなく、その相互作用から生じます。同じように構成され、同じように失敗するコンポーネントの共通の痛みを和らげることに全員で取り組めば、痛みを経験することは少なくなります。

  3. 置き換えることもできるが、置き換えなくともよい: Railsはおまかせスタックですが、特定のフレームワークやライブラリを他の物で置き換えることはできます。ただ、それは必須ではありません。つまり、特別な違いを好むような明確で個人的なパレットを開発するまで、そのような決定を遅らせることができるということです。

なぜなら、Railsに入ってきた、あるいはRailsにとどまっている、最も経験豊富で熟練したプログラマーでさえ、メニューのすべてに反対しているわけではないはずだからです(もしそうであれば、おそらくRailsに留まっていなかったでしょう。) そのため、彼らは真摯に代替品の選別を行い、その後、他の人々と同様に残りの部分を楽しむようにしています。

唯一のパラダイムはない No one paradigm

一つの中心的なアイデアを掲げて、そこから論理的帰結としてアーキテクチャの基盤を導こうとする強く熱心な主張があります。このような規律には純粋さがあるため、プログラマーが自然とこの明るい光に惹かれるのは明らかです。

Railsはそうではありません。一枚の完璧な反物ではありません。キルトです。多くの異なるアイデアやパラダイムの複合体です。その多くのものは、単独で一つ一つ対比させれば、通常は対立していると見られるようなものかもしれません。でも、私たちがやろうとしているのはそのような対立ではありません。それは、唯一の勝者が宣言されなければならない、優れたアイデアの選手権ではないのです。

Rails MVCの中のビューを構築しているテンプレートを見てみましょう。デフォルトでは、これらのテンプレートからコードを抽出されたヘルパーは、すべてを大きな鍋に突っ込まれた関数にすぎません! 名前空間すら一つにまとめられています。なんと衝撃的で恐ろしいことでしょう、昔ながらのPHPのスープのようなものです!

しかし、ビューテンプレートの多くの抽象化がそうであるように、相互作用をほとんど必要としない個々の関数を提示するという点では、PHPは正しかったと私は考えています。そして、この目的のためには、単一の名前空間、メソッドの大きな鍋は、合理的な選択であるだけでなく、優れた選択でもあります。

だからといって、ビューを構築する際に、よりオブジェクト指向的なものに手を伸ばしたくなることもないわけではありません。プレゼンターのコンセプトは、相互に依存している多くのメソッドとその下にあるデータをラップすることで、依存関係によって酸っぱくなってしまったメソッドのスープの完璧な解毒剤になることがあります。しかし、これは一般的に適合するものではなく、稀に適合するものであることが示されています。

一方、私たちは通常、MVCにレイヤー化されたケーキのうちのモデルについては、オブジェクト指向の素晴らしさの最高の砦として扱っています。オブジェクトにちょうど良い名前を見つけ、凝集度を高め、結合度を減らすことがドメインモデリングの面白さです。ビューとは全く違うレイヤーなので、私たちは別のアプローチをとっています。

しかし、ここでも私たちはシングルパラダイムのドグマを支持していません。Railsのconcerns、Rubyのミックスインの特殊版は、個々のモデルに非常に広い拡張を与えるためによく使われています。これは、関係するメソッドが相互作用するデータやストレージに直接アクセスできるようにすることで、アクティブレコードパターンによく合っています。

Active Recordフレームワークの基礎でさえも、一部の純粋主義者を怒らせています。私たちは、データベースと直接連動するために必要なロジックを、ビジネスドメインのロジックと混ぜてしまっています。なんという境界の侵犯! その通りですね。これは、ドメインモデルの状態を維持するために、事実上常に何らかのデータベースとつながっているウェブアプリを実現するための実用的な方法であることが明らかになったためです。

思想的に柔軟であることが、Railsがこのような幅広い問題に取り組むことを可能にしています。ほとんどの個別のパラダイムは、問題空間のある範囲内では非常にうまく機能しますが、自然な範囲を超えて適用されると、厄介なものになったり、硬直化したりします。たくさんの重複するパラダイムを適用することで、私たちは側面をカバーし、後方をガードします。最終的にできるフレームワークは、個々のパラダイムが許容していたであろうよりもはるかに強力で、より有能なものになります。

さて、プログラミングについての多くのパラダイムとこのような多元的な関係を持つことの代償は、概念的なオーバーヘッドです。オブジェクト指向プログラミングを知っているだけではRailsを楽しむことはできません。手続き的な経験や関数的な経験も十分に積んでいる方が望ましいのです。

これはRailsの多くのサブ言語にも当てはまります。私たちは、例えばビューのためのJavaScriptや、時折複雑なクエリのためのSQLを学ばなければならないことから、あなたを遮ろうとはしていません。少なくとも、可能性のピークに達するまでは。

学習の負担を軽減する方法としては、フレームワークのあらゆる側面を理解する前に、簡単に始められるようにすることが挙げられます。怒涛のハローワールドが用意されているのはこのためです。あなたのためのテーブルはすでに準備されていて、前菜が並べられています。

本当の価値のあるものを早い段階で提供することで、Railsの実践者に早くレベルアップしてもらおうという考えです。彼らの学習の旅を障害ではなく、楽しめるものとして受け止めてください。

美しいコードの称賛 Exalt beautiful code

私たちは、コンピュータや他のプログラマーに理解されるためだけにコードを書くのではなく、あたたかな美しさの光を浴びるためにコードを書きます。美的に優れたコードはそれ自体が価値であり、精力的に追求されるべきものです。だからといって、美しいコードが常に他の懸念事項に勝るというわけではありませんが、優先順位のテーブルに完全に座るべきです。

では、美しいコードとは何でしょうか? Rubyではたいていの場合、ネイティブのRubyイディオムと各ドメイン固有言語の力との間のどこかにあります。これは曖昧な基準ですが、取り組んでみる価値は十分にあります。

以下はActive Recordの簡単な例です。

  class Project < ApplicationRecord
    belongs_to :account
    has_many :participants, class_name: 'Person'
    validates_presence_of :name
  end

これは DSL のように見えますが、実際にはただのクラス定義で、シンボルとオプションを取る 3 つのクラスメソッドを呼び出しています。派手なものは何もありません。しかし、確かに見事です。確かにシンプルです。この数少ない宣言から、膨大な量のパワーと柔軟性を得ることができます。

美しさの一部は、これらの呼び出しが以前の原則を尊重していることから来ています。 belongs_to :account を呼び出すとき、外部キーは account_id と呼ばれ、それがプロジェクトのテーブルにあることを前提としています。participantsアソシエーションの役割の指定としてclass_namePersonを必要とする場合、そのクラス名の定義を必要とするだけです。そこから我々は、再び、外部キーと他の設定ポイントを導出します。

データベース移行システムからの別の例を見てみましょう。

    class CreateAccounts < ActiveRecord::Migration
      def change
        create_table :accounts do |t|
          t.integer :queenbee_id
          t.timestamps
        end
      end
    end

これがフレームワークの力の本質です。プログラマは特定の規約に従ってクラスを宣言します。例えば、#changeを実装したActiveRecord::Migrationのサブクラスのように、フレームワークはその周りのすべての下回りをつなげ、これが呼び出すべきメソッドであることを知ることができます。

こうして、プログラマの書くべきコードは非常に少なくなります。マイグレーションの場合、rails db:migrateを呼び出してデータベースをアップグレードして新しいテーブルを追加するだけでなく、別の呼び出しでこのテーブルを削除することもできます。これは、プログラマーが同様のことを実現するために、自分自身で呼び出すライブラリを使ってワークフローをつなぎ合わせるのとは大きく異なります。

しかし、美しいコードはもっと些細な場合もあります。何かを可能な限り短くしたり、強力にしたりすることよりも、宣言のリズムが流れるようにすることの方が重要なのです。

以下の2つの文も同じことを行います。

    if people.include? person
      

    if person.in? people

しかし、フローとフォーカスは微妙に異なっています。最初の文では、焦点はコレクションにあります。それが主語です。2つ目の文では、主語は明らかに人です。2つのステートメントの間には長さの差はあまりありませんが、条件がその人についてのものである場所で使われると、2つ目のステートメントの方がはるかに美しく、私を笑顔にしてくれる可能性が高いと主張します。

鋭利なナイフの提供 Provide sharp knives

Rubyは機能の引き出しの中に鋭いナイフをたくさん含んでいます。それは偶然そうなったのではなく、そのように設計されたためです。最も有名なのはモンキーパッチング、既存のクラスやメソッドを変更するものです。

この力は、普通の人間であるプログラマーには扱えないと揶揄されてきました。より制限の多い環境から来た人たちは、言語の利用者にこういった機能を与えるような絶大な信頼は、Rubyを破滅させる様々な災難を引き起こすことを想像していました。

もし何でも変更できるのであれば、String#capitalize を上書きして "something bold".capitalize"Something bold" ではなく "Something Bold" を返すようにすることを止める仕組みがあるでしょうか? このような変更はローカルアプリケーションではうまくいくかもしれませんが、元の実装に依存する様々な補助コードを壊してしまうことになります。

「何もない」、というのが答えです。あなたが鋭いナイフを使うのを理性によって止めさせるための、プログラム的な仕掛けはRubyにはありません。私たちはそのような良識を、慣習によって、ナッジによって、そして教育によって強制しています。キッチンで鋭いナイフを使うことを禁止し、トマトを切るのにもスプーンを使うことを強制したりするのではありません。

なぜなら、モンキーパッチの裏側にあるのは、2.day.previous(現在から2日前の日付を返す)のような驚異的な偉業を成し遂げる力だからです。今、あなたはそれが割りに合わない取引と思うかもしれません。プログラマーがString#capitalizeを上書きするのを防ぐためなら2.days.previousを失うことを望むかもしれません。もしあなたがそのようなスタンスであれば、あなたはRubyには向いていないかもしれません。

しかし、コアクラスやメソッドを変更する力が言語としてのRubyを破滅させたと主張するのは難しいでしょう -- ある程度のセキュリティのためであればそのような自由を手放してしまう人でさえも。それどころか、この言語が繁栄したのは、プログラマーの役割についての異なる急進的な視点を提供したからに他なりません。鋭利なナイフを渡しても信頼するということです。

そして、単に信頼するだけでなく、そのような有能なツールの使い方を教えることができたのです。ほとんどのプログラマーは、指を切らずに鋭いナイフを使いこなすことができる、より優れたプログラマーになりたいと思っているだろうと仮定することで、職業全体を向上させることができるということです。それは信じられないほど野心的な考えであり、他のプログラマーについての多くのプログラマーの直感に反しています。

なぜなら、鋭利なナイフの価値が争われるとき、それは常に他のプログラマーについてのものだからです。私はまだ一人のプログラマが手を挙げて「自分のこの力は信用できないから、この力を奪ってくれ!」と言うのを聞いたことがありません。それはいつも「他のプログラマーがこれを悪用すると思う」というものです。その父権主義的な路線は私には魅力を感じたことがありません。

そこでRailsの話になります。フレームワークで提供されるナイフは、言語で提供されるナイフほど鋭くはありませんが、それでも切りたくてたまらない人はたくさんいます。このようなツールをキットの一部として提供することに謝罪はしません。実際には、私たちは、仲間のプログラマーの願望に十分な信頼を持っていることを祝うべきなのです。

Railsの多くの機能は、時間の経過とともに「自由度が高すぎる」として議論されてきました。しかし、現在流行している例として、concerns機能があります。これはRubyの組み込み機能であるモジュールに加えたシンタックスシュガーの薄い層で、1つのクラスで複数の、関連しながらも独立して理解される懸念事項(concernsの名前の由来です)をカプセル化できるように設計されています。

concernsは、プログラマーがオブジェクトを肥大化させてしまいかねない、ごちゃごちゃしたものを詰め込むための新しい引き出しのセットを提供してしまっている、という批判があります。そして、それは本当です。concernsは確かにそのように使うことができます。

しかし、concernsのような機能を提供しないことは壮大な誤りです。この力を温和な手で使っても、概念の部分的分離を巧みに行うことができれば、プログラマーは素晴らしいアーキテクチャへの道を歩むことになるでしょう。もしあなたが過剰なconcernsからキッチンシンクを守ることを信頼できないのであれば、まばゆい優雅の指針を手に入れることはできないでしょう。

鋭いナイフを振り回すことを学んでいないプログラマーは、まだメレンゲを作ることはできません。ここでは最適な言葉として「まだ」と言っておきます。私は、すべてのプログラマには、権利とまではいかないまでも、完全に有能なRubyとRailsのプログラマになる道があると信じています。そして、有能というのは、コンテキストに応じて、引き出しの中のさまざまな、時に危険なツールをいつ・どのように使うべきかを知っている、という意味です。

だからといって、彼らがそこにたどり着くのを助ける責任を放棄しているわけではありません。言語とフレームワークは、誰もが専門家になれるように手助けし、導く忍耐強いチューターでなければなりません。一方で、そこに至る唯一の信頼性の高いコースは間違いだらけの場所--間違って使用されるツール、血、汗、そしておそらく涙--をうまく通過することを認識しています。他の方法は単にありません。

Ruby on Railsは、シェフになりたいやシェフになった人のための環境です。最初は料理をすることから始めるかもしれませんが、キッチンを運営するまでの道のりを歩んでいくことができます。その旅の一環として、業界最高のツールを使わせるほどあなたを信頼できないと誰にも言わせないようにしましょう。

価値の統合されたシステム Value integrated systems

Railsは様々な文脈で利用できますが、その初恋は統合システムーー壮大なモノリス! すなわち問題全体に対処できるシステム全体ーーを作ることです。つまり、Railsはライブアップデートを行うために必要なフロントエンドのJavaScriptから、本番でデータベースをどのようにバージョン間で移行するかまで、すべてに関わっているということです。

これまで議論してきたように、これは非常に広い範囲ですが、一人の人間が理解するのが現実的ではないというほどではありません。Railsは特に、ジェネラリストの個人がこれらの完全なシステムを作れるようにすることを目指しています。その目的は、専門家を小さなニッチ分野に隔離し、永続的な価値を持つものを構築するためにそのような完全なチームを必要とすることではありません。

このように個人に力を与えることに焦点を当てているのが、統合されたシステムなのです。統合システムでは、たくさんの不必要な抽象化を削減し、レイヤー間の重複を減らし(サーバーとクライアントの両方のテンプレートのような)、何よりも、どうしても必要になるまでは、システムを分散することを避けることができます。

システム開発における複雑さの多くは、AとBの間の呼び出し方法を制限する新しい境界を導入することに由来します。オブジェクト間のメソッド呼び出しは、マイクロサービス間のリモートプロシージャコールよりもはるかに単純です。分散の巣穴に足を踏み入れた人たちを待ち受けるのは、失敗状態や待ち時間の問題、依存関係の更新スケジュールなど、まったく新しい世界の苦痛です。

このディストリビューションが必要な場合も時にはあります。他の人がHTTPで呼び出せるようなAPIをWebアプリケーションに作りたいのであれば、それを我慢してこれらの問題の多くに対処しなければなりません(リクエストをアウトバウンドで送るよりもインバウンドで処理する方がはるかに簡単ですが、あなたのダウンタイムは他の誰かの障害状態です!)。しかし、これは少なくとも、あなた自身の個人的な開発経験に与えられた、限定的なダメージです。

もっとひどいことは、システムが早まって分散化されてしまい、サービスや、あるいはマイクロサービスに分断されてしまうことです。このような状況は、モダンなインターネットアプリケーションを作りたければ、何度もシステムを構築しなければならないという単なる誤解から始まっていることも多くあります。サーバ側で一度、JavaScriptクライアント側で一度、ネイティブモバイルアプリケーションのそれぞれで一度、などです。これは自然の法則に則ったことではなく、そうである必要はありません。

アプリケーション全体の大きな塊を複数のアプリやアクセスにまたがって共有することは完全に可能です。ネイティブモバイルアプリに埋め込まれたものと同じコントローラとビューのしくみをデスクトップウェブで使用することも同様です。壮大で見事なモノリスこと統合システムの中に、可能な限りのことを集中させることができるのです。

これはすべて、スピード、ユーザーエクスペリエンスなど、開発者を早まった分散化に誘導しがちな点については何も妥協することなく実現されています。

これこそが、私たちが求める「すべてのもののほとんどを備え持つ」ことなのです。使いやすく理解しやすい統合された単一のシステムによる、個別にチューニングされた分散アプリケーションの力です。

安定性よりも進歩 Progress over stability

Railsのように10年以上前から存在しているシステムでは、自然と硬直化に向かう傾向があります。どんな変更でも、過去の挙動に依存していた誰かにとって、問題になる可能性はいくらでもありえます。そして、実際にそれが当てはまる当人にとっては公平な理由です。

しかし、あまり保守的な声に耳を傾けすぎてしまうと、その反対側に何があるかが見えなくなってしまいます。私たちは、進化と成長のために、時にはあえて壊したり、やり方を変えたりしなければなりません。この進化こそが、これからの(数?)十年にわたってRailsが生存と繁栄に適した状態を維持することになるのです。

これは論理的には理解しやすいですが、これを受け入れて実践するのははるかに難しいことです。特に、Railsのメジャーバージョンでの下位互換性のない変更が原因で、自分のアプリケーションが壊れてしまった場合はなおさらです。そのようなときこそ、私たちは安定性よりも進歩を大切にし、壊れたものをデバッグして、それを解明し、時代とともに歩んでいく強さを与えてくれる、この価値を覚えておく必要があるのです。

これは、必要のない、あるいは過剰なダメージをいたずらに与えてもよいということではありません。2.xから3への大規模なRailsの移行は、その当時の関係者の多くに傷跡として今でも残っています。実に大変なことでした。深刻な大変動により、3へ移行できず2.xに長い間取り残されてしまった多くの人たちは、納得できないほど酸っぱくなっていました。しかし、大局的には、やはり実行する価値のあることでした。

これは、私たちがこれからも続けていかなければならない厳しい交渉です。今日行った変更によって、5年後のRailsはより良い状態になっているでしょうか? ジョブキューやWebSocketsのような別の問題領域を採用したほうが、数年後のRailsの利益になるのでしょうか? もしそうだとしたら、それを取り入れて作業しようではありませんか。

この作業はRails自体だけでなく、より大きなRubyコミュニティでも行われる必要があります。Railsは、Rubyコミュニティの人々がより新しいバージョンを採用するように促すことで、Rubyの進歩を支援する最前線に立つべきです。

これまでのところ、私たちはこの点では非常によくやっています。私が始めたときから、Ruby 1.6、1.8、1.9、2.0、2.1、2.2、2.3、2.4、2.5、そして現在は2.6へと移行してきました。この間、多くの大きな変更がありましたが、RailsはRubyの後ろ盾となり、誰もがより速くプログラムを利用できるようにするために存在していました。これは、RailsがRubyの主要な普及者としての特権と義務の一部です。

これは補助ツールチェーンにも当てはまります。Bundlerはかつて物議をかもしたアイデアでしたが、未来を共有するための礎となるものだとRailsが主張したことで、今日では当たり前のように使われるようになりました。アセットパイプラインや、永続的なコマンドプロセスであるSpringなどについても同じことが言えます。これら3つはいずれも、成長の痛みを経験したか、あるいは今も経験していますが、長期的に見て価値があることが明らかであるため、私たちはそれを押し通してきました。

進歩とは、最終的にはほとんどの場合、変化を推し進めようとする人々と、その意欲にかかっているのです。Rails CoreRails Committersのようなグループに終身の席がないのはこのためです。どちらのグループも、フレームワークの進歩に積極的に取り組んでいる人たちのためのものです。ある人にとっては、そのような進歩への関与はわずか数年で終わり、私たちは彼らのサービスに永遠に感謝するでしょうが、他の人にとっては何十年も続くかもしれません。

同様に、このことはコミュニティの新しいメンバーを歓迎し、奨励し続けることが非常に重要な理由でもあります。私たちは、より良い進歩を遂げるために、新鮮な血と新鮮なアイデアを必要としています。

大きな傘を広げる Push up a big tent

多くの物議を醸すアイデアを持つRailsは、すべての人に常にすべての信条を完全に尊重することを求めれば、すぐに偏った思想の隠者による孤立した集団になる可能性があります。だから私たちはそうしません!

私たちには意見の相違が必要です。方言が必要です。思想や人物の多様性が必要です。このアイデアのるつぼの中にこそ最高の共有物があるのです。多くの人々が、コードや考察に基づく議論を通じて、協力しています。

このように、このドクトリンでは理想化された形を説明していますが、日常の現実はもっと繊細な(そして興味深い)ものです。Railsがこのような大規模なコミュニティを一つの傘のもとで支えることができるのは、リトマス試験がない(あったとしてもほとんどない)からです。

私がしばしば重大な不満を表明してきたテスト用DSLであるRSpecの継続的な成功は、完璧な証拠です。私はなぜこれではいけないと思うのか、顔を真っ赤にしてわめき散らすことができますが、にも関わらずRSpecは依然として花を咲かせ、繁栄することができます。その点の方が遥かに重要なのです

APIとしてのRailsの到来についても同じことが言えます。私の個人的な焦点とこだわりは、ビューを含む統合システムにありますが、クライアントとサーバを分割したいと考えている人にも、Railsがうまくいく余地があることは間違いありません。私たちはこれを二次的なミッションとして共存できる限り受け入れるべきであり、それは確実に可能だと信じています。

大きな傘を広げるということは、すべての人に万能であろうとすることではありません。すべての人を歓迎し、自分の飲み物を持ってくることを許可するということです。他の人にも参加してもらうことで、私たちの魂や価値観を失う必要はありませんし、おいしい飲み物の新しい混ぜ方も学べるかもしれません。

これはタダではできません。歓迎するための努力が必要です。特に、あなたの目標が、すでにコミュニティの一部になっている人たちと同じような人たちをより多く集めることだけではないのであれば、なおさらです。参入障壁を下げることは、常に真剣に取り組むべき仕事です。

ドキュメントのスペルミスの修正を始めた隣の人が、次の素晴らしい機能を実装することになるかどうかは誰にもわかりません。しかし、あなたが微笑んで、どんな小さな貢献にも感謝を示すことで、モチベーションを高めさせ、その可能性も生まれるかもしれません。

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

flashの使い方

フラッシュ

画面に一度だけ表示されるメッセージで別のページに遷移や、時間がたつと消えるメッセージ

フラッシュを利用するには特殊な変数 flash を利用する
flash[:notice]に文字を設定すると、<%= flash[:notice] %> で表示できる

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

renderメソッドとredirect_toの使い方

renderメソッド

別のアクションを経由せずに直接ビューを表示できる
render("フォルダ名/ファイル名")
のように表示したいビューを指定する

コントローラー
render("ccc/ddd") ビューファイルを指定

ccc/ddd.html.erb

renderメソッドを使った場合は、そのアクション内で定義した@変数をビューでそのまま使うことができる

コントローラー
@fefefe = test
render("ccc/ddd")
-+-+-+-+-+-+-+
ccc/ddd.html.erb
<%= @fefefe =>

redirect_toメソッド

redirect_toはURLを指定して
redirect_to(URL)
のように書く

コントローラー
redirect_to("/aaa/bbb")

ルーター
get "aaa/bbb" => "aaa#bbb"

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

バリデーションの使い方

バリデーションはモデルで設定する
validates :content, {presence: true, length:{maximum: 10}}
content : バリデーションをかけるカラム

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

バリデーションやエラーメッセージ

バリデーションはモデルで設定する
validates :content, {presence: true, length:{maximum: 10}}
content : バリデーションをかけるカラム

バリデーションでfefefe.saveなどが失敗した場合は
fefefe.errors.full_messages
の中にバリデーションで引っかかったエラーの内容が自動的にはいる

<% @post.errors.full_messages.each do |message| %>
     <%= message %>
<% end %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む