user1611830
user1611830

Reputation: 4867

several methods to same instance - DRY

Sorry if this is too simple. I'm looking for a way to make my ruby code dry : I want to call a number of methods on the same instance variable @var = Model.new(param) :

@var.method1
@var.method2
@var.method3
...

Is it possible to use the send method to write one line of code ? Btw, is it possible to call a block on Model.new to produce some more concise code ?

Upvotes: 3

Views: 139

Answers (4)

Uri Agassi
Uri Agassi

Reputation: 37409

When I wrote my original answer, I missed the last sentence in your question:

Btw, is it possible to call a block on Model.new to produce some more concise code ?

And the answer to this question is YES. This pattern is a builder pattern, which is implemented in several gems in ruby (such as tire).

The pattern states that the initialize method receives a block, which is run in the context of the created object, using instance_eval. If the block receives a parameter, the instance object is passed to it instead of changing the block's scope:

class Model
  def initialize(name, &block)
    @name = name
    block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
  end

  def method1
    # something
  end
  def method2
    # something
  end
  def method3
    # something
  end
end

And its usage will be something either like this:

@var = Model.new('model') do
         method1
         method2
         method3
       end

or, alternatively:

@var = Model.new('model') do |m|
         m.method1
         m.method2
         m.method3
       end

Upvotes: 0

Uri Agassi
Uri Agassi

Reputation: 37409

I believe that DRY should be used to make your code more maintainable, and more readable. I don't think it should be used to shorten the number of characters you type, or show-off your code acrobatics.

Both @Arup's and @p11y's solutions are great, within a context, but as a general rule (before knowing anything about your class or methods), I believe that writing

@var.method1
@var.method2
@var.method3

is more readable and maintainable than writing either

%i[method1 method2 method3].each(&@var.method(:send)) 

(you need to be fluent in advanced ruby to understand this)

or

@var.method1
    .method2
    .method3

(again the vanishing act is more confusing to the future reader than helpful)

Always think about who will read your code in 6 months, and what will be the clearest way for him to understand what's happening.

Upvotes: 6

Arup Rakshit
Arup Rakshit

Reputation: 118271

Do as below :

%i[method1 method2 method3].each { |m| @var.send(m) }

If you want to make it more short,use :

%i[method1 method2 method3].each(&@var.method(:send))

Upvotes: 3

Patrick Oscity
Patrick Oscity

Reputation: 54684

If you build method1, method2, etc. such that they return the instance itself using self, you can build a chainable interface. For example:

class Foo
  def method1
    # do something
    self
  end

  def method2
    # do something
    self
  end

  def method3
    # do something
    self
  end

  # more methods...
end

@var = Foo.new

@var.method1.method2.method3

# or if this gets too long

@var.method1
    .method2
    .method3

Upvotes: 5

Related Questions