Issue with define_method in Ruby 3.0 - ruby

I'm working on an upgrade from Ruby 2.7 to 3.0 and faced an issue with the keyword arguments change.
Previously we used define_method in our code for some purposes. However, with the keyword arguments change, it can no longer handle an array of arguments properly anymore.
class Foo
def test(a: 1, b: 2)
puts a
puts b
end
old_method = instance_method(:test)
define_method(:test) do |*args, &block|
old_method.bind(self).call(*args, &block)
end
end
Foo.new.test(a: 1)
This will raise wrong number of arguments (given 1, expected 0) (ArgumentError). And it previously worked in Ruby 2.7. Is there anything we can do to get the *args to work again?

Try
define_method(:test) do |*args, **kwords, &block|
old_method.bind(self).call(*args, **kwords, &block)
end
Ruby 3.0 is changing the handling of method arguments which give a deprecation warning in 2.7. There is some unexpected behavior including a keywords in a *args array. This had to be done using a hash and there needs to be more of a distinction between a hash argument and keywords. So in ruby 3.x you need to capture an arbitrary collection of keywords in a double splat because they cannot be included in the splat argument.
Mostly my understanding is shaky but I read this as an explicit example in the explanation of the changed to the arguments in the release notes for ruby 3.0.

Related

How to override Ruby + method?

Just for learning purpose I am trying to override the Ruby + method, but I am not getting the desired output.
class Integer
def +(my_num)
"Plus method overridden"
end
end
puts 5.+(9)
Please let me know what I am doing wrong here.
It seems you use ruby < 2.4. If so you want to patch Fixnum and not Integer. Be careful as the system itself uses numbers as well.
class Fixnum
alias_method :add, :+
def +(other)
puts 'plus method overridden'
add(other)
end
end
puts 5 + 9
The Ruby Language Specification allows Integer to have implementation-specific subclasses. See Section 15.2.8.1, lines 27-33.
It looks like your implementation does have such subclasses. In that case, the + method may be overridden in a subclass.
My best guess is that you have an implementation which distinguishes between Fixnums and Bignums, and that our Integer#+ gets overridden by Fixnum#+.
By the way, even if what you were trying to do were working, it wouldn't be overriding, it would be overwriting.
Also note that if what you were trying to do were working, you would have most likely broken your Ruby process, since Integers are fundamental and widely-used all over the place in Ruby.

Why does Ruby call the `call` method when I don't supply the method name?

Given the following module:
module Foo
def self.call
'foo'
end
end
I would of course expect the following to work:
puts Foo.call # outputs "foo"
However, I did not expect this to work:
puts Foo.() # outputs "foo"
Apparently when the method name is left off, Ruby assumes that I want to call the call method. Where is this documented, and why does it behave that way?
Proc#call:
Invokes the block, setting the block’s parameters to the values in params using something close to method calling semantics. Generates a warning if multiple values are passed to a proc that expects just one (previously this silently converted the parameters to an array). Note that prc.() invokes prc.call() with the parameters given. It’s a syntax sugar to hide “call”.
I did some research and found method #() is a syntactic sugar of the method #call..Look at the error as below :
module Foo
def self.bar
12
end
end
Foo.()
#undefined method `call' for Foo:Module (NoMethodError)
As OP defined the #call method in module Foo class,Foo#call is called in an attempt of Foo.().
Here is some more examples :
"ab".method(:size).() # => 2
"ab".method(:size).call # => 2
"ab".() # undefined method `call' for "ab":String (NoMethodError)
See here what Matz said So compromise with object.() syntax introduced in 1.9...
Apparently, as Arup is saying, this is syntactic sugar introduced a while ago, possibly for the single cause of making Proc objects easier to work with. (You don't have to explicitly call them, but can just do prc.() instead).
I also concluded that this is definitely a Ruby 1.9+ feature. If I switch my JRuby to 1.8 mode, I get this instead:
SyntaxError: spam.rb:12: syntax error, unexpected tLPAREN2
So, somewhere in the changelogs for Ruby 1.9 it can probably be found, if someone really wants to dig it out from the caves... :)

Is there a short way to write `{|x| x}`?

