我试图使用acts_as_tenant
gem为多个组织范围SQL查询。然而,RSpec/FactoryGirl正在玩一些肮脏的把戏。
一点背景:在我的应用程序中,User
和Grant
都属于Organization
。只有admin用户可以创建/删除Grant
。
像gem的文档建议,我已经插入acts_as_tenant :organization
到我的Grant
和User
模型。我也有set_current_tenant_through_filter
并在我的application_controller.rb
中定义了before_action :set_organization
。User
和Grant
的查询范围仅为当前用户的Organization
:
def set_organization
if current_user
current_organization = Organization.find(current_user.organization_id)
set_current_tenant(current_organization)
end
end
一切似乎都很好。现在编写控制器测试:
# grants_controller_spec.rb
describe GrantsController do
let(:organization) { create(:organization) }
let(:admin) { create(:user, admin: true, organization_id: organization.id) }
...
before(:each) { log_in admin }
...
end
管理部分抛出一个奇数错误:
Failure/Error: let(:admin) { create(:user, admin: true, organization_id: organization.id) }
ActiveRecord::RecordInvalid:
Validation failed: Organization can't be blank
所以,即使我已经明确地将组织的外键传递给FactoryGirl,它仍然有识别Organization
的问题。
当我注释掉特定于acts_as_tenant
的代码时,错误就消失了。我怎样才能使测试永远绿色呢?
这实际上与我上面发布的代码没有什么关系。罪魁祸首是这一行,在我的User
模型中:
attr_accessor :organization_id
我认为这一行阻止了实际的organization_id
数据库列与模型一起保存。我的UsersController
代码可以澄清这一点:
def create
@user = User.new(user_params)
organization = Organization.find_by(name: params[:user][:organization_name])
if organization.authenticated?(params[:user][:organization_password])
@user.organization_id = organization.id
@user.save
...
end
因此organization_id
确实被设置为organization.id
,但attr_accessor
被先求值,因此organization_id
成为一个无用的虚拟属性。并且db列organization_id
被保存为nil
,即没有传递任何内容给它。这反过来又导致acts_as_tenant
抱怨,因为我将current_tenant
设置为当前用户的组织,而当前用户没有使用外键进行设置。
我给自己和所有遇到这个问题的人的建议是:检查你的db列名是否被你的虚拟属性遮蔽了