ruby/irb

undefined method `extract_ruby_args=' for module IRB::Command (NoMethodError)

rebuilt opened this issue · 8 comments

Description

In a rails console, running the following command fails measure = Measure.first
But the following succeeds m = Measure.first

The error message of the measure = Measure.first command is

(eval at /home/nelson/.asdf/installs/ruby/3.3.1/lib/ruby/gems/3.3.0/gems/irb-1.13.1/lib/irb/command/internal_helpers.rb:22):1:in `<main>': undefined method `extract_ruby_args=' for module IRB::Command (NoMethodError)

IRB::Command.extract_ruby_args = Measure.first

Maybe IRB bug!

When I revert to using irb version 1.12.0 all commands work correctly, but on versions 1.13.0 and 1.13.1, the command fails as described above.

I have other models in my application but so far, only measure.rb fails in this way. I'm still testing to see if the problem affects any other model.

Result of irb_info

Ruby version: 3.3.1
IRB version: irb 1.13.1 (2024-05-05)
InputMethod: RelineInputMethod with Reline 0.5.7
Completion: Autocomplete, RegexpCompletor
RUBY_PLATFORM: x86_64-linux
LANG env: en_US.UTF-8
East Asian Ambiguous Width: 1

Terminal Emulator

What's your terminal emulator?
Kitty

Setting Files

Are you using ~/.irbrc and ~/.inputrc?
No

I assume there's a clash with the irb command
measure measureenables the mode to measure processing time.measure :off disables it.

measure = 1 is a measure command call with args = "= 1". Related to #803.
The error message is not user friendly so it should be improved.

I ran into the same thing when trying to assign some source code string to a variable named source. I guess this would be because of #824? Not seeing much else in the changelog.

I was curious how python handles this: its repl also has some buildins. The help command for example works, as long as you don't reassign it. exit as well, which means you can only close with ctrl+d, not with exit() anymore. I believe irb should do the same.

In #824, we decided commands should always take precedence over variable names or methods (mostly my idea tbf). This is to avoid situations like having help in the scope of the binding.irb breakpoint unexpectedly disabling the command completely.

But now I think we can do a more sophisticated check to support this precedence order:

  1. Local variables defined in the IRB session
  2. Command name
  3. Local variables or methods in the program context

So in this example:

help = 1
binding.irb

when users type help in the IRB console, it'd execute the command.
But if users do help = 1, we should allow it, and the subsequent help call would retrieve the local variable instead.

WDYT @tompng?

I think it is hard to distinguish local variables defined in program context from local variables defined in IRB session, especially when using chws or rdbg command. And I think the behavior is complex/hard to understand for user.

How about simply make the precedence:

  1. Local variables
  2. Command name
  3. Methods

(This will be a simpler version of #803 (comment))
We still have irb_commandname. When input starts with existing local variable and command name, it is possible to show a navigation message for it.

irb> help = 1
=> 1
irb> help + 1
If you want to use help command, try irb_help instead
=> 2

especially when using chws or rdbg command

That's a good point 🤔

And I think the behavior is complex/hard to understand for user.

I personally don't think that'd be the case as they are the ones who define the local variables.

How about simply make the precedence:

Then we'll need to change that for irb:rdbg session together too. Otherwise the behaviour will be inconsistent:

$ ruby test.rb 

From: test.rb @ line 3 :

    1: help = 1
    2: 
 => 3: binding.irb

irb(main):001> help
=> 1
irb(main):002> debug
irb:rdbg(main):003> help

### Control flow

* `s[tep]`
  * Step in. Resume the program until next breakable point.
* `s[tep] <n>`
  * Step in, resume the program at `<n>`th breakable point.

Less inconsistency is better, but I think it will be an enhancement even if there is an inconsistency.
We can change how ruby/debug handles command later.

Just FYI I suspect I'm seeing this bug by following the usage example of the Rouge gem in IRB:

require 'rouge'
=> true
source = File.read('README.md')

This results in

(eval at /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/internal_helpers.rb:22):1:in `<top (required)>': undefined method `extract_ruby_args=' for module IRB::Command (NoMethodError)

IRB::Command.extract_ruby_args = File.read('README.md')
            ^^^^^^^^^^^^^^^^^^^^
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/internal_helpers.rb:22:in `eval'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/internal_helpers.rb:22:in `block in ruby_args'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/internal_helpers.rb:21:in `catch'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/internal_helpers.rb:21:in `ruby_args'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/load.rb:79:in `execute'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/command/base.rb:42:in `execute'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb/context.rb:603:in `evaluate'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1049:in `block (2 levels) in eval_input'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1380:in `signal_status'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1041:in `block in eval_input'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1120:in `block in each_top_level_statement'
	from <internal:kernel>:187:in `loop'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1117:in `each_top_level_statement'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1040:in `eval_input'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1021:in `block in run'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1020:in `catch'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:1020:in `run'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/3.3.0/irb.rb:904:in `start'
	from /home/me/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/irb-1.13.1/exe/irb:9:in `<top (required)>'
	from /home/me/.rbenv/versions/3.3.5/bin/irb:25:in `load'
	from /home/me/.rbenv/versions/3.3.5/bin/irb:25:in `<main>'
Maybe IRB bug!

Assigning to a var not named "source" fixes the problem.