learning_to_swim
learning_to_swim

Reputation: 361

Ruby - Invoking a class from a CONSTANT that contains the class name

I have a class that calls different suppliers to find if an item is available. How do I execute the class that each constant returns?

class ItemProvider

   ADAPTER_ONE = Providers::ItemFromSupplierOne
   ADAPTER_TWO = Providers::ItemFromSupplierTwo

   def get_item(item)
     id = ItemDetail.new(item)
     %w(ADAPTER_ONE ADAPTER_TWO).each do |provider| 
         item_detail = provider.new(id)
         break if item_detail.valid?
     end
   item_detail
end

Upvotes: 0

Views: 68

Answers (2)

sirfilip
sirfilip

Reputation: 1095

Yup you have an array of strings not constants but if you want to go down that road in using classes from strings well it will be nice if you look at http://blog.sidu.in/2008/02/loading-classes-from-strings-in-ruby.html#.UuGdmGQ1i2w .Maybe it is not directly related to your problem but it is a good read.

Upvotes: 1

Chuck
Chuck

Reputation: 237010

Your problem is that you aren't making an array that contains the constants' values; you're making an array with the strings "ADAPTER_ONE" and "ADAPTER_TWO". The %w() syntax always makes an array of strings — it doesn't resolve variable names.

What you want is to change your get_item code to something like this:

def get_item(item)
  id = ItemDetail.new(item)
  [ADAPTER_ONE, ADAPTER_TWO].each do |provider| 
    item_detail = provider.new(id)
    break item_detail if item_detail.valid?
  end or nil # break automatically makes the block return the value you break with
end

As an aside, personally, I think I'd rewrite it like this:

def get_item(item)
  id = ItemDetail.new(item)
  [ADAPTER_ONE, ADAPTER_TWO].map {|provider| provider.new(id) }.find &:valid?
end

Upvotes: 2

Related Questions