Ruby - Keyword Arguments - Can you treat all of the keyword arguments as a hash? How?

后端 未结 5 850
借酒劲吻你
借酒劲吻你 2020-12-15 16:51

I have a method that looks like this:

def method(:name => nil, :color => nil, shoe_size => nil) 
  SomeOtherObject.some_other_method(THE HASH THAT          


        
5条回答
  •  失恋的感觉
    2020-12-15 17:30

    Yes, this is possible, but it's not very elegant.

    You'll have to use the parameters method, which returns an array of the method's parameters and their types (in this case we only have keyword arguments).

    def foo(one: 1, two: 2, three: 3)
      method(__method__).parameters
    end  
    #=> [[:key, :one], [:key, :two], [:key, :three]]
    

    Knowing that, there's various ways how to use that array to get a hash of all the parameters and their provided values.

    def foo(one: 1, two: 2, three: 3)
      params = method(__method__).parameters.map(&:last)
      opts = params.map { |p| [p, eval(p.to_s)] }.to_h
    end
    #=> {:one=>1, :two=>2, :three=>3}
    

    So your example would look like

    def method(name: nil, color: nil, shoe_size: nil)
      opts = method(__method__).parameters.map(&:last).map { |p| [p, eval(p.to_s)] }.to_h
      SomeOtherObject.some_other_method(opts)
    end
    

    Think carefully about using this. It's clever but at the cost of readability, others reading your code won't like it.

    You can make it slightly more readable with a helper method.

    def params # Returns the parameters of the caller method.
      caller_method = caller_locations(length=1).first.label  
      method(caller_method).parameters 
    end
    
    def method(name: nil, color: nil, shoe_size: nil)
      opts = params.map { |p| [p, eval(p.to_s)] }.to_h
      SomeOtherObject.some_other_method(opts)
    end
    

    Update: Ruby 2.2 introduced Binding#local_variables which can be used instead of Method#parameters. Be careful because you have to call local_variables before defining any additional local variables inside the method.

    # Using Method#parameters
    def foo(one: 1, two: 2, three: 3)
      params = method(__method__).parameters.map(&:last)
      opts = params.map { |p| [p, eval(p.to_s)] }.to_h
    end
    #=> {:one=>1, :two=>2, :three=>3}
    
    # Using Binding#local_variables (Ruby 2.2+)
    def bar(one: 1, two: 2, three: 3)
      binding.local_variables.params.map { |p|
        [p, binding.local_variable_get(p)]
      }.to_h
    end
    #=> {:one=>1, :two=>2, :three=>3}
    

提交回复
热议问题