Rahul
Rahul

Reputation: 321

Passing class as argument in Ruby

I have a Ruby class with all static methods. I want to use these methods in another class. Passing just the class name works but wondering if its the right way to go or is there another way.

class Foo
    IDENTIFIER = 'UYT78'
    def self.some_data
      DB.fetch_records
    end

    # and so forth
end

class Bar
    IDENTIFIER = 'XXXXX'
    def self.some_data
      DB.fetch_records
    end

    # and so forth
end

class Reporter

  def report
    Foo.some_data.select {|x| x.id == Foo::identifier}
  end

  def other_report
    Bar.some_data.select {|x| x.id == Foo::identifier}
  end
end

Can Reporter be changed to accept Foo as argument to the methods so these methods can be re-used based on the argument being passed?

Reporter.report(Foo)
Reporter.report(Bar)

and report method looks like

def report(klass)
  klass.some_data.select {|x| x.id == Foo::identifier}
end

This approach works but passing a class name like this as argument doesn't feel right? Or may be it is?

Upvotes: 0

Views: 2203

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110675

Perhaps this is what you are looking for.

class Foo
  IDENTIFIER = 'UYT78'
  def self.some_data
    ['ABC21', 'UYT78']
  end
end

class Bar
  IDENTIFIER = 'XXXXX'
  def self.some_data
    ['XXXXX', 'DEF38']
  end
end

class Reporter
  def report(klass)
    k = Module.const_get(klass)
    k.public_send(:some_data).select {|x| x == k::IDENTIFIER}
  end
end

reporter = Reporter.new

reporter.report("Foo") #=> ["UYT78"] 
reporter.report("Bar") #=> ["XXXXX"] 
reporter.report(:Bar)  #=> ["XXXXX"] 

Upvotes: 0

Rafayet Monon
Rafayet Monon

Reputation: 1179

What you are trying to do is nice but rather than passing the class like that I would use ruby's require.

foo.rb

class Foo
  IDENTIFIER = 'UYT78'.freeze

  def self.some_data
    'Foo'
  end
end

bar.rb

class Bar
  IDENTIFIER = 'XXXXX'.freeze

  def self.some_data
    'Bar'
  end
end

reporter.rb

require './foo'
require './bar'

class Reporter

  def report
    p Foo.some_data
    p Foo::IDENTIFIER
  end

  def other_report
    p Bar.some_data
    p Bar::IDENTIFIER
  end
end

Reporter.new.report
puts '----------------'
Reporter.new.other_report

Which would return like below (Just and example)-

"Foo"
"UYT78"
----------------
"Bar"
"XXXXX"

In my opinion it's much cleaner.

Upvotes: 1

SteveTurczyn
SteveTurczyn

Reputation: 36860

That's fine... generally it's referred to as duck-typing... as long as the class passed supports #some_data method, it doesn't really matter what specific class it is. If it looks like a duck, and quacks like a duck...

It's possible you may want to store the target class at initialization.

class Reporter
  attr_accessor :klass
  def initialize(klass)
    self.klass = klass
  end

  def report
    klass.some_data.select
  end
end

my_foo_reporter = Reporter.new(Foo)

my_foo_reporter.report # calls some_date on Foo class

Upvotes: 1

Related Questions