【Rails】技術面接対策の記事の質問を多少深ぼる記事⑨
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
この記事は前回の記事の続きものです。
(前回の記事)
本記事ではQ49 ~ Q53を多少深掘りします。
Q49: selectとmapとcollectの違いを説明してください
回答:
3つともブロックをひとつ引数として受け取ります。
select
コレクションのサブセットを取得するのに使います。!付きのselect!を呼ぶと、元のコレクションが改変されます。
i = [1,2,3,4,5] i.select {|x| x % 2 == 0} # => [2, 4]
map
コレクションの各要素に対して操作を実行し、更新されたコレクションを出力します。!付きのmap!を呼ぶと、本のコレクションが改変されます。
i = [1,2,3,4,5] i.map {|x| x+1} # => [2,3,4,5,6]
collect mapのエイリアスなので動作は同じです。
上記のブロックを引数に取るメソッドですが、個人的にはあまり使ったことがないのですが、現場だとどうなのでしょうか、、。
selectメソッドはコレクションのサブセットを取得するとありますが、要は配列の中から条件にマッチするものだけを取り出すということですね。ですので、ブロックの中身は条件式になります。
i = [1,2,3,4,5,6,7,8,9] i.select {|x| x % 3 == 0} # => [3, 6, 9] # 3の倍数を取得
上記のメソッドにはないのですが、filterメソッドというselectのエイリアスメソッドもあります。フィルタリングという意味でこちらの方が直感的に何をするのかがわかりますね。
i = [1,2,3,4,5,6,7,8,9] i.filter {|x| x % 3 == 0} # => [3, 6, 9]
mapメソッドは配列の中身を更新して取得する時に便利ですね。
i = [1,2,3,4,5] i.map {|x| x * 2} # => [2,4,6,8,10]
ちなみに返り値がない場合、その位置にある要素はnilになります。例えば、ブロックの中に条件式を含ませると返り値がnilになります。
i = [1,2,3,4,5] i.map {|x| x * 2 if x % 2 == 0} # => [nil,4,nil,8,nil] 2の倍数だけ2倍になり、その他はnilとなって返却される
しかし上記のようにnilが返ってしまうのは好ましくない場合が多そうですね。なにせ条件に合わない要素だけその要素の位置にnilが返ってきてしまうので。ですので、要素の評価をして条件にマッチしたものだけを加工して返却する処理をしてくれるメソッドも用意されています。それはfilter_mapメソッドです。
i = [1,2,3,4,5] i.filter_map {|x| x * 2 if x % 2 == 0} # => [4,8] 条件に合わなかった要素はnilとして返却されない
collectはmapのエイリアスでmapと同じ挙動となります。
i = [1,2,3,4,5] i.collect {|x| x * 2} # => [2,4,6,8,10]
また、ブロックを引数に取るメソッドとして有名なeachがあります。上記のメソッド群とは違い、戻り値は元の配列と同じになります。元の配列を加工する必要がない処理などではeachを使った方が良さそうですね。
i = [1,2,3,4,5] i.each {|x| x * 2} # => [1,2,3,4,5]
Q50: RailsのCRUD verbと、それに対応するアクションを述べてください
回答:
verb アクション GET index GET new POST create GET show GET edit PATCH/PUT update DELETE destroy
CRUDのルーティングをよしなにやってくれるresourcesをroutes.rbに記載してから、ルーティングを確認するとよくわかると思います。
Rails.application.routes.draw do resources :posts end
$ bundle exec rails routes Prefix Verb URI Pattern posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format) posts#edit post GET /posts/:id(.:format) posts#show PATCH /posts/:id(.:format) posts#update PUT /posts/:id(.:format) posts#update DELETE /posts/:id(.:format) posts#destroy
サーバから情報を取得してくる時に使用するGETのアクションとしては以下の4つです。基本的にはページを表示するため、ページ表示のためのヘルパーメソッドが生成されます。
- index (posts_path)
- new (new_post_path)
- edit (edit_post_path)
- show (post_path(@post))
indexアクションの用途は基本的に一覧表示です。
def index @posts = Post.all end
newアクションは新規作成モデル作成のためにからのインスタンスをビューに渡します。
def new @post = Post.new end
editアクションとshowアクションはそれぞれ既に作成されたモデルの詳細ページ及び編集ページですので、既に作成されたそのモデルを表示します。
def show @post = Post.find(params[:id]) end
def edit @post = Post.find(params[:id]) end
サーバへ情報を登録する時に使用するPOSTに対応するのはcreateアクションです。新規データを作成してそれをサーバに保存します。基本的にはsaveメソッドを使用して保存します。
def create @post = current_user.posts.new(post_params) if @post.save redirect_to post_path(@post), success: 'ポストを作成しました' else flash.now[:danger] = 'ポストを作成できませんでした' render :new end end
サーバに保存されたデータを更新するPUT、PATCHはupdateアクションです。
def update @post = current_user.posts.find(params[:id]) if @post.update(post_params) redirect_to post_path(@post), success: 'ポストを更新しました' else flash.now[:danger] = 'ポストを更新できませんでした' render :edit end end
PUTとPATCHが両方とも更新ですので、updateアクションは2つ表示されていますが、これらの違いについては下記記事を参照ください。
保存されたデータを削除するDELETEはdestroyアクションに対応しています。
def destroy @post = current_user.posts.find(params[:id]) @post.destroy! redirect_to posts_path, success: 'ポストを削除しました' end
このCRUDはRailsでは当たり前に出てきますのでしっかりと説明できるようにしておきます。
Q51: createアクションへのルーティングを、resourcesを使わないで定義してください
回答:
resources
ありの場合 resources :photosresources
なしの場合 post '/photos', to: 'photos#create', as: :create_photo
Q50で使用した例をそのまま流用します。
Rails.application.routes.draw do resources :posts end
上述しましたがresources
を使えばRailsがよしなにCRUDに対応したルーティングを定義してくれます。
$ bundle exec rails routes Prefix Verb URI Pattern POST /posts(.:format) posts#create
これと同じように定義するには以下のように定義します。
post '/posts', to: 'posts#create'
ルーティングも同じように定義されています。
$ bundle exec rails routes Prefix Verb URI Pattern POST /posts(.:format) posts#create
ちなみに回答例にあるasオプションは名前付きヘルパーを指定します。resourcesではcreateアクションは名前付きヘルパーが指定されていませんが、asを使うことで指定できます。
post '/posts', to: 'posts#create', as: :create_post
$ bundle exec rails routes Prefix Verb URI Pattern create_post POST /posts(.:format) posts#create
Q52: Rubyの3段階のアクセス制御を述べてください
回答:
public
このメソッドは任意のオブジェクトから呼び出せるprotected
このメソッドは、そのメソッドが定義されているクラスと、そのクラスのサブクラスからしか呼び出せないprivate
このメソッドは、そのオブジェクト自身しか呼び出せない
public
はデフォルトのメソッドのアクセス権ですので、普段定義しているメソッドと変わりありません。クラスの外でも呼び出しが可能です。
Class Hoge # デフォルトでpublic def hoge p 'hoge' end end Hoge.new.hoge # => 'hoge'
順番が変わりますが、privateはクラス外からの呼び出しができなくなります。privateと明示するとその下で定義されたメソッドはprivateメソッドになります。
Class Hoge private def hoge p 'hoge' end end Hoge.new.hoge # => エラーになる
よってそのインスタンスやクラスの中でのみ使用することができます。Railsですとストロングパラメータのメソッドで使われるのが一般的です。
def create @user = User.new(user_params) if @user.save redirect_to root_path else render :new end end private def user_params params.require(:user).permit(:name, :email, :image, :password, :password_confirmation) end
メソッドはクラスの中でのみ使用されているのでエラーになりません。
そして最も使ったことがないprotected
ですが、基本的にはprivate
と同様の挙動となり、クラスの外からメソッドを呼び出すとエラーになります。
Class Hoge protected def hoge p 'hoge' end end Hoge.new.hoge # => エラーになる
異なる点としてはselfをレシーバーとすると、private
ではエラーになりますが、protected
ではエラーにはなりません。
Class Hoge def hoge_public self.hoge_protected # => 'protected!!' self.hoge_private # エラーになる end private def hoge_private p 'private!!' end protected def hoge_protected p 'protected!!' end end Hoge.new.hoge_private # => エラーになる Hoge.new.hoge_protected # => エラーになる
他の記事をいくつか読みましたが、protected
を使う機会はあまりないようです。。。
Q53: Rubyのシングルトンの使いみちを説明してください
回答:
シングルトンは、クラスにインスタンスを1つしか持たせないデザインパターンです。シングルトンはRuby界隈では嫌われがちですが、Rubyにはシングルトン用のモジュールも付属しています。
require 'singleton' class Thing include Singleton end puts Thing.instance # => #<Thing:0x00007fdd492cf488>
シングルトンについては解説は回答とほぼ内容は同じですが下記説明の方がしっくりきました。
シングルトンとは、オブジェクト指向プログラミングにおけるクラスのデザインパターンの一つで、実行時にそのクラスのインスタンスが必ず単一になるよう設計すること。
そのクラスのインスタンスがかなるらず単一となるようにとあるように、そのクラスの元のインスタンスは複数存在することができず、ただ一つのみしか存在できないということです。
別記事での例を見てみます。
# Singletonは、Mix-inしたクラスのinstanceは同一のインスタンスを返すようになる require 'singleton' # シングルトン class SingletonObject # instanceメソッドが定義され、newメソッドがprivateに設定される include Singleton attr_accessor :counter def initialize @counter = 0 end end
シングルトンのクラスを定義するためには下記二つの記載が必須のようです。
# Singletonは、Mix-inしたクラスのinstanceは同一のインスタンスを返すようになる require 'singleton'
singleton
ファイルをシングルトンを定義するファイル内に読み込むことでシングルトンを定義することができるようです。
# instanceメソッドが定義され、newメソッドがprivateに設定される include Singleton
また、Singletonモジュールを読み込むことでそのファイル内にinstanceメソッドが定義され、newメソッドがprivate
に設定されます。つまり、インスタンスを作成するnewメソッドの外部呼び出しができないため、物理的にインスタンス作成を断ち切っています。
obj3 = SingletonObject.new # private method `new' called for SingletonObject:Class (NoMethodError) # ↑ newでのインスタンスの作成に失敗
代わりに単一インスタンスを作成する方法としてinstanceメソッドが定義されています。しかし、単一のインスタンスしか持たないため、別の変数にinstanceメソッドを使用してインスタンスを格納しても、それは全て同じインスタンスを参照しているため、クラスで定義されている値も同じ値を参照することになります。
obj1 = SingletonObject.instance obj1.counter += 1 puts(obj1.counter) # 1 obj2 = SingletonObject.instance obj2.counter += 1 puts(obj2.counter) # 2 # ↑ 前回の+1が引き継がれている #
終わりに
今回はここまで!!!!
そしてこのシリーズも今回で最後になります!長かったですが大変勉強になりました!
改めて自分はGem頼りエンジニアをしていたのだなと痛感したのでこれからも基本を振り返って行きたいと思います。
以上!引き続き本ブログをよろしくお願いします!