From: brad.krane@... Date: 2021-04-16T18:54:01+00:00 Subject: [ruby-core:103485] [Ruby master Feature#17808] Feature Request: JS like splat of Object properties as named method parameters Issue #17808 has been updated by Lithium (Brad Krane). Thanks for the quick update. That almost works but to automate this the to_hash function needs to be aware of the methods named arguments. The below works in one case and fails in another with an object having more instance variables than the called method and it throws a `[ArgumentError] unknown keyword: :color` as there is now an extra parameter included that is not one of the named method params. Also would this be dangerous to begin with modifying the Kernel to_hash? ``` ruby module Kernel def to_hash new_hash = self.instance_variables.reduce({}) do |hash, instance_var| hash[instance_var.to_s[1..-1].to_sym] = self.instance_variable_get instance_var hash end end end # Then you can pass it a list of symbols. # Then for your method: def some_method name:, weight: puts "#{name} weighs #{weight}" end class Dog attr_reader :name, :weight def initialize name:,weight: @name = name @weight = weight end end a_dog = Dog.new name: 'fido', weight: '7kg' some_method(**a_dog) # Works great! class CockerSpanel < Dog def initialize name:,weight:,color: super name: name, weight: weight @color = color end end another_dog = CockerSpanel.new name: 'Jessie', weight: '5kg', color: 'black' some_method **another_dog ``` NVM on the hash question it is a problem pasting into irb crashes with ``` shell C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:1080: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:1063: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/reline/unicode.rb:70: warning: Using the last argument as keyword parameters is deprecated C:/Ruby27-x64/lib/ruby/2.7.0/irb/ext/save-history.rb:110: warning: Using the last argument as keyword parameters is deprecated Traceback (most recent call last): 13096: from C:/Ruby27-x64/bin/irb.cmd:31:in `
' 13095: from C:/Ruby27-x64/bin/irb.cmd:31:in `load' 13094: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `' 13093: from C:/Ruby27-x64/lib/ruby/2.7.0/irb.rb:399:in `start' 13092: from C:/Ruby27-x64/lib/ruby/2.7.0/irb.rb:470:in `run' 13091: from C:/Ruby27-x64/lib/ruby/2.7.0/irb.rb:470:in `catch' 13090: from C:/Ruby27-x64/lib/ruby/2.7.0/irb.rb:471:in `block in run' 13089: from C:/Ruby27-x64/lib/ruby/2.7.0/irb.rb:536:in `eval_input' ... 13084 levels... 4: from C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:93:in `block in reset' 3: from C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:93:in `block in reset' 2: from C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:93:in `block in reset' 1: from C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:93:in `block in reset' C:/Ruby27-x64/lib/ruby/2.7.0/reline/line_editor.rb:93:in `block in reset': stack level too deep (SystemStackError) ``` Running the file itself we get ``` shell C:\Users\Brad Krane\Documents\src\ruby-splat>ruby ruby-splat.rb fido weighs 7kg Traceback (most recent call last): 1: from james.rb:38:in `
' ruby-splat.rb:13:in `some_method': unknown keyword: :color (ArgumentError) C:\Users\Brad Krane\Documents\src\ruby-splat> ``` How could I automate the to_hash knowing the method arguments required so as to prevent the `unknown keyword: :color (ArgumentError)` Or should I be specifying my to_hash methods individually and if I did how can I specify the correct parameters? ---------------------------------------- Feature #17808: Feature Request: JS like splat of Object properties as named method parameters https://bugs.ruby-lang.org/issues/17808#change-91583 * Author: Lithium (Brad Krane) * Status: Open * Priority: Normal ---------------------------------------- I'm pretty sure there is no equivalent Ruby for a very convenient JS way to enter named function parameters as below: ``` javascript const test = ({param1, param2, param3 = null, param4 = false, param5 = null, }={}) => { console.log(`${param1}, ${param2}, ${param3}, ${param4}, ${param5}\n`) } let obj = { param1: 23, param2: 234, param3: 235, param4: 257 }; test({...obj}); ``` which is super convenient and as far as I'm aware there is no standard Ruby equivalent. It can be accomplished in Ruby but the call syntax is far less nice. A couple examples below: ``` ruby # Implementing such a feature wouldn't be too difficult. # Ok so here is what you could do it. Patch Kernel with a method splat. (Sorry in advance for formatting) module Kernel def splat obj, method: new_hash = method.parameters.reduce({}) do |hash, attrr| hash[attrr.last] = obj.send(attrr.last) hash end end end # Then you can pass it a list of symbols. # Then for your method: def some_method name:, weight: puts "#{name} weighs #{weight}" end class Dog attr_reader :name, :weight def initialize name:,weight: @name = name @weight = weight end end a_dog = Dog.new( name: 'fido', weight: '7kg') hash_puppy = a_dog.splat(a_dog, method: method(:some_method) ) some_method(**hash_puppy) ``` or what I think is a bit better: ``` ruby # Same class as above a_dog = Dog.new( name: 'fido', weight: '7kg') def other_splat obj, method: new_hash = method.parameters.reduce({}) do |hash, attrr| if obj.class <= Hash hash[attrr.last] = obj[attrr.last] else hash[attrr.last] = obj.send attrr.last end hash end method.call **new_hash end other_splat(a_dog, method: method(:some_method)) # above line should be like: # some_method ...a_dog ``` Source: https://gist.github.com/bradkrane/e051d205024a5313cb4a5b9eb1eae0e3 I'm sure I'm missing a possibly more clever way to accomplish this, but I'm pretty sure something like `other_splat(a_dog, method: method(:some_method))` is about as close as it can get, unless I'm missing something? It would be quite nice to have a similar syntax as JS but in Ruby: `some_method ...a_dog` Thanks for your time and attention! -- https://bugs.ruby-lang.org/ Unsubscribe: