はじめに
突然ですが、質問です。
書き方は違いますが、①と②が同じだって分かりますか?
# Userのアソシエーションはこのようになっています
# has_many :posts, dependent: :destroy
# has_many :active_relationships, class_name: 'Relationship',
# foreign_key: 'follower_id',
# dependent: :destroy
# has_many :passive_relationships, class_name: 'Relationship',
# foreign_key: 'followed_id',
# dependent: :destroy
# has_many :following, through: :active_relationships, source: :followed
# has_many :followers, through: :passive_relationships, source: :follower
# Userモデルのメソッドです
def feed
Post.where(user_id: following_ids << id) #=> ①
end
def feed
Post.where(:user_id => self.following_ids.<<(self.id)) #=> ②
end
分からない方は、読んで勉強になることがあるかもしれません。
よければ読んでいただけると幸いです。
書くに至った経緯について
はじめまして。みけたと申します。
3月末で公務員を辞めて、Railsなどの勉強を始めて5ヶ月近く経過します。
主にMENTAというサービスでだいそんさんという方にお世話になっており、
スパルタコースというコースを受講しながら、日々勉強に励んでおります。
様々な課題が課されるのですが、その課題の1つがインスタクローンの実装です。
現在、私はこの実装に取り組んでいるところです。
先日、後発で受講を始めたメンティーさんからトップ画面にフィードを表示させる機能について、
コミュニティ内で質問がありました。
私はその範囲の実装を既に終えており、勉強がてらに質問に答えてみようと思ったのですが、
実力不足で答えられるほどの力がなかったので、改めて調べて、全力で答えてみることにしました。
それなりに気合を入れて答えたので、コミュニティ外の初学者の方にも何か役に立てばと思い、
Qiitaという形で公開してみることにしました。
前提条件について
自身の投稿とフォローしているユーザーの投稿がトップページに表示される仕様となっています。
Userモデルにfeed
というメソッドを実装して、posts_controller.rb
の
index
アクションにて該当の投稿を取得し、Viewにその投稿を渡すようなコードを書くのですが、
このfeed
メソッドについて質問がありました。
アソシエーションについて
なお、タイムスタンプ(created_atとupdated_atのカラム)は省略しています。
また、ER図の書き方は勉強不足のため、誤りがある可能性が高いです。。。
とりあえず、users
はたくさんのposts
を持っていて、@user.following
と書けばフォローしている
ユーザーが取得できることが分かれば、今回の記事は十分に理解できるかと思います。
なお、アソシエーションについては、この記事では説明しません。
以下の記事などを参考にするとよいかと思います。
- 【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸 - Qiita
- 【Rails】アソシエーションを図解形式で徹底的に理解しよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
質問
current_user.feed
で何をやっているのか分からない!!!
該当のコードについて
def index
@posts = if current_user
# このcurrent_user.feedが分からない!!!
# feedメソッドについては、user.rbを参照してください
current_user.feed.includes(:user).page(params[:page]).order(created_at: :desc)
# 以下、省略
# == Schema Information
#
# Table name: users
#
# id :bigint not null, primary key
# avatar :string(255)
# crypted_password :string(255)
# email :string(255) not null
# salt :string(255)
# username :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_users_on_email (email) UNIQUE
#
class User < ApplicationRecord
authenticates_with_sorcery! # sorceryというgemを使っています
mount_uploader :avatar, AvatarUploader # carrierwaveを使っています(今回の説明では関係ない)
# validationは関係ないので省略します
# post.rbやcomment.rbなどで適切にアソシエーションが設定されていると仮定してください
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :like_posts, through: :likes, source: :post
has_many :active_relationships, class_name: 'Relationship',
foreign_key: 'follower_id',
dependent: :destroy
has_many :passive_relationships, class_name: 'Relationship',
foreign_key: 'followed_id',
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
# 色々とメソッドがあるんですが、関係ないので省略します
# このfollowing_idsとか<<とか、何なの!?
def feed
Post.where(user_id: following_ids << id)
end
end
なお、includes(:user).page(params[:page]).order(created_at: :desc)
についてはこの記事で取り扱いませんが、気になる方は適宜調べてください。
補足をしておくと、page(params[:page])
と書けるのはkaminari
というgemを導入しているからです。
クラス・インスタンスの概念を把握する
まず、クラスとインスタンスの関係について自分なりに説明します。
これは個人的な理解なので、誤った解釈を導く可能性があります。
参考程度にお聞きください。
誤解を恐れずに言いますと、こんな感じです。
クッキーの型取りする銀色のやつと、実際に型取りされたクッキーの関係に似てますかね笑
- クラス: 抽象的な概念としての型
- インスタンス: 型から縁取られて出てきた具体的な個体
例えば、概念として「犬」という言葉を私たちは理解してますけど、
その時点で理解している「犬」というのは、すごいふわふわしたものです。
別に柴犬でもダックスフンドでも犬ですし、同じ柴犬でも大きさが微妙に違います。
けど、概念としての犬であることには違いないわけで、それを私たちは全て犬として認識しています。
この認識している概念としての犬こそが、クラスとしての犬です。
(便宜的に、犬クラスは犬種と長さという属性を持つクラスだと定義しましょう。)
ただ、この世界に存在するのは、全て具体的な犬です。
犬インスタンスです! 犬オブジェクトです!
犬インスタンスは、属性(attributesというやつです)を持っています。
犬インスタインス(id:1)は、犬種:柴犬、長さ:1mという属性を持っているかもしれませんが、
犬インスタンス(id:2)は、犬種:ダックス、長さ:50cmという属性かもしれません。
ちなみに、英語が多少できる人なら、インスタンスの意味から覚えるといいかもしれません。
インスタンスは、for example と同じ意味の for instance の instance です。
クラスの具体例なので、インスタンスって言うんですね!・・・きっと笑
メソッドについて考える
クラスとインスタンスについて把握して上で、メソッドについて考えてみましょう。
メソッドというのは「できることリスト」みたいなものです。
便宜的に、犬クラスは「走る」というメソッドを持っている、ということにしましょう。
犬クラスというのは概念的なものなので、走ることはできません。
走らせるとエラーになります。走れるのは犬インスタンスだけです。
(User.saveなどと書いてエラーになったとき、犬クラスは走れないことを思い出しましょう)
逆に犬オブジェクトから、犬種(という属性)が柴犬である犬を探すことはできません。
柴犬を見つけてきたい場合、犬クラスから探しましょう。
(@user.whereなどと書いてエラーになったとき、あなたはペットのゴン太という柴犬から、
柴犬を探そうとしているなんだか残念な人だという自覚を持ちましょう)
current_user
って何?
かなり冗長に説明しましたが、そろそろ具体的に考えていきましょう。
まず、current_user
から考えてみましょう。
current_user
は、sorcery
というgemを導入することにより使えるメソッドです。
公式GitHubをみると、そのメソッドのコードが書いてあります。
# attempts to auto-login from the sources defined (session, basic_auth, cookie, etc.)
# returns the logged in user if found, nil if not
def current_user
unless defined?(@current_user)
@current_user = login_from_session || login_from_other_sources || nil
end
@current_user
end
# https://github.com/Sorcery/sorcery/blob/a02c1247642357129ea739354c22978a06fccaa9/lib/sorcery/controller.rb#L87
# 87行目にcurrent_userメソッドが記載されています
- Sorcery/sorcery: Magical Authentication
- login_from_sesssionってどういうメソッドか気になる方はこちらからどうぞ
- login_from_other_sourcesってどういうメソッドか気になる方はこちらからどうぞ
え、メソッドなの!? と思われたあなた!
その発想が出てくるのは非常によいと思います!(初学者のくせに偉そう・・・)
辿っていくと分かりますが、sessionのuser_id
を使って、
Userクラスから該当のユーザーを取得すると書いています。
なので、まあ結果として、current_user
は、Userクラスのインスタンス
として考えてほぼ差し支えありません。
ただ、正確にはcurrent_user
はメソッドであって、その返り値がUserクラスの
インスタンスになるように設計されていることを覚えておいてください。
feed
って何クラスの何メソッド?
feed
というメソッドですが、Userモデル(Userクラス)のインスタンスメソッドです。
インスタンスメソッドなので、userインスタンスのみ実行できます。
先ほど言ったとおり、current_user
はメソッドではあるのですが、
そのメソッドを実行した結果として、Userクラスから該当のログインユーザーを取得します。
(面倒なので、以後このログインユーザーをcurrent_user
と呼びます)
このcurrent_user
は、userインスタンスであるため、
Userモデルのインスタンスメソッドであるfeed
というメソッドが使ます。
クラス | インスタンス | メソッド |
---|---|---|
User | current_user |
feed |
current_user
はfeed
メソッドを実行すると、
Post.where(user_id: following_ids << id)
の返り値を取得します。
current_user.feed
#=> `Post.where(user_id: following_ids << id)`の返り値を取得
feed
メソッドの中身を見ていく(following_idsとは)
続いて、Post.where(user_id: following_ids << id)
を見ていきます。
特に見慣れない部分がfollowing_ids << id
の箇所だと思います。
私もこれを見て、「え、あ、えと、分からない、ごめんなさい」ってなりました 笑
おそらく、多くの初学者にとって、ここが1番のつまづきポイントだと思います。
following_ids
ですが、これはアソシエーションでhas_many
書くと使えるメソッドです。
「え、メソッド!」と思ったかもしれませんが、Railsガイドに書いてます。
collection_singular_idsメソッドは、そのコレクションに含まれるオブジェクトの
idを配列にしたものを返します。
ちなみに、@user.comments
と書くときのcomments
も実はメソッドです。
collection
メソッドは、関連付けられたすべてのオブジェクトのリレーションを返します。
関連付けられたオブジェクトがない場合は、空のリレーションを1つ返します。
@books = @author.books
@user.comments
と書くとき、.comments
と続けると@user
に紐づくコレクションを
取得できるという覚えゲーでもあまり問題ありません。
むしろ、そう覚えても問題ないよう設計されているRailsが素晴らしいんです。
ただ、実はこれもメソッドだと知っておくと、理解がより深まっていくのではないでしょうか。
binding.pry
を使ってみよう
では、binding.pry
を使って、following_ids
の挙動を確認しましょう。
まず、posts_controller.rb
のindexアクションにbinding.pryって書きます。
書く場所は、ここにします。
gem 'pry-rails'
は導入しておいてください。
導入方法や使い方は、この記事などを参考にするとよいでしょう。
def index
binding.pry
@posts = if current_user
current_user.feed.includes(:user).page(params[:page]).order(created_at: :desc)
# 省略
binding.pry
と書いた後、ブラウザでトップ画面をリロードしましょう。
すると、リロードしている状態で画面が止まるはずです。
ターミナルで確認してみると、rails c
の時と同じような形でデバッグができます。
デバッグを始める前に、しつこいですが、クラスとインスタンスとメソッドのことを思い出しましょう。
following_ids
は何ですか。そう、メソッドですよね。では、何のメソッドでしょう。
そう、インスタンスのメソッドですよね。
具体的なコレクション(複数のオブジェクト)がコレクションの各IDを返すメソッドなので、
これはインスタンスメソッドです。
なので、いきなりfollowing_ids
と書くとエラーになります。
今回はログインしているユーザーがフォローしているユーザーたちのID、つまりfollowing_ids
を
取得したいので、current_user.following_ids
と入力してみましょう。
フォローしているユーザーがいれば、配列が返ってくるはずです。
Processing by PostsController#index as HTML
From: 〜〜〜/app/controllers/posts_controller.rb @ line 5 PostsController#index:
3: def index
4: binding.pry
=> 5: @posts = if current_user
6: current_user.feed.includes(:user).page(params[:page]).order(created_at: :desc)
[1] pry(#<PostsController>)> current_user.following_ids
User Load (1.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 7 LIMIT 1
↳ (pry):6
(1.6ms) SELECT `users`.`id` FROM `users` INNER JOIN `relationships` ON `users`.`id` = `relationships`.`followed_id` WHERE `relationships`.`follower_id` = 7
↳ (pry):6
=> [3, 10]
ここで、もしかしたら疑問に思ったかもしれません。
「あれ、feed
メソッドではcurrent_user
なんて書かずにfollowing_ids
と
入力していたような気がするけど、なんでだろう???」と。
self
は省略されている
モデルで書くインスタンスメソッドですが、ここではself
が省略されています。
つまり、こういうことです。
# ①と②は同じ
Post.where(user_id: following_ids << id) #=> ①
Post.where(user_id: self.following_ids << self.id) #=> ②
ちなみに、scivolaさんにご指摘いただきましたが、解釈の順番はこうなっています。
Post.where(user_id: (self.following_ids << self.id))
どうですか?
分かりやすくなったと感じた方もいるかと思います。
では、current_user.feed
と書いた場合について考えてみましょうか。
要はこういうことが起きています。
Post.where(user_id: current_user.following_ids << current_user.id)
続いて、id: 1
であるユーザーについて考えみましょう。
要はこういうことです。
Post.where(user_id: User.find(1).following_ids << User.find(1).id)
だいたい分かってきたんじゃないでしょうか。
ここではself
が省略されていますが、インスタンスが持っているメソッドが使用されていたり、
インスタンスが持っている属性の取得が行われているのです。
奥さん、<<
ってメソッドなんですって
タイトルのとおりですが、「<<」はメソッドなんです。
え、メソッドって文字じゃなくていいんですか。。。 これはさすがにビビりますよね。
(私だけですかね。。。)
これは、Array(配列)クラスのインスタンスメソッドなんです。
ary = [1]
ary << 2
p ary # [1, 2]
では、振り返っていきましょう。
current_user.feed
と書く場合、following_ids << id
がどういう挙動になるか確認します。
# current_user(id: 1)が、`id: 4`と`id: 6`のユーザーを2人フォローしていると仮定
current_user.follwing_ids << current_user.id
[4, 6] << 1
[4, 6, 1]
分かりましたか。
つまり、フォローしているユーザーのidの配列に対して、自分のユーザーidを加えているのです。
そういえば、引数って分かりますか?
先ほどの事例の場合、難しいところがなかったので、すっと理解できたかと思います。
ただ、重要になってくるので、ary << 2
と書く時、2
は引数であると覚えてください。
抽象的に説明しても分かりづらいので、まず引数があるメソッドと引数がないメソッドを見てみましょう。
大して犬好きでもないんですけど、犬の例で説明してしまっているので、初志貫徹したいと思います。
Class Dog
# 本当はここに色々なくちゃいけないと思いますが、とりあえずスルーします
def bark
"わんわん"
end
def run(speed)
"#{speed}キロでダッシュした!"
end
end
ちゃんとした方々に怒られそうですが、Dogというクラス、つまりDogという抽象的な概念があって、
その型から生み出されたdogインスタンスには、bark
とrun
というメソッドがあると想定してください。
じゃあ、dogインスタンスを誕生させましょう。
本当はdog
という変数にすることが一般的なんでしょうけど、
せっかくなので愛着を持って、wankoという変数にしましょう。
wanko = Dog.new
このwankoですが、bark
とrun
ができます。
wanko.bark #=> "わんわん"
wanko.run(100) #=> "100キロでダッシュした!"
無理させて100キロでダッシュさせてしまいましたが、この100というのが引数です。
メソッド内で変数を扱いたい場合、・・・と言うと分かりづらいので言い換えます。
引数を使うと走る速度に幅を持たすことができて、それはなぜかというと、
引数という「?」な速度をメソッド内に仮で書いておくことで、後から代入させる設計にできるからです。
この引数ですが、Rubyだと括弧を省略してこのように書くこともできます。
この書き方、個人的には気持ち悪いですが、この自由な感じこそがRubyの特徴らしいです。
wanko.run 100 #=> "100キロでダッシュした!"
following_ids.<<(id)
って書いてもいいんですよ
さて、冗長な引数の説明にイラだった方もいるかもしれないですが、
それは<<
がメソッドであり、id
が引数だっていうことを改めてよく知ってほしいからです。
だって、メソッドって多くの場合どうやって書きますか?
そう、wanko.run(100)
っていうのがよく見る形ですよね。
それにもかかわらず、following_ids << id
の<<
がメソッドだって言われてもしっくりこないのは、
<<
のインパクトもさることながら、書き方がよく見る形ではないからだと思うんです。
ということで、書き換えてみましょう。
こんな感じで。
following_ids.<<(id)
これまでと雰囲気が一転したので、すごく気持ち悪い感じになってしまいましたが、
<<
はメソッドであって、id
は引数なので、これでいいんです!!!
・・・これで動くのか疑問に思った方、いるかと思います。
私も理論専攻で先走ってこの文章を書いていたので、動くのかちょっと不安でした。。。
ただ、書き換えてみたら、やっぱりちゃんと動きました! よかった!
ちなみに、<<
と似たappend
やpush
といった似たメソッドがあります。
なので、こんな感じで書き換えることが可能です。
(<<
は単一の要素、append
やpush
は複数の要素を追加できるメソッドという違いがあります)
# ①と②と③は同じ
# following_ids << id の書き換え
following_ids.<<(id) #=> ①
following_ids.append(id) #=> ②
following_ids.push(id) #=> ③
user_id: following_ids << id
について考えよう
さて、徐々に範囲を拡大しましょう。
user_id: following_ids << id
について考えましょう。
これは、ハッシュというやつです。
ハッシュは、キーと値の組み合わせでデータを管理するオブジェクトのことです。(チェリー本のP147)
キー | 値 |
---|---|
:user_id | following_ids と current_user.id |
:user_id
というシンボル形式のキーがあって、
その値がfollowing_ids
とcurrent_user.id
です。
(フォローしているユーザーのids + ログインユーザーのid)
このハッシュが、whereというメソッドの引数になっていると認識しましょう。
:user_id
がどこから出てきたのか分からないあなた、
さらっとでよいので、ハッシュやシンボルについて勉強しましょう!
勉強をすると、以下のとおり書き換えができることも分かるはずです。
Post.where(user_id: following_ids << id)
Post.where(:user_id => following_ids << id)
ちなみに、=>
を使う書き方は、ハッシュロケット記法といいます。
ハッシュロケット記法は、古い記法です。
未だにアプリなんかで使っていると古いコードをコピペしたのがバレます笑
=>
が出てきたら、書き換えをするのを怠らないようにしましょう。
where
メソッドについて考えよう
さて、そろそろ全体を理解することができそうですね。
次は、where
メソッドについて理解しましょう。
where
メソッドですが、これはPostというクラスに対して実行するクラスメソッドです。
(若干不安ですが、たぶんクラスメソッドと言い切って良いと思います)
where
メソッドには引数があり、様々な形で書くことができます。
# 文字列で検索
Post.where("body = 'みけたさんカッコイイですね'")
# ハッシュ形式で検索
Post.where(body: 'みけたさんカッコイイですね')
さて、Post.where(user_id: following_ids << id)
の場合において、
where
メソッドの引数が何になるのか考えましょう。
user_id: following_ids << id
はどのような形式になるのでしょうか。
既に考察したとおり、user_id: following_ids << id
はハッシュ形式になります。
そのことを踏まえて、また順を追って、具体的な事例に即して理解していきます。
# Post.where(user_id: following_ids << id)について
# current_user(id: 1)が、`id: 4`と`id: 6`のユーザーを2人フォローしていると仮定
Post.where(user_id: current_user.follwing_ids << current_user.id)
Post.where(user_id: [4, 6] << 1)
Post.where(user_id: [4, 6, 1])
Railsガイドでは、この事例の場合においてどのようなSQLが発行されるか説明しています。
そう、発行されるSQLは、以下のとおりですね。
where
メソッドは、Post全体の中からuser_id
が条件に合致するものを探しているのです。
SELECT * FROM post WHERE (posts.orders_count IN (4,6,1))
まとめ
さて、冒頭から最後まで流れを追って確認しましょう。
すんなりと理解できるようになっていないでしょうか。
# Postクラスのオブジェクトであるcurrent_userがfeedメソッドを実行し、返り値を取得
current_user.feed
# では、feedメソッドの中身を見ていきましょう
def feed
Post.where(user_id: following_ids << id)
end
# feedメソッドをcurrent_userが使ったので、①と②は同じ
current_user.feed #=> ①
Post.where(user_id: current_user.following_ids << current_user.id) #=> ②
# current_user(id: 1)が、`id: 4`と`id: 6`のユーザーを2人フォローしていると仮定する
Post.where(user_id: [4, 6] << 1)
Post.where(user_id: [4, 6, 1])
【補足】 破壊的メソッドと非破壊的メソッド
# current_user(id: 1)が、`id: 4`と`id: 6`のユーザーを2人フォローしていると仮定
current_user.follwing_ids << current_user.id
[4, 6] << 1
[4, 6, 1]
分かりましたか。
つまり、フォローしているユーザーのidの配列に対して、自分のユーザーidを加えているのです。
以上のように書きましたが、正しい理解をする上で、<<
はcurrent_user.following_ids
という
インスタンス自体を変更し、その変更した配列自体を返り値として返す「破壊的メソッド」であるという
ことを押さえておく必要があります。(scivolaさん、ありがとうございます)
え、どういうことかって。
こういうことです。
# `<<`は、current_user.following_ids自体を変更し、その変更したcurrent.following_ids自体を返す
current_user.follwing_ids << current_user.id
[4, 6] << 1
[4, 6, 1]
# current_user.following_idsを実行してみる
current_user.following_ids
[4, 6, 1]
実は、インスタンス自体は[4, 6]
のままで変更しないけど、
返り値として[4, 6, 1]
を返す+
という非破壊的なメソッドもあります。
# `+`は、current_user.following_ids自体を変更しない
# current_user.idは配列形式にする必要がある
# Integer型の値を引数とする場合、to_aryメソッドによる暗黙の型変換を試みるが、TypeError: no implicit conversion of Integer into Arrayが発生
current_user.follwing_ids + [current_user.id]
[4, 6] + [1]
[4, 6, 1]
# current_user.following_idsを実行してみる
current_user.following_ids
[4, 6]
メソッドの返り値が何なのか意識することで、レベルアップが図れます!(どの立場・・・)
【補足】 アソシエーションしている場合の<<
メソッドもあります!
話の流れ上、完全に話の腰を折るので最後に付け足すように書きましたが、
実は<<
メソッドには違うものがあります。
(とはいっても、その違いをあまり意識することなく使えるよう設計されていますが)
まずRailsガイドを見てください。
collection<<メソッド
は、1つ以上のオブジェクトをコレクションに追加します。
このとき、追加されるオブジェクトの外部キーは、呼び出し側モデルの主キーに設定されます。
@author.books << @book1
つまり、アソシエーションしている場合に使える<<
メソッドというのがRailsで定義されていて、
この<<
メソッドを使う場合、オブジェクト自体を追加することができるんです。
# current_user(id: 1)が、`id: 4`と`id: 6`のユーザーを2人フォローしていると仮定
# 表現が難しいので、イメージで書きます
current_user.following << current_user
[User.find(4), User.find(6)] << User.find(1)
[User.find(4), User.find(6), User.find(1)]
# 各userオブジェクトには、ユーザーの属性であるusernameやemailなどが保管されているとイメージしてください
ちなみに、先ほどの事例においては配列のように表現してしまいましたが、
正確にはCollectionProxy
というクラスのオブジェクトです。
(scivolaさん、ありがとうございます)
# current_user(id: 1)が、`id: 4`と`id: 6`のユーザーを2人フォローしていると仮定
# 長くなるので、パスワード関係やcreated_atなどは省略します
current_user.following << current_user
# 以下がcurrent_user.following
[<User:0x00007fc5677d2f10
id: 4,
email: "number4@example.com",
username: "4番さん",
avatar: nil>,
<User:0x00007fc5677d2740
id: 6,
email: "number6@example.com",
username: "6番さん",
avatar: nil>
]
# 以下がcurrent_user
<User:0x00007fc54dbced18
id: 1,
email: "number1@example.com",
username: "1番さん(ログインユーザー)",
avatar: nil>
# current_user.following << current_user
[<User:0x00007fc5677d2f10
id: 4, #=> 以下省略>,
<User:0x00007fc5677d2740
id: 6, #=> 以下省略>,
<User:0x00007fc54dbced18
id: 1, #=> 以下省略>
]
なお、もし英語が苦手でなければ、こちらを参照してもよいかもしれません。
事例つきで、挙動について紹介してくれています。
【蛇足】 dashはいいぞ!
私はdashを愛用しています。
メソッドについて調べる時に、非常に便利です。
さくっと調べることができて、使い方や引数も分かります。
英語なのがネックですが、日本語だと情報量が限られるのでそこは我慢しましょう。
Google検索だと調べるのに時間がかかるところ、数秒で調べられるのはメリットです。
気軽に調べられると、調べる回数が増えます!!
また、英語が苦手な方は、翻訳機能が使えるようなカスタマイズをしてみてはどうでしょう。
試せていないですが、こんなQiita記事を上げている方がいました。
Qiita等でも結構紹介されているので、Dashの導入を検討してみてください。
体験版がダウンロードできるので、是非試してもらいたいです。これは本当に便利です!
有料なものはちょっとという方は、DevDocsを使うとよいかもしれません。
最近試したのですが、単純調べるだけであればこちらでも十分かもしれません!