【初心者向け】アソシエーションって何?というあなたへ【rails】
webサービスを作っているとモデルがたくさん生成されてしんどくなってくるんですが、それらのアソシエーションをしたくて解説記事を探してもなかなかわかりづらくてよりしんどみが増してくる今日この頃です。
そんな迷える子羊な僕のために僕自身が僕自身の手で僕の解釈を解説してみました。そんな内容です。
今回の記事は下記サイトの内容を踏襲してます。
僕の記事は基本そうですが、参考サイト以上の情報は出てこないのでこれらのサイトを難なく理解できる方はそちらを読んだ方が早いと思います。
(書いてる僕自身rails初心者ですので間違っている箇所等あれば遠慮なくご指摘いただけると幸いです。)
アソシエーションとは
アソシエーション(association)を日本語にすると「「関連付け」だそうです。 railsでコードにするとだいたいこんな感じになります。
では関連付けとはどういうことかというと、簡単に言うと「こっちのモデルはあっちのモデルとこういう関係性にありますよ〜」という説明書きのことです。
これをしておくことでrailsのシステムがモデルの関係性を把握してくれて、モデル間のデータのやりとりなどをなんかこう、スムーズに行うことができたりします。
モデルってなんやねん
こんな感じです。
この場合だと黄色がモデル、青がインスタンスというように呼ばれます。
モデルは性質や特性、できることなどのインスタンスに共通する内容が書かれた説明書のようなイメージです。
一方インスタンスはモデルという説明書に沿った特徴・性質を持つ具体的なモノのことを指します。それぞれのインスタンスはモデルに沿った同一の特徴を持ちますが、インスタンスごとに異なった特徴も持っています。
この例だと「車」モデルの中に「プリウス」「タント」「NOTE」といったインスタンスが含まれてますね。いずれのインスタンスも「車」モデルに属してはいますがいずれもメーカーや搭載シート数など固有の特徴を持っています。
例えば
それではここにもし新たに「タイヤ」モデルができた場合、「車」モデルと「タイヤ」モデルの間にはどういう関係性があるんでしょう?
そんな時にモデルで事前に関係性を示しておくのがアソシエーションです。
このように、railsを使ったwebサービスではこういったモデルをたくさん用意して、それらの関連付け(アソシエーション)をする工程が必要となります。今回はその工程でのお話というわけですね。
アソシエーションの種類
さて本題です。
railsでサポートされているRelationは以下の6種類です。
belongs_to
has_many
has_one
has_many :through
has_and_belongs_to_many
has_one :through
早速解説していきましょう。
belongs_to と has_many
has_many
belongs_to
具体例
先ほども登場した「車」モデルと「タイヤ」モデルのアソシエーションの例ですね。
「車」モデルのインスタンスであるプリウスは「タイヤ」モデルのインスタンスであるタイヤを必ず4つ所有します。
これはプリウスに限った話じゃなくて「車」モデルに属するインスタンスであればどんなものにでも当てはまることなので、「車」モデルのインスタンスは「タイヤ」モデルに対して常に「1対多」の関係が成り立つことになります。
つまり「車」モデルは「タイヤ」モデルに対してhas_many
の関係を持っていると言えます。
一方タイヤ1に注目して見るとどうでしょう。プリウスは複数のタイヤを持っていますが、タイヤ1が所有されている「車」モデルのインスタンスはプリウスだけですね。タイヤ1がその他の「車」インスタンスに所有されることはまずありえません。
このように各「タイヤ」インスタンスから見ると、「タイヤ」モデルと「車」モデルの間には常に「1対1」の関係性が成り立つことになります。つまり「タイヤ」モデルは「車」モデルに対してbelongs_to
の関係を持っていると言えます。
has_one
has_one
具体例
続いてhas_one
の例です。
これは前述のhas_many
が持つ相手方インスタンスが1つに限られる場合の話だと思ってください。
今回はマイナンバーを例に出しますが、マイナンバーは政府によって例外なく日本国民全員に1つずつ与えられますね。1人が複数のマイナンバーを持つことはありえません。常に1対1の関係性です。
ここでhas_one
とbelongs_to
は同じ1対1に対して使うアソシエーションですが、その違いは「所有している」か「所属している(所有されている)」かです。今回の例では「人物」が「マイナンバー」を所有し、「マイナンバー」は「人物」に所有されています。ということで「人物」モデルから見た「マイナンバー」モデルとの関係性はhas_one
となります。
この辺りは曖昧な部分も多いので、詳細について知りたい方は主キー(primary key)と外部キー(foreign key)などについて調べてみるといいかもしれません。
has_many :through と has_and_belongs_to_many
has_many :through, has_and_belongs_to_many
具体例
「1対1」、「1対多」ときたところで「多対多」です。上の図では学校などでの友人関係のつもりで例に出しています。深い意図はないので純粋な目で見てください。
少し複雑になりました。A男から見てF助は友達ですが、F助もまたA男以外にたくさんの友達を持ちます。つまりどのインスタンスから見ても相手方のインスタンスを複数所有しているわけですね。車とタイヤのように明確な所有と所属の関係が成立しないためhas_many
と belongs_to
では表現できません。
このような場合にhas_many :through
やhas_and_belongs_to_many
を用います。(図では例としてhas_many :through
を出していますがhas_and_belongs_to_many
でも代用可能です。)
この例だと一見して2つのモデルが直接関係を持ち合っているように見えますが、実際は以下の図のように2つのモデルを中継する3つ目のモデルが存在します。
このように中間モデルを使うことによって両サイドのモデルは中間モデルに対してhas_many
を、中間モデルは各モデルに対してbelongs_to
を持つことになり、多対多の関係性をhas_many
とbelongs_to
の関係に置き換えられるという仕組みなわけですね。
で、どっちを使えばいいの?
お察しの通り、基本的な働きは同じなhas_many :through
とhas_and_belongs_to_many
です。どのような判断基準のもとどちらを使えばいいんでしょう?
参照元のサイトには以下のように書いてありましたので、参考にしてみてください。
どちらを使用するかについてですが、経験上、リレーションシップのモデルそれ自体を独立したエンティティとして扱いたい(両モデルの関係そのものについて処理を行いたい)のであれば、中間に結合モデルを使用するhas_many :throughリレーションシップを選ぶのが最もシンプルです。リレーションシップのモデルで何か特別なことをする必要がまったくないのであれば、結合モデルの不要なhas_and_belongs_to_manyリレーションシップを使用するのがシンプルです(ただし、こちらの場合は結合モデルが不要な代わりに、専用の結合テーブルを別途データベースに作成しておく必要がありますので、お忘れなきよう)。
Active Record の関連付け (アソシエーション) | Rails ガイド
has_one :through
has_one :through
具体例
こちらはhas_many :through
の1対1バージョンだと捉えていただけるとわかりやすいかなと。
正直これについては具体的な用途が思いつかなかったんですが、
- モデル構造的には中間モデルを介して紐づけられるけど、参照する場合には直接的に(
has_one
のように)参照したい
というような場合に使うことになるのかなぁと思っています。
何かいい例が見つかれば後日追記でもしようかと思います。
まとめ
今回はアソシエーションの種類を概念的な部分を中心にまとめてみました。なんとなくモデルのアソシエーションについて理解できたでしょうか。
いつか実際のコードなども使いながら具体的な用途も紹介できたらなーと思っているので気長に待っていていただけるとうれしいです。
【rails】sessionでログイン情報を管理したい
railsでログイン/ログアウトの機能を作ろうとしたところ素朴な疑問にぶつかりました。
- ログイン/ログアウトの情報ってどこに保存するの?
ログイン情報っていちいちサーバー上に保存/削除して管理するほどのものでもないよなーと不思議に思いまして。
そこでいろいろ調べていたところ、やたらと「セッション(session)」という単語を見かけたので簡単にまとめてみました。
(だいたい引用です。)
セッションとは
そもそもセッションってなんやねん、ということでこちらを。
HTTPはステートレスなプロトコルです。文字通り「ステート (state)」が「ない (less)」ので、HTTPのリクエストひとつひとつは、それより前のリクエストの情報をまったく利用できない、独立したトランザクションとして扱われます。HTTPは言ってみれば、リクエストが終わると何もかも忘れて次回最初からやり直す健忘症的なプロトコルであり、過去を捨てた旅から旅の流れ者的なプロトコルです (しかし、だからこそこのプロトコルは非常に頑丈なのです)。この本質的な特性のため、ブラウザのあるページから別のページに移動したときに、ユーザーのIDを保持しておく手段がHTTPプロトコル内「には」まったくありません。ユーザーログインの必要なWebアプリケーションでは、セッションと呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザとRailsサーバーなど) に別途設定します。
セッションについてはまぁこれが全てで、要するにHTTPでは状態を記憶できないので、別レイヤーでそれを行いますよという話。 これをユーザーのログイン・ログアウトで考えると、一度ユーザーがログインやログアウトをしたはいいけど、ユーザーのログイン状態をHTTPでは記憶できないので、セッションでその情報を保持しましょうということ。 Railsでは基本的にsessionメソッドを使ってこの機能を実現する。sessionメソッドで作成されたセッションは自動で暗号化されるが、ブラウザを閉じると破棄される。
なるほど。どうやら状態(ステータス)を保持できないHTTPさんの代わりにセッションさんがステータスを記憶しておいてくれる、ということらしいです。
具体的な動き
セッションという言葉について大まかに理解できたところで次に湧いてくるのは具体的なプログラム上での疑問。
- どこにどういう形で記憶されるの?
- 記憶されたステータスにはどうやってアクセスするの?
ひとつずつ見ていきます。
1. どこにどういう形で記憶されるの?
結論としてはWebブラウザ上のcookieに保存されるようです。1
cookieについてはこちら。
クッキーとはサーバーから送られてくる会員証のようなもので、次にそのページにアクセスするときにサーバーに「私はこの会員IDですよ」と見せる。それにより、一人ひとりに違う内容のページを表示させたり、前訪れたときの続きとして訪問者に対応したりできる。
要はサーバー側ではなく、WebページにアクセスしているPC側のブラウザにcookieとして情報を保存するんですね。
2. 記憶されたステータスにはどうやってアクセスするの?
続いてrailsにおけるセッションへのアクセス方法。
railsではsession
というハッシュが用意されているので、そこに任意のキーに対して値を格納するだけでOKなようです。
session[キー] = 値
実際にログイン/ログアウトを管理する場面を想定した具体的な使い方は以下のようになります。
class LoginsController < ApplicationController def create session[:login_id] = user.id end def destroy session.delete(:login_id) end end
ここではLoginsController
のcreate
アクションでログインを、destroy
アクションでログアウトを管理しています。session
の:login_id
キーでuser.id
をそれぞれ保存・削除しています。
コントローラー名は任意なので管理しやすい名前のコントローラーを作成しましょう。
(※上記はセッションでステータスを管理することを簡潔に示した例なので、実際のシステムに組み込む場合にはバリデーションなどを必要とする場合が多くなることに気をつけてください。)
まとめ
ざっとこんな感じです。
今回書いたのは本当に簡潔なさわりの部分だけでセッションもまだまだ奥が深いようですので、気になる方は色々と調べてみてください。
注意事項
ページ最下段の注釈にも書いていますがセッションをcookieで管理するのはセッション管理方法の一つにすぎないみたいです。railsのセッションが必ずしもcookieで管理されているとは限らないので注意してください。
どんな管理方法が最適なのかは様々な議論があるようなので、調べてみると面白いかもしれませんね。
下記の記事なども参考にしてみてください。
わかった気になりたい人のための正規表現 ~メタ文字の見分け方~
見事に3記事坊主をキメて以来久しぶりの更新になります。
今回の記事から僕の所属する開発チームの先輩方とローテーションで回しながらブログを更新していくことになりました。
経緯などはこちらの先輩の記事を参照していただけると。
そんなわけで僕は金曜日更新の担当になりました。
今後は(少なくとも)金曜日には記事を更新していくと思うのでよろしくお願いします。
正規表現とは
本題です。
今回は今まで直視せず避け続けてきた正規表現と向き合います。
正規表現といえば/\d{2,5}[-(]\d{1,4}[-)]\d{4}/
みたいな呪文のことですが、プログラムの中でテキストデータから任意の文字列を抜き出す際に使います。
先ほど挙げた呪文では「03-****-****」や 「090-****-****」といった電話番号のような文字列を抜き出すことができます。
てなわけで今回は「正規表現を使いたい!」と思った時にだいたいこの辺り押さえとけばある程度読める/書けるんじゃね?っていうポイントをまとめておきます。他でもない未来の自分のために。
ここで書くのはほんとにポイントだけです。詳細についてはググれば山ほど出てくるので気になる人は調べてみてください。僕はこの辺りの記事を参考にしました。
ちなみに上の記事(全4回)の内容をすべて理解すれば今回の記事は全く読む必要がなくなるのでがっつり正規表現を勉強したい方は上記の記事を、それとなくわかった気になりたい方はこの記事を読みましょう。
正規表現を読む上で
正規表現にはメタ文字があります。メタ文字とは、普通の文字と違い正規表現としての何らかの意味を持つ文字/文字列のことです。
そもそも正規表現の文字列の中にメタ文字とそれ以外の文字が入り混じっていることが諸悪の根源ですよね。自分が初心者なのでわかるのですが、正規表現初心者の方が正規表現を読めないのは
- メタ文字を見分けることができない
- メタ文字の意味がわからない
からだと思っています。
逆に言うと正規表現の呪文からメタ文字を見分けることができ、かつメタ文字の意味を理解できさえすればある程度は正規表現を読めるのではないでしょうか。
というわけで
- メタ文字の見分け方
- メタ文字の意味を理解する
の2点について書こうかなと思ったんですが、「メタ文字の意味を理解する」について書き出すと1記事にまとまらないし上記の丁寧な記事もあるのでこの記事では「メタ文字の見分け方」に絞って書いていこうと思います。何事も棲み分けです。
メタ文字の話の前に
そもそも論になりますが基本的に正規表現は/
で始まり/
で終わります。(※言語によっては/
でない場合もあるかもしれません)
/
の前後にオプションの文字をつけることもありますが、基本的には/
で囲われてる部分が正規表現なんだな〜くらいの認識でいいと思います。
ですので冒頭で挙げた例/\d{2,5}[-(]\d{1,4}[-)]\d{4}/
において/
は正規表現の始まりと終わりを示すだけのものなので、正規表現としての中身は\d{2,5}[-(]\d{1,4}[-)]\d{4}
の部分になります。
※これ以降の正規表現の例では/
は省略します。
メタ文字の見分け方
早速いくつかの見分け方を挙げていきます。
\ + アルファベット でメタ文字
アルファベットの前に\
がついていたらそいつはメタ文字です。
同じアルファベットでもd
は普通の文字だし\d
はメタ文字です。
仮にabc\defg
という文字列が並んでいた場合a
,b
, c
, e
, f
, g
は普通の文字ですが\d
、お前はメタ文字だ。
これは\ + アルファベット
というところがポイントで、「\
がついていれば全てメタ文字」というわけではないので注意が必要です。
後述しますが、逆に\*
はメタ文字じゃなかったりします。闇が深い。
記号はだいたいメタ文字
記号はだいたいメタ文字です。警戒してください。
特に頻出なのは{}
, []
, ()
, +
, *
, ?
, .
, ^
, $
, |
などです。正規表現においてこれらの記号には常にメタ文字として何らかの意味を持っています。
もちろん全ての記号文字がメタ文字というわけではなく、特に正規表現としての意味を持たない記号もあります。詳細についてはいろいろ調べてみてください。
しかし、?
などは疑問文で多用しますし単に?
という文字として扱いたい場面もあります。
そのような場合は対象となる記号の前に\
をつけることで、正規表現としての?
の意味をエスケープすることができます。
そのため先ほど挙げたように\*
はメタ文字ではなく普通の文字として扱われるのです。
カッコで囲われていたらメタ文字のグループ
大抵の場合、メタ文字は複数の文字列である意味を持った正規表現として扱われます。むしろ1文字で独立して存在する場合の方が稀です。
複数の文字列がメタ文字として扱われる場合、()
, {}
, []
などで囲われていることが多いです。
例えば[A-Z]abc
とあった場合、[A-Z]
は正規表現として意味を持つメタ文字のグループであり abc
は普通の文字です。
まとめ
- \ + アルファベット
- 記号
- カッコ
この辺りはだいたいメタ文字だと思って文字列を眺めましょう。
ちなみに冒頭に挙げた\d{2,5}[-(]\d{1,4}[-)]\d{4}
ですが、上の項目を意識して眺めるとざっくり\d
, {2,5}
, [-(]
, \d
, {1,4}
, [-)]
, \d
, {4}
のようにグルーピングできる気がしてきますね。
メタ文字をメタ文字として識別できるだけでグッと正規表現の文字列が読みやすくなるので、このように意識しながら正規表現を眺めてみてください。
あとはメタ文字を理解するだけ
正規表現、わかった気になれたでしょうか。
ここまで来ればあとはもうメタ文字の意味を理解してうまいこと使いこなすだけで正規表現マスターですね。
今回は詳しい説明はしません(できません)が各メタ文字の解説は下記サイトなどで調べられるので参考にしてみてください。
正規表現言語 - クイック リファレンス | Microsoft Docs
それでは素敵な正規表現ライフを!
【謎挙動】form_tagとtext_fieldで検索フォームを作成【rails】
検索フォームを作ろうとしてform_tag
とtext_field
を使った際に謎の挙動が見られたのでメモ書き。(一応解決されました。)
環境は以下の通りです。
- rails 5.2.0
結論
まず結論。
form_tag
とtext_field
を使って検索フォームを実装する場合、以下のように書くのが正解なようです。
# tasks_controller.rb def index @tasks = Task.all end def search search_word = "content like '%" + params[:task][:content] + "%'" @tasks = Task.where(search_word) end
<!-- index.html.erb --> <body> <div> <%= form_tag search_path do %> <%= text_field :task, :content %> <%= submit_tag "検索" %> <% end %> </div> ... ... </body>
これで検索フォームに入力した単語(search_word)を持つデータがTaskテーブルからsearchアクションの@tasks
に入力されます。
問題の謎挙動
さて早速ですが問題の挙動です。
以下のようなコードを書いた時に起こりました。
# tasks_controller.rb def index @tasks = Task.all @task = Task.new end def search search_word = "content like '%" + params[:content] + "%'" @tasks = Task.where(search_word) end
まずコントローラーを上のように設定し、indexページにsearchアクションを仕込ませてparams[:content]
を検索ワード(search_word)として抜き出そうという狙いでした。(Taskテーブルにcontentカラムを持たせています。)
そして実際にindexページに書いた間違いコードが以下の二つ。
<!-- index.html.erb 間違いその1 --> <div> <%= form_tag search_path do %> <%= text_field @task, :content %> <%= submit_tag "検索" %> <% end %> </div>
<!-- index.html.erb 間違いその2 --> <body> <%= form_tag search_path @task do |f| %> <%= text_field f, :content %> <%= submit_tag "検索" %> <% end %> </body>
これらはどちらもform_tag
やtext_field
の用法としては間違いです。
しかしその2ではparams[:content]
でうまく検索ワードを抜き出せてしまいました。
なぜ「その2」では間違った記法にも関わらず正しい挙動をしたのか、また「その1」と「その2」はほとんど等価なプログラムのように見えるのになぜ「その1」では正しく挙動しないのか、わかったことを簡単にまとめます。
paramsを見てみる
まず、これらの書き方によって得られるparams
を見てみます。
searchアクションにbinding.pry
を仕込んでparams
の中身を調べます。
# index.html.erb 間違いその1 [1] pry(#<TasksController>)> params => <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"wB4+spI/twQq+OS7wGOkjP7rYuGlDN/kXYbeN9lESqFTHSU38EcWMmG00Z5cVGKl0EFcQ5HSf2qpijb8r+zCuA==", "#<Task:0x00007f9f327081d0>"=>{"content"=>"hoge"}, "commit"=>"検索", "controller"=>"tasks", "action"=>"search"} permitted: false>
# index.html.erb 間違いその2 [1] pry(#<TasksController>)> params => <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"IE7LzRafXk1idX7E5tHpQMsvs1WsRrnv1Udbcbm2aRCzTdBIdOf/eyk5S+F65i9p5YWN95iYGWEhS7O6zx7hCQ==", "content"=>"hoge", "commit"=>"検索", "controller"=>"tasks", "action"=>"search"} permitted: false>
その1ではparams
の中に"#<Task:0x00007f9f327081d0>"=>{"content"=>"hoge"}
というハッシュがネストしており、params[:content]
では"hoge"
にアクセスできない状態になっています。
一方その2ではparams
ハッシュの中にうまく"content"=>"hoge"
が格納されており、params[:content]
で"hoge"
にアクセスできていますね。
間違いその1について
<!-- index.html.erb 間違いその1 --> <div> <%= form_tag search_path do %> <%= text_field @task, :content %> <%= submit_tag "検索" %> <% end %> </div>
その1で間違っているのはtext_field
の使い方です。
<%= text_field hoge, fuga %> <%= submit_tag %>
とした場合、text_field
は 文字列 "hoge"
をキーとしたハッシュ"hoge"=>{"fuga"=>"入力文字"}
をparams
に返します。
この辺りがtext_field
のよしなに力を感じる部分で、仮にhoge
が:hoge
でも@hoge
でも@@hoge
であったとしてもいったん文字列"hoge"
としてキーにするようです。
今回の間違いその1ではhoge
に該当する部分に@task
が入っていました。
この@task
はコントローラーのindexアクションから空のクラスTask.new
を受け取ってきます。
Task.new
を文字列に変換すると
>> Task.new.to_s => #<Task:0x00007f9f327081d0>
となるので、結果としてparams
には"#<Task:0x00007f9f327081d0>"=>{"content"=>"hoge"}
というハッシュが出来上がります。
ちなみにtext_field
にはもうちょっとだけrailsのよしなに力が働いていて、キーとして与えられた文字列(例でいうhoge
やfuga
)が変数として使われていないかindexアクションまで確認に行ってくれているようです。
そのため今回の場合だと@tasks
は複数のクラスを抱えていることを認知しているため
<%= text_field @tasks, fuga %>
のような使い方をすると、@tasks
をキーとするには不適切と判断しエラーを出力します。
hoge
やfuga
は基本的にはどんな文字列でも大丈夫ですが、そのページで使用するクラス変数などは中身を参照してよしなに判断されてしまうので注意が必要です。
さすがはrailsさん、完全に初見殺し気が利いていますね。
間違いその2について
<!-- index.html.erb 間違いその2 --> <body> <%= form_tag search_path @task do |f| %> <%= text_field f, :content %> <%= submit_tag "検索" %> <% end %> </body>
続いてその2でparams[:content]
にアクセスできている理由です。
そもそも論なんですが、form_tag
ではその2のようなブロック変数|f|
は受け取らない設計になっています。
ですので「その2」のdo-end
ブロック内においてブロック変数f
はnil
として扱われています。
よって<%= text_field f, :content %>
でf
は無視され params
に直接"content"=>"hoge"
が入力されることになります。
本来の用法ではありませんがエラーを吐かずに動いてしまうため、製作者の意図に反してこのような挙動をとってしまう可能性があります。 こちらも注意が必要です。
まとめ
text_field
- 引数をいったん文字列として
params
のキーにする - 使用できる文字列かをよしなに判断してくれる
- 配列やハッシュなど複数の値を持つ場合はエラーを吐く可能性がある
form_tag
- ブロック変数は取らない
- ブロック変数を持たせても動作するが、ブロック変数は
nil
扱いとなる
思う存分にrailsマジックに翻弄された事例でした。 皆さんも十分にお気をつけください。
Ruby初心者によるRuby初心者のためのRuby基礎
Rubyはいろいろ柔軟すぎてまったくコードが読めなかったので備忘録も兼ねてざっと整理してみました。
まだRubyやRailsを触り始めて2〜3週間程度なのでなにか間違っていた場合はご指摘お願いします。
※ちょくちょく余談で深めな部分に言及してますがあまり気にしないでください。
文字列
- ダブルクォート(")、シングルクォート(')どちらでも括れる
- シングルクォート(')では変数が文字列展開されない
- カラ文字はカラ文字扱い
>> "foo" => "foo" >> 'foo' => "foo" >> "" => ""
- +で結合できる
>> "foo" + "bar" => "foobar" >> "foo" + "" => "foo"
変数
変数の初期化
- 宣言 -> 必要なし。どこでも使い始められる。
- 型の指定 -> 必要なし
- 数値、文字列ともに代入可能
- 命名規則 -> 小文字のみを使う。単語は_で繋ぐ(snake_case)
#変数の宣言、型の宣言は必要なし #代入と共にいきなり使える >> valu = 1 => 1 >> char = "foo" => "foo"
変数の文字列展開
- #{変数名}でダブルクォート内(" ")でも格納している文字列を展開できる
- シングルクォート内(' ')では展開できない
- 「#{変数名}」を出力したい場合は
'#{変数名}'
もしくは"\#{変数名}"
>> char = "foo" => "foo" >> char + "bar" => "foobar" >> "char" + "bar" => "charbar" >> "#{char}" + "bar" => "foobar" >> "#{char}bar" => "foobar" >> '#{char}bar' => "\#{char}bar" >> "\#{char}bar" => "\#{char}bar"
変数のスコープ
>> def puts_name >> name = "Tanaka Taro" >> puts name >> end => :puts_name >> puts_name Tanaka Taro => nil >> puts name Traceback (most recent call last): 1: from (irb):11 NameError (undefined local variable or method `name' for main:Object)
メソッド内で初期化された変数は初期化されたメソッド内からしか呼び出せない。
上の例ではputs_name
メソッドで初期化された変数name
はputs_name
内では呼び出せるが、puts_name
外から呼び出そうとするとエラーになる。
※余談
- printメソッドやputsメソッドでは"\n"が改行を意味する
- 「\」もしくは「''」で普通表示されない特殊文字を表示できる
>> print "foo" foo=> nil >> print "foo\n" foo => nil >> print "foo\\n" foo\n=> nil >> print 'foo\n' foo\n=> nil
>> puts "foobar" foobar => nil >> puts "foo\nbar" foo bar => nil >> puts "foo\\nbar" foo\nbar => nil >> puts 'foo\nbar' foo\nbar => nil
配列
配列の基本
- 変数同様宣言や型の指定は必要ない
- 配列の中にどんなオブジェクトも格納可能
- 数値や文字列、nilを同時に格納可能
[]
やArray.new
で初期化できる- 要素への参照は要素番号(0~)を指定
>> [] #中身のないカラの配列 => [] >> Array.new #ArrayはArrayクラスのオブジェクト => [] >> [] == Array.new => true >> array = [1, 2, "three", 4, "five", nil] => [1, 2, "three", 4, "five", nil] >> array[0] => 1 >> array[2] => "three" >> array[5] => nil
配列の大きさ取得
- 配列の大きさは
length
もしくはsize
で取得
>> array = [1, 2, "three", 4, "five", nil] => [1, 2, "three", 4, "five", nil] >> array.length => 6 >> array.size => 6
配列の各要素への繰り返し処理
each
もしくはmap
を使う- 処理の書き方は
{}
もしくはdo-end
で
>> array = [1, 2, "three", 4, "five", nil] => [1, 2, "three", 4, "five", nil] >> >> # {}を使った記述 >> array.each{ |value| puts "value = #{value}"} value = 1 value = 2 value = three value = 4 value = five value = nil => [1, 2, "three", 4, "five", nil] >> >> # do-end を使った記述 >> array.each do |value| >> puts "value = #{value}" >> end value = 1 value = 2 value = three value = 4 value = five value = nil => [nil, nil, nil, nil, nil, nil]
※余談
each
とmap
の違い
※2018/8/25追記
each
とmap
についてはこちらの記事にもまとめました。
each
処理1
>> def hoge >> (1..10).to_a.each do |i| >> unk = i + 1 >> end >> end => :hoge >> ihr = hoge => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
処理2
>> array = [1, 2, 3, 4, 5] >> i = 0 >> array.each do |value| >> puts "value = #{value}" >> puts "i = #{i}" >> i = value + 1 >> puts "i = value + 1" >> puts "i = #{i}" >> puts"-------------------" >> end value = 1 i = 0 i = value + 1 i = 2 ------------------- value = 2 i = 2 i = value + 1 i = 3 ------------------- value = 3 i = 3 i = value + 1 i = 4 ------------------- value = 4 i = 4 i = value + 1 i = 5 ------------------- value = 5 i = 5 i = value + 1 i = 6 ------------------- => [1, 2, 3, 4, 5]
eachメソッドは、配列の要素の数だけブロックを繰り返し実行します。繰り返しごとにブロック引数には各要素が順に入ります。戻り値はレシーバ自身です。
map
処理1
>> def huga >> (1..10).to_a.map do |i| >> unk = i + 1 >> end >> end => :huga >> ihr2 = huga => [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
処理2
>> array = [1, 2, 3, 4, 5] >> i = 0 >> array.map do |value| >> puts "value = #{value}" >> puts "i = #{i}" >> i = value + 1 >> puts "i = value + 1" >> puts "i = #{i}" >> puts"-------------------" >> end value = 1 i = 0 i = value + 1 i = 2 ------------------- value = 2 i = 2 i = value + 1 i = 3 ------------------- value = 3 i = 3 i = value + 1 i = 4 ------------------- value = 4 i = 4 i = value + 1 i = 5 ------------------- value = 5 i = 5 i = value + 1 i = 6 ------------------- => [nil, nil, nil, nil, nil]
参考:map, map! (Array) | Rubyリファレンス
mapメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。collectメソッドの別名です。
each
では配列の要素(処理1のi
, 処理2のvalue
)を返す。処理1では配列の要素i
を返し、処理2ではvalue
を返す。いずれの処理においても処理内で要素自身を更新していないため処理後に返る値は入力された配列のまま。
一方map
ではブロックの戻り値を返す。処理1ではhoge
メソッドが返す値unk
を返し、処理2ではブロックをメソッド化していないためブロックは値を返さない。よって処理1では元の配列に1が足され、処理2ではnil
が返る。
ちなみにmap
はどうやらメソッドの最後に評価した値を返すようで、上記処理2の例ではputs "-------------------"
の評価値を返している。puts
はnil
を返すため、配列はnil
が入った状態で返ってくる。
よってmap
処理の最後の行にj = 1
という処理を追加した場合、返る配列は以下のように[1, 1, 1, 1, 1]
になる。
>> array = [1, 2, 3, 4, 5] >> i = 0 >> array.map do |value| >> puts "value = #{value}" >> puts "i = #{i}" >> i = value + 1 >> puts "i = value + 1" >> puts "i = #{i}" >> puts"-------------------" >> j = 1 >> end ... ... (省略) ... => [1, 1, 1, 1, 1]
メソッド
メソッドの定義
- クラス内で定義されたメソッドはそのクラスから作成されたオブジェクト(インスタンス)に対してのみ使用できる。
- 引数の()は省略可能
def メソッド名(引数1, 引数2, ..., 引数n) 実行する処理1 実行する処理2 ... end
メソッドの呼び出し
- オブジェクトに続いて
.
で繋ぐことで、そのオブジェクトのクラスで定義されているメソッドを呼び出すことができる。 - 引数の()は省略可能
メソッド名(引数1, 引数2, ..., 引数n) メソッド名 引数1, 引数2, ..., 引数n オブジェクト名.メソッド名(引数1, 引数2, ..., 引数n) オブジェクト名.メソッド名 引数1, 引数2, ..., 引数n
- メソッド命名規則
- 一般的に全て小文字
- 単語間は_で繋ぐ(snake_case)
>> def sum_puts(num1, num2) >> puts(num1 + num2) >> end => :sum_puts >> sum_puts(1, 2) 3 => nil >> sum_puts 1, 2 3 => nil
- メソッドの定義位置はメソッドの呼び出し前
- 定義前に呼び出すとエラーになる
>> sum_puts(1, 2) Traceback (most recent call last): 1: from (irb):1 NoMethodError (undefined method `sum_puts' for main:Object) >> def sum_puts(num1, num2) >> puts num1 + num2 >> end => :sum_puts >> sum_puts(1, 2) 3 => nil
※余談
値渡しと参照渡し
要はrubyではメソッド引数での値の渡し方は値渡しである、ということ。
値渡しと参照渡しについては以下の記事が詳しく解説している。
簡単にサンプルコードを書いてみた。
>> def sample(num_method) >> i = num_method >> puts "i = num_method" >> puts "---------------------" >> puts "num_method = #{num_method}" >> puts "i = #{i}" >> puts "---------------------" >> puts "num_method id = #{num_method.object_id}" >> puts "i id = #{i.object_id}" >> puts "---------------------" >> i = i + 1 >> puts "i = i + 1" >> puts "---------------------" >> puts "num_method = #{num_method}" >> puts "i = #{i}" >> puts "---------------------" >> puts "num_method id = #{num_method.object_id}" >> puts "i id = #{i.object_id}" >> puts "---------------------" >> num_method = num_method + 3 >> puts "num_method = num_method + 3" >> puts "---------------------" >> puts "num_method = #{num_method}" >> puts "i = #{i}" >> puts "---------------------" >> puts "num_method id = #{num_method.object_id}" >> puts "i id = #{i.object_id}" >> end => :sample >> num = 10 => 10 >> num.object_id => 21 >> sample(num) i = num_method --------------------- num_method = 10 i = 10 --------------------- num_method id = 21 i id = 21 --------------------- i = i + 1 --------------------- num_method = 10 i = 11 --------------------- num_method id = 21 i id = 23 --------------------- num_method = num_method + 3 --------------------- num_method = 13 i = 11 --------------------- num_method id = 27 i id = 23 => nil >> num => 10 >> num.object_id => 21
sample
メソッド内の num_method
,i
ともに始めのうちは引数num
と同じ値、同じobject_id
を持っている。
その後メソッド内で変数の中身が書き換わると同時にそれぞれの変数のobject_id
が変化している。
最終的にどちらも元の変数num
と違うobject_id
となり、num
の値は元のものから変化しない。
破壊的メソッドによる値の渡し方
強制的にレシーバーの値を変更する破壊的メソッドではどのように値を渡しているのかを検証。
破壊的メソッドを使用する前後でのobject_id
を調べる。
>> # sample メソッド >> def sample(str) >> puts "str = #{str}" >> puts "str id = #{str.object_id}" >> end => :sample >> >> # sample_upcase! メソッド >> def sample_upcase(str) >> puts "str = #{str}" >> puts "str id = #{str.object_id}" >> puts "str.upcase = #{str.upcase}" >> puts "str.upcase id = #{str.upcase.object_id}" >> i = str.upcase >> puts "i = #{i}" >> puts "i id = #{i.object_id}" >> puts "str = #{str}" >> puts "str id = #{str.object_id}" >> end => :sample_upcase >> >> # sample_upcase! メソッド >> def sample_upcase!(str) >> puts "str = #{str}" >> puts "str id = #{str.object_id}" >> puts "str.upcase! = #{str.upcase!}" >> puts "str.upcase! id = #{str.upcase!.object_id}" >> i = str.upcase! >> puts "i = #{i}" >> puts "i id = #{i.object_id}" >> puts "str = #{str}" >> puts "str id = #{str.object_id}" >> end => :sample_upcase! >> >> # str 変数に "hello" を代入、object_id 確認 >> str = "hello" => "hello" >> puts "str id = #{str.object_id}" str id = 70154707881220 => nil >> >> # sample メソッド呼び出し >> sample(str) str = hello str id = 70154707881220 => nil >> >> # str 変数に格納されている文字列と object_id の確認 >> puts "str = #{str}" str = hello => nil >> puts "str id = #{str.object_id}" str id = 70154707881220 => nil >> >> #sample_upcase メソッド呼び出し >> sample_upcase(str) str = hello str id = 70154707881220 str.upcase = HELLO str.upcase id = 70154697977560 i = HELLO i id = 70154719397600 str = hello str id = 70154707881220 => nil >> >> # str 変数に格納されている文字列と object_id の確認 >> puts "str = #{str}" str = hello => nil >> puts "str id = #{str.object_id}" str id = 70154707881220 => nil >> >> # sample_upcase! メソッド呼び出し >> sample_upcase!(str) str = hello str id = 70154707881220 str.upcase! = HELLO str.upcase! id = 8 i = i id = 8 str = HELLO str id = 70154707881220 => nil >> >> # str 変数に格納されている文字列と object_id の確認 >> puts "str = #{str}" str = HELLO => nil >> puts "str id = #{str.object_id}" str id = 70154707881220 => nil
この例からわかること
str
の参照するobject_id
は破壊的メソッド(upcase!
)使用前後で変わらない。- 破壊的でないメソッドではレシーバー(
str
)とは違うobject_id
に値を返す。
肝心のsample_upcase!
メソッドの挙動ですが、i
に空文字かnil
が入っているなど少し挙動がおかしいようなのでもう少し検証。
>> str = "hello" => "hello" >> str.object_id => 70287705130100 >> str.upcase! => "HELLO" >> str.upcase! => nil >> str => "HELLO" >> str.object_id => 70287705130100 >> str.upcase!.object_id => 8 >> nil.object_id => 8
破壊的メソッドupcase!
によって大文字変換を行う際に、対象の文字列がすでにすべて大文字だとnil
が返ってくる。
そしてnil
オブジェクトのobject_id
が8。
つまり先ほどの例ではsample_upcase!
メソッド内のputs "str.upcase! = #{str.upcase!}"
処理によってstr
内の文字列が大文字に変換され、それ以降の処理puts "str.upcase! id = #{str.upcase!.object_id}"
, i = str.upcase!
ではnil
が参照されていたことになる。
それでは肝心の破壊的メソッドupcase!
が実行される瞬間はどこを参照しているのかというと、
>> str = "hello" => "hello" >> str.object_id => 70287705130100 >> str.upcase!.object_id => 70287705130100 >> str => "HELLO" >> str.object_id => 70287705130100
きちんとレシーバーであるstr
のobject_id
を参照している。
結論、
- 非破壊的メソッド
upcase
はレシーバーと異なるobject_id
で文字列変換を行う - 破壊的メソッド
upcese!
はレシーバーと同じobuject_id
で文字列変換を行う - 破壊的メソッドを使用してレシーバーの値から変更がない場合、破壊的メソッドは
nil
を返す
クラス
クラスの定義
- 定義位置はクラスの呼び出し前
- クラスの命名規則
- 頭文字は大文字
- 単語の頭文字を大文字にして単語を区切る(CamelCase)
# クラスの定義 class クラス名 end
>> JapanesePeople.class # classメソッド = オブジェクトのクラスを返すメソッド Traceback (most recent call last): 1: from (irb):1 NameError (uninitialized constant JapanesePeople) # クラスの定義前なのでエラー出力 >> class JapanesePeople >> >> end => nil >> JapanesePeople.class => Class # JapanesePeopleクラスが作成されていることが確認された
オブジェクトの生成
- オブジェクトを生成するとクラス内の
initialize
メソッドが実行される。 initialize
メソッドはクラス定義時に省略可能。initialize
メソッドを使うことでオブジェクト生成時に必ず行うべき処理を実行することができる。
# オブジェクトの生成
オブジェクト名 = クラス名.new()
>> class JapanesePeople >> def initialize(name, age, from) >> @name = name >> @age = age >> @from = from >> puts "name: #{@name}, age: #{@age}, from: #{@from}" >> end >> end => :initialize >> tanaka = JapanesePeople.new("Tanaka", 25, "Tokyo") name: Tanaka, age: 25, from: Tokyo => #<JapanesePeople:0x00007fcd2640abc8 @name="Tanaka", @age=25, @from="Tokyo">
インスタンス変数
- クラス内の全てのメソッドで共有できる変数。
- 変数名の頭に@をひとつ付ける
- クラスから作成されるインスタンスごとに独立した変数
>> class JapanesePeople >> def initialize(name, age, from) >> @name = name >> @age = age >> @from = from >> puts "name: #{@name}, age: #{@age}, from: #{@from}" >> end >> def introduction >> puts "#{@from}出身の#{@name}です。#{@age}歳です。" >> end >> end => :introduction >> tanaka = JapanesePeople.new("Tanaka", 25, "Tokyo") name: Tanaka, age: 25, from: Tokyo => #<JapanesePeople:0x00007fcd2640abc8 @name="Tanaka", @age=25, @from="Tokyo"> >> suzuki = JapanesePeople.new("Suzuki", 30, "Fukuoka") name: Suzuki, age: 30, from: Fukuoka => #<JapanesePeople:0x00007fcd2642b620 @name="Suzuki", @age=30, @from="Fukuoka"> >> tanaka.introduction Tokyo出身のTanakaです。25歳です。 => nil >> suzuki.introduction Fukuoka出身のSuzukiです。30歳です。 => nil
インスタンス変数はtanaka
とsuzuki
で独立しているが、どちらもJapanesePeople
クラスなのでintroduction
メソッドは共通。
クラス変数
- 同じクラスであればインスタンスを超えて参照可能な変数。
- もちろんクラス内のどのメソッドからも参照可能。
- 変数名の頭に@@を付ける。
>> class JapanesePeople >> def initialize(name, age, from) >> @name = name >> @age = age >> @from = from >> @@like = "kome" >> puts "name: #{@name}, age: #{@age}, from: #{@from}, like: #{@@like}" >> end >> def introduction >> puts "#{@from}出身の#{@name}です。#{@age}歳です。#{@@like}が好きです。" >> end >> def like_change_kome >> @@like = "kome" >> end >> def like_change_sakura >> @@like = "sakura" >> end >> end => :like_change_sakura >> tanaka = JapanesePeople.new("Tanaka", 25, "Tokyo") name: Tanaka, age: 25, from: Tokyo, like: kome => #<JapanesePeople:0x00007fcd26b9a118 @name="Tanaka", @age=25, @from="Tokyo"> >> suzuki = JapanesePeople.new("Suzuki", 30, "Fukuoka") name: Suzuki, age: 30, from: Fukuoka, like: kome => #<JapanesePeople:0x00007fcd28c5e2c8 @name="Suzuki", @age=30, @from="Fukuoka"> >> tanaka.introduction Tokyo出身のTanakaです。25歳です。komeが好きです。 => nil >> suzuki.introduction Fukuoka出身のSuzukiです。30歳です。komeが好きです。 => nil >> tanaka.like_change_sakura => "sakura" >> tanaka.introduction Tokyo出身のTanakaです。25歳です。sakuraが好きです。 => nil >> suzuki.introduction Fukuoka出身のSuzukiです。30歳です。sakuraが好きです。 => nil >> suzuki.like_change_kome => "kome" >> tanaka.introduction Tokyo出身のTanakaです。25歳です。komeが好きです。 => nil >> suzuki.introduction Fukuoka出身のSuzukiです。30歳です。komeが好きです。 => nil
クラス変数である@@like
はJapanesePeople
クラスのtanaka
, suzuki
どちらにも常に共通であり、どちら側のメソッドからも参照して値を変更することが可能。
クラスの継承
- クラスの継承方法
class クラス名 < 継承したいクラス名 end
クラスを継承することで、継承元のクラスで定義したメソッドなどを継承先のクラスでも使用することができる。
>> class Person >> def introduction >> puts "私は人間です。" >> end >> end => :introduction >> class Japanese < Person >> end => nil >> Tanaka = Japanese.new() => #<Japanese:0x00007ffb0099f3b0> >> Tanaka.introduction 私は人間です。 => nil
Japanese
クラスはPerson
クラスを継承しているため、Japanese
クラスのインスタンスであるTanaka
はJapanese
クラスで定義されていなくてもPerson
クラスのintroduction
メソッドを使用することができる。
シンボル
:symbol
など、:
が前置された文字列のこと。
変数と違い、シンボル自身に値や文字列を格納することはできない。
シンボルを表すクラス。シンボルは任意の文字列と一対一に対応するオブジェクトです。
文字列の代わりに用いることもできますが、必ずしも文字列と同じ振る舞いをするわけではありません。 同じ内容のシンボルはかならず同一のオブジェクトです。
文字列との違い
文字列は、同じ文字列でも参照するたびにobject_id
の違うオブジェクトが参照されている。
何度参照しても表面上の文字列としては同じものだが、参照するたびにオブジェクトとしては違うものという判定になる。
>> "symbol".object_id => 70193690175060 >> "symbol".object_id => 70193690258700 >> "symbol".object_id => 70193693840280
>> "symbol" == "symbol" => true >> "symbol".object_id == "symbol".object_id => false
一方シンボルは文字列の組み合わせに対して常に同じobject_id
をとる。
何度参照しても常に同じobject_id
となり、同じオブジェクトとみなされる。
>> :symbol.object_id => 392008 >> :symbol.object_id => 392008 >> :symbol.object_id => 392008
>> :symbol == :symbols => true >> :symbol.object_id == :symbol.object_id => true
ハッシュ
ハッシュの定義
- 配列と似た構造
- キーを指定してオブジェクト(値)を格納する。
- 配列番号のみでなく任意の数値や文字列でオブジェクトを取り出せる。
- {}は省略可能
- キーには数値、文字列、シンボルなどを始めあらゆるオブジェクトを採用可能。
nil
やtrue
,false
, クラス, モジュールなど
# キーが数値、文字列の場合 ハッシュ名{キー1 => オブジェクト1, キー2 => オブジェクト2, ...} ハッシュ名 キー1 => オブジェクト1, キー2 => オブジェクト2, ... # キーがシンボルの場合 ハッシュ名{:キー1 => オブジェクト1, :キー2 => オブジェクト2, ...} ハッシュ名 :キー1 => オブジェクト1, :キー2 => オブジェクト2, ... ハッシュ名{キー1: オブジェクト1, キー2: オブジェクト2, ...} ハッシュ名 キー1: オブジェクト1, キー2: オブジェクト2, ... # ハッシュにオブジェクトを追加 ハッシュ名[キー] = オブジェクト # ハッシュの値を参照 ハッシュ名[キー]
- 変数や配列、ハッシュは中身のオブジェクトがキーとして割り当てられる
- よって中身が変わるとエラーとなる
>> greet = "hello" => "hello" >> array = [1, 2, 3, 4, 5] => [1, 2, 3, 4, 5] >> person = {name: "tanaka", age: 25} => {:name => "tanaka", :age => 25} >> hash = {greet => "hoge", array[0] => "fuga", person[:name] => "hogefuga"} => {"hello"=>"hoge, "1=>"fuga", "tanaka"=>"hogefuga"}
ハッシュをハッシュにネストする
ハッシュのオブジェクトにハッシュを持っている状態
>> family = { >> name: family_name = { >> dat: "toshio", >> mam: "kaori", >> son: "yuta", >> daughter: "saori" >> }, >> num: 4 >> } => {:name=>{:dat=>"toshio", :mam=>"kaori", :son=>"yuta", :daughter=>"saori"}, :num=>4}
モジュール
モジュールの定義
- 定義位置はモジュールの呼び出し前
- モジュールの命名規則
- 頭文字は大文字
- 単語の頭文字を大文字にして単語を区切る(CamelCase)
module モジュール名 処理 end
>> module Person >> def introduction >> puts "私は人間です。" >> end >> end => :introduction
モジュールについてはこちらの記事がわかりやすかったのでここでは簡単な紹介程度で。
上記の記事で、
- モジュールはインスタンスを作れない
- モジュールは継承できない
とあったので、これだけ確認しておきます。
モジュールはインスタンス(オブジェクト)を生成できない
- そもそもModuleクラスにはインスタンスを生成するnewメソッドは用意されていない。
>> module Person >> def introduction >> puts "私は人間です。" >> end >> end => :introduction >> Person.new() NoMethodError: undefined method `new' for Person:Module from (pry):10:in `__pry__'
モジュールは継承できない
- モジュールを継承しようとするとエラーが出力される。
>> module Person >> def introduction >> puts "私は人間です。" >> end >> end => :introduction >> module Japanese < Person SyntaxError: unexpected '<' module Japanese < Person ^ >> class Japanese < Person >> end TypeError: superclass must be a Class (Module given) from (pry):6:in `__pry__'
おわりに
何かご指摘やご要望があれば遠慮なくコメントお願いします。
【初心者】Ruby on Rails での開発環境を整える
筆者はRuby on Railsの勉強を始めたばかりの新米エンジニアです。
自分なりにプロジェクト(なにか適当なWebページらしきもの)を作る過程を適当に書き起こしていきます。
完全なる備忘録ですが、ついでにこれからRailsを始める方の参考にでもなれば幸いです。
この記事のゴール
Railsでプロジェクトをスタートできる環境を作る
初心者が無駄に高い目標を設定したところでどうせ挫折するので、ゆっくり地道にやります。
具体的には以下のものをインストールする過程を書いています。
この記事の対象者
ターミナル触ったことない方はこの辺りの記事を参考にしていただけると。
ちなみに今回はMacBookProにRailsを入れていきます。
さっそく始めてみる
今回はこの辺りの記事を参考に環境を整えていこうと思います。
みなさん片っ端からrbenvなるものを導入しているようなので調べて見たんですが、どうやらRubyのバージョンを簡単に切り替えられるようになるみたいです。
入れておいて邪魔になるものでもなさそうなので今回はrbenvを入れる方向で進もうと思います。
コマンドラインツールのインストール
まずはコマンドラインツールのインストールから。
必要な方は以下のコマンドでインストールできます。
僕の環境ではすでにインストール済みだったのでここはスルー。
$ xcode-select --install
インストールされていれば以下のコマンドでバージョンが表示されるはずです。
$ xcode-select -v xcode-select version 2349.
Homebrewのインストール
次にHomebrewのインストール。
HomebrewはMac OS Xのパッケージマネージャーで、導入することでソフトウェアの入手が楽になるらしい。
Homebrewやパッケージマネージャーについてはこの辺りの記事がわかりやすかったです。
Homebrew公式を参考に以下のコマンドを実行。
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
インストールの確認のためバージョンを表示させます。
$ brew -v Homebrew 1.6.4
表示されましたね。これでHomebrewをインストール完了。
rbenvのインストール
続いてrbenvをインストール。
$ brew install rbenv $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile
ここでも確認のためバージョンを表示します。
$ rbenv -v rbenv 1.1.1
無事インストールされているようです。
また、このときrbenv単体だけだとこの後のRubyのインストールができないので同時にruby-buildもインストールしておきます。
$ brew install ruby-build
Rubyのインストール
先ほどインストールしたrbenvでRubyをインストールしていきます。
$ rbenv install --list
でインストール可能なバージョンの一覧が出てくるので目当てのバージョンをインストールします。
ここでは最新バージョンのバージョン2.5.1を指定しています。
$ rbenv install 2.5.1
インストールが完了すると下記コマンドのいずれかでインストールしたRubyのバージョンが確認できます。
$ rbenv versions $ ruby -v
ruby 2.5.1
バージョンが表示されたら無事Rubyのインストールは完了。
※余談ですが、rbenvを使わずにHomebrewのみでも下記コマンドでインストール可能です。
$ brew install ruby
参考:Rubyのインストール
Bundlerのインストール
続いてBundlerをインストールしていきます。
Bundlerやgemについては以下の記事がわかりやすかったです。
参考:Bundlerとは?
下記コマンドでBundlerをインストールします。
$ gem install budler
インストールを確認するためバージョンを表示します。
$ bundler -v Bundler version 1.16.1
バージョンが表示されました。これでBundlerのインストールは完了です。
Railsのインストール
いよいよRailsをインストールします。
$ gem install rails
実行が終了したら確認のためバージョンを表示します。
$ rails -v
これまで通りバージョンが表示されると思いきや、
$ rails -v Rails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command.
変なエラーが出ました。
どうやら$ sudo gem install rails
のコマンドでrailsをインストールしろ、ということなので実行しバージョンを確認。
$ sudo gem install rails ...(略)... $ rails -v Rails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command.
ハマった
これ以降ずっと同じことの繰り返し。
というわけでググり申してみると、以下のような記事に行き着いた。
つまるところこのエラーの原因は、
gemとはrubyそれぞれがもつツールである。しかし、rbenvをいれてもMacOSXはデフォルトでrubyとgemを持っているから、いくらgemコマンドを打ち込んでもデフォルトのrubyのgemが起動してしまう。gemコマンドが自分が使いたいrbenvのrubyで動くように、きちんとPATHを指定する必要がある。そうすれば、railsだって動く
ということだそうです。
そして具体的な解決策はexport PATH="$HOME/.rbenv/shims:$PATH"
を追記することだそうですけど、その記入先が.bashrc
,.bash_profile
とそれぞれの記事で微妙に違う。
というわけで.bashrc
,.bash_profile
について調べてみると下記の記事が。
sshでログインすると、.bash_profile -> bashrcの順に実行されます。
ログイン時に一回だけ実行したい時
-> .bash_profileに記載
シェルを起動する度に実行したい時(一般的な設定)
-> .bashrcに記載
とのことなのでvimを起動して.bashrc
に記入。
$ vim ~/.bashrc
export PATH="$HOME/.rbenv/shims:$PATH"
その後ターミナルを再起動させて、もう一度Railsをインストール。
$ gem install rails ...(略)... $ rails -v Rails 5.2.0
バージョン表示されました!
これにてやっとこさRailsをインストール完了。
おわりに
以上がRailsで開発を始めるための下準備かと思います。
また足りないものがあったら随時追記していきます。