This is probably a really easy question but I'd like to have a concern which adds a validation whose parameters depend on methods in the derived class. For instance, I'd like to have a concern SlugHelpers as follows
module SlugHelpers
extend ActiveSupport::Concern
included do
validates :slug, uniqueness: { case_sensitive: false, message: "Slug must be unique", scope: slug_scope }, presence: true,
end
class_methods do
def slug_scope
[]
end
end
end
But then have a model Post which overrides slug_scope, e.g.,
class Post < ApplicationRecord
include SlugHelpers
def self.slug_scope
[:stream_id, :stream_type]
end
I want the slug_scope defined in Post to override the slug_scope used in the included do (though I might be calling that wrong, maybe I need self.class.slug_scope) but as written I think that this won't work (isn't the included do executed before the derived class defines its methods)?
Can I do this somehow using prepended do? Or is the way I wrote this roughly correct? I know that included modules are entered into inheritance chain before the derived class but I'm obviously kinda confused about when/where code in an included do block gets executed.
CodePudding user response:
As described by @mu_is_too_short you can just create a class method which adds the validations to the class when called:
module SlugHelpers
extend ActiveSupport::Concern
class_methods do
def has_slug(name = :slug, scope: :default_value)
validates_uniqueness_of name,
case_sensitive: false,
message: "Slug must be unique",
scope: scope
validates_presence_of name
end
end
end
This is the generally preferred way in Ruby to make the behavior provided by modules configurable.
class Foo
include SlugHelpers
has_slug(scope: [:foo, :bar])
end
class Bar
include SlugHelpers
has_slug(scope: [:baz, :bar])
end
While you could actually call a method on the class when the module is included it would produce very strange results if you want to override the behavior in subclasses since it still would be evaluated just when the module was included.