1

Perhaps a duplicate but I searched both SO and Google and couldn't find an answer even though it should be something trivial..

A Ruby method requires multiple args..

def initialize(options = {})
  requires!(options, :arg1, :arg2, ...)
  super
end

How should one call such method with a variable number of parameters without knowing in advance these required parameters?

I know already Ruby 1.9.2 and later we can use the parameters method on a method to get the list of parameters for that method, and from there I can imagine we can reuse that to call the method since we knew its parameters. Correct?

If not correct, what is the best way to interact with such methods that take variable number of required parameters?

  • 1
    look into one of these https://stackoverflow.com/questions/812058/ruby-optional-parameters https://stackoverflow.com/questions/16322706/optional-arguments-with-default-value-in-ruby https://stackoverflow.com/questions/15139017/ruby-optional-parameters-and-multiple-parameters https://stackoverflow.com/questions/10234406/ruby-methods-and-optional-parameters – Subash Jul 16 '18 at 00:15
  • what do you mean by "interact with such method"? Need to look at required arguments? – Raj Jul 16 '18 at 01:36
  • to interact as to "call it" without knowing its parameters requirements. – Hossam Hossny Jul 16 '18 at 02:32
  • 1
    How and why would you call a method unless you know what it does? This is what documentation is for, and why it is so important. In order to use a library, or even a language, you first must learn what methods are available to you, what they do, how they work, etc, etc. This is primarily done by reviewing documentation for it, hence the reason documentation is distributed with things, and nobody uses those that don't.. – ForeverZer0 Jul 16 '18 at 03:42
  • I feel like adding a reference to an older question remained unanswered.. https://stackoverflow.com/questions/48220723/ruby-how-to-initialize-an-active-merchant-gateway-instance-with-credentials In this problem, it is required to initialize an instance of the gateway method without at first knowing what mandatory parameters the gateway method requires. – Hossam Hossny Jul 16 '18 at 05:28
  • @ForeverZer0 the activemerchant example in https://stackoverflow.com/questions/48220723/ruby-how-to-initialize-an-active-merchant-gateway-instance-with-credentials is a good answer for your question on how and why. – Hossam Hossny Jul 17 '18 at 10:37
  • Yeah, just enforces my point... – ForeverZer0 Jul 17 '18 at 10:41
  • Ok at first I thought you are confused, but now it seems you have a full understanding to my question.. Are you saying I should be avoiding using activemerchant gem because they have a method in their code (the gateway initialize method) with variable names parameters? Or you are saying I should be reading about around 3,000 lines of code to know what parameters name the gateway initialize method use? I am giving a real life example here in the above activemerchant question! – Hossam Hossny Jul 17 '18 at 11:46

3 Answers3

0

Your question basically equates to "how do I know about something that I don't know about".

The answer to this, to put bluntly, is go learn about it. In the context of programming and how to call a method, the same logic applies, and this is the sole purpose of documentation, and the same way that you learned to write Ruby code. I am sure you have like all of us here spent plenty of time looking through documentation on how to write code, what methods are available to us, what they are used for, how to invoke them, and so on.

If you come across the existence of a function, and all you can originally tell about it is that it takes a variable number of arguments, then you need to figure it out. This typically means looking up the documentation for it, or if you have access to the source, to examine the method body and see if you can determine how it is used.

This is why good documentation is essential, even more so for compiled languages where one cannot actually see body of a method and/or its signature. If no documentation exists, then IMHO, it is crap and you shouldn't be using it. It's like writing a book with your own made-up vocabulary and expecting people to have to try and decipher every word to even read it. You can write the most beautiful piece of code, and if you don't explain how to use it, what's the point of making it public?

To put simply, if you don't understand a method or what it does, you shouldn't be "interacting" with it, at least if you care about your code being robust. How are you supposed to debug code that you don't even understand? There is no explain_how_to_use_this(method) function to call, you're going to have to research it and do some old-fashioned learning.

ForeverZer0
  • 2,379
  • 1
  • 24
  • 32
  • Why do you think Ruby has introduced the `parameters` method? The method does solve my problem actually, but I am asking about the best way to do it other than this! – Hossam Hossny Jul 17 '18 at 13:03
  • According to Ruby official documentation https://ruby-doc.org/core-2.2.2/Method.html#method-i-parameters I should be able to use the `parameters` method in order to initialize a gateway in active_merchant. Now require "active_merchant" ActiveMerchant::Billing::Base::gateway( :trust_commerce ).parameters should be giving me something like "login" (which solves my problem since I know now the "login" parameter and then able to ActiveMerchant::Billing::Base::gateway.(:trust_commerce).("login" => "value"). The funny this is when I do this it gives me a no method error!! – Hossam Hossny Jul 17 '18 at 14:12
  • Technically your answer about "Reading the documentation" is the only valid answer so far since the official documentation about the method literally says this https://www.rubydoc.info/gems/activemerchant/ActiveMerchant/Billing/Gateway#options-instance_method But that raises another problem. Is the official Ruby documentation wrong about that `parameters` method? It gave me a no method error in my case when it simply should have given me the parameters of the method I want. Is there an explaination for this? – Hossam Hossny Jul 17 '18 at 14:15
0

I don't think the method should be concerned with what arguments are passed in. All it should care about are the parameter necessary for it to do whatever it wants to do.

This way, you would have the initialize method taking the keys from the hash which concerns it and not really worrying about the others.

class Person
  def inititialize(opts: {})
    @name = opts[:person_name] # could be nil
  end
end

class Car
  def initialize(opts: {})
    @brand = opts[:car_brand] # could be nil
  end
end

opts = { person_name: "Ivan", car_brand: "Fiat", something_else: "ignored" }

c = Car.new(opts) # is unaware of what is in 'opts'
#<Car:0xd34db33f>
p = Person.new(opts) # is unaware of what is in 'opts'
#<Person:0x1337b347>

I think encapsulation as a principle covers this. Methods should only know what they need to know to function. A scope should not legislate on the data coming in from its environment.

If you keep this in mind, you can pass anything into the method and allow the method do as it wishes.

Igbanam
  • 5,904
  • 5
  • 44
  • 68
  • I never mentioned that the method is concerned. I was asking about calling a method that takes variable parameters. In your example can I pass `opt = {"Ivan", "Fiat"}` without knowing the `keys` names in the hash? I think encapsulation indeed covers that so thanks for pointing me out to the right direction though. – Hossam Hossny Jul 17 '18 at 12:32
  • can you take a look at this https://stackoverflow.com/questions/48220723/ruby-how-to-initialize-an-active-merchant-gateway-instance-with-credentials and tell me how to initialize a gateway instance passing only the values of the parameters keys the gateway initialize method take? – Hossam Hossny Jul 17 '18 at 12:35
0

As far as I've understood, you do not want to provide a hash as an argument, but rather an array of values, skipping key names.

If you are using a third party library, and that method is defined there - it is not possible because that method was designed to receive an argument in a specified form (in your example hash must have some specific keys), and Ruby does not provide any capability to know what user had in mind (If you want to go extreme you can write of course a source file parser that would extract an arguments that come from that hash).

If you are designing method by your own, you can just:

def initialize(arguments = {}) 
  case arguments
  when Hash
    @foo = arguments[:foo]
    @bar = arguments[:bar]
  when Array
    @foo = arguments[0]
    @foo = arguments[1]
  else
    raise ArgumentError.new("argument of type Hash or Array expected")
  end
end
Sheppard
  • 157
  • 1
  • 10