所以我为这些问题看起来很菜鸟而道歉。我是rails的新手,作为第一个任务,我还引入了Neo4J,因为如果我发展这个项目,它似乎是最合适的。
我将解释操作流程,然后展示一些示例代码。我现在正在尝试添加步骤 3-5。
- 用户通过FB登录
- 第一次登录将创建一个用户节点。如果用户存在,则只需检索该用户+节点
- 创建用户节点后,考拉宝石用于访问FB图形API
- 使用该应用程序检索每个朋友的朋友列表。
- 遍历每个好友,添加两个用户之间的双向友情关系
由于 3-5 只需要在用户首次加入时发生,因此我认为我可以在与回调关联的方法中执行此操作after_save
。不过,此逻辑存在缺陷,因为我需要在某个时候使用其他属性更新用户,并且它将再次调用after_save。我可以通过更新来防止这种情况发生吗?
供参考的会话控制器
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
方法中并正确检索用户和朋友节点,以便我可以将两者链接在一起。
现在它不知道用户和friend_user是节点
如果您看到其他不良代码实践,请告诉我!
提前:感谢您的帮助@subvertallchris。我相信你会回答我的很多这样的问题。
这是一个非常好的问题!我认为你走在正确的轨道上,但有一些事情你可以改变。
首先,您需要调整该has_many
方法。您的关联始终需要在节点处终止,而不是在 ActiveRel 类处终止,因此您需要将其重写为如下所示的内容:
has_many :both, :friends, model_class: 'User', rel_class: 'Friendship'
否则你会遇到一些问题。
为了 Neo4j 风格上的一致性,您可能需要考虑重命名您的关系类型。我有很多不好的例子,如果我给你不好的想法,很抱歉。 FRIENDS_WITH
会是一个更好的关系名称。
至于处理你的大问题,你可以在这里做很多事情。
编辑!废话,我忘记了最重要的部分!放弃after_save
回调并使负载存在/创建新用户行为两种方法。
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_by
.我将假设每个用户都有一个facebook_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
create_friendship
方法应该返回 true 或 false,所以只需使该方法的最后一个语句执行此操作,您就可以返回它返回的任何内容。就这么简单:
def self.create_friendship(user, friend_node)
Friendship.new(from_node: user, to_node: friend_node).save
end
create
不返回 true 或 false,它返回结果对象,因此将save
链接到新对象将获得所需的内容。你不需要在那里设置变量,除非你打算在方法中更多地使用它。
此时,您可以轻松地向ActiveRel模型添加after_create
回调,该回调将在from_node
上执行某些操作,该回调始终是您刚刚创建的用户。您可以根据需要从那里更新用户的属性。控制这种行为正是ActiveRel存在的原因。
我可能会再做一点,不过。首先将facebook
内容移动到模块中。它将使您的用户模型更清晰、更专注。
# 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
你的模特很容易变成这些凌乱的抓包。很多博客都会鼓励这样做。与冲动作斗争!
这应该是一个良好的开端。如果您有任何问题或我搞砸了什么,请告诉我,有很多代码,我可能需要澄清或调整其中的一些代码。不过,希望它有所帮助。