2

I am new to Ruby, so let me describe the context of my problem first: I have a json as input which has the following key / value pair:

{
 "service": "update"
}

The value has many different values for example: insert,delete etc. Next there is a method x which handles the different requests:

def x(input)

  case input[:service]      
  services = GenericService.new

  when "update"
    result = services.service(UpdateService.new,input)
  when "insert"
    result = services.service(InsertService.new,input)
  when "delete"
    result = services.service(DeleteService.new,input)
  ....
  ....
  else
    raise "Unknown service"
  end
  puts JSON.pretty_generate(result)    
end

What is bothering me is that I still need to use a switch statement to check the String values (reminds me of 'instance of' ugh..). Is there a cleaner way (not need to use a switch)?

Finally I tried to search for an answer to my question and did not succeed, if however I missed it feel free to comment the related question.

Update: I was thinking to maybe cast the string to the related class name as follows: How do I create a class instance from a string name in ruby? and then call result = services.services(x.constantize.new,input) , then the class names ofcourse needs to match the input of the json.

Community
  • 1
  • 1
M. Suurland
  • 725
  • 12
  • 31

3 Answers3

3

You can try something like:

def x(input)

  service_class_name = "#{input[:service].capitalize}Service"
  service_class = Kernel.const_get(service_class_name)
  service_class.new(input).process

end

In addition you might want to check if this is a valid Service class name at all.

I don't understand why you want to pass the service to GenericService this seems strange. let the service do it's job.

Pascal
  • 8,464
  • 1
  • 20
  • 31
1

If you're trying to instatiate a class by it's name you're actually speaking about Reflection rather than Polymorphism.

In Ruby you can achieve this in this way:

byName = Object.const_get('YourClassName')

or if you are in a Rails app

byName= 'YourClassName'.constantize

Hope this helps

rick
  • 1,869
  • 13
  • 22
0

Just first thoughts, but you can do:

eval(services.service("#{input[:service].capitalize}Service.new, #{input})") if valid_service? input[:service]

def valid_service?
    w%(delete update insert).include? input[:service]
end

As folks will no doubt shout, eval needs to be used with alot of care

ABrowne
  • 1,574
  • 1
  • 11
  • 21
  • 1
    Neverever use "eval". It is "evil". – Pascal Apr 28 '16 at 11:36
  • @pascalbetz - I would agree you can get yourself into alot of trouble with eval, I prefer the the const_get as it'll just throw an error if you try grabbing something that doesn't exist, hence the need for the sanity check on inbound. – ABrowne Apr 28 '16 at 11:43