We often shorten a block using the & notation on a symbol like this:
some_array.group_by(&:foo)
Is there a similar way to shorten expressions like {|x| x}?
some_array.group_by{|x| x}
If there were a method Object#self that returns self, then we can do
some_array.group_by(&:self)
but unfortunately, there is no such method. In terms of the number of characters, it may be longer, but readability improves.
Yes. #itself was implemented in Ruby 2.2.0.
You can access the Ruby core team discussion about this feature here.
As an interesting analogue, the #ergo method has been proposed, which would yield the receiver to a given block.
If you haven't yet upgraded to Ruby 2.2.0, you may wish to backport #itself and/or define #ergo as follows:
class Object
def itself; self end
def ergo
fail ArgumentError, "Block expected!" unless block_given?
yield self
end
end
And then:
some_array.group_by &:itself
Well, there's no built-in as far as I know, but you can make a reusable identity block:
id = Proc.new {|x| x}
some_array.group_by(&id)
And then if you really wish this were a language feature:
class Object
def it
Proc.new {|x| x}
end
end
And then you can do:
some_array.group_by(&it)
wherever you like. This may void your warranty.
Yes! The method Kernel#itself was added in Ruby 2.2.0. This method simply returns the object it was called on, so you can write:
some_array.group_by(&:itself)
You can see the extensive discussion of this feature here: https://bugs.ruby-lang.org/issues/6373. The patch was submitted by Rafael França in message #53. You can see it in the official Ruby source by looking in object.c.
If you are using a version of Ruby older than 2.2.0, you can easily add Kernel#itself into your project by putting this code somewhere in your project and making sure it gets required:
module Kernel
def itself
self
end
end if !Kernel.instance_methods.include?(:itself)
However, monkey-patching a part of the Ruby core like that can be dangerous and I would not recommend it if you are making reusable code, like a gem. Instead I would recommend just making your own identity function, as suggested by user2246674:
module MyLibrary
IDENT = Proc.new { |x| x }
array.group_by(&IDENT)
end

Ruby Method without Quotes

I'm writing a ruby method that takes a string input but I don't want to type the quotes.
For example:
def noquotes(input)
puts input
end
noquotes('12Dec11Bel01') # ---> 12Dec11Bel01
noquotes(12Dec11Bel01) # ---> Currently yields an error
What I'd like to be able to do is enter the method input without the quotes (second example) and still get the right result. I tried using .to_str to ensure the input was treated as a string, but it didn't work.
Hehe, sorry, but you can't mangle with the syntax tree in Ruby. If you don't make quotes, it will be parsed as a variable or method call.
What you can do is
def method_missing(meth, *args)
meth.to_s
end
but use that wisely and with scoping, as in
class DSL # You'd use that here
def dsl(&block)
instance_eval(block)
end
def method_missing(meth, *args)
meth.to_s
end
def noquotes(input)
puts input
end
end
def dsl(&block)
DSL.new.dsl(&block)
end
dsl do
noquotes(foobar)
end
Use with caution and only if you know what you're doing! And only in DSL. And not even there. Really. Don't do it.
This isn't possible without doing horrible maintainence-nightmare things. Think about how the Ruby interpreter needs to parse your input. Without the quotes, it has no way to know that 12Dec11Bel01 is intended as a string, and not a call to another method or the name of a variable.
Type the quotes as skip the parentheses. It's the same number of characters.

How to get the name of the calling method?

is there a way in Ruby to find the calling method name inside of a method?
For example:
class Test
def self.foo
Fooz.bar
end
end
class Fooz
def self.bar
# get Test.foo or foo
end
end
puts caller[0]
or perhaps...
puts caller[0][/`.*'/][1..-2]
In Ruby 2.0.0, you can use:
caller_locations(1,1)[0].label
It's much faster than the Ruby 1.8+ solution:
caller[0][/`([^']*)'/, 1]
Will get included in backports when I get the time (or a pull request!).
Use caller_locations(1,1)[0].label (for ruby >= 2.0)
Edit: My answer was saying to use __method__ but I was wrong, it returns the current method name.
I use
caller[0][/`([^']*)'/, 1]
How about
caller[0].split("`").pop.gsub("'", "")
Much cleaner imo.
Instead you can write it as library function and make a call wherever needed. The code goes as follows :
module CallChain
def self.caller_method(depth=1)
parse_caller(caller(depth+1).first).last
end
private
# Copied from ActionMailer
def self.parse_caller(at)
if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
file = Regexp.last_match[1]
line = Regexp.last_match[2].to_i
method = Regexp.last_match[3]
[file, line, method]
end
end
end
To trigger the above module method you need to call like this:
caller = CallChain.caller_method
code reference from
In order to see the caller and callee information in any language, whether it be ruby or java or python, you would always want to look at the stack trace. In some languages, such as Rust and C++, there are options built into the compiler to turn on some sort of profiling mechanism you can view during run time. I do belive one exists for Ruby called ruby-prof.
And as mentioned above, you could look into the execution stack for ruby. This execution stack is an array containing backtrace location objects.
Essentially all you need to know about this command is as follows:
caller(start=1, length=nil) → array or nil

Resources