我有两个模型,User
和Account
。
# account.rb
belongs_to :user
# user.rb
has_one :account
Account
有一个属性name
。在我看来,我多次打电话给current_user.account.name
,我听说这不是一个很好的方法。所以我非常迅速,我在user.rb
def account_name
self.account.name
end
所以现在在我看来,我可以简单地调用current_user.account_name
,如果关联发生变化,我只会在一个地方更新它。但我的问题是,我测试这种方法吗?如果我这样做,我怎么测试它没有任何神秘的客人?
答案 0 :(得分:1)
我同意current_user.account.name没有任何问题 - 而Sandi Metz会告诉我们"用户对账户了解太多"这是你可以真正避免使用Active Record的事情。
如果你发现你正在做很多这些方法遍布用户模型,你可以使用rails delegate方法:
delegate :name, :to => :account, :prefix => true
使用:prefix => true选项将在User模型中为方法添加前缀,因此它是account_name。在这种情况下,我认为你可以在方法上写一个非常简单的单元测试,它返回某些东西只是因为帐户中的属性会改变你的测试会失败所以你会知道你需要更新委托方法。
答案 1 :(得分:0)
我个人认为没有任何理由。只需使用current_user.account.name。
如果您担心效率,请让current_user返回加入帐户的用户。
答案 2 :(得分:0)
这有点偏离主题。所以,如果不感兴趣或有帮助,请提前道歉。
TL; DR:不要将您的模型知识放在您的视图中。让你的控制器变瘦。这就是我一直在做的事情。
在我目前的项目中,我一直在努力确保我的观点完全不了解系统的其他部分(减少耦合)。这样,如果您决定更改实施方式(例如,current_user.account.name
与current_user.account_name
),那么您无需进入视图并进行更改。
每个控制器操作都提供一个@results
哈希,其中包含视图需要正确呈现的所有内容。 @results
哈希的结构本质上是视图和控制器之间的契约。
因此,在我的控制器中,@results
可能看起来像{current_user: {account: {name: 'foo'}}}
。在我看来,我会做@results[:current_user][:account][:name]
之类的事情。我喜欢使用HashWithIndifferentAccess
,所以我也可以@results['current_user']['account']['name']
进行操作,而不会让事情爆炸或行为不端。
此外,我一直在尽可能多地将控制器中的逻辑移动到服务对象中(我称之为'管理员')。我发现我的经理(这些是PORO)比控制器更容易测试。所以,我可能会:
# app/controllers/some_controller.rb
class SomeController
def create
@results = SomeManager.create(params)
if @results[:success]
# happy routing
else
# sad routing
end
end
end
现在,我的控制器超级瘦,并且除了路由之外不包含任何逻辑。他们对我的模特一无所知。 (事实上,几乎所有的控制器动作看起来完全相同,基本上是相同的六行代码。)同样,我喜欢这个,因为它创造了分离。
当然,我需要经理:
#app/managers/some_manager.rb
class SomeManager
class << self
def create(params)
# do stuff that ends up creating the @results hash
# if things went well, the return will include success: true
# if things did not go well, the return will not include a :success key
end
end
end
因此,实际上,@results
的结构是视图和管理器之间的契约,而不是视图和控制器之间的契约。