Search code examples
rubyreturn-type

Method returning different type if block_given?


Would it be bad practice to have a method that returns self on block_given? and a different type if a block was not provided?

The example:

Config#item will return the item if a block is not given, and will return Config if it is given.

class Item
  :attr_reader :key

  def initialize(key)
    @key = key
  end

  def do_stuff
    puts "#{key} doing some stuff"
    self
  end
end

class Config
  attr_reader :items

  def initialize
    @items = {}
  end

  def item(key)
    itm = @items[key] ||= Item.new(key)
    if block_given?
      yield(itm)
      self
    else
      itm
    end
  end
end

Usage:

cnf = Config.new
cnf.item("foo") do |itm|
  itm.do_stuff
end

.item("bar") do |itm|
  itm.do_stuff
end

foo = .item("foo").do_stuff
cnf.item("baz").do_stuff
foo.do_stuff

The model is meant to use the same method item as a getter and as a way to refer to an item that needs to be configured or which configuration needs to be reopened.


Solution

  • Would it be bad practice to have a method that returns self on block_given? and a different type if a block was not provided?

    No. In fact, there is an extremely well-known example of a method that has this exact signature: each. each returns self if a block is given, and an Enumerator when no block is given. In fact, many methods in Enumerable return an Enumerator when no block is given and something else if there is a block.

    (I am actually surprised that you haven't encountered each or Enumerable so far.)