Consider multiple ActiveRecord classes with overlapping fields and functionality and many of those overlapping fields having the same validation. I'm attempting to share validation, but not run the shared code if a condition is met (based on one of the Model's non-overlapping attributes).
class Book < ApplicationRecord
include SharedValidation
end
class Magazine < ApplicationRecord
include SharedValidation
end
module SharedValidation
extend ActiveSupport::Concern
include ActiveModel::Validations
validates_presence_of :name, :publisher, :author
end
So let's say Magazine.is_deleted
is a Magazine-only field and we only want to run shared validations if is_deleted is false. Any ideas on how this could be accomplished in the Class?
Note: I've attempted to modify the module by performing field detection and evaluation, but am not sure if this makes sense or if it's working:
module SharedValidation
extend ActiveSupport::Concern
include ActiveModel::Validations
included do
proc do |rcd|
has_deleted_field = self.column_names.include?('is_deleted')
if (has_deleted_field && !rcd.is_deleted) || !has_deleted_field
validates_presence_of :name, :publisher, :author
end
end
end
end
CodePudding user response:
It looks like instead of including the module conditionally, you can add the conditional to the validation methods (within the SharedModule).
Using your samples:
class Book < ApplicationRecord
include SharedValidations
end
class Magazine < ApplicationRecord
include SharedValidations
end
module SharedValidations
extend ActiveSupport::Concern
include ActiveModel::Validations
def deleted
return unless self.class.column_names.include?("is_deleted")
is_deleted
end
included do
validates :name, :publisher, presence: true, unless: :deleted
end
end
Magazine has name
, publisher
and is_deleted
columns.
Book only has name
, publisher
- without is_deleted
.
And it looks like this setup works.
irb> book = Book.new()
=> #<Book id: nil, name: nil, publisher: nil, created_at: nil, updated_at: nil>
irb> book.valid?
=> false
irb> book.errors.full_messages
=> ["Name can't be blank", "Publisher can't be blank"]
irb> magazine = Magazine.new
=> #<Magazine id: nil, name: nil, publisher: nil, is_deleted: nil, created_at: nil, updated_at: nil>
irb> magazine.valid?
=> false
irb> magazine.errors.full_messages
=> ["Name can't be blank", "Publisher can't be blank"]
irb> magazine.is_deleted=true
=> true
irb> magazine.valid?
=> true