模块 IRB

IRB

模块 IRB(“交互式 Ruby”)提供了一个类似 shell 的界面,支持用户与 Ruby 解释器进行交互。

它作为一个 读取-求值-打印循环 (REPL) 运行,它

  • 读取 您键入的每个字符。您可以修改 IRB 上下文以更改输入的工作方式。请参阅 输入

  • 每次读取到语法完整的段落时,求值 代码。

  • 在求值后打印。您可以修改 IRB 上下文以更改输出的工作方式。请参阅 输出

示例

$ irb
irb(main):001> File.basename(Dir.pwd)
=> "irb"
irb(main):002> Dir.entries('.').size
=> 25
irb(main):003* Dir.entries('.').select do |entry|
irb(main):004*   entry.start_with?('R')
irb(main):005> end
=> ["README.md", "Rakefile"]

键入的输入还可以包括 IRB 特定的命令

如上所示,您可以使用 shell 命令 irb 启动 IRB

您可以通过键入命令 exit 来停止 IRB 会话

irb(main):006> exit
$

此时,IRB 调用数组 IRB.conf[:AT_EXIT] 中找到的所有钩子,然后退出。

启动

启动时,IRB

  1. 解释(作为 Ruby 代码)配置文件的内容(如果给定)。

  2. 哈希 IRB.conf 和默认值构建初始会话上下文;哈希内容可能受到 命令行选项的影响,以及配置文件中的直接赋值的影响。

  3. 将上下文分配给变量 conf

  4. 将命令行参数分配给变量 ARGV

  5. 打印 提示符

  6. 初始化脚本的内容放到 IRB shell 上,就像它是用户键入的命令一样。

命令行

在命令行上,所有选项都位于所有参数之前;第一个未被识别为选项的项目被视为参数,所有后续项目也一样。

命令行选项

许多命令行选项会影响哈希 IRB.conf 中的条目,而哈希 IRB.conf 又会影响 IRB 会话的初始配置。

选项的详细信息在下面的相关小节中进行了描述。

可以在 帮助消息中看到 IRB 命令行选项的粗略列表,如果您使用命令行选项 --help,也会显示该列表。

如果您对特定选项感兴趣,请查阅索引。

命令行参数

命令行参数在数组 ARGV 中传递给 IRB

$ irb --noscript Foo Bar Baz
irb(main):001> ARGV
=> ["Foo", "Bar", "Baz"]
irb(main):002> exit
$

命令行选项 -- 导致后面的所有内容都被视为参数,即使那些看起来像选项的参数也是如此

$ irb --noscript -- --noscript -- Foo Bar Baz
irb(main):001> ARGV
=> ["--noscript", "--", "Foo", "Bar", "Baz"]
irb(main):002> exit
$

配置文件

您可以通过配置文件初始化 IRB

如果给定命令行选项 -f,则不会查找配置文件。

否则,如果存在配置文件,IRB 会读取并解释它。

配置文件可以包含任何 Ruby 代码,并且可以方便地包含以下用户代码:

  • 可以在 IRB 中调试。

  • 配置 IRB 本身。

  • 需要或加载文件。

配置文件的路径是以下第一个找到的路径:

  • 变量 $IRBRC 的值(如果已定义)。

  • 变量 $XDG_CONFIG_HOME/irb/irbrc 的值(如果已定义)。

  • 文件 $HOME/.irbrc(如果存在)。

  • 文件 $HOME/.config/irb/irbrc(如果存在)。

  • 当前目录中的文件 .irbrc(如果存在)。

  • 当前目录中的文件 irb.rc(如果存在)。

  • 当前目录中的文件 _irbrc(如果存在)。

  • 当前目录中的文件 $irbrc(如果存在)。

如果搜索失败,则不存在配置文件。

如果搜索成功,则将配置文件读取为 Ruby 代码,因此可以包含任何您喜欢的 Ruby 编程。

如果读取了配置文件,则方法 conf.rc? 返回 true,否则返回 false。哈希条目 IRB.conf[:RC] 也包含该值。

哈希 IRB.conf

哈希 IRB.conf 中的初始条目由以下因素确定:

  • 默认值。

  • 命令行选项,可能会覆盖默认值。

  • 配置文件中的直接赋值。

您可以通过键入 IRB.conf 来查看哈希。

条目含义的详细信息在下面的相关小节中进行了描述。

