4

したがって、これらの質問がどれほど初心者に見えるかについてお詫び申し上げます。私はレールに不慣れで、最初のタスクとしてNeo4Jも導入しました。プロジェクトを成長させるのに最適だと思われたからです。

アクションの流れを説明してから、サンプルコードを示します。現在、ステップ 3-5 に追加しようとしています。

  1. ユーザーは FB 経由でログインします
  2. 最初のログインでユーザー ノードが作成されます。ユーザーが存在する場合、そのユーザー+ノードを取得するだけです
  3. ユーザー ノードが作成された後、koala gem を使用して FB グラフ API にアクセスします。
  4. アプリを使用している各フレンドのフレンドリストを取得します。
  5. 各フレンドを調べて、2 人のユーザー間の双方向のフレンドシップ関係を追加します。

3-5 はユーザーが最初に参加したときにのみ発生する必要があるため、コールバックに関連付けられたメソッドでこれを実行できると考えましたafter_save。ただし、ある時点で追加の属性を使用してユーザーを更新する必要があり、after_save が再度呼び出されるため、このロジックには欠陥があります。更新でこれを防ぐことはできますか?

参照用のSessionsController

  def create
    user = User.from_omniauth(env["omniauth.auth"])
    session[:user_id] = user.id  
    redirect_to root_url
  end

  def destroy
    session.delete(:user_id)
    redirect_to root_path
  end

したがって、私のuser.rbには次のようなものがあります

 has_many :both, :friendships

  after_save :check_friends


  def self.from_omniauth(auth)
    @user = User.where(auth.slice(:provider, :uid)).first

    unless @user
      @user = User.new
      # assign a bunch of attributes to @user

      @user.save!
    end
    return @user
  end

  def facebook
    @facebook ||= Koala::Facebook::API.new(oauth_token)

    block_given? ? yield(@facebook) : @facebook
      rescue Koala::Facebook::APIError => e
      logger.info e.to_s
      nil
  end

  def friends_count
    facebook { |fb| fb.get_connection("me", "friends", summary: {}) }
  end

  def check_friends(friendships)
    facebook.get_connection("me", "friends").each do |friend|
      friend_id = friend["id"]
      friend_node = User.where(friend_id)
      Friendship.create_friendship(user,friend_node)
      return true
    end
  end

友情.rb

  from_class User
  to_class   User
  type 'friendship'

  def self.create_friendship(user,friend_node)
    friendship = Friendship.create(from_node: user, to_node: friend_node)
  end   

リレーションシップ ノードの作成方法が正しいかどうかはわかりません。作成したばかり@userのように、それをメソッドに組み込みcheck_friends、ユーザー ノードとフレンド ノードを適切に取得して、2 つをリンクできるようにするにはどうすればよいでしょうか。

現時点では、user と friend_user がノードであることを認識していません。

他の悪いコード プラクティスを見つけた場合は、お知らせください。

事前に: @subvertallchris の助けに感謝します。このような私の多くの質問にあなたが答えてくれると確信しています。

4

1 に答える 1

5

これは本当に素晴らしい質問です!正しい方向に進んでいると思いますが、変更できる点がいくつかあります。

has_manyまず、その方法を調整する必要があります。アソシエーションは、ActiveRel クラスではなく、常にノードで終了する必要があるため、次のように書き直す必要があります。

has_many :both, :friends, model_class: 'User', rel_class: 'Friendship'

そうしないと、いくつかの問題が発生します。

Neo4j のスタイルの一貫性のために、リレーションシップ タイプの名前を変更することを検討してください。悪い例がたくさんあるので、悪い考えを与えてしまったら申し訳ありません。FRIENDS_WITHより良い関係名になります。

大きな問題の処理に関しては、ここでできることがたくさんあります。

編集!くそ、肝心なことを忘れてた!そのafter_saveコールバックを捨てて、既存の読み込み/新しいユーザー動作の作成を 2 つの方法にします。

class SessionsController < ApplicationController
  def create
    user = User.from_omniauth(env["omniauth.auth"])
    @user = user.nil? ? User.create_from_omniauth(env["omniauth.auth"]) : user
    session[:user_id] = @user.id
    redirect_to root_url
  end

  def destroy
    session.delete(:user_id)
    redirect_to root_path
  end
end


class User
  include Neo4j::ActiveNode
  # lots of other properties
  has_many :both, :friends, model_class: 'User', rel_class: 'Friendship'

  def self.from_omniauth(auth)
    User.where(auth.slice(:provider, :uid)).limit(1).first
  end

  def self.create_from_omniauth(auth)
    user = User.new
    # assign a bunch of attributes to user
    if user.save!
      user.check_friends
    else
      # raise an error -- your user was neither found nor created
    end
    user
  end

  # more stuff
end

それはそれを始めることであなたの問題を解決するでしょう. 全体をトランザクションにラップしたい場合があるので、それについては wiki を読んでください。

しかし、まだ終わっていません。あなたのオリジナルを見てみましょうcheck_friends

def check_friends(friendships)
  facebook.get_connection("me", "friends").each do |friend|
    friend_id = friend["id"]
    friend_node = User.where(friend_id)
    Friendship.create_friendship(user,friend_node)
    return true
  end
end

実際には引数を渡していないので、それを取り除きます。また、単一のノードのみを探していることがわかっている場合は、 を使用しますfind_byfacebook_id各ユーザーにプロパティがあると仮定します。

def check_friends
  facebook.get_connection("me", "friends").each do |friend|
    friend_node = User.find_by(facebook_id: friend["id"])
    Friendship.create_friendship(user,friend_node) unless friend_node.blank?
  end
end

メソッドは true または false を返す必要があるため、create_friendshipメソッドの最後のステートメントがそれを行うようにするだけで、返されるものは何でも返すことができます。それはこれと同じくらい簡単です:

def self.create_friendship(user, friend_node)
  Friendship.new(from_node: user, to_node: friend_node).save
end

createtrue または false を返さず、結果のオブジェクトを返すためsave、新しいオブジェクトにチェーンすると、必要なものが得られます。メソッド内でさらに使用する予定がない限り、そこに変数を設定する必要はありません。

この時点で、ActiveRel モデルにコールバックを簡単に追加できます。このコールバックは、作成したばかりの User に対してafter_create何かを実行します。from_nodeただし、そこから必要に応じてユーザーのプロパティを更新できます。この種の動作を制御することが、まさに ActiveRel が存在する理由です。

私はおそらくそれをもう少し作り直したでしょう。モジュールに自分のものを移動することから始めfacebookます。User モデルをよりクリーンに、より集中的に保つことができます。

# models/concerns/facebook.rb

module Facebook
  extend ActiveSupport::Concern

  def facebook
    @facebook ||= Koala::Facebook::API.new(oauth_token)

    block_given? ? yield(@facebook) : @facebook
      rescue Koala::Facebook::APIError => e
      logger.info e.to_s
      nil
  end

  def friends_count
    facebook { |fb| fb.get_connection("me", "friends", summary: {}) }
  end
end

# now back in User...

class User
  include Neo4j::ActiveNode
  include Facebook
  # more code...
end

あなたのモデルがこれらの散らかったグラブバッグになるのは本当に簡単です. 多くのブログがこれを奨励します。衝動と戦ってください!

これは良いスタートになるはずです。ご不明な点がございましたら、または私が何かを台無しにしてしまった場合はお知らせください。多くのコードがあり、その一部を明確にしたり微調整したりする必要がある可能性があります。しかし、それが役立つことを願っています。

于 2014-10-24T23:07:54.960 に答える