Reputation: 1904
Say you have a model that defines a scope, the conditions of which are also used in an instance method:
class Thing < ActiveRecord::Base
scope :complete?, -> {
where(
foo: true,
bar: true,
baz: true
)
}
def complete?
foo? && bar? && baz?
end
end
If you needed to change the definition of complete?
, you would have to remember to change both the scope and the instance method. Is there a nice way to consolidate these two definitions and make the code a bit more DRY?
The problem gets slightly more complex if one of the conditions is itself an instance method:
class Thing < ActiveRecord::Base
scope :complete?, -> {
where(
foo: true,
bar: true,
# can't use instance method `base?` here for `baz` condition
# you would have to duplicate the logic:
name: 'baz'
)
}
def baz?
name == 'baz'
end
def complete?
foo? && bar? && baz?
end
end
Upvotes: 0
Views: 133
Reputation: 230296
DRY means "don't repeat yourself". In those snippets I don't see any duplication whatsoever. So there's literally nothing to DRY up.
What you probably meant, though, is
there is one piece of information from which two methods are generated. If this information changes, how to be able to change it only once, so that I don't accidentally forget to update some other place?
Well, you can do some metaprogramming gymnastics such as this:
class Thing < ActiveRecord::Base
FIELDS_FOR_COMPLETE = {
foo: true,
bar: true,
baz: true,
}
scope :complete?, -> {
where(FIELDS_FOR_COMPLETE)
}
def complete?
FIELDS_FOR_COMPLETE.all? do |method_name, expected_value|
self.public_send(method_name) == expected_value
end
end
end
It's up to you to decide which one is easier to read, to understand and to maintain.
Me, I choose the first (somewhat duplicated) version. It makes me think less. And it's also immediately obvious to any other rails programmer, which might join the project.
Upvotes: 3