james246
james246

Reputation: 1904

Make scope and instance method with same conditions more DRY

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

Answers (1)

Sergio Tulentsev
Sergio Tulentsev

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

Related Questions