如果您对特定条目感兴趣,请查阅索引。

关于初始化优先级的说明

  • 哈希 IRB.conf 中的条目与命令行选项之间的任何冲突都将有利于哈希条目。

  • 哈希 IRB.conf 仅在解释配置文件时影响上下文一次;随后对其进行的任何更改都不会影响上下文,因此本质上是无意义的。

初始化脚本

默认情况下,第一个命令行参数(在任何选项之后)是 Ruby 初始化脚本的路径。

IRB 读取初始化脚本并将其内容放到 IRB shell 上,就像它是用户键入的命令一样。

命令行选项 --noscript 导致将第一个命令行参数视为普通参数(而不是初始化脚本);--script 是默认值。

输入

本节描述了允许您更改 IRB 输入工作方式的功能;另请参阅 输入和输出

输入命令 History

默认情况下,IRB 将最多 1000 个输入命令的历史记录存储在名为 .irb_history 的文件中。如果找到 配置文件,则历史记录文件将与该文件位于同一目录中,否则位于 ~/ 中。

如果历史记录文件不存在,则新的 IRB 会话会创建它,如果历史记录文件存在,则会追加到该文件。

您可以通过在配置文件中添加以下内容来更改文件路径:IRB.conf[:HISTORY_FILE] = filepath,其中 filepath 是字符串文件路径。

在会话期间,方法 conf.history_file 返回文件路径,方法 conf.history_file = new_filepath 将历史记录复制到 new_filepath 处的文件,该文件将成为会话的历史记录文件。

您可以通过在配置文件中添加以下内容来更改保存的命令数:IRB.conf[:SAVE_HISTORY] = n,其中 n 是以下之一:

  • 正整数:要保存的命令数。

  • 负整数:要保存所有命令。

  • 零或 nil:不保存任何命令。

在会话期间,您可以使用方法 conf.save_historyconf.save_history= 来检索或更改计数。

命令别名

默认情况下,IRB 定义了多个命令别名

irb(main):001> conf.command_aliases
=> {:"$"=>:show_source, :"@"=>:whereami}

您可以使用以下内容更改配置文件中的初始别名:

IRB.conf[:COMMAND_ALIASES] = {foo: :show_source, bar: :whereami}

您可以使用配置方法 conf.command_aliases= 随时替换当前别名;因为 conf.command_aliases 是一个哈希,您可以对其进行修改。

文件结束符

默认情况下,IRB.conf[:IGNORE_EOF]false,这意味着键入文件结束符 Ctrl-D 会导致会话退出。

您可以通过在配置文件中添加 IRB.conf[:IGNORE_EOF] = true 来反转该行为。

在会话期间,方法 conf.ignore_eof? 返回设置,方法 conf.ignore_eof = boolean 设置它。

SIGINT

默认情况下,IRB.conf[:IGNORE_SIGINT]true,这意味着键入中断字符 Ctrl-C 会导致会话退出。

您可以通过在配置文件中添加 IRB.conf[:IGNORE_SIGING] = false 来反转该行为。

在会话期间,方法 conf.ignore_siging? 返回设置,方法 conf.ignore_sigint = boolean 设置它。

自动完成

默认情况下,IRB 启用 [自动完成](en.wikipedia.org/wiki/Autocomplete#In_command-line_interpr eters)

您可以通过以下任一方式禁用它:

  • 在配置文件中添加 IRB.conf[:USE_AUTOCOMPLETE] = false

  • 给定命令行选项 --noautocomplete--autocomplete 是默认值)。

如果启用了自动完成,则方法 conf.use_autocomplete? 返回 true,否则返回 false

设置在会话期间可能不会更改。

自动缩进

默认情况下,IRB 会自动缩进代码行以显示结构(例如,它会缩进块的内容)。

当前设置由配置方法 conf.auto_indent_mode 返回。

默认的初始设置为 true

irb(main):001> conf.auto_indent_mode
=> true
irb(main):002* Dir.entries('.').select do |entry|
irb(main):003*   entry.start_with?('R')
irb(main):004> end
=> ["README.md", "Rakefile"]

您可以使用以下内容更改配置文件中的初始设置:

IRB.conf[:AUTO_INDENT] = false

请注意,当前 设置可能不会IRB 会话中更改。

输入方法

IRB 输入方法决定了如何读取命令输入;默认情况下,会话的输入方法是 IRB::RelineInputMethod。除非 TERM 环境变量的值为 ‘dumb’,在这种情况下会使用最简单的输入方法。

您可以通过以下方式设置输入方法:

方法 conf.use_multiline? 及其同义词 conf.use_reline 返回:

  • 如果给出了选项 --multiline,则返回 true

  • 如果给出了选项 --nomultiline,则返回 false

  • 如果两者都没有给出,则返回 nil

方法 conf.use_singleline? 及其同义词 conf.use_readline 返回:

  • 如果给出了选项 --singleline,则返回 true

  • 如果给出了选项 --nosingleline,则返回 false

  • 如果两者都没有给出,则返回 nil

输出

本节介绍了允许您更改 IRB 输出工作方式的功能;另请参阅 输入和输出

返回值打印(回显)

默认情况下,IRB 打印(回显)所有输入命令返回的值。

您可以通过以下方式更改初始行为并禁止所有回显:

  • 添加到配置文件:IRB.conf[:ECHO] = false。(此条目的默认值为 nil,与 true 含义相同。)

  • 提供命令行选项 --noecho。(默认值为 --echo。)

在会话期间,您可以使用配置方法 conf.echo= 更改当前设置(设置为 truefalse)。

如上所述,默认情况下 IRB 打印所有输入命令返回的值;但 IRB 对赋值语句返回的值提供了特殊处理,这些值可能是:

  • 以截断方式打印(以适应单行输出),这是默认设置;省略号 (...) 作为后缀,表示截断。

    irb(main):001> x = 'abc' * 100

“abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc…”

  • 完整打印(无论长度如何)。

  • 抑制(根本不打印)。

您可以通过以下方式更改初始行为:

  • 添加到配置文件:IRB.conf[:ECHO_ON_ASSIGNMENT] = false。(此条目的默认值为 niL,与 :truncate 含义相同。)

  • 提供命令行选项 --noecho-on-assignment--echo-on-assignment。(默认值为 --truncate-echo-on-assignment。)

在会话期间,您可以使用配置方法 conf.echo_on_assignment= 更改当前设置(设置为 truefalse:truncate)。

默认情况下,IRB 通过调用方法 inspect 格式化返回值。

您可以通过以下方式更改初始行为:

  • 添加到配置文件:IRB.conf[:INSPECT_MODE] = false。(此条目的默认值为 true。)

  • 提供命令行选项 --noinspect。(默认值为 --inspect。)

在会话期间,您可以使用方法 conf.inspect_mode= 更改设置。

多行输出

默认情况下,IRB 在多行响应前添加换行符。

您可以通过添加到配置文件来更改初始默认值:

IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] = false

在会话期间,您可以使用方法 conf.newline_before_multiline_output?conf.newline_before_multiline_output= 来检索或设置该值。

示例

irb(main):001> conf.inspect_mode = false
=> false
irb(main):002> "foo\nbar"
=>
foo
bar
irb(main):003> conf.newline_before_multiline_output = false
=> false
irb(main):004> "foo\nbar"
=> foo
bar

求值 History

默认情况下,IRB 不保存求值(返回值)的历史记录,并且相关的方法 conf.eval_history___ 是未定义的。

您可以开启该历史记录,并设置要存储的最大求值次数:

  • 在配置文件中:添加 IRB.conf[:EVAL_HISTORY] = n。(以下示例假设我们已添加 IRB.conf[:EVAL_HISTORY] = 5。)

  • 在会话中(任何时间):conf.eval_history = n

如果 n 为零,则存储所有求值历史记录。

执行上述任何操作:

  • 设置求值历史记录的最大大小;定义方法 conf.eval_history,该方法返回求值历史记录的最大大小 n

    irb(main):001> conf.eval_history = 5
    => 5
    irb(main):002> conf.eval_history
    => 5
  • 定义变量 _,其中包含最近一次的求值,如果没有则为 nil;与方法 conf.last_value 相同。

    irb(main):003> _
    => 5
    irb(main):004> :foo
    => :foo
    irb(main):005> :bar
    => :bar
    irb(main):006> _
    => :bar
    irb(main):007> _
    => :bar
  • 定义变量 __

    • __ 未修饰:包含所有求值历史记录。

      irb(main):008> :foo
      => :foo
      irb(main):009> :bar
      => :bar
      irb(main):010> :baz
      => :baz
      irb(main):011> :bat
      => :bat
      irb(main):012> :bam
      => :bam
      irb(main):013> __
      =>
      9 :bar
      10 :baz
      11 :bat
      12 :bam
      irb(main):014> __
      =>
      10 :baz
      11 :bat
      12 :bam
      13 ...self-history...

      请注意,当求值是多行时,其显示方式会有所不同。

    • __[m]

      • 正数 m:包含给定行号的求值,如果该行号不在求值历史记录中,则为 nil

        irb(main):015> __[12]
        => :bam
        irb(main):016> __[1]
        => nil
      • 负数 m:包含从末尾算起的第 m 个求值,如果该求值不在求值历史记录中,则为 nil

        irb(main):017> __[-3]
        => :bam
        irb(main):018> __[-13]
        => nil
      • m:包含 nil

        irb(main):019> __[0]
        => nil

提示符和返回格式

默认情况下,IRB 使用其 :DEFAULT 提示模式中定义的提示符和返回值格式。

默认提示符和返回格式

默认提示符和返回值如下所示:

irb(main):001> 1 + 1
=> 2
irb(main):002> 2 + 2
=> 4

提示符包括:

  • 正在运行的程序的名称(irb);请参阅 IRB 名称

  • 当前会话的名称(main);请参阅 IRB 会话

  • 一个 3 位数的行号(从 1 开始)。

默认提示符实际上定义了三种格式:

  • 一种用于大多数情况(如上所示)。

    irb(main):003> Dir
    => Dir
  • 一种用于键入的命令是语句延续时(添加尾随星号)。

    irb(main):004* Dir.
  • 一种用于键入的命令是字符串延续时(添加尾随单引号)。

    irb(main):005' Dir.entries('.

您可以在键入以下字符时看到提示符的变化:

irb(main):001* Dir.entries('.').select do |entry|
irb(main):002*   entry.start_with?('R')
irb(main):003> end
=> ["README.md", "Rakefile"]

预定义的提示符

IRB 有几个预定义的提示符,存储在哈希 IRB.conf[:PROMPT] 中。

irb(main):001> IRB.conf[:PROMPT].keys
=> [:NULL, :DEFAULT, :CLASSIC, :SIMPLE, :INF_RUBY, :XMP]

要查看它们的完整数据,请键入 IRB.conf[:PROMPT]

大多数提示符定义都包含表示 IRB 名称、会话名称和行号等值的说明符;请参阅 提示符说明符

您可以通过以下方式更改初始提示符和返回格式:

  • 添加到配置文件:IRB.conf[:PROMPT] = mode,其中 mode 是提示模式的符号名称。

  • 提供命令行选项:

    • --prompt mode:将提示模式设置为 mode。其中 mode 是提示模式的符号名称。

    • --simple-prompt--sample-book-mode:将提示模式设置为 :SIMPLE

    • --inf-ruby-mode:将提示模式设置为 :INF_RUBY,并抑制 --multiline--singleline

    • --noprompt:抑制提示;不影响回显。

您可以使用以下方法检索或设置当前提示模式:

conf.prompt_modeconf.prompt_mode=

如果您对默认值以外的提示符和返回格式感兴趣,可以尝试使用其他一些格式进行试验。

自定义提示符

您还可以定义自定义提示符和返回格式,这可以在 IRB 会话中或在配置文件中完成。

如上所述,IRB 中的提示符实际上定义了三个提示符。对于简单的自定义数据,我们将使所有三个都相同。

irb(main):001* IRB.conf[:PROMPT][:MY_PROMPT] = {
irb(main):002*   PROMPT_I: ': ',
irb(main):003*   PROMPT_C: ': ',
irb(main):004*   PROMPT_S: ': ',
irb(main):005*   RETURN: '=> '
irb(main):006> }
=> {:PROMPT_I=>": ", :PROMPT_C=>": ", :PROMPT_S=>": ", :RETURN=>"=> "}

如果您在配置文件中定义了自定义提示符,您还可以通过添加以下内容使其成为当前提示符:

IRB.conf[:PROMPT_MODE] = :MY_PROMPT

无论在哪里定义,您都可以在会话中将其设置为当前提示符:

conf.prompt_mode = :MY_PROMPT

您可以使用各种配置方法查看或修改当前提示符数据:

  • conf.prompt_mode, conf.prompt_mode=.

  • conf.prompt_c, conf.c=.

  • conf.prompt_i, conf.i=.

  • conf.prompt_s, conf.s=.

  • conf.return_format, return_format=.

提示符说明符

提示符的定义可以包含说明符,这些说明符将被替换为某些值:

  • %N:正在运行的程序的名称。

  • %mself.to_s 的值。

  • %Mself.inspect 的值。

  • %l:字符串类型的指示;为 "'/] 之一。

  • %NNi:缩进级别。NN 是一个 2 位数字,指定缩进级别的位数(03 将产生 001)。

  • %NNn:行号。NN 是一个 2 位数字,指定行号的位数(03 将产生 001)。

  • %%:文字 %

详细程度

默认情况下,IRB 详细程度被禁用,这意味着输出较小而不是较大。

您可以通过以下方式启用详细程度:

  • 添加到配置文件:IRB.conf[:VERBOSE] = true(默认值为 nil)。

  • 提供命令行选项 --verbose(默认值为 --noverbose)。

在会话期间,您可以使用方法 conf.verboseconf.verbose= 来检索或设置详细程度。

帮助

命令行选项 --version 会使 IRB 打印其帮助文本并退出。

版本

命令行选项 --version 会使 IRB 打印其版本信息并退出。

输入和输出

颜色 高亮显示

默认情况下,IRB 颜色高亮显示是启用的,并用于

  • 输入:在您键入时,IRB 读取键入的字符并突出显示它识别的元素;它还会突出显示错误,例如不匹配的括号。

  • 输出:IRB 突出显示语法元素。

您可以通过以下方式禁用颜色高亮显示

  • 添加到配置文件:IRB.conf[:USE_COLORIZE] = false(默认值为 true)。

  • 给出命令行选项 --nocolorize

调试

命令行选项 -d 将变量 $VERBOSE$DEBUG 设置为 true;这些对 IRB 输出没有影响。

警告

命令行选项 -w 抑制警告。

命令行选项 -W[级别] 设置警告级别;

  • 0=静默

  • 1=中等

  • 2=详细

其他功能

加载模块

您可以指定要在启动时 require 的模块名称。

数组 conf.load_modules 确定在会话启动期间要 require 的模块(如果有)。该数组仅在会话启动期间使用,因此初始值是唯一重要的值。

默认初始值为 [](不加载模块)

irb(main):001> conf.load_modules
=> []

您可以通过以下方式设置默认初始值

  • 命令行选项 -r

     $ irb -r csv -r json
    irb(main):001> conf.load_modules
    => ["csv", "json"]
  • 哈希条目 IRB.conf[:LOAD_MODULES] = 数组

    IRB.conf[:LOAD_MODULES] = %w[csv, json]
    

请注意,配置文件条目会覆盖命令行选项。

RI 文档目录

您可以指定要在启动时加载的 RI 文档目录的路径(除了默认目录之外);有关 RI 的详细信息,请键入 ri --help

数组 conf.extra_doc_dirs 确定在会话启动期间要加载的目录(如果有)。该数组仅在会话启动期间使用,因此初始值是唯一重要的值。

默认初始值为 [](不加载额外的文档)

irb(main):001> conf.extra_doc_dirs
=> []

您可以通过以下方式设置默认初始值

  • 命令行选项 --extra_doc_dir

    $ irb --extra-doc-dir your_doc_dir --extra-doc-dir my_doc_dir
    irb(main):001> conf.extra_doc_dirs
    => ["your_doc_dir", "my_doc_dir"]
  • 哈希条目 IRB.conf[:EXTRA_DOC_DIRS] = 数组

    IRB.conf[:EXTRA_DOC_DIRS] = %w[your_doc_dir my_doc_dir]
    

请注意,配置文件条目会覆盖命令行选项。

IRB 名称

您可以为 IRB 指定一个名称。

默认初始值为 'irb'

irb(main):001> conf.irb_name
=> "irb"

您可以通过哈希条目 IRB.conf[:IRB_NAME] = 字符串 设置默认初始值

IRB.conf[:IRB_NAME] = 'foo'

应用程序名称

您可以为 IRB 会话指定应用程序名称。

默认初始值为 'irb'

irb(main):001> conf.ap_name
=> "irb"

您可以通过哈希条目 IRB.conf[:AP_NAME] = 字符串 设置默认初始值

IRB.conf[:AP_NAME] = 'my_ap_name'

配置监视器

您可以通过在配置文件中将 proc 分配给 IRB.conf[:IRB_RC] 来监视配置的更改

IRB.conf[:IRB_RC] = proc {|conf| puts conf.class }

每次配置更改时,都会使用参数 conf 调用该 proc

编码

命令行选项 -E ex[:in] 设置初始外部 (ex) 和内部 (in) 编码。

命令行选项 -U 将两者都设置为 UTF-8。

命令

请使用 help 命令查看可用命令的列表。

IRB 会话

IRB 有一个特殊功能,允许您一次管理多个会话。

您可以使用 Irb.irb 创建新会话,并使用提示中的 jobs 命令获取当前会话的列表。

配置

命令行选项或 IRB.conf 指定 Irb.irb 的默认行为。

另一方面,IRB 的命令行选项中的每个 conf 都用于单独配置 IRB.irb。

如果为 IRB.conf[:IRB_RC] 设置了 proc,则将在执行该 proc 后调用它,并将当前会话的上下文作为其参数。可以使用此机制配置每个会话。

会话变量

每个 Irb 会话中都有一些变量,它们可能会派上用场

_:执行的命令的值,作为局部变量 __:已评估命令的历史记录。仅当 IRB.conf[:EVAL_HISTORY] 不为 nil 时(这是默认值)才可用。另请参阅 IRB::Context#eval_history=IRB::History__[line_no]:返回给定行号 line_no 处的评估值。如果 line_no 是负数,则返回最接近的返回值的 line_no 行之前的返回值。

限制

键入到 IRB 中的 Ruby 代码的行为与文件中的 Ruby 代码相同,除了

  • 因为 IRB 在语法完成之后立即评估输入,所以某些结果可能略有不同。

  • 分叉可能不会表现良好。

nop.rb -

by Keiju ISHITSUKA(keiju@ruby-lang.org)

multi.rb -

by Keiju ISHITSUKA(keiju@ruby-lang.org)

irb/multi-irb.rb - 多个 irb 模块

by Keiju ISHITSUKA(keiju@ruby-lang.org)

frame.rb -

by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)

irb/help.rb - 打印用法模块

by Keiju ISHITSUKA(keiju@ishitsuka.com)

irb/lc/error.rb -

by Keiju ISHITSUKA(keiju@ruby-lang.org)

irb/lc/ja/error.rb -

by Keiju ISHITSUKA(keiju@ruby-lang.org)

output-method.rb - irb 使用的输出方法

by Keiju ISHITSUKA(keiju@ruby-lang.org)

常量

ExtendCommand
TOPLEVEL_BINDING

公共类方法

start(ap_path = nil) 单击以切换源代码

初始化 IRB 并在 TOPLEVEL_BINDING 创建一个新的 Irb.irb 对象

# File irb.rb, line 895
def start(ap_path = nil)
  STDOUT.sync = true
  $0 = File::basename(ap_path, ".rb") if ap_path

  setup(ap_path)

  if @CONF[:SCRIPT]
    irb = Irb.new(nil, @CONF[:SCRIPT])
  else
    irb = Irb.new
  end
  irb.run(@CONF)
end

私有类方法

easter_egg(type = nil) 单击以切换源代码
# File irb/easter-egg.rb, line 109
        def easter_egg(type = nil)
  print "\e[?1049h"
  type ||= [:logo, :dancing].sample
  case type
  when :logo
    Pager.page do |io|
      logo_type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode_large : :ascii_large
      io.write easter_egg_logo(logo_type)
      STDIN.raw { STDIN.getc } if io == STDOUT
    end
  when :dancing
    STDOUT.cooked do
      interrupted = false
      prev_trap = trap("SIGINT") { interrupted = true }
      canvas = Canvas.new(Reline.get_screen_size)
      Reline::IOGate.set_winch_handler do
        canvas = Canvas.new(Reline.get_screen_size)
      end
      ruby_model = RubyModel.new
      print "\e[?25l" # hide cursor
      0.step do |i| # TODO (0..).each needs Ruby 2.6 or later
        buff = canvas.draw do
          ruby_model.render_frame(i) do |p1, p2|
            canvas.line(p1, p2)
          end
        end
        buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m"
        print "\e[H" + buff
        sleep 0.05
        break if interrupted
      end
    rescue Interrupt
    ensure
      print "\e[?25h" # show cursor
      trap("SIGINT", prev_trap)
    end
  end
ensure
  print "\e[0m\e[?1049l"
end