ほぼ置き換えられるものとして、ruby2.3から &. 通称、ぼっち演算子という機能が追加さ れました。このぼっち演算子を使うと、コードはこのようになります。
コラム: &. (
アンパサンド ドット、通称:ぼっち演算子)と .try メソッド
<td><%= book.published_on&.strftime('%Y年 %m月') %></td>
当然、rubyのバージョンが2.3より古いものでは、ぼっち演算子は使えません。対応する Rubyのバージョンがいくつかを意識して、ぜひ新しいものも取り入れていってください。
④ リダイレクトの変更
Tagの画面遷移で詳細ページを省略することにしたのでした。デフォルトは、新規登録のあ とと編集の後に、詳細ページへ遷移するようになっています。これらはコントローラーで設 定を変更することができます。
設定前(デフォルト)
• tags#new -> tags#create -> tags#show
• tags#edit -> tags#update -> tags#show
設定後
• tags#new -> tags#create -> tags#index
• tags#edit -> tags#update -> tags#index
app/controllers/tags_controller.rb
を開いてください。def create
@tag = Tag.new(tag_params) respond_to do |format|
if @tag.save
format.html { redirect_to @tag, notice:
'Tag was successfully created.' }
format.json { render :show, status: :created, location: @tag } else
format.html { render :new }
format.json { render json: @tag.errors, status: :unprocessable_entity }
end end end
if @tag.save
のところでタグ のデ ータを保 存しています。これが 成 功したら、ま保存したデータのことを指していますので、保存したデータの詳細ページへ遷移しま す。この指定方法は省略した書き方で、
tag_path(@tag)
と同じ意味です。これをtags#index
へ遷移するように書き換えます。format.html { redirect_to tags_url, notice:
'Tag was successfully created.' }
これも遷移先を示す別の書き方ですが、tags_pathと遷移するところは同じです。ただし、
2.3.0 :004 > app.books_url
=> "http://www.example.com/books"
2.3.0 :005 > app.books_path => "/books"
このように、httpで始まるurlなのか、そのうしろのpathだけなのかという違いがあります。 viewのようなテンプレートでは_path でも大きな問題にはなりませんが、このリダイレク トはhttpステータスコードの300番台のことですので、ここでは _url を指定しておく方
が好ましいでしょう。
commit
⑤ 中間テーブルの実装
sample_designのbooks_show.htmlを見てください。ここにタグ付け(Tagging)
情報を載せています。テンプレートを修正しただけでは、データはうまく保存されません。
いままでは、BookやTagモデル単体で画面遷移をしてデータを作成する方法ばかりでし たが、コントローラやモデルを変更して、中間テーブルのデータを作成する方法を説明しま す。方法はいろいろありますが、ここでは片方(Book)の情報に依存したやり方を解説します。
まず、Taggingモデルの内容を確認しましょう。 app/models/tagging.rb を開い てください。
class Tagging < ApplicationRecord belongs_to :book
belongs_to :tag end
Taggingのデータには、bookとtagの両方の情報が必要です。scaffoldをすると き、
db/migrateのファイルにフィールドに関係を表す references
を指定すると、中Tagモデルから見た関係は自動では追加してくれませんので、以下のように追記してくだ さい。
app/models/book.rb class Book < ApplicationRecord has_many :taggings end
a p p / m o d e l s / t a g . r b
c l a s s T a g < A p p l i c a t i o n R e c o r d h a s _ m a n y :taggings end
ここで、BookとTagはリレーションで多対多の関係ですので、以下のようにBook とTagの 関 係 も 追 加して お き ましょう。
app/models/book.rb class Book<
ApplicationRecord has_many :taggings has_many :tags, through:
:taggings end
app/models/tag.rb class Tag
< ApplicationRecord has_many :taggingshas_many :books, through: :taggings end
これらのリレーションの設定はいろいろな機能の実装前に忘れずに設定してください。
commit
では、sample_designのtaggings_form.htmlを見てください。前提として関係 先のBookの情報があり、それに付随するタグを追加するというやり方です。タグを選択 する部分は、以下のように書き直してください。
変更前
<select name="tagging[tag_id]" id="tagging_tag_id">
<option value="1">文学・小説</option>
<option value="2">科学・テクノロジー</option>
<option value="3">文庫</option>
<option value="4">ハードカバー</option>
<option value="5">雑誌</option>
</select>
変更後
<%= f.select :tag_id, Tag.all.map{|t| [t.name, t.id]}, class: "form-control" %>
Tag.all
でタグ情報をすべて取り出し、.map
メソッドで配列に直します。 f.select は、配列でデータを渡すことでセレクトボックスを作ってくれます。配列の1つ目が表示する文字、
2つ目がvalueです。このとき、valueへは文字を渡さずにデータのidを渡すようにして ください。このvalueが:tag_idとしてコントローラへ送信されます。
tag_id
は、こうしてセレクトボックスから情報をもらいます。もう1つのbook_id
は、urlから情報をもらいます。このページのurlは次のところです。
new_book_tagging GET /books/:book_id/taggings/new(.:format) taggings#new book_taggings POST /books/:book_id/taggings(.:format) taggings#create
ここに、:book_id があります。この情報は、コントローラのアクション内で取得すること ができますので、それを利用します。
まず、テンプレートでタグ のセレクトボックスの 上に表 示しているBookの 情 報は、
tagging#new
から インスタンス 変 数 で 取 得しましょう。app/controllers/
taggings_controller.rb
のnewアクションを次のように変更します。def new
@tagging = Tagging.new
@book = Book.find(params[:book_id]) # 追加 end
こうして取得した@book を使って、テンプレートで本のタイトルなどを表示してみてくださ い。最後に、formタグですが、ここに :book_id を渡してcreateのpathを作ります。
いま、コントローラで @book を取得したのでこれを使って簡単に設定することができます。
以下のように form_for の行を変更してください。
変更前(デフォルト)<%=form_for(tagging)do|f|%>
変更後<%=form_for([@book,tagging])do|f|%>
あとはapp/views/books/_form.html.erb などを参考にしながら、taggingのテンプレー ト全体を修正してみてください。
commit
コントローラに情報を渡すテンプレートの準備ができたので、taggings#createを修 正しましょう。
変更前 def create
@tagging = Tagging.new(tagging_params) respond_to do |format|
if @tagging.save
format.html { redirect_to @tagging, notice:
'Tagging was successfully created.' } format.json { render :show, status:
:created, location: @tagging }
else
format.html { render :new }
format.json { render json: @tagging.
errors, status: :unprocessable_entity } end
end end
まず、tag_idはストロングパラメータとしてtagging_paramsに含まれて渡されますので、
このままで大丈夫です。book_idは、newアクションと同じように、@bookを追加し て取得しましょう。createしたあとのリダイレクト先の変更も忘れないでください。そうす ると、createアクションは次のようになります。
変更後 def create
@book = Book.find(params[:book_id]) @tagging = Tagging.new(tagging_params) @tagging.book_id = @book.id
respond_to do |format|
if @tagging.save
format.html { redirect_to @book, notice:
'Tagging was successfully created.' } format.json { render :show, status:
:created, location: @tagging }
else
format.html { render :new }
format.json { render json: @tagging.
errors, status: :unprocessable_entity } end
end end
これで books#show -> taggings#new -> taggings#create ->
commit
同じようにbooks#show -> taggings#edit -> taggings#update ->
books#show の流れも修正してみてください。editとupdateアクションは次のように なります。
def edit
@book = Book.find(params[:book_id]) end
def update
@book = Book.find(params[:book_id]) respond_to do |format|
if @tagging.update(tagging_params)
format.html { redirect_to @book, notice:
'Tagging was successfully updated.' }
format.json { render :show, status: :ok, location: @tagging } else
format.html { render :edit }
format.json { render json: @tagging.errors,
status: :unprocessable_entity }
end end end
updateアクションでは、すでに@taggingに book_id が入っていますから、ここで は代入する必要はありません。リダイレクトのために @book を用意します。
commit
。
初めてデフォルトのコントローラのshowやeditアクションを見ると、何もないアクションで面食 らうと思います。もうご存知のとおり、フィルタと呼ばれるbefore_ actionで設定しているから です。ここではaf ter_ actionというものも使えます。また、モデルにはコールバックと呼ばれる ものがあります。before_ save,af ter_ save,before_create,af ter_createなど、
いろいろなタイミングのものがあります。さらに、Rubyは他のクラスやモジュールを継承しますの で、その元/先で何か処理をしているときもあります。いろいろなプロジェクトに関わっていくと、コ ントローラのアクションだけ見ていると、保存しているつもりが保存されなかったり、意図しない(よ うに見える)データに変換されてしまったりして、びっくりすることもあります。テキストでプログラミ ングを学ぶと、どうしてもその部分だけを注目しがちですが、Rail sがどういうふうに動作している か全体をイメージしながら実装していくと、思わぬ動作になったときに予測がつけられるようになり ます。そうして俯瞰できるようになるのが、上級者の第一歩です。
コラム:フィルタとコールバックと継承を俯瞰する
3. 管理者ログイン認証
(1) . Gemを利用したログイン認証
誰でもアクセスできる状態をなくすために、ログイン認証を実装しましょう。ここでは便利な gemにお任せです。今までのようにrailsgenerateを利用して、そこに機能を追加す る方法が一般的ですが、gemに実装されている機能を使って実装する方法を紹介します。
余談ですが、ここに書いている方法は、gemのREADMEに書いてあります(もちろん英 語です)。他のgemもそうですが、使い方を自分で読んで使えるようになることを目指して ください。