sto*_*e45 0 ruby rubygems ruby-on-rails
我将acts_as_bookablegem 用于 Rails 应用程序中的一些基本预订/预订内容,我需要向Bookinggem 创建的模型添加额外的验证。
我的意思是,在 gem 内部,位于lib/acts_as_bookable/booking.rb以下模块/类:
module ActsAsBookable
class Booking < ::ActiveRecord::Base
self.table_name = 'acts_as_bookable_bookings'
belongs_to :bookable, polymorphic: true
belongs_to :booker, polymorphic: true
validates_presence_of :bookable
validates_presence_of :booker
validate :bookable_must_be_bookable,
:booker_must_be_booker
# A bunch of other stuff
end
end
Run Code Online (Sandbox Code Playgroud)
这很好。但是,我想添加一个额外的逻辑来阻止预订者预订同一个可预订实例。基本上,一个新的验证器。
我以为我可以在我的/models目录中添加一个名为的文件,acts_as_bookable.rb然后像这样修改类:
module ActsAsBookable
class Booking
validates_uniqueness_of :booker, scope: [:time, :bookable]
end
end
Run Code Online (Sandbox Code Playgroud)
但这不起作用。我可以修改 gem 本身(我已经分叉了它以更新一些依赖项,因为它是一个非常古老的 gem),但这并不是正确的解决方案。这是特定于此应用程序实现的逻辑,因此我的直觉是它属于此特定项目内的覆盖,而不是基础 gem。
我在这里做错了什么?是否有更合适的更好/替代方法?
为您控制之外的对象创建猴子补丁/增强的一种干净方法是创建一个单独的模块:
module BookingMonkeyPatch
extend ActiveSupport::Concern
included do
validates_uniqueness_of :booker, scope: [:time, :bookable]
end
end
Run Code Online (Sandbox Code Playgroud)
这使您可以单独测试monkeypatch - 您可以通过包含模块来“打开monkeypatch”:
ActsAsBookable::Booking.include(BookingMonkeyPatch)
Run Code Online (Sandbox Code Playgroud)
这可以在初始化程序或生命周期中的任何其他地方完成。
虽然如果可预订是一个多态关联,您需要使用:
validates_uniqueness_of :booker_id, scope: [:time, :bookable_id, :bookable_type]
Run Code Online (Sandbox Code Playgroud)
仅传递关联名称时,唯一性验证无法正常工作,因为它会创建基于数据库列的查询。这是一个泄漏抽象的例子。
看: