Search code examples
ruby-on-railsrubyirb

Prevent ruby/rails from printing results of assignment expressions


When I do something like this in rails:

irb(main):060:0> users = User.where(name: "Joe")

The console will evaluate users and print it out (maybe with inspect, or to_s, I'm not 100% sure). This results in hundreds of lines of output that I don't really want.

I know I can do something like this to prevent the output on one specific line:

irb(main):060:0> users = User.where(name: "Joe"); nil

Because nil will be evaluated and printed out instead of users.

I could also turn off all evaluations with:

irb(main):060:0> conf.echo = false

The first solutions is annoying because you have to remember to add that on every line you want to suppress output. And the second is annoying, because sometimes you do want output. Many times you just want to type the name of a variable to have it printed.

Python doesn't seem to have this problem. If I type a = {1: 2}, the python shell doesn't evaluate a, it just shows the prompt again. But if I type {1: 2} or just a, it does print out the value.

It seems like python's console checks whether there's a left-hand-side of the statement being evaluated to decide whether to print the result or not.

Question:

Can something like that be done in ruby's console? So that statements like a = {1 => 2} are not evaluated, but statements like a and {1 => 2} are?

Update:

I pushed this merge request to the irb repo, perhaps it'll be accepted and then there will be a way: https://github.com/ruby/irb/pull/12 .


Solution

  • Edit:

    A version of the MR referenced below was merged to irb, but tweaked a few times since then. So to get the behavior you want it depends on the irb version:

    If you have at least 1.2.0, but less than 1.2.6: the default behavior is to not print anything for assignment statements. You have to set IRB.conf[:ECHO_ON_ASSIGNMENT] = true if you want the old behavior (of always printing everything).

    If you have 1.2.6, the default value for ECHO_ON_ASSIGNMENT was changed to true, and a new config value: OMIT_ON_ASSIGNMENT was added which will truncate long output for assignment statements, it defaults to true. So you have to set IRB.conf[:ECHO_ON_ASSIGNMENT] = false if you don't want anything to be printed for assignment statements. You have to set IRB.conf[:OMIT_ON_ASSIGNMENT] = false if you want the old behavior (of always printing everything).

    If you have 1.2.7 or above, OMIT_ON_ASSIGNMENT was removed in favor of letting ECHO_ON_ASSIGNMENT accept true, false, or :truncate, with the default value being :truncate. A value of true will print everything for assignment statements, false will print nothing, and :truncate will print a truncated version (like setting OMIT_ON_ASSIGNMENT to true in 1.2.6).

    Note that the versions above versions of the irb gem, not ruby versions (even though irb is included in ruby's standard library). You can check what version of irb you have by looking at IRB::VERSION. Very old versions of irb don't set this constant, so if it's missing, you definitely have a version lower than 1.2.0.

    Original answer:

    Until this pull request (or one like it) is merged, you can put the following into your ~/.irbrc file:

    require 'ripper'
    module IRB
      class Irb
        ASSIGNMENT_NODE_TYPES = [:assign, :opassign, :massign]
        def assignment_expression?(line)
          ASSIGNMENT_NODE_TYPES.include?(Ripper.sexp(line)&.dig(1,-1,0))
        end
    
        def output_value # :nodoc:
          return if assignment_expression?(@context.last_line)
          printf @context.return_format, @context.inspect_last_value
        end
      end
    
      class Context
        attr_accessor :last_line
        def evaluate(line, line_no, exception: nil) # :nodoc:
          @line_no = line_no
          @last_line = line
          if exception
            line = "begin ::Kernel.raise _; rescue _.class; #{line}; end"
            @workspace.local_variable_set(:_, exception)
          end
          set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
        end
      end
    end
    

    Note that since this monkey-patches IRB::Irb and IRB::Context, there's no guarantee that it will be compatible with future releases.