0

Given the following classes:

class Collector
end

class Iterator
  def initialize data, collector
    @collector = collector
    @data      = data
  end

  ...
end

class Parser
  def initialize data, collector
    @collector = collector
    @data      = data
  end

  ...
end

There are multiple Iterator and Parser classes scoped in modules. They need the parent class for basic info and configuration that is dependant on the type and source of data.

Using a gem like https://github.com/RidiculousPower/sender would be perfect, but it seems slow and doesn't compile anymore.

What would be an elegant way to have the instantiator object instead of always repeating it in new calls and ìnitialize methods?

brauliobo
  • 5,843
  • 4
  • 29
  • 34
  • What do you mean by "repeating"? Could these both be from a parent class that implements `initialize` and then they can add their own behaviour? – tadman May 28 '21 at 14:23
  • 1
    Normally you can work around this by having methods in your caller that wrap themselves into the call, like a convenience method that does `X.new(self)` – tadman May 28 '21 at 14:24
  • Can you include another example that shows how instances of those classes interact? In particular the repeating `new` calls. – Stefan May 28 '21 at 14:29
  • What is an "instantiator object"? I can't find that term anywhere in the ISO Ruby Programming Language Specification, the Ruby/Spec, the Programming Ruby book, the The Ruby Programming Language book, or indeed anywhere on Google, except for this question. – Jörg W Mittag May 28 '21 at 14:41

1 Answers1

2

This answer shows how you can access the calling object using the binding_of_caller gem, but keep in mind that this functionality is meant for debugging, not production code.

You could call binding.of_caller(1).eval('self') in the initializers of the classes instantiated by the Collector, but you'd be making your code a lot harder to understand to the average Rubyist. You'd also be coupling the Iterator/Parser objects to the object that calls them, which is a pretty unusual kind of coupling. It would certainly make them less reusable and so less "elegant" by my standards.

If I understood you correctly, this is the only way to do what you're asking. For the record, here's some working code that accomplishes what (I think) you're asking:

require "binding_of_caller"

class Collector
  def demonstrate_that_this_works
    Iterator.new.collector == self
  end
end

class Iterator
  def initialize
    @collector = binding.of_caller(1).eval("self")
  end

  attr_reader :collector
end

# This will return true:
Collector.new.demonstrate_that_this_works

For what it's worth, passing in dependencies explicitly is considered elegant object oriented design in the Ruby community. I have been programming Ruby for about ten years and would encourage you to pass the collector down explicitly if the other objects depend on it. The code will be much easier to understand (and test, if you with to unit test it) this way.