セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

ruby チュートリアル 14章メモ

ActiveRecord through

n:mの関係を表すのに使用。1:nの関係はhas_manybelongs_toで表した。また後者の繋ぎ方であると、直接的な結びつきになるのに対して、throughを使用する場合は、2モデル間にクッションとなるモデルを定義してそこに関係をしまっていくイメージ。チュートリアルではユーザ間のフォローに使用して、誰が誰をフォローしているかを表すために使用。
f:id:thinline196:20181205135325p:plain
f:id:thinline196:20181205135432p:plain

userに追加する

userモデルと新たに作成したrelationshipモデルに関係性を追記する。

# app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy # micropostは外部キーがuser_idであったためこれでok
  has_many :active_relationships, class_name:  "Relationship", #外部キーはfollower_idとして定義しなければならない(followerというクラスは存在しない)
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  ...
end


# app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end


定義されるメソッド

active_relationship.follower フォロワーを返します active_relationship.followed フォローしているユーザーを返します user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力) user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す

非同期通信

form_withを使用して非同期通信を行う場合、remote:trueを指定してあげることで非同期通信となる。一つ注意としては、form_withではデフォルトでremote:trueが設定されている。(前身のform_forではデフォルトではなく明示的に指定が必要であった) なので、非同期にしたくない場合にlocal:trueを指定してあげることを忘れないようにする。

Unobtrusive Javascript

RailsではJSを前面に出さないようにする習わしがあるらしい。 http://railscasts.com/episodes/205-unobtrusive-javascript

respond_to

コントローラ内で使用した。リクエストで指定されたフォーマットでレスポンスを返すためのメソッド。

    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end

例えばこれがcreateメソッド内で呼ばれているのであれば、views/対応するモデル複数名/create.js.erbJSでのレスポンスの際に呼ばれるファイルとなる。

にしてもこの記事は酷いと思う。 => https://techacademy.jp/magazine/17744

Ajaxのテスト(RSpec)

RSpecajaxフォームのテストを行うにはxhrを追記してあげる。具体的には下。

    xhr :get ~~~

model間のRailsの予測

# models/user.rb
has_many :followeds, through: :active_relationships

上の1行をみて、railsactive_relationshipsfollowed_idを使って対象ユーザを取得しようとする(複数形を単数形にする). 自分でカラムを指定する場合、自分で名前を指定したい場合は下のように書く。

has_many :following, through: :active_relationships, source: :followed

followed+_idカラムを対象とし、userモデル内で呼び出すときはfollowingという名前で呼び出すことが可能。

個人的なメモ

# /models/user.rb
  has_many :active_relationships, class_name: "Relationship",
                                  foreign_key: "follower_id",
                                  dependent: :destroy
  has_many :following, through: :active_relationships, source: :followed

上のコードがuserモデルに書いてあることで、follower_idfollowed_idのカラム(どちらもUserモデルにbelongs_to)をもつRelationshipモデルから、user.followingで、relationshipのfollowedにあるid(userがフォローしているアカウント)が配列で取得できる。鍵になっているのが上の定義で、active_relationshipsを呼ぶことで、関連付いている自分のidfollower_idを頼りに、関係あるデータだけを取得してきている。よって、下のfollowingの定義では実質”関係あるデータの中から”のみデータを探索することができている。(下の定義だけ見てては、どうやって自分に関係あるレコードを抽出している理解できず、困惑していた)

memberとcollection

config/route内でresourcesを使用してルーティングを設定するとき、collectionメソッドを使用すると、resourcesで設定したルーティングにつけ加わる感じで、新たなアクション&ルーティングを定義できる。

resources :users do
  collection do
    get :tigers
  end
end


memberメソッドを使用すると特定のデータに対するアクションを生成することが可能になる。チュートリアルでは、ユーザのフォロー/フォロワーを表示するために使用。(~/users/1/following)

  resources :users do
    member do
      get :following, :followers
    end
  end


map

rubyのmapメソッドは、列挙可能なオブジェクトを配列に変換してくれる。

[3] pry(main)> [1,2,3,4].map{|i| i.to_s}
=> ["1", "2", "3", "4"]

[4] pry(main)> [1,2,3,4].map(&:to_s) #省略系
=> ["1", "2", "3", "4"]

[7] pry(main)> [1,2,3,4].map(&:to_s).join(",")
=> "1,2,3,4"
[8] pry(main)>

チュートリアルではユーザがフォローしている人のidを取得するの使用

>> User.first.following.map(&:id)
=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

ActiveRecordでは、下のようなメソッドも用意されていて全く同じ動きをする。これはhas_many :followingの関連付けを行った時に自動で生成され、+_idsをつける形で実装される。

>> User.first.following_ids
=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

サブクエリ

SELECT文のなかにSELECT文を記述する(括弧でくくる)。一般的に可読性はよくなる。チュートリアルでは効率をあげるためとの表記があった。これは、既存実装のfollowing_idsを使用した場合、DBへのアクセスが二回発生するからとのこと。サブクエリとして自分で実装することで、DB内でちょちょいとやってくれるらしい。