<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.mslinn.com/feed/ruby.xml" rel="self" type="application/atom+xml" /><link href="https://www.mslinn.com/" rel="alternate" type="text/html" /><updated>2026-05-13T07:53:40-04:00</updated><id>https://www.mslinn.com/feed/ruby.xml</id><title type="html">Mike Slinn | Ruby</title><author><name>Mike Slinn</name></author><entry><title type="html">Highline Gem and Workalike</title><link href="https://www.mslinn.com/ruby/6650-highline.html" rel="alternate" type="text/html" title="Highline Gem and Workalike" /><published>2025-09-25T00:00:00-04:00</published><updated>2025-09-25T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/6650-highline</id><content type="html" xml:base="https://www.mslinn.com/ruby/6650-highline.html"><![CDATA[<!-- #region Highline -->
<h2 id="highline" class="clear">Highline</h2>
<p>
  <a href='https://rubygems.org/gems/highline/' target='_blank' rel="nofollow"><code>Highline</code></a>
  is a gem that is often found in CLIs that are built with <code>Option&shy;Parser</code>.
  It provides a way to ask the user questions and get answers,
  similar in syntax to methods that Thor provides for the same purpose.
</p>
<p>
  The <a href='https://www.rubydoc.info/gems/highline/1.6.21/HighLine:agree' target='_blank' rel="nofollow"><code>agree</code> method</a>
  is particularly useful.
  Both the <code>agree</code> and
  <a href='https://www.rubydoc.info/gems/highline/1.6.21/HighLine#ask-instance_method' target='_blank' rel="nofollow"><code>ask</code> methods</a>
  have a quirky feature:
  putting a space at the end of the question string suppresses the newline between the question and the answer.
</p>
<p>
  The optional <code>character</code> parameter causes the first character that the user types to be immediately
  processed without requiring them to press <kbd>Enter</kbd>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>my_cli.rb</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id13d123ead46d'><button class='copyBtn' data-clipboard-target='#id13d123ead46d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>require 'highline'
<span class='unselectable'>=> true
irb(main):002* </span>begin
<span class='unselectable'>irb(main):003* </span>printf "Work work work"
<span class='unselectable'>irb(main):004> </span>end while HighLine.agree "\nAll done! Do you want to do it again? ", character = true
<span class='unselectable'>Work work work
All done! Do you want to do it again?
Please enter "yes" or "no".
All done! Do you want to do it again? </span>y
<span class='unselectable'>Work work work
All done! Do you want to do it again? </span>y
<span class='unselectable'>Work work work
All done! Do you want to do it again? </span>n
<span class='unselectable'>=> nil
irb(main):005> </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Workalike -->
<h2 id="workalike">Workalike</h2>
<p>
  Below is some code that I wrote for
  <a href='/ruby/6800-nugem.html'>Nugem</a>.
  This could be hived into a separate gem, if someone was to ask.
  The <a href='https://github.com/JEG2/highline#:~:text=inject/import%20HighLine%20methods%20on%20Kernel' target='_blank' rel="nofollow"><code>import</code> statement</a>
  imports the methods of the Highline library directly into the current namespace.
</p>
<div class="codeLabel">highline_wrappers.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id1b7bbe63bbb3"><button class='copyBtn' data-clipboard-target='#id1b7bbe63bbb3'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'highline/import'

# Augment Highline
module HighlineWrappers
  def self.maybe_redirect_stdin
    input_from_env = ENV.fetch('nugem_argv', nil)
    return unless input_from_env

    require 'stringio'
    $stdin = StringIO.new(input_from_env)
    puts 'Reading STDIN from the nugem_argv environment variable.'
  end

  def yes_no?(prompt = 'More?', default_value: true)
    answer_letter = ''
    suffix = default_value ? '[Y/n]' : '[y/N]'
    default_letter = default_value ? 'y' : 'n'
    acceptable_responses = %w[y n]
    until acceptable_responses.include? answer_letter
      if $stdin.tty? # Read from terminal
        answer_letter = ask("#&#123;prompt&#125; #&#123;suffix&#125; ") do |q|
          q.limit = 1
          q.case = :downcase
        end
      else
        $stdin.read # from pipe
      end
      answer_letter = default_letter if answer_letter.empty?
    end
    answer_letter == 'y'
  end

  # Invokes yes_no? with the default answer being 'no'
  def no?(prompt = 'More?')
    yes_no? prompt, default: false
  end

  # Invokes yes_no? with the default answer being 'yes'
  def yes?(prompt = 'More?')
    yes_no? prompt, default: true
  end
end

# Monkey patch Highline
class HighLine
  alias highline_ask ask

  def ask(template_or_question, answer_type = nil, &amp;)
    if $stdin.tty? # highline handles terminal I/O
      highline_ask(template_or_question, answer_type, &amp;)
    else
      read_from_pipe(template_or_question, answer_type, &amp;)
    end
  end

  def read_from_pipe(prompt, answer_type, &amp;)
    if prompt.end_with? ' '
      print prompt
    else
      puts prompt
    end
    q = HighLine::Question.new(prompt, answer_type, &amp;)
    q.answer = $stdin.read
    unless q.answer
      puts 'EOF encountered.'.red
      exit! 1
    end
    puts q.answer
    return q.answer if q.answer == '' # Caller must provide the default value or action

    unless q.valid_answer?
      error_message = q.responses[:not_valid] ||
                      q.responses[:not_in_range] ||
                      'Validation failed.'
      puts "Input '#&#123;q.answer&#125;' is invalid: #&#123;error_message&#125;".red
      exit! 33
    end
    q.answer
  end
end
</pre>



<p>
  The above code can be mixed into any class or module.
  They ask the user a question and return <code>true</code> or <code>false</code> based on the user&rsquo;s response.
</p>
<p>
  The methods in the code issue a prompt and then read just one character from the user.
  The user can respond by typing the single character <kbd>y</kbd> to answer &ldquo;yes&rdquo;
  or <kbd>n</kbd> to answer &ldquo;no&rdquo;.
  If the user presses <kbd>Space</kbd> or <kbd>Enter</kbd> without typing anything else,
  the default answer is used.
</p>
<p>
  The following <code>irb</code> session shows how to use the <code>yes?</code> and <code>no?</code> methods.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>irb session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id2e4bbd5f55ed'><button class='copyBtn' data-clipboard-target='#id2e4bbd5f55ed' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>irb(main):152> </span>yes? 'asdf'
<span class='unselectable'>asdf [Y/n] </span><kbd>Enter</kbd> or <kbd>Space</kbd>
<span class='unselectable'>=> true<br>
irb(main):153> </span>yes? 'asdf'
<span class='unselectable'>asdf [Y/n] </span>n
<span class='unselectable'>=> false<br>
irb(main):154> </span>no? 'asdf'
<span class='unselectable'>asdf [y/N] </span><kbd>Enter</kbd> or <kbd>Space</kbd>
<span class='unselectable'>=> false<br>
irb(main):155> </span>no? 'asdf'
<span class='unselectable'>asdf [y/N] </span>n
<span class='unselectable'>=> false<br>
irb(main):156> </span>no? 'asdf'
<span class='unselectable'>asdf [y/N] y
=> true </span><br></pre>

</div>

<!-- endregion -->


<!-- #region Data Pipelines -->
<h2 id="pipes">Data Pipelines</h2>
<p>
  <code>highline_wrappers.rb</code>, above, provides support for data pipelines.
  The following is an example of a pipeline that feeds a multiline string with three lines into the <code>STDIN</code> of a one-line Ruby program.
  The Ruby program merely reverses the order of the lines that it receives and sends the result to <code>STDOUT</code>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id0cf15912dfd8'><button class='copyBtn' data-clipboard-target='#id0cf15912dfd8' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>printf "a\nb\nc\n" | ruby -e 'puts $stdin.readlines.reverse'
<span class='unselectable'>c
b
a </span></pre>

</div>

<!-- endregion -->

<p>
  You could use data pipelines to drive a CLI you wrote that uses my version of \
  <code>ask</code>, <code>yes?</code> and <code>no?</code>.
</p>
<!-- endregion -->
<!-- endregion -->


<!-- #region Debugging With Visual Studio Code -->
<h2 id="debug">Debugging With Visual Studio Code</h2>
<p>
  When Visual Studio Code debugs a Ruby program that it launched, the debugee
  does not enjoy the niceties that a shell like Bash would have provided.
  For example, there is no way to specify an input stream in a Visual Studio Code launch configuration,
  and my experience with attaching to external terminals from <code>rdbg</code> and <code>ruby_lsp</code> has not been good.
  Using Visual Studio Code to debug a program that performs user dialog is problematic.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Visual Studio Code launch configurations</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id6a3a6bb4f3ae'><button class='copyBtn' data-clipboard-target='#id6a3a6bb4f3ae' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>{
  {
    "env": {
      "VO_DEBUGGING": "true"
    },
    "name": "rdbg nugem jekyll test --force --private --tag=tag1",
    "script": "${workspaceFolder}/exe/nugem jekyll test --force --private --tag=tag1",
    "request": "launch",
    "type": "rdbg",
    "useBundler": true,
    <span class='bg_yellow_nopad'>"useTerminal": true,</span>
  },
  {
    "env": {
      "VO_DEBUGGING": "true"
    },
    "name": "ruby_lsp nugem jekyll test --force --private --tag=tag1",
    "program": "ruby exe/nugem jekyll test --force --private --tag=tag1",
    "request": "launch",
    "type": "ruby_lsp",
  },
}</pre>

</div>

<!-- endregion -->
<p>
  This makes debugging a CLI that interacts with a user through dialog difficult.
  I considered the following possible solutions:
</p>

<p class="numbered">
  Launch the the program under a monitor, pause execution, and then
  attach Visual Studio Code to the monitor.
  That works well, but that can involve a lot of fussing until things work just right.
</p>
<p>
  BTW, when the program first launches, it examines the <code>VO_DEBUGGING</code> environment variable
  to see if it should load the rest of the program from the project directory,
  or if it should load the rest of itself from an installed gem.
  All my gems work that way.
</p>
<p class="numbered">
  Redirect <code>STDIN</code> to read from a pipeline.
  This approach works well for scripted environments, but does not help us with debugging via Visual Studio Code.
</p>
<p class="numbered">
  Redirect <code>STDIN</code> to read from an environment variable.
  This approach works well for all scenarios.
</p>
<!-- endregion -->


<!-- #region Solution: nugem_argv -->
<h2 id="sol">Solution: <span class="code">nugem_argv</span></h2>
<p>
  The third option is the best choice.
</p>
<p>
  I decided to make the program look for an environment variable called <code>nugem_argv</code>.
  If present, its value would be used as <code>STDIN</code>.
</p>

<div class="codeLabel">playground/nugem_argv.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="idbf6d46ca6fc0"><button class='copyBtn' data-clipboard-target='#idbf6d46ca6fc0'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def maybe_redirect_stdin
  input_from_env = ENV.fetch('nugem_argv', nil)
  return unless input_from_env

  require 'stringio'
  $stdin = StringIO.new(input_from_env)
  puts 'Reading STDIN from the nugem_argv environment variable.'
end

# Define the environment variable
ENV['nugem_argv'] = "Hello from Mars\nHello from Venus\nGoodbye\n"
maybe_redirect_stdin
# Now any code that reads from standard input (like gets) will read from ENV['nugem_argv'].

# Echo STDIN to STDOUT line by line until EOF.
# Do not call $stdio.eof? to test EOF because it will block until the process exits
# if STDIN is a pipe or socket.
# Instead, detect when STDIN reaches EOF by gets returning nil
while line = gets # rubocop:disable Lint/AssignmentInCondition
  puts line
end
</pre>




<p>
  We can test the above:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idae28215a3b9e'><button class='copyBtn' data-clipboard-target='#idae28215a3b9e' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>ruby playground/nugem_argv.rb
<span class='unselectable'>Reading STDIN from the nugem_argv environment variable.
Hello from Mars
Hello from Venus
Goodbye </span></pre>

</div>

<!-- endregion -->

<p>
  After incorporating the <code>maybe_redirect_stdin</code> method shown above into <code>nugem</code>,
  we can use the following launch configurations for testing CLI user dialog:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Visual Studio Code Launch Configurations with STDIO from nugem_argv</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='iddb776ce0066b'><button class='copyBtn' data-clipboard-target='#iddb776ce0066b' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>{
  {
    "debugPort": "0",
    "env": {
      <span class='bg_yellow_nopad'>"nugem_argv": "Hello from Mars\nHello from Venus\nGoodbye\n",</span>
      "VO_DEBUGGING": "true"
    },
    "name": "Nugem overwrite /tmp/nugem_test",
    "script": "${workspaceFolder}/exe/nugem ruby test -o /tmp/nugem_test --force",
    "request": "launch",
    "type": "rdbg",
    "useBundler": true,
  },
  {
    "env": {
      <span class='bg_yellow_nopad'>"nugem_argv": "Hello from Mars\nHello from Venus\nGoodbye\n",</span>
      "VO_DEBUGGING": "true"
    },
    "name": "ruby_lsp nugem jekyll test --force --private --tag=tag1",
    "program": "ruby exe/nugem jekyll test --force --private --tag=tag1",
    "request": "launch",
    "type": "ruby_lsp",
  },
}</pre>

</div>

<!-- endregion -->
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><summary type="html"><![CDATA[The Highline Gem and a workalike that flexibly handles many input sources.]]></summary></entry><entry><title type="html">Custom Ruby Bindings</title><link href="https://www.mslinn.com/ruby/6900-custom_binding.html" rel="alternate" type="text/html" title="Custom Ruby Bindings" /><published>2025-09-05T00:00:00-04:00</published><updated>2025-09-18T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/6900-custom_binding</id><content type="html" xml:base="https://www.mslinn.com/ruby/6900-custom_binding.html"><![CDATA[<!-- #region intro -->
<p>
  Ruby <a href='https://www.rubydoc.info/stdlib/erb/ERB' target='_blank' rel="nofollow">ERB</a>
  expands Ruby expressions embedded in HTML or markup such that the encapsulated expression is replaced by its value.
  ERB ultimately calls <code>eval</code>, with some wrapping for safety and binding control.
</p>

<h2 id="why">Export Execution Contexts Via Ruby Bindings</h2>
<p>
  I have been developing the next-generation <a href='/ruby/6800-nugem.html'><code>Nugem</code></a> code
  for a fair while yet.
  This command-line program was being constructed so that it would generate
  new working Ruby gems from command-line parameters.
</p>
<p>
  When generating a new Ruby gem,
  <code>Nugem</code> needs to inflate ERB templates embedded within file system templates.
  The problem I encounted was that the values I wanted to inflate ERB templates with were defined in very different binding contexts.
</p>
<p>
  To me, this sounded like there was a need for rudimentary management of Ruby binding context.
</p>
<!-- endregion -->


<!-- #region Easy, Simple, Flexible Ruby Binding Management -->
<h2 id="esfrbm">Easy, Simple, Flexible Ruby Binding Management</h2>
<p>
  I was not happy with what I could see out there on the interwebs for assembling custom Ruby <code>Bindings</code>,
  so I created the <code>CustomBinding</code> Ruby gem.
  It provides a simple and flexible way of constructing a custom Ruby binding,
  and simplifies the rendering of ERB templates from a custom Ruby binding.
</p>
<p>
  <code>CustomBinding</code> provides an easy, simple and flexible way of specifying
  variable and method references in ERB templates.
  The <code>CustomBinding#render</code> method resolves the embedded references
  against the <code>CustomBinding</code> internal state.
</p>
<p>
  <code>Nugem</code> uses ERB templates to create every type of text file that a Ruby Gem needs.
  This includes Ruby code, RSpec tests, markdown files, <code>.gitignore</code>, and more.
</p>
<p>
  ERB templates support expressions that contain:
</p>
<ol>
  <li>References to <code>self</code></li>
  <li>Local, instance, and global variable names</li>
  <li>Local and instance public method names</li>
  <li>Computation using the above</li>
</ol>
<p>
  An ERB template consists of a <code>String</code> expression enclosed in single or double quotes, or provides as a here doc.
</p>
<!-- endregion -->


<!-- #region object_id -->
<h2 id="object_id"><span class='code'>object_id</span></h2>
<p>
  Every Ruby object has a unique identifier.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idb1912cdb7708'><button class='copyBtn' data-clipboard-target='#idb1912cdb7708' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>x = Object.new
<span class='unselectable'>=> #&lt;Object:0x00007fcd79d5c9a8><br>
irb(main):002> </span> x.object_id
<span class='unselectable'>=> 7160 </span></pre>

</div>

<!-- endregion -->

<p>
  If you find two variables with the same unique identifier,
  then they are aliases, or mirrors, for each other.
  This information could be helpful as you explore how Ruby <code>Binding</code>s work.
</p>
<!-- endregion -->


<!-- #region Binding -->
<h2 id="binding">Bindings</h2>
<!-- #region implicit -->
<p>
  For ERB to work, a mechanism must exist that can look up the value of a variable or obtain a method body from its name.
  This mechanism is called a <i>binding</i>.
  In Ruby, a <a href='https://www.rubydoc.info/stdlib/core/Binding' target='_blank' rel="nofollow"><code>Binding</code></a>
  instance is an object that encapsulates the execution context at a specific point in the code.
  Each <code>Binding</code> instance maintains an <i>execution environment</i>,
  sometimes referred to as the <i>execution state</i>.
</p>
<p>
  Each <code>Binding</code> instance in Ruby encapsulates the execution state at the point where it was created.
  This includes the current values of local variables, the value of <code>self</code>, method visibility,
  and the context for constant and method lookup.
  This execution environment can later be used to evaluate code with <code>eval</code>.
</p>
<p>
  ERB is not the only thing that uses bindings; debuggers, the Ruby REPL, and code generators also use bindings.
</p>
<!-- endregion -->


<!-- #region The Top-Level Binding -->
<h3 id="ttlb">The Top-Level Binding</h3>
<!-- #region implicit -->
<p>
  Ruby provides a constant called <code>TOPLEVEL_BINDING</code> that returns the binding of the top-level scope.
  Use it to access the top-level scope from anywhere in the program.
  Because the top-level scope never contains local variables,
  <code>TOPLEVEL_BINDING</code> does not contain local variables either.
</p>
<!-- endregion -->


<!-- #region self and main -->
<h4 id="selfmain"><span class='code'>self</span> and <span class='code'>main</span></h4>
<p>
  When Ruby starts, it sets <code>self</code> to an instance of <code>Object</code>.
  That instance is not bound to a constant or variable named <code>main</code>.
  The string <code>"main"</code> is just what you see if you print <code>self</code>:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idf10e790f510d'><button class='copyBtn' data-clipboard-target='#idf10e790f510d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>TOPLEVEL_BINDING.eval 'self'
<span class='unselectable'>=> main<br>
irb(main):002> </span>puts TOPLEVEL_BINDING.eval 'main'
<span class='unselectable'>(eval):1:in `<main>': undefined local variable or method `main' for main:Object (NameError)<br>
main
^^^^
        from (irb):18:in `eval'
        from (irb):18:in `<main>'
        from /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/irb-1.15.2/exe/irb:9:in `<top (required)>'
        from /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/rubygems.rb:319:in `load'
        from /home/mslinn/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/rubygems.rb:319:in `activate_and_load_bin_path'
        from /home/mslinn/.rbenv/versions/3.2.2/bin/irb:25:in `<main>' </span></pre>

</div>

<!-- endregion -->

<p>
  If a <code>binding</code> is created at the top-level,
  then binding&rsquo;s <code>self.to_s</code> returns the string <code>"main"</code>.
  <code>self</code> is an instance of <code>Object</code>.
  There is no separate <code>main</code> class or module in Ruby;
  this output just indicates that the binding currently provides a particular execution context.
  No standalone documentation page for <code>main</code> exists.
</p>
<p>
  Note that IRB starts with <code>TOPLEVEL_BINDING</code>;
  the <code>binding</code> tends to accumulate things as code is executed.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idc42b4ed341be'><button class='copyBtn' data-clipboard-target='#idc42b4ed341be' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>self
<span class='unselectable'>=> main </span><br>
<span class='unselectable'>irb(main):002> </span>self.class
<span class='unselectable'>=> Object </span></pre>

</div>

<!-- endregion -->
<p>
  There is no global variable or constant called <code>main</code>.
  The String <code>"main"</code> is just the default output of <code>to_s</code> for that particular <code>Object</code> instance.
</p>
<!-- endregion -->


<!-- #region TOPLEVEL_BINDING is Not Empty -->
<h4 id="tine"><span class="code">TOPLEVEL_BINDING</span> Is Not Empty</h4>
<p>
  The top-level binding is not empty:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide24b45ba5a7d'><button class='copyBtn' data-clipboard-target='#ide24b45ba5a7d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>puts TOPLEVEL_BINDING.eval 'self.public_methods'
<span class='unselectable'>to_s
inspect
conf
pretty_print_cycle
pretty_print
pretty_print_inspect
pretty_print_instance_variables
hash
singleton_class
dup
itself
methods
singleton_methods
protected_methods
private_methods
public_methods
instance_variables
instance_variable_get
instance_variable_set
instance_variable_defined?
remove_instance_variable
instance_of?
kind_of?
is_a?
display
pretty_inspect
public_send
extend
clone
&lt;=>
class
===
!~
frozen?
then
tap
nil?
yield_self
eql?
respond_to?
method
public_method
singleton_method
define_singleton_method
freeze
object_id
send
to_enum
enum_for
!
equal?
__id__
__send__
==
!=
instance_eval
instance_exec
=> nil </span></pre>

</div>

<!-- endregion -->

<p>
  Note that the above incantation could have been written without <code>self</code>,
  since it is always implied if a receiver is not explicitly specified:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Equivalent IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idefb6bbbae3c9'><button class='copyBtn' data-clipboard-target='#idefb6bbbae3c9' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>puts TOPLEVEL_BINDING.eval 'public_methods'
<span class='unselectable'>to_s
inspect
conf
pretty_print_cycle
pretty_print
pretty_print_inspect
pretty_print_instance_variables
hash
singleton_class
dup
itself
methods
singleton_methods
protected_methods
private_methods
public_methods
instance_variables
instance_variable_get
instance_variable_set
instance_variable_defined?
remove_instance_variable
instance_of?
kind_of?
is_a?
display
pretty_inspect
public_send
extend
clone
&lt;=>
class
===
!~
frozen?
then
tap
nil?
yield_self
eql?
respond_to?
method
public_method
singleton_method
define_singleton_method
freeze
object_id
send
to_enum
enum_for
!
equal?
__id__
__send__
==
!=
instance_eval
instance_exec
=> nil </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region main Methods -->
<h3 id="mm"><span class="code">main</span> Methods</h3>
<p>
  When Ruby finishes initializing, <code>main</code> has the mixture of all the public methods of
  <code>Object</code>, <code>Kernel</code>, and <code>BasicObject</code>.
</p>
<ul>
  <li>
    From <code>Kernel</code>: <code>load</code>, <code>puts</code>, <code>print</code>,
    <code>p</code>, <code>gets</code>, <code>raise</code>, <code>rand</code>, <code>sleep</code>, and <code>require</code>.
  </li>
  <li>
    From <code>Object</code>:
    <code>object_id</code>, <code>is_a?</code>, <code>class</code>, <code>tap</code>, and <code>public_send</code>.
  </li>
  <li>
    From <code>BasicObject</code>:
    <code>==</code>, <code>!=</code>, <code>!</code>, <code>__send__</code>, and <code>equal?</code>.
  </li>
</ul>
<p>
  The full list can be obtained like this:
</p>

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida367f2a85c67'><button class='copyBtn' data-clipboard-target='#ida367f2a85c67' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>ruby -e 'puts TOPLEVEL_BINDING.eval("methods.sort")'</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Binding and Self -->
<h2 id="bso"><span class='code'>binding</span> and <span class='code'>self</span></h2>
<p>
  In Ruby, every <code>Binding</code> instance contains a <code>self</code> object.
</p>
<p>
  A <code>Binding</code> object captures the execution context at a specific point in the code,
  including the current value of <code>self</code>,
  local variables, instance variables, class variables, global variables,
  and all methods.
</p>
<p>
  The following code accesses the <code>self</code> of a <code>Binding</code>
  with <code class="bg_yellow_nopad">Binding#receiver</code>:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1e67f5275fd3'><button class='copyBtn' data-clipboard-target='#id1e67f5275fd3' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>x = Object.new
<span class='unselectable'>=> #&lt;Object:0x00007fcd79d5c9a8><br>
irb(main):002> </span>b = x.instance_eval { binding }
<span class='unselectable'>=> #&lt;Binding:0x00007f973f4d5138><br>
irb(main):003> </span>puts b.<span class='bg_yellow_nopad'>receiver</span>.public_methods.sort
<span class='unselectable'>!
!=
!~
&lt;=>
==
===
__id__
__send__
class
clone
define_singleton_method
display
dup
enum_for
eql?
equal?
extend
freeze
frozen?
hash
inspect
instance_eval
instance_exec
instance_of?
instance_variable_defined?
instance_variable_get
instance_variable_set
instance_variables
is_a?
itself
kind_of?
method
methods
nil?
object_id
pretty_inspect
pretty_print
pretty_print_cycle
pretty_print_inspect
pretty_print_instance_variables
private_methods
protected_methods
public_method
public_methods
public_send
remove_instance_variable
respond_to?
send
singleton_class
singleton_method
singleton_methods
tap
then
to_enum
to_s
yield_self </span></pre>

</div>

<!-- endregion -->

<p>
  You can also evaluate <code>self</code> within the <code>Binding</code>:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id98ca88a5543f'><button class='copyBtn' data-clipboard-target='#id98ca88a5543f' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>irb(main):003> </span>puts binding.eval 'self'
<span class='unselectable'>=> main </span></pre>

</div>

<!-- endregion -->

<p>
  You can interrogate <code>self</code> to discover the values of its properties:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide829415ae37d'><button class='copyBtn' data-clipboard-target='#ide829415ae37d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>irb(main):004> </span>binding.eval '<span class='bg_yellow_nopad'>self.</span>singleton_methods'
<span class='unselectable'>=> [:to_s, :inspect, :conf] </span><br>
<span class='unselectable'>irb(main):005> </span>binding.eval '<span class='bg_yellow_nopad'>self.</span>local_variables'
<span class='unselectable'>=> [:b, :x, :_] </span><br>
<span class='unselectable'>irb(main):006> </span>binding.eval '<span class='bg_yellow_nopad'>self.</span>instance_variables'
<span class='unselectable'>=> [] </span>
<span class='unselectable'>irb(main):007> </span>puts binding.eval '<span class='bg_yellow_nopad'>self.</span>global_variables'
$-p
$-l
$-a
$@
$;
$-F
$-I
$:
$"
$LOAD_PATH
$LOADED_FEATURES
$-v
$VERBOSE
$-W
$-w
$-d
$DEBUG
$PROGRAM_NAME
$0
$&
$`
$'
$+
$=
$?
$$
$stdin
$stdout
$>
$stderr
$DEBUG_RDOC
$_
$~
$!
$/
$,
$\
$-0
$&lt;
$.
$FILENAME
$-i
$*
=> nil %}</pre>

</div>

<!-- endregion -->

<p>
  The implied caller (called a <i>receiver</i> in Ruby) is <code>self</code>,
  so the above can be written without the <code>self</code> prefix.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idc9f8b2a12d8f'><button class='copyBtn' data-clipboard-target='#idc9f8b2a12d8f' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>irb(main):004> </span>binding.eval 'singleton_methods'
<span class='unselectable'>=> [:to_s, :inspect, :conf] </span><br>
<span class='unselectable'>irb(main):005> </span>binding.eval 'local_variables'
<span class='unselectable'>=> [:b, :x, :_] </span><br>
<span class='unselectable'>irb(main):006> </span>binding.eval 'instance_variables'
<span class='unselectable'>=> [] </span>
<span class='unselectable'>irb(main):007> </span>puts binding.eval 'global_variables'
$-p
$-l
$-a
$@
$;
$-F
$-I
$:
$"
$LOAD_PATH
$LOADED_FEATURES
$-v
$VERBOSE
$-W
$-w
$-d
$DEBUG
$PROGRAM_NAME
$0
$&
$`
$'
$+
$=
$?
$$
$stdin
$stdout
$>
$stderr
$DEBUG_RDOC
$_
$~
$!
$/
$,
$\
$-0
$&lt;
$.
$FILENAME
$-i
$*
=> nil %}</pre>

</div>

<!-- endregion -->

<p>
  <code>my_binding.<wbr>local_variables</code> is not equivalent to <code>my_binding.<wbr>receiver.<wbr>local_variables</code> or
  <code>my_binding.<wbr>self.<wbr>local_variables</code>.
</p>
<p>
  <code>my_binding.<wbr>local_variables</code> returns an array of the names of local variables available in the context captured by the binding.
</p>
<p>
  <code>my_binding.<wbr>receiver.<wbr>local_variables</code>
  calls the <code>local_variables</code> method on the object that is <code>self</code> in the binding.
  This usually returns the local variables in the current scope of that object, which is typically not the same as those
  in the binding unless you are in the same context.
</p>
<p>
  For example:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idac2ec63c537d'><button class='copyBtn' data-clipboard-target='#idac2ec63c537d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>x = 42
<span class='unselectable'>=> 42<br>
irb(main):002> </span>b = binding
<span class='unselectable'>=&gt; #&lt;Binding:0x000073a8689ec918&gt;<br>
irb(main):003> </span>b.local_variables
<span class='unselectable'>=> [:b, :x, :_]<br>
irb(main):004> </span>b.receiver.send 'local_variables'
<span class='unselectable'>=> [:b, :x, :_] </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Leaky Top-Level Definitions -->
<h3 id="toplevel">Leaky Top-Level Definitions</h3>
<p>
  The <i>top-level binding context</i> is the outermost scope,
  and is not encapsulated within any class or module.
</p>
<p>
  The <code>hello</code> method shown below is defined at the top-level binding context / outermost scope.
  Its method definition exists separately in the top-level binding context,
  distinct from all other entries.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id59c99e7a6b9c'><button class='copyBtn' data-clipboard-target='#id59c99e7a6b9c' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def hello
  'hi'
end

p main.method(:hello) # => <span class='bg_yellow_nopad'>#&lts;Method: Object#hello></span></pre>

</div>

<!-- endregion -->
<p>
  As you can see, <code>hello</code> is not shown as a method of <code>main</code>.
  This is because it is <span class='bg_yellow'>an instance method of <code>Object</code></span>,
  available everywhere because every object inherits from <code>Object</code>.
  Promiscuous top-level definitions that leak from the <code>main</code> instance
  into the <code>Object</code> definition are said to <i>leak</i> globally.
</p>
<p>
  All top-level definitions leak, including methods, constants, local variables, and instance variables.
  The precise mechanism varies for each type of definition, but the result is the same:
  definitions propagating through <code>Object</code> to everything inheriting from <code>Object</code>.
  To prevent accidental leakage, which would pollute the global namespace,
  wrap your definitions within a module or class instead of defining them at the top level.
</p>
<!-- endregion -->
<!-- endregion -->
<!-- endregion -->


<!-- #region Variable and Method Resolution -->
<h2 id="vamr">Variable and Method Resolution</h2>
<p>
  Here’s how variable and method resolution works:
</p>
<ol>
  <li>
    If a name matches a local variable in the binding, the value is returned.
  </li>
  <li>
    Ruby next tries to resolve instance variables and method references on the current <code>self</code> object.
    Instance variables are only accessible if the binding was created inside an object where they exist.
    Methods are accessible if the object in the binding responds to them.
  </li>
  <li>
    Ruby then looks up constants through the
    <a href='https://cirw.in/blog/constant-lookup.html' target='_blank' rel="nofollow">normal constant lookup rules</a>.
  </li>
  <li>
    If no suitable binding is found, ERB falls back to <code>TOPLEVEL_BINDING</code>.
  </li>
</ol>
<!-- endregion -->


<!-- #region Interrogating a Binding -->
<h2 id="iab">Interrogating a Binding</h2>
<p>
  To list methods and variables in a Ruby binding you can use two different
  but equivalent syntaxes:
</p>

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB Session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id67840ed95d84'><button class='copyBtn' data-clipboard-target='#id67840ed95d84' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>my_binding.local_variables # Preferred
<span class='unselectable'>=> [:my_binding, :_]<br>
irb(main):003> </span>my_binding.eval 'local_variables'
<span class='unselectable'>=> [:my_binding, :_] </span></pre>

</div>

<!-- endregion -->

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code to list instance variables</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id6ba1034e329a'><button class='copyBtn' data-clipboard-target='#id6ba1034e329a' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>my_binding.instance_variables</pre>

</div>

<!-- endregion -->

<p>
  The binding and the object it is attached to are distinct.
  This means they have separate methods and variables.
  You can examine the public methods of a <code>Binding</code> instance like this:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code to list public methods of a binding</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id821b4f38c107'><button class='copyBtn' data-clipboard-target='#id821b4f38c107' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>my_binding.public_methods</pre>

</div>

<!-- endregion -->

<p>
  To see the methods of the object the binding is attached to,
  first access <code>binding.receiver</code>
  (this is <code>self</code> within the <code>Binding</code> instance):
</p>

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide3eb4c05c693'><button class='copyBtn' data-clipboard-target='#ide3eb4c05c693' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>my_binding
  .receiver
  .public_methods
  .sort</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Example ERB -->
<h2 id="ex1">Example ERB</h2>
<p>
  The following simple example shows a template <code>String</code> being
  interpreted according to the local <code class='bg_pink_nopad'>binding</code>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id395d93f9460a'><button class='copyBtn' data-clipboard-target='#id395d93f9460a' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable numbered_line'> 1: </span>require 'date'
<span class='unselectable numbered_line'> 2: </span>require 'erb'
<span class='unselectable numbered_line'> 3: </span>
<span class='unselectable numbered_line'> 4: </span>name = 'Mike'
<span class='unselectable numbered_line'> 5: </span>age = ((Date.today - Date.civil(1956, 10, 15)) / 365).to_i
<span class='unselectable numbered_line'> 6: </span>template = '<span class='bg_yellow_nopad'>&lt;%= name %></span> is <span class='bg_yellow_nopad'>&lt;%= age %></span> years old.'
<span class='unselectable numbered_line'> 7: </span>
<span class='unselectable numbered_line'> 8: </span>erb = ERB.new template
<span class='unselectable numbered_line'> 9: </span>puts erb.result <span class='bg_pink_nopad'>binding</span>
</pre>

</div>

<!-- endregion -->
<p>
  The above code contains <span class='bg_yellow'>two ERB expressions</span> in the template on line 6.
  Both ERB expressions implicitly use the <code>binding</code> to look up the
  value of the given variable name or method name.
  When I ran the above, the output was:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Output</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id0f439393dd3b'><button class='copyBtn' data-clipboard-target='#id0f439393dd3b' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>Mike is 68 years old.</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Purpose of custom_binding -->
<h2 id="purpose">Purpose of <span class="code">custom_binding</span></h2>
<p>
  It is a good practice to evaluate ERBs in a different portion of a code base than where the computation might be performed.
  For example, <a href='https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller' target='_blank' rel="nofollow">Model-View-Controller</a>
  architectures are based on this style of programming.
  However, this means that the variables and methods that the ERB needs to evaluate
  are defined in different binding contexts than where they are used.
</p>
<p>
  <code>custom_binding</code> to the rescue!
  It allows you to define a custom binding either by creating new binding or mirroring an existing binding.
  You can add mirrors of objects in your code to the custom binding.
  The result is a completely normal binding that can be accessed from anywhere in your program.
</p>
<p>
  Objects can contain variables and methods.
  When mirrored in the custom binding,
  variables and functions stored within objects are always in sync with the objects they mirror.
</p>
<p>
  <code>Custom&shy;Binding#<wbr>render</code> invokes <code>ERB#<wbr>render</code> with the custom binding.
</p>
<p>
  The <a href='https://github.com/mslinn/custom_binding/blob/master/spec/custom_binding_spec.rb' target='_blank' rel="nofollow"><code>custom_binding</code> RSpec tests</a>
  show how to call <code>Custom&shy;Binding#<wbr>result</code>,
  which in turn invokes <code>ERB#<wbr>result</code> with the appropriate <code>binding</code> object.
  Here is the
  <a href='https://github.dev/mslinn/custom_binding/tree/master/playground' target='_blank' rel="nofollow">GitHub&rsquo;s online editor</a>
  for the <code>custom_binding</code> project.
</p>
<!-- endregion -->


<!-- #region Usage -->
<h2 id="use">Usage</h2>
<p>
  Some of the RSpec tests are shown below as regular code to simplify the examples.
  This code is provided in the
  <a href='https://github.com/mslinn/custom_binding/tree/master/playground' target='_blank' rel="nofollow"><code>playground</code></a>
  directory of the <code>custom_binding</code> Git project.
</p>

<p>
  <code>playground/examples.rb</code> defines a custom binding with specific objects mirrored from other binding contexts
  that the ERB will reference.
</p>

<div class="codeLabel">Test data from playground/examples.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id62c368889301"><button class='copyBtn' data-clipboard-target='#id62c368889301'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button># Test data
class Foo
  def initialize
    @foo = 42
  end
end

# Another class to demonstrate adding an instance to the mirrored binding
class Bar
  attr_accessor :bar

  def initialize(bar)
    @bar = bar
  end

  def tell_me_a_story = 'A man was born. He lived, then died.'
end

</pre>




<p>
  The following method shows how <code>Nugem</code> uses <code>CustomBinding</code> with the above test data.
</p>
<div class="codeLabel">nugem_test method from playground/examples.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id952d60d33e2a"><button class='copyBtn' data-clipboard-target='#id952d60d33e2a'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button># Just exercise those CustomBinding methods required by Nugem:
#  - CustomBinding.new
#  - CustomBinding#add_object_to_binding_as
#  - CustomBinding#eval
def nugem_test
  puts 'nugem_test'
  custom_binding = CustomBinding::CustomBinding.new binding # the current scope include the hello method

  bar = Bar.new 'value of bar.bar'
  custom_binding.add_object_to_binding_as('local_bar', bar)
  custom_binding.add_object_to_binding_as('@test_bar', bar)

  another_bar = Bar.new 'value of another_bar.bar'
  custom_binding.add_object_to_binding_as('@another_test_bar', another_bar)

  puts &lt;&lt;-END_MSG
  local_bar.eval '1+2' = #&#123;custom_binding.eval '1+2'&#125;
  local_bar.result '&lt;%= 1+2 %>' = #&#123;custom_binding.result '&lt;%= 1+2 %>'&#125;

  hello = #&#123;custom_binding.eval('hello')&#125;

  local_bar.tell_me_a_story         = #&#123;custom_binding.eval('local_bar.tell_me_a_story')&#125;
  @test_bar.tell_me_a_story         = #&#123;custom_binding.eval('@test_bar.tell_me_a_story')&#125;
  @another_test_bar.tell_me_a_story = #&#123;custom_binding.eval('@another_test_bar.tell_me_a_story')&#125;

  local_bar.bar         = #&#123;custom_binding.eval('local_bar.bar')&#125;
  @test_bar.bar         = #&#123;custom_binding.eval('@test_bar.bar')&#125;
  @another_test_bar.bar = #&#123;custom_binding.eval('@another_test_bar.bar')&#125;
  END_MSG
end

</pre>




<p>
  Output is:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Output of nugem_test method</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id2daf042b45c0'><button class='copyBtn' data-clipboard-target='#id2daf042b45c0' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>nugem_test
  hello = Hello from Mars

  local_bar.tell_me_a_story         = A man was born. He lived, then died.
  @test_bar.tell_me_a_story         = A man was born. He lived, then died.
  @another_test_bar.tell_me_a_story = A man was born. He lived, then died.

  local_bar.bar         = value of bar.bar
  @test_bar.bar         = value of bar.bar
  @another_test_bar.bar = value of another_bar.bar</pre>

</div>

<!-- endregion -->

<p>
  The following method shows how to uses all <code>CustomBinding</code> methods,
  again with the same test data.
</p>
<div class="codeLabel">full_test method from playground/examples.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id8d51ae8b17af"><button class='copyBtn' data-clipboard-target='#id8d51ae8b17af'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button># Exercise all CustomBinding methods
def full_test
  puts 'full_test'
  custom_binding = CustomBinding::CustomBinding.new Foo.new

  # Create a new (internal) binding for the same object
  mirrored_binding = custom_binding.mirror_binding
  # Both original_binding and mirrored_binding are for the same object (obj).
  # Any changes to @foo via either binding are reflected in the other,
  # because they reference the same object’s instance variable.

  # Set or update the instance variable via the original binding
  custom_binding.eval '@foo = 100'
  puts '  custom_binding:   @foo = ' + custom_binding.eval('@foo').to_s # => 100
  puts '  mirrored_binding: @foo = ' + mirrored_binding.eval('@foo').to_s # => 100

  # Update the reference in the original binding via the new binding
  mirrored_binding.eval '@foo = 200'
  puts '  custom_binding:   @foo = ' + custom_binding.eval('@foo').to_s # => 200
  puts '  mirrored_binding: @foo = ' + mirrored_binding.eval('@foo').to_s # => 200

  # Add a new instance of Bar to the mirrored binding
  bar = Bar.new 'value of bar.bar'
  custom_binding.add_object_to_binding_as('@another', bar)
  puts '  custom_binding:   @another.bar = ' + custom_binding.eval('@another.bar') # => 'value of bar.bar'
  puts '  mirrored_binding: @another.bar = ' + mirrored_binding.eval('@another.bar') # => 'value of bar.bar'

  # Change the value via the original binding
  custom_binding.eval '@another.bar = "value of bar.bar changed"'
  puts '  mirrored_binding: @another.bar = ' + mirrored_binding.eval('@another.bar') # => 'value of bar.bar changed'

  # Ensure this still works:
  puts '  custom_binding:   @foo = ' + custom_binding.eval('@foo').to_s # => 200
  puts '  mirrored_binding: @foo = ' + mirrored_binding.eval('@foo').to_s # => 200
end

</pre>




<p>
  Output is:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Output of full_test method</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id08843469e7c8'><button class='copyBtn' data-clipboard-target='#id08843469e7c8' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>full_test
  custom_binding:   @foo = 100
  mirrored_binding: @foo = 100
  custom_binding:   @foo = 200
  mirrored_binding: @foo = 200
  custom_binding:   @another.bar = value of bar.bar
  mirrored_binding: @another.bar = value of bar.bar
  mirrored_binding: @another.bar = value of bar.bar changed
  custom_binding:   @foo = 200
  mirrored_binding: @foo = 200</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Ruby Quirks -->
<h2 id="quirks">Ruby Quirks</h2>
<p>
  Ruby methods defined at the top level, also known as global methods,
  are associated with the <code>Kernel</code> module and are generally
  accessible from anywhere.
  However, they are conceptually tied to <code>Object</code> as a
  <code>private</code> method due to encapsulation rules.
</p>
<p>
  In other languages, like Java or C#,
  top-level methods would be defined within a specific class or would be static,
  not private methods of a general <code>Object</code> instance.
</p>
<p>
  As an example, lets define a top-level method called <code>hi</code>
  and see how Ruby classifies it.
  The following code displays the result of every type of inquiry possible for Ruby methods.
  Note that some functionality overlaps;
  the <code>methods</code> method returns both public and protected
  methods of the receiver,
  while the <code>public_methods</code> only returns public methods.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>IRB session</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida0e0276dffbd'><button class='copyBtn' data-clipboard-target='#ida0e0276dffbd' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>def hi = 'Hello from Mars'
<span class='unselectable'>=> :hi<br>
irb(main):002> </span>methods.select{ |x| x == :hi }
<span class='unselectable'>=> []<br>
irb(main):003> </span><span class="bg_yellow_nopad">private_methods</span>.select{ |x| x == :hi }
<span class='unselectable'>=> <span class="bg_yellow_nopad">[:hi]</span><br>
irb(main):004> </span>protected_methods.select{ |x| x == :hi }
<span class='unselectable'>=> []<br>
irb(main):005> </span>public_methods.select{ |x| x == :hi }
<span class='unselectable'>=> []<br>
irb(main):006> </span>singleton_methods.select{ |x| x == :hi }
<span class='unselectable'>=> [] </span></pre>

</div>

<!-- endregion -->

<p>
  Thus, if you define a top-level Ruby method and then do not see it defined the way you expected, you know why.
  Nothing is broken, Ruby just has a few oddities.
</p>
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><summary type="html"><![CDATA[Custom Ruby bindings.]]></summary></entry><entry><title type="html">Ruby’s to_s and inspect Methods</title><link href="https://www.mslinn.com/ruby/5000-to_s-inspect.html" rel="alternate" type="text/html" title="Ruby’s to_s and inspect Methods" /><published>2025-09-04T00:00:00-04:00</published><updated>2025-09-05T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/5000-to_s-inspect</id><content type="html" xml:base="https://www.mslinn.com/ruby/5000-to_s-inspect.html"><![CDATA[<!-- #region intro -->
<p>
  The <code>to_s</code> and <code>inspect</code> methods are defined in <code>Object</code> and all classes inherit them.
  To be precise, <code>Object#to_s</code> is inherited from <code>Kernel#to_s</code>.
  Many core classes override <code>inspect</code> to show more detail.
</p>
<p class="pullQuoteFull">
  Output from recursive processes can be so enormous that the system stops functioning effectively.
</p>
<!-- endregion -->


<!-- #region Normal Output Vs Debug Output -->
<h2 id="novdo">Normal Output Vs Debug Output</h2>
<p>
  Each method has a different purpose:
</p>
<ul>
  <li><code>inspect</code> is for debugging.</li>
  <li><code>to_s</code> is for normal operation.</li>
</ul>
<p>
  To compare with Python, <code>to_s</code> is like <code>__str__</code> and returns a string.
  In contrast, <code>inspect</code> is like <code>__repr__</code>;
  for example, inspecting a class instance displays the class name, the instance&rsquo;s object_id in hexadecimal,
  and then the method recursively calls itself for each instance variable within the class instance.
</p>
<p>
  The default output of both methods can be very different, as shown in this <code>irb</code> session:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>irb</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id122b09e5a08b'><button class='copyBtn' data-clipboard-target='#id122b09e5a08b' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>nil.to_s
<span class='unselectable'>=> ""<br>
irb(main):002> </span>nil.inspect
<span class='unselectable'>=> "nil" </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Stack Dumps -->
<h2 id="dumps">Stack Dumps</h2>
<p>
  Stack dumps can be problematic when working with large data structures.
  For example, when debugging Jekyll plugins and a stack dump occurs,
  even a powerful computer can be brought to its virtual knees for 10 minutes
  or longer as the default implementation of <code>inspect</code>
  painfully traverses the entire data structure and displays it on screen after
  screen of mostly useless information.
  The only way to terminate the dump is to terminate the Visual Studio Code
  process or press the reset button on your computer.
</p>
<p>
  When defining a custom class that has a large data structure  to traverse,
  be sure to define <code>inspect</code> in such a way that it displays a minimum of information.
  Your code could respond to an environment variable that determines whether
  recursive data structures should be traversed or not, for example.
  This will eliminate the superfluous output when dumping stacks containing your custom classes.
</p>
<!-- endregion -->


<!-- #region Monkey Patching -->
<h2 id="mp">Monkey Patching</h2>
<p>
  If you are working with a Ruby gem that is dumping huge amounts of output as it raises exceptions,
  fear not, you can monkey patch the gem quite easily.
  For example, the Jekyll gem has large recursive data structures that really get in your face when the plugin you are working on raises <code>StandardError</code>.
  The solution is to selectively override the problematic <code>inspect</code> method definitions in the gem with
  significantly less verbose definitions.
  Some of the classes that you will probably need to override are
  <code>Jekyll::Page</code>, <code>Jekyll::Post</code>, and <code>Jekyll::Document</code>.
</p>
<p>
  The following example code below demonstrates a minimal set of monkey patches for Jekyll.
  With this in place, stack dumps will be a lot easier to understand at a high level.
  You could tweak the code to display more information if desired.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>monkey_patch_jekyll.rb</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id9bc3d84712b0'><button class='copyBtn' data-clipboard-target='#id9bc3d84712b0' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>module Jekyll
  class Page
    def inspect
      "#&lt;Jekyll::Page path=#{path}>"
    end
  end

  class Document
    def inspect
      "#&lt;Jekyll::Document relative_path=#{relative_path}>"
    end
  end

  class Site
    def inspect
      "#&lt;Jekyll::Site source=#{source}>"
    end
  end

  class Post
    def inspect
      "#&lt;Jekyll::Post path=#{path}>"
    end
  end
end</pre>

</div>

<!-- endregion -->
<span class='emoji big' style='float: right; margin-left: 5px;'>&#x1F601;</span>
<p>
  There are two ways of invoking the above.
  Once invoked, stack dumps that reference Jekyll data structures will be dramatically shorter.
  Life will be so much better!
</p>
<ol>
  <li>
    To just affect dumps from the Jekyll project you are working on,
    place <code>monkey_patch_jekyll.rb</code> in your project&rsquo;s <code>_plugins/</code> folder,
    The full path from the Jekyll project root would be <code>_plugins/monkey_patch_jekyll.rb</code>.
  </li>
  <li>
    To affect dumps from every Jekyll project that uses your plugin,
    place <code>monkey_patch_jekyll.rb</code> in the <code>lib/</code> directory of the plugin
    and <code>require</code> it from any source file that is loaded.
    <!-- #region pre -->
    <div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idc02fb8270779'><button class='copyBtn' data-clipboard-target='#idc02fb8270779' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require_relative 'monkey_patch_jekyll.rb'</pre>

</div>

    <!-- endregion -->
  </li>
</ol>
<!-- endregion -->


<!-- #region The Joy of Aliases -->
<h2 id="aliases">The Joy of Aliases</h2>
<p>
  The following code sets up <code>to_s</code> and a minimal version of <code>inspect</code>,
  but also makes the original version of <code>inspect</code> available as <code>inspect_original</code>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idce93ee5a5cac'><button class='copyBtn' data-clipboard-target='#idce93ee5a5cac' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>class MyClass
  def initialize
    @x = 1
    @y = 'a'
  end

  def to_string = "x=#{@x} y=#{@y}"

<div class="bg_yellow_nopad">  # Order is important:
  alias inspect_original inspect
  alias inspect to_string
  alias to_s to_string
</div>end</pre>

</div>

<!-- endregion -->

<p>
  We can exercise the above code to verify that <code>to_s</code> and
  <code>inspect</code> are <a href='https://www.rubydoc.info/stdlib/core/Module:alias_method' target='_blank' rel="nofollow">aliased</a>
  to the <code>to_string</code> method,
  and the original <code>inspect</code> method is still available as <code>inspect_original</code>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>irb</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id3cc4c16d6874'><button class='copyBtn' data-clipboard-target='#id3cc4c16d6874' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>irb(main):013> </span>my_class = MyClass.new
<span class='unselectable'>=> x=1 y=a <br>
irb(main):014> </span>my_class.to_s
<span class='unselectable'>=> "x=1 y=a"<br>
irb(main):015> </span>my_class.to_string
<span class='unselectable'>=> "x=1 y=a"<br>
irb(main):016> </span>my_class.inspect
<span class='unselectable'>=> "x=1 y=a"<br>
irb(main):017> </span>my_class.inspect_original
<span class='unselectable'>=> "#&lt;MyClass:0x000074ce7f3aab98 @x=1, @y=\"a\">" </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><summary type="html"><![CDATA[Ruby&rsquo;s to_s and inspect methods.]]></summary></entry><entry><title type="html">Ruby Splat Operator and System Calls</title><link href="https://www.mslinn.com/ruby/4300-splat-system.html" rel="alternate" type="text/html" title="Ruby Splat Operator and System Calls" /><published>2025-06-09T00:00:00-04:00</published><updated>2025-06-09T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/4300-splat-system</id><content type="html" xml:base="https://www.mslinn.com/ruby/4300-splat-system.html"><![CDATA[<!-- #region intro -->
<p>
  The family of Ruby methods that
  <a href='https://stackoverflow.com/q/2232/553865' target='_blank' rel="nofollow">invoke external programs</a>
  is well known, but one important way of invoking them is not well known.
</p>
<p>
  One of the allowable syntaxes for
  <a href='https://docs.ruby-lang.org/en/master/Kernel.html#method-i-system' target='_blank' rel="nofollow"><code>Kernel.system</code></a> is:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id62f3e4118e1b'><button class='copyBtn' data-clipboard-target='#id62f3e4118e1b' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>system('/bin/program', arg1, arg2, arg3)</pre>

</div>

<!-- endregion -->
<p>
  The splat operator, also known as the <i>spread operator</i> or <i>spread syntax</i>,
  is a feature available in various programming languages.
  A splat operator composes or decomposes various types of collections, including tuples, arrays and dictionaries.
  Many programming languages have a splat operator, including CoffeScript, Julia, PHP, Python, Ruby, and Scala.
  The <a href='https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/method-parameters' target='_blank' rel="nofollow">C# params operator</a>
  and the <a href='https://github.com/jashkenas/coffeescript/issues/45' target='_blank' rel="nofollow">CoffeScript ellipsis</a>
  are similar.
</p>
<p>
  One of the many things that Ruby&rsquo;s splat operator can do is to
  destructure an array into individual arguments for a method call.
  The following code example yields equivalent code to the previous <code>system</code> call example:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida278b3f441a2'><button class='copyBtn' data-clipboard-target='#ida278b3f441a2' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>array = ['/bin/program', 1, 2, 3]
system *array</pre>

</div>

<!-- endregon -->
<p class="clear">
  This can be very helpful if the arguments are strings that would require escaping if converted to a string.
  Implementing an incantation that generates a correct escape sequence can be painful and is a common source of errors.
  The splat operator dispenses with the need to craft a properly escaped argument string.
  For example:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id9ca19dbe6baf'><button class='copyBtn' data-clipboard-target='#id9ca19dbe6baf' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>array = ['program_name', "abc'def 123"]
system *array</pre>

</div>

<!-- endregion -->

<p>
  Let&rsquo;s try this in <code>irb</code>:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>irb</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idcf8d441d28ab'><button class='copyBtn' data-clipboard-target='#idcf8d441d28ab' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>irb
<span class='unselectable'>irb(main):001> </span>array = ["git", "status", "/path with/spaces"]
<span class='unselectable'>=> ["git", "status", "/path with/spaces"]
irb(main):002> </span>system *array
<span class='unselectable'>On branch master
Your branch is up to date with 'origin/master'.<br>
nothing to commit, working tree clean
=> true </span></pre>

</div>

<!-- endregion -->

<p>
  You can see the Ruby splat operator in action with a <code>Kernel.system</code> call in my
  <a href='https://github.com/mslinn/commit/blob/6f137b558cda90cce0299824e1af2a6b3bea497c/lib/commit/git_commit.rb#L182-L191' target='_blank' rel="nofollow"><code>commit</code> Gem</a>:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idac66f223f462'><button class='copyBtn' data-clipboard-target='#idac66f223f462' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button># @param command can be a String or an Array of String
def run(command, verbose: true, do_not_execute: false)
  if command.instance_of?(Array)
    puts command.join(' ') if verbose
    <span class='bg_yellow_nopad'>system(*command)</span> unless do_not_execute
  else
    puts command if verbose
    `#{command}`.chomp unless do_not_execute
  end
end</pre>

</div>

<!-- endregion -->

<p>
  For the curious, you might be interested to know that you can directly preface an array literal with a splat operator:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id2e2a70ef6543'><button class='copyBtn' data-clipboard-target='#id2e2a70ef6543' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>system *['program_name', "abc'def 123"]</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Related Stack Overflow Answer -->
<h2 id="rsoa">Related Stack Overflow Answer</h2>
<p>
  <a href='https://stackoverflow.com/a/9370070/553865' target='_blank' rel="nofollow">This Stack Overflow answer</a>
  shows a similar approach that utilizes the splat operator in another way to invoke <code>system</code>.
</p>
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><category term="Ruby" /><summary type="html"><![CDATA[The Ruby splat operator and system calls.]]></summary></entry><entry><title type="html">Visualizing Dependencies with bundle graph</title><link href="https://www.mslinn.com/ruby/7000-bundle-graph.html" rel="alternate" type="text/html" title="Visualizing Dependencies with bundle graph" /><published>2024-07-26T00:00:00-04:00</published><updated>2024-07-26T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/7000-bundle-graph</id><content type="html" xml:base="https://www.mslinn.com/ruby/7000-bundle-graph.html"><![CDATA[<!-- #region intro -->
<p>
  You can visualize the dependencies between Ruby gems by using
  the <code>bundle graph</code> subcommand.
  This subcommand generates a PNG file of the dependencies of current Ruby project as a dependency graph.
</p>
<p>
  The <code>bundle graph</code> subcommand is not installed by default.
  Instead, it is implemented as a
  <a href='https://bundler.io/guides/bundler_plugins.html' target='_blank' rel="nofollow">Bundler plugin</a>.
  The Git project is <a href='https://github.com/rubygems/bundler-graph' target='_blank' rel="nofollow">here</a>.
</p>
<p>
  For the curious, see the <a href='https://bundler.io/guides/plugins.html' target='_blank' rel="nofollow">list of known Bundler plugins</a> .
</p>
<p>
  If you attempt to run <code>bundle graph</code> without installing the plugin,
  you will get the following error:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide204416825f9'><button class='copyBtn' data-clipboard-target='#ide204416825f9' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>bundle graph
Could not find command "graph".</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Installation -->
<h2 id="install">Installation</h2>
<p>
  Bundler plugins can either be installed globally or for specific projects.
  I think the plugin is best installed globally, not per-project.
  Follow these instructions for a global installation.
</p>
<p>
  The documented <a href='https://github.com/rubygems/bundler-graph' target='_blank' rel="nofollow">installation instructions</a> are incomplete.
  This article provides more information.
  Here is the help message:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id0cec43ce7fe8'><button class='copyBtn' data-clipboard-target='#id0cec43ce7fe8' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>bundler plugin help install
<span class='unselectable'>Usage:
  bundler plugin install PLUGINS<br/>
Options:
  [--source=URL of the RubyGems source to fetch the plugin from]
  [--version=The version of the plugin to fetch]
  [--git=URL of the git repo to fetch from]
  [--local-git=Path of the local git repo to fetch from (deprecated)]
  [--branch=The git branch to checkout]
  [--ref=The git revision to check out]
  [--path=Path of a local gem to directly use]<br/>
Description:
  Install plugins either from the rubygems source provided (with --source option), from a git source provided with
  --git, or a local path provided with --path. If no sources are provided, it uses Gem.sources </span></pre>

</div>

<!-- endregion -->

<p>
  Installing the <code>graph</code> plugin for Bundler requires three steps.
</p>
<p class="numbered">
  Globally install the <code>ruby-graphviz</code> gem.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id9c0e894c2e79'><button class='copyBtn' data-clipboard-target='#id9c0e894c2e79' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>gem install ruby-graphviz
Fetching ruby-graphviz-1.2.5.gem<br/>
You need to install GraphViz (https://graphviz.org) to use this Gem.<br/>
For more information about Ruby-Graphviz :
* Doc: https://rdoc.info/github/glejeune/Ruby-Graphviz
* Sources: https://github.com/glejeune/Ruby-Graphviz
* Issues: https://github.com/glejeune/Ruby-Graphviz/issues<br/>
Successfully installed ruby-graphviz-1.2.5
Parsing documentation for ruby-graphviz-1.2.5
Installing ri documentation for ruby-graphviz-1.2.5
Done installing documentation for ruby-graphviz after 5 seconds
1 gem installed</pre>

</div>

<!-- endregion -->

<p class="numbered">
  Install the <code>graphviz</code> dependency.
  On WSL/Ubuntu I installed it like this:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1dce2b6983b6'><button class='copyBtn' data-clipboard-target='#id1dce2b6983b6' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>sudo apt install graphviz</pre>

</div>

<!-- endregion -->

<p class="numbered">
  To install the Bundler plugin globally, first move to your home directory, then type the following.
  If you instead type this incantation from a Ruby project,
  the plugin will only be installed for that project:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idb44387f90e76'><button class='copyBtn' data-clipboard-target='#idb44387f90e76' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>cd<br>
<span class='unselectable'>$ </span>bundler plugin install bundler-graph
<span class='unselectable'>Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Fetching bundler-graph 0.2.1
Installing bundler-graph 0.2.1
Installed plugin bundler-graph </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Running Bundle Graph -->
<h2 id="run">Running Bundle Graph</h2>
<p>
  With the <code>bundler-graph</code> plugin installed,
  it can be invoked from the command line as follows:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id04c7c21dda83'><button class='copyBtn' data-clipboard-target='#id04c7c21dda83' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>bundle graph
<span class='unselectable'>/my/current/directory/gem_graph.png </span></pre>

</div>

<!-- endregion -->
<p>
  The plugin creates a PNG image called <code>gem_graph.png</code>.
  To view it in WSL, type:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id4be2e257ffb3'><button class='copyBtn' data-clipboard-target='#id4be2e257ffb3' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>explorer.exe gem_graph.png</pre>

</div>

<!-- endregion -->
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><category term="Ruby" /><summary type="html"><![CDATA[Visualizing dependencies with bundle graph.]]></summary></entry><entry><title type="html">NuGem</title><link href="https://www.mslinn.com/ruby/6800-nugem.html" rel="alternate" type="text/html" title="NuGem" /><published>2024-01-10T00:00:00-05:00</published><updated>2024-12-22T00:00:00-05:00</updated><id>https://www.mslinn.com/ruby/6800-nugem</id><content type="html" xml:base="https://www.mslinn.com/ruby/6800-nugem.html"><![CDATA[<!-- #region intro -->
<div class="gem_banner right rounded shadow" style='' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/nugem' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>nugem</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/nugem.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/nugem.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/nugem.svg"
        style='width: 100%;'
        title='nugem version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/nugem" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for nugem'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for nugem'
      />
    </picture>
</div>

    </a>
  </div>
</div>

<p>
  <code>NuGem</code> creates a scaffold project for a new gem in a new Git repository.
  After you add your special code to the gem scaffold,
  the project is ready to be released to a public or private gem server.
</p>
<!-- endregion -->


<!-- #region Features -->
<h2 id="features">Features</h2>
<p>This gem generates a new working Visual Studio Code project with the following features:</p>
<ul>
  <li>Compatible with <code>rbenv</code>.</li>
  <li><code>Gemfile</code> and <code>.gemspec</code> files set up.</li>
  <li>Generates a README with badges.</li>
  <li>Visual Studio Code project is set up with current Ruby extensions.<ul>
      <li>Rubocop configured.</li>
      <li>Shellcheck configured.</li>
      <li>Markdown lint configured.</li>
      <li>Launch configurations set up for testing.</li>
    </ul>
  </li>
  <li>Can automatically create a public or private Git repository on GitHub for your new gem.</li>
  <li>Creates a test infrastructure based on <code>rspec</code>.</li>
  <li>Your gem can be publicly released to <code>rubygems.org</code>.</li>
  <li>Optionally create the gem as:<ul>
      <li>A plain old gem.</li>
      <li>A Jekyll plugin (tag or block tag).</li>
    </ul>
  </li>
</ul>
<p>The following features are still in development, so they probably do not work yet:</p>
<ul>
  <li>Automatically creates Git repositories on BitBucket or Geminabox.</li>
  <li>Creates a test infrastructure based on <code>minitest</code> and <code>minitest-reporters</code>.</li>
  <li>Your gem can be privately released to a Geminabox gem server.</li>
  <li>Your gem can include a <a href="https://github.com/rails/thor">Thor-based executable</a>.</li>
  <li>Optionally create the gem as:<ul>
      <li>A Jekyll plugin (filter, generator, or hooks).</li>
      <li>A Rails plugin, possibly with a mountable engine.</li>
    </ul>
  </li>
</ul>
<!-- endregion -->


<!-- #region installation -->
<h2 id="installation">Installation</h2>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida45330d167b5'><button class='copyBtn' data-clipboard-target='#ida45330d167b5' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>gem install nugem</pre>

</div>


<p>If you are using <a href='/ruby/1000-ruby-setup.html'><code>rbenv</code></a> to manage Ruby instances, type:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide774f970b750'><button class='copyBtn' data-clipboard-target='#ide774f970b750' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>rbenv rehash</pre>

</div>


<p>To update the program:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id20e17d64dab7'><button class='copyBtn' data-clipboard-target='#id20e17d64dab7' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>gem update nugem</pre>

</div>

<!-- endregion -->


<!-- #region Subcommands and Options -->
<h2 id="subcommands-and-options">Subcommands and Options</h2>
<p>
  <code>NuGem</code> has 4 subcommands <code>gem</code>, <code>jekyll</code>, <code>help</code> and <code>rails</code>.
  Currently, only <code>gem</code>, <code>jekyll</code> and <code>help</code> have been properly tested.
</p>


<!-- #region help-subcommand -->
<h3 id="help-subcommand"><span class="code">help</span> Subcommand</h3>
<p>The following lists the available subcommands:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idcb73b1b9b01b'><button class='copyBtn' data-clipboard-target='#idcb73b1b9b01b' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem help</pre>

</div>


<p>The following provides detailed help for the specified subcommand:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idc4f248db3f9e'><button class='copyBtn' data-clipboard-target='#idc4f248db3f9e' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem help SUBCOMMAND</pre>

</div>

<!-- endregion -->


<!-- #region Common Options -->
<h3 id="common-options">Common Options</h3>
<p>The <code>gem</code>, <code>jekyll</code> and <code>rails</code> subcommands have common options.</p>
<p>The default option values assume that:</p>
<ul>
  <li>You do not want an executable for your gem scaffold</li>
  <li>The gem project will be hosted on a public GitHub Git repository</li>
  <li>The gem will be released to <code>rubygems.org</code></li>
</ul>
<p>Common options for the <code>gem</code>, <code>jekyll</code> and <code>rails</code> subcommands are:</p>
<dl>
  <dt><code>--executable</code></dt>
  <dd>add an executable based on Thor.</dd>

  <dt><code>--host</code></dt>
  <dd>
    specifies the Git host; possible values are <code>bitbucket</code>,
    <code>github</code> and <code>geminabox</code>.
  </dd>

  <dt><code>--out_dir</code></dt>
  <dd>
    specifies the directory to write the generated gem to.
    The default is <code>generated/</code>.
  </dd>

  <dt><code>--private</code></dt>
  <dd>the remote repository is made private,
    and on release the gem will be pushed to a private Geminabox server.
  </dd>

  <dt><code>--quiet</code></dt>
  <dd>reduces verbosity.</dd>

  <dt><code>--no-todos</code></dt>
  <dd>do not generate <code>TODO:</code> strings in generated code.</dd>
</dl>
<!-- endregion -->


<!-- #region Common Behavior -->
<h3 id="common-behavior">Common Behavior</h3>
<p>The <code>gem</code>, <code>jekyll</code> and <code>rails</code> subcommands have common behavior.</p>
<p>
  Gem scaffolds are created within the <code>generated/</code> directory of the current directory by default.
</p>
<p>
  If your user name is not already stored in your Git global config,
  you will be asked for your GitHub or BitBucket user name.
  You will also be asked to enter your GitHub or BitBucket password when the remote repository is created for you.
</p>
<p>
  After you create the gem, edit the <code>gemspec</code> and change the summary and the description.
  Then commit the changes to Git and invoke <code>rake release</code>,
  and your gem will be published.
</p>
<!-- endregion -->


<!-- #region gem-subcommand -->
<h3 id="gem-subcommand"><span class="code">gem</span> Subcommand</h3>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1e4240f2e5f7'><button class='copyBtn' data-clipboard-target='#id1e4240f2e5f7' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem gem NAME [COMMON_OPTIONS] [--test-framework=minitest|rspec]</pre>

</div>

<p><code>NAME</code> is the name of the gem to be generated.</p>
<p>
  The default test framework for the <code>gem</code> subcommand is <code>rspec</code>,
  but you can specify <code>minitest</code> instead like this:
  (Warning: this has not been properly tested)
</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida579d6001535'><button class='copyBtn' data-clipboard-target='#ida579d6001535' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem gem my_gem --test-framework=minitest</pre>

</div>

<!-- endregion -->


<!-- #region jekyll-subcommand -->
<h3 id="jekyll-subcommand"><span class="code">jekyll</span> Subcommand</h3>
<p>The <code>jekyll</code> subcommand extends the <code>gem</code> subcommand and creates a new Jekyll plugin with the given NAME:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida55bac6987fb'><button class='copyBtn' data-clipboard-target='#ida55bac6987fb' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem jekyll NAME [OPTIONS]</pre>

</div>

<p><code>NAME</code> is the name of the Jekyll plugin gem to be generated.</p>

<p>In addition to the common options, the <code>jekyll</code>-specific <code>OPTIONS</code> are:</p>
<p>
  <code>--block</code>, <code>--blockn</code>, <code>--filter</code>, <code>--hooks</code>, <code>--tag</code>, and <code>--tagn</code>.
  (Warning: only <code>&#8209&#8209block</code> and <code>&#8209&#8209tag</code> been properly tested.)
</p>
<p>Each of these options causes <code>nugem</code> to prompt the user for additional input.</p>

<p>The test framework for <code>jekyll</code> plugins is <code>rspec</code>.</p>
<p>
  All the above options can be specified more than once, except the <code>&#8209&#8209hooks</code> option.
  For example:
</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idc3e0f49dd2c4'><button class='copyBtn' data-clipboard-target='#idc3e0f49dd2c4' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem jekyll test_tags --tag my_tag1 --tag my_tag2</pre>

</div>


<p>
  The above creates a Jekyll plugin called <code>test_tags</code>,
  which defines Jekyll tags called <code>my_tag1</code> and <code>my_tag2</code>.
  You might use these tags in an HTML document like this:
</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>my_page.html</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id95507c7aa54a'><button class='copyBtn' data-clipboard-target='#id95507c7aa54a' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>&lt;pre>
my_tag1 usage: {% my_tag1 %}
my_tag2 usage: {% my_tag2 %}
&lt;/pre></pre>

</div>


<p>For more information, type:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id447e7e44ae23'><button class='copyBtn' data-clipboard-target='#id447e7e44ae23' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem help jekyll</pre>

</div>

<!-- endregion -->


<!-- #region rails-subcommand -->
<h3 id="rails-subcommand"><span class="code">rails</span> Subcommand</h3>
<p>The <code>rails</code> subcommand extends the <code>gem</code> subcommand and creates a new Rails plugin with the given NAME:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1167edcf52e5'><button class='copyBtn' data-clipboard-target='#id1167edcf52e5' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem rails NAME [OPTIONS]</pre>

</div>


<p><code>NAME</code> is the name of the Ruby on Rails plugin gem to be generated.</p>
<p>In addition to the common options, <code>rails</code> <code>OPTIONS</code> are
  <code>--engine</code> and <code>--mountable</code>.
</p>
<p>You can specify if the plugin should be an engine (<code>--engine</code>) or a mountable engine (<code>--mountable</code>).</p>
<p>Each of these options causes <code>nugem</code> to prompt the user for additional input.</p>
<p>The test framework for <code>rails</code> gems is <code>minitest</code>.</p>
<p>For more information, type:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id9a5f05ff0ecb'><button class='copyBtn' data-clipboard-target='#id9a5f05ff0ecb' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>nugem help rails</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Did It Work? -->
<h2 id="did-it-work-">Did It Work?</h2>
<p>The following shows all files that were committed to the newly created Git repository,
  after <code>nugem jekyll</code> finished making two tag blocks:</p>
<!-- #region  -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide0ac6422b78d'><button class='copyBtn' data-clipboard-target='#ide0ac6422b78d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>git ls-tree --name-only --full-tree -r HEAD
<span class='unselectable'>.envrc
.gitignore
.rspec
.rubocop.yml
.simplecov
.travis.yml
.vscode/extensions.json
.vscode/launch.json
.vscode/settings.json
CHANGELOG.md
Gemfile
LICENCE.txt
README.md
Rakefile
bin/attach
bin/console
bin/rake
bin/setup
demo/Gemfile
demo/_bin/debug
demo/_config.yml
demo/_drafts/2022/2022-05-01-test2.html
demo/_includes/block_tag_template_wrapper
demo/_layouts/default.html
demo/_posts/2022/2022-01-02-redact-test.html
demo/assets/css/style.css
demo/assets/images/404-error.png
demo/assets/images/404-error.webp
demo/assets/images/favicon.png
demo/assets/images/jekyll.png
demo/assets/images/jekyll.webp
demo/assets/js/clipboard.min.js
demo/assets/js/jquery-3.4.1.min.js
demo/blog/blogs.html#by_pub_date
demo/blog/index.html
demo/index.html
jekyll_test.code-workspace
jekyll_test.gemspec
lib/jekyll_test.rb
lib/jekyll_test/version.rb
lib/my_block1.rb
lib/my_block2.rb
spec/jekyll_test_spec.rb
spec/spec_helper.rb
test/jekyll_test_test.rb
test/test_helper.rb </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->
<!-- endregion -->


<!-- #region Visual Studio Code Support -->
<h2 id="visual-studio-code-support">Visual Studio Code Support</h2>

<!-- #region Nugem Project -->
<h3 id="nugem-project">Nugem Project</h3>

<!-- #region Plugins -->
<h4 id="plugins">Plugins</h4>
<p>If you have not installed the
  <a href="https://marketplace.visualstudio.com/items?itemName=devonray.snippet">Snippets</a> extension,
  Visual Studio Code will suggest that you do so the first time you open this project with Visual Studio Code.
  You can also review the list of suggested extensions of with the <kbd>CTRL</kbd>-<kbd>P</kbd>
  <code>Extensions: Show Recommended Extensions</code> command.
</p>
<!-- endregion -->


<!-- #region Snippets -->
<h4 id="snippets">Snippets</h4>
<p>The predefined snippets for <code>nugem</code> are defined in
  <a href=".vscode/nugem.json.code-snippets"><code>.vscode/nugem.json.code-snippets</code></a>.
  These snippets are focused on maintaining <code>nugem</code> itself.
</p>
<!-- endregion -->


<!-- #region File Associations -->
<h4 id="file-associations">File Associations</h4>
<p><code>.vscode/settings.json</code> defines file associations for various flavors of Thor templates in the
  <code>&quot;files.associations&quot;</code> section.
  You can disable them by commenting some or all of those definitions.</p>
<!-- endregion -->
<!-- endregion -->


<!-- #region Generated Projects -->
<h3 id="generated-projects">Generated Projects</h3>

<!-- #region Plugins -->
<h4 id="plugins">Plugins</h4>
<p>Similarly, for each gem project generated by <code>nugem</code>, Visual Studio Code will suggest
  the user install missing extensions the first time those projects are opened.</p>
<!-- endregion -->

<!-- #region Snippets -->
<h4 id="snippets">Snippets</h4>
<p>The predefined snippets for gem projects generated by <code>nugem</code> are defined in
  their <code>.vscode/gem.json.code-snippets</code> file.
  These snippets are focused on writing Jekyll plugins.</p>
<!-- endregion -->
<!-- endregion -->
<!-- endregion -->
<!-- endregion -->


<!-- #region Development -->
<h2 id="development">Development</h2>
<p>After checking out the repository, run <code>bin/setup</code> to install dependencies.
  Then, run <code>rake test</code> to run the tests.
  You can also run <code>bin/console</code> for an interactive prompt that will allow you to experiment.</p>
<p>To install this gem onto your local machine, run:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idb4b4faf9affa'><button class='copyBtn' data-clipboard-target='#idb4b4faf9affa' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>exec rake install</pre>

</div>


<p>To release a new version, run:</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id8afd59a0b896'><button class='copyBtn' data-clipboard-target='#id8afd59a0b896' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>exec rake release</pre>

</div>


<p>The above will create a Git tag for the version, push Git commits and tags,
  and push the <code>.gem</code> file to <a href='https://rubygems.org' target='_blank' rel="nofollow"><code>rubygems.org</code></a>.</p>
<!-- endregion -->


<!-- #region Contributing -->
<h2 id="contributing">Contributing</h2>
<p>Bug reports and pull requests are welcome on GitHub at
  <a href='https://github.com/mslinn/nugem' target='_blank' rel="nofollow"><code>github.com/<wbr>mslinn/<wbr>nugem</code></a>.</p>
</ul>
<!-- endregion -->

<!-- #region See Also -->
<h2 id="see-also">See Also</h2>
<ul>
  <li><a href='https://rubygems.org/gems/gem-release' target='_blank' rel="nofollow"><code>gem-release</code></a></li>
  <li><a href='https://bundler.io/v2.4/man/bundle-gem.1.html' target='_blank' rel="nofollow"><code>bundle gem</code></a></li>
  <li><a href='https://bundler.io/guides/creating_gem.html' target='_blank' rel="nofollow">Deveoping a RubyGem using Bundler</a></li>
</ul>
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><category term="Ruby" /><summary type="html"><![CDATA[NuGem, a Ruby gem generator.]]></summary></entry><entry><title type="html">My Ruby Gems</title><link href="https://www.mslinn.com/ruby/6799-my-gems.html" rel="alternate" type="text/html" title="My Ruby Gems" /><published>2023-11-29T00:00:00-05:00</published><updated>2024-03-20T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/6799-my-gems</id><content type="html" xml:base="https://www.mslinn.com/ruby/6799-my-gems.html"><![CDATA[<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/commit' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>commit</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/commit.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/commit.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/commit.svg"
        style='width: 100%;'
        title='commit version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/commit" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for commit'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for commit'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/custom_binding' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>custom_<wbr>binding</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/custom_binding.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/custom_binding.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/custom_binding.svg"
        style='width: 100%;'
        title='custom_binding version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/custom_binding" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for custom_binding'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for custom_binding'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/gem_support' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>gem_<wbr>support</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/gem_support.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/gem_support.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/gem_support.svg"
        style='width: 100%;'
        title='gem_support version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/gem_support" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for gem_support'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for gem_support'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/insert_after' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>insert_<wbr>after</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/insert_after.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/insert_after.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/insert_after.svg"
        style='width: 100%;'
        title='insert_after version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/insert_after" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for insert_after'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for insert_after'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/live_set' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>live_<wbr>set</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/live_set.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/live_set.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/live_set.svg"
        style='width: 100%;'
        title='live_set version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/live_set" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for live_set'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for live_set'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/media_trim' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>media_<wbr>trim</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/media_trim.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/media_trim.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/media_trim.svg"
        style='width: 100%;'
        title='media_trim version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/media_trim" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for media_trim'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for media_trim'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/midi_create' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>midi_<wbr>create</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/midi_create.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/midi_create.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/midi_create.svg"
        style='width: 100%;'
        title='midi_create version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/midi_create" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for midi_create'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for midi_create'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/move_media' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>move_<wbr>media</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/move_media.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/move_media.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/move_media.svg"
        style='width: 100%;'
        title='move_media version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/move_media" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for move_media'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for move_media'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/nugem' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>nugem</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/nugem.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/nugem.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/nugem.svg"
        style='width: 100%;'
        title='nugem version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/nugem" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for nugem'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for nugem'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<div class="gem_banner right rounded shadow" style='float: inline-start;' title='Latest Ruby gem'>
  <div class="center one_column">
    <a class='imgImgUrl' href='https://github.com/mslinn/stabilize_video' target='_blank'>
      <!-- Badge banner with version info -->
<code class='banner_label'>stabilize_<wbr>video</code>
<div class='imgWrapper imgFlex center' style='margin-bottom: 0;'>
    <picture class='imgPicture'>
      <source srcset="https://badge.fury.io/rb/stabilize_video.svg" type="image/webp">
      <source srcset="https://badge.fury.io/rb/stabilize_video.svg" type="image/png">
      <img alt='Latest Ruby gem version'
        class="imgImg"
        src="https://badge.fury.io/rb/stabilize_video.svg"
        style='width: 100%;'
        title='stabilize_video version'
      />
    </picture>
  </a>
</div>

      <!-- Remainder of badge content -->
<div class='imgWrapper imgFlex center' style='width: 120px;'>
  <a href="https://github.com/mslinn/stabilize_video" target='_blank' class='imgImgUrl'>
    <picture class='imgPicture'>
      <source srcset="/blog/images/git/github-mark.webp" type="image/webp">
      <source srcset="/blog/images/git/github-mark.png"  type="image/png">
      <img alt='GitHub project for stabilize_video'
        class="imgImg"
        src="/blog/images/git/github-mark.png"
        style='width: 100%;'
        title='Git project for stabilize_video'
      />
    </picture>
</div>

    </a>
  </div>
</div>


<h2 id="jekyll" class="clear" style="margin-top: 3em;">Looking for My Jekyll Plugins?</h2>
<p>
  My Jekyll plugins packaged as Ruby gems are shown on their <a href='/jekyll_plugins/'>own page</a>.
  The above gems are unrelated to Jekyll.
</p>]]></content><author><name>Mike Slinn</name></author><category term="Ruby" /><summary type="html"><![CDATA[My Ruby Gems.]]></summary></entry><entry><title type="html">Parsing Command Line Arguments with OptionParser</title><link href="https://www.mslinn.com/ruby/6600-option_parser.html" rel="alternate" type="text/html" title="Parsing Command Line Arguments with OptionParser" /><published>2023-10-03T00:00:00-04:00</published><updated>2025-09-25T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/6600-option_parser</id><content type="html" xml:base="https://www.mslinn.com/ruby/6600-option_parser.html"><![CDATA[<!-- #region intro -->
<p>
  The Ruby language has many libraries for parsing command-line arguments.
  This article discusses <code>Option&shy;Parser</code>, which
  is one of the most popular Ruby libraries for parsing arguments.
  <code>Option&shy;Parser</code> is popular because it is both powerful and easy to use,
  and it is part of the Ruby runtime library.
</p>
<p>
  <a href='https://github.com/ruby/optparse' target='_blank' rel="nofollow">This</a> is the <code>Option&shy;Parser</code> GitHub repository.
</p>
<!-- endregion -->


<!-- #region Documentation -->
<h2 id="docs1">Documentation</h2>
<!-- #region implicit -->
<p>
  The detailed Ruby documentation for <code>Option&shy;Parser</code> is
  <a href='https://docs.ruby-lang.org/en/master/optparse/option_params_rdoc.html' target='_blank' rel="nofollow">here</a>.
  I am not going to restate the documentation, since
  <a href='https://www.google.com/search?q=ruby+OptionParser' target='_blank' rel="nofollow">many articles have been written about <code>Option&shy;Parser</code></a>.
  Instead, this section will just discuss important details that are not usually discussed
  or are assumed by many writers to be obvious,
  even though the omitted details might not be obvious to many readers.
</p>
<h3 id="concepts">This Article Teaches Two Main Concepts</h3>
<p class="numbered">
  <code>OptionParser</code> returns parsed values in a hash.
</p>
<p class="numbered">
  <code>OptionParser</code> has an internal accumulator that
  composes the subject of each <code>OptionParser#on</code> method call.
  Some idiomatic Ruby code must be written to describe the desired parsing.
  Although these idioms become familiar over time,
  the first few times you are exposed to them, they will seem awkward.
  This article discusses the idiomatic Ruby when it arises.
</p>
<!-- endregion -->


<!-- #region Denoting Keyword Options -->
<h3 id="dov">Denoting Keyword Options</h3>
<p>
  Options can merely be keywords; for example, an option such as <code>-f</code> (<code>--force</code>)
  might be used to specify that any previous output from a program should be overwritten.
  Keywords are actually implemented as a key/value pair with value <code>true</code>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code fragment</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id9f93bd8dcbca'><button class='copyBtn' data-clipboard-target='#id9f93bd8dcbca' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>options = {}
OptionParser.new do |parser|
  parser.on '-f', '--force'
end.order!(ARGV, into: options)</pre>

</div>

<!-- endregion -->
<p>
  If the program containing <code>Option&shy;Parser</code> code is launched with the <code>-f</code> option,
  the above code fragment would store <code>{force: true}</code> in the <code>options</code> hash.
</p>
<p>
  The name <code>force</code> is used because that is the long-form name of the option.
</p>
<p>
  The <code>parser.on</code> statement causes the parser instance to retain the instruction for scanning for
  the <code>-f/--force</code> option, should it be encountered.
</p>
<p>
  The <code>OptionParser#order!</code> method accepts the command-line arguments from
  the operating system via <code>ARGV</code>, uses the parsing instruction that was set up
  via the <code>on</code> method call, and stores the results in the <code>options</code> hash.
</p>
<!-- endregion -->


<!-- #region Name/Value Options -->
<h3 id="nvo">Name/Value Options</h3>
<p>
  Options with arbitrary values can also be defined, and they are also stored as key/value pairs.
</p>
<p>
  For example, an option named <code>verbose</code> might require a string value such as <code>low</code>,
  <code>medium</code> or <code>high</code>.
  In the following minimal example, the default value for <code>verbose</code> is <code>'low'</code>.
  The presence of a placeholder,
  in this example <span class='bg_yellow_nopad'><code>VERBOSE</code></span>,
  indicates that the parsed value should
  be stored in the result hash with a key named after the long option name.
  The placeholder value is not actually relevant, and its length is also unimportant;
  the Ruby documentation calls placeholders <i>dummy tokens</i>.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code fragment</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id00279f171397'><button class='copyBtn' data-clipboard-target='#id00279f171397' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>options = { verbose: 'low' }
OptionParser.new do |parser|
  parser.on '-v', '--verbose=<span class='bg_yellow_nopad'>VERBOSE</span>'
end.order!(ARGV, into: options)</pre>

</div>

<!-- endregion -->

<p>
  The following code fragment is functionally identical to the previous even though the value of the placeholder / dummy token is different.
</p>

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Equivalent Ruby code fragment</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id2cd26adc6cc6'><button class='copyBtn' data-clipboard-target='#id2cd26adc6cc6' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>options = { verbose: 'low' }
OptionParser.new do |parser|
  parser.on '-v', '--verbose=<span class='bg_yellow_nopad'>ABC</span>'
end.order!(ARGV, into: options)</pre>

</div>

<!-- endregion -->

<p>
  As before, the name <code>verbose</code> is used for the name of the resulting entry in the resulting hash
  because the long form of the option is <code>--verbose</code>.
  The value placeholder is shown in yellow.
  You could also write the above as follows, but there is no reason to do so because specifying the value placeholder twice just represents more typing:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code fragment</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id7b9182687aed'><button class='copyBtn' data-clipboard-target='#id7b9182687aed' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>options = { verbose: 'low' }
OptionParser.new do |parser|
  parser.on '-v <span class='bg_yellow_nopad'>VERBOSE</span>', '--verbose=<span class='bg_yellow_nopad'>VERBOSE</span>'
end.order!(ARGV, into: options)</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Positional Parameters Delimit Options -->
<h3 id="ppdo">Positional Parameters Delimit Options</h3>
<p>
  The documentation does not mention a crucial limitation of how <code>Option&shy;Parser</code>
  parses command lines:
</p>
<p class="alert rounded shadow">
  Option parsing stops on the first positional parameter.
</p>
<p>
  A <i>positional parameter</i> is a token that does not begin with a dash and is not an option value.
</p>
<p>
  This means, for example, that the <span class='bg_yellow_nopad'>first two options</span> in the following
  command line are parsed, but the <span class='bg_pink_nopad'>remaining options</span> are not,
  unless the command line is manipulated:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id61a2271f8f68'><button class='copyBtn' data-clipboard-target='#id61a2271f8f68' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>my_program <span class='bg_yellow_nopad'>-o --option=value</span> positional_parameter <span class='bg_pink_nopad'>-p -q --another-option=value2</span></pre>

</div>

<!-- endregion -->
<p>
  Subcommands that follow the above syntax are fully discussed in the <code>Option&shy;Parser</code> documentation.
  Later in this article I discuss why the above syntax is unnecessarily awkward,
  and in <a href='/ruby/6800-nugem.html'>NuGem</a> I discuss an alternative,
  <a href='https://github.com/mslinn/nugem/blob/wip/lib/nested_options_parser.rb' target='_blank' rel="nofollow"><code>Nested&shy;Option&shy;Parser</code></a>,
  based on a novel usage of <code>Option&shy;Parser</code>
  that is more user-friendly.
</p>
<!-- endregion -->
<!-- endregion -->


<!-- #region Sod -->
<h2 id="sod">Sod</h2>
<p>
  The F/OSS library called <a href='https://github.com/bkuhlmann/sod' target='_blank' rel="nofollow"><code>sod</code></a>
  enhances <code>Option&shy;Parser</code>.
  Although <code>sod</code> can be used as an <code>Option&shy;Parser</code> wrapper that provides enhanced capability,
  you can also just use selected features of <code>sod</code> while continuing to use <code>Option&shy;Parser</code> as usual.
  That is the approach taken in this article.
  One <code>sod</code> feature that I especially like is the
  <a href='https://github.com/bkuhlmann/sod?tab=readme-ov-file#types-1' target='_blank' rel="nofollow">extra data types</a>
  that it supports for parsing arguments:
</p>
<ul>
  <li>
    <a href='https://github.com/bkuhlmann/sod/?tab=readme-ov-file#pathname' target='_blank' rel="nofollow"><code>Pathname</code></a>
    returns a <a href='https://rubyapi.org/o/s?q=Pathname' target='_blank' rel="nofollow"><code>Pathname</code></a> instance.
  </li>
  <li>
    <a href='https://github.com/bkuhlmann/sod/?tab=readme-ov-file#version' target='_blank' rel="nofollow"><code>Version</code></a>
    returns a
    <a href='https://alchemists.io/projects/versionaire' target='_blank' rel="nofollow"><code>Versionaire::Version</code></a>
    instance.
  </li>
  <li>
    <a href='https://github.com/bkuhlmann/sod/?tab=readme-ov-file#custom' target='_blank' rel="nofollow">Custom</a>
    parsers are possible that return arbitrary data types.
  </li>
</ul>
<!-- endregion -->


<!-- #region Installation -->
<h2 id="install">Installation</h2>

<!-- #region implicit -->
<p>
  <a href='https://docs.ruby-lang.org/en/master/OptionParser.html' target='_blank' rel="nofollow"><code>Option&shy;Parser</code></a>
  is part of the standard Ruby runtime library,
  so once Ruby itself is installed, there are no mandatory additional steps for installation.
</p>
<!-- endregion -->


<!-- #region Gemfile -->
<h3 id="gemfile">Gemfile</h3>
<p>
  If you are building an application,
  add the following lines to your application&rsquo;s <code>Gemfile</code>:
</p>
<!-- #region -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Gemfile</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id4ac4fc7f4988'><button class='copyBtn' data-clipboard-target='#id4ac4fc7f4988' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>gem 'optparse'
gem 'sod' # If you want extra datatypes</pre>

</div>

<!-- endregion -->
<p>
  And then execute:
</p>

<!-- #region -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida575feeb45b5'><button class='copyBtn' data-clipboard-target='#ida575feeb45b5' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>bundle</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Gem -->
<h3 id="gemfile">Gem</h3>
<p>
  If you are building a gem,
  add the following line to your gem&rsquo;s <code>.gemspec</code>:
</p>
<!-- #region -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>your_gem.gemspec</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idded3ea7200dd'><button class='copyBtn' data-clipboard-target='#idded3ea7200dd' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>spec.add_dependency 'optparse'
spec.add_dependency 'sod' # If you want extra datatypes</pre>

</div>

<!-- endregion -->
<p>
  And then execute:
</p>

<!-- #region -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id9ecb0d468436'><button class='copyBtn' data-clipboard-target='#id9ecb0d468436' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>bundle</pre>

</div>

<!-- endregion -->
<!-- endregion -->
<!-- endregion -->


<!-- #region Usage -->
<h2 id="use">Usage</h2>
<!-- #region implicit -->
<p>
  My <a href='https://github.com/mslinn/stabilize_video' target='_blank' rel="nofollow"><code>stabilize_video</code></a>
  program is an example of how I like to use <code>Option&shy;Parser</code>.
  Below are portions of three Ruby source files:
</p>
<ol>
  <li><code>option.rb</code> parses the options and generates the help text.</li>
  <li><code>stabilize_video.rb</code> parses the positional parameters.</li>
  <li><code>stablize.rb</code> receives the positional parameters and options.</li>
</ol>
<p>
  The following import is required before invoking creating an <code>Option&shy;Parser</code> instance:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code fragment</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1426665b279d'><button class='copyBtn' data-clipboard-target='#id1426665b279d' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'
require 'sod' # If you want to use sod
require 'sod/types/pathname' # If you want to parse Pathnames</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Parsing Options -->
<h3 id="parsing_options">Parsing Options</h3>
<p>
  Let&rsquo;s look at an initial version of the <code>parse_options</code> method,
  which uses <code>Option&shy;Parser</code> to parse the optional arguments.
  <a href='#opi'>Later in this article</a>
  I show a more flexible implementation that allows the parsing code to be testable.
</p>
<div class="codeLabel">Portion of options.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id6394dde210f7"><button class='copyBtn' data-clipboard-target='#id6394dde210f7'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def parse_options
  <span class='bg_yellow_nopad'>options = &#123; shake: 5, loglevel: 'warning' &#125;</span>
  OptionParser.new do |parser|
    parser.program_name = File.basename __FILE__
    @parser = parser

    parser.on('-f', '--overwrite', 'Overwrite output file if present')
    parser.on('-l', '--loglevel <span class='bg_yellow_nopad'>LOGLEVEL</span>', Integer, "Logging level (#&#123;VERBOSITY.join ', '&#125;)")
    parser.on('-s', '--shake <span class='bg_yellow_nopad'>SHAKE</span>', Integer, 'Shakiness (1..10)')
    parser.on('-v', '--verbose <span class='bg_yellow_nopad'>VERBOSE</span>', 'Verbosity')
    parser.on('-z', '--zoom <span class='bg_yellow_nopad'>ZOOM</span>', Integer, 'Zoom percentage')

    parser.on_tail('-h', '--help', 'Show this message') do
      help
    end
  end.order!(<span class='bg_yellow_nopad'>into: options</span>)
  help "Invalid verbosity value (#&#123;options[:verbose]&#125;), must be one of one of: #&#123;VERBOSITY.join ', '&#125;." if options[:verbose] &amp;&amp; !options[:verbose] in VERBOSITY
  help "Invalid shake value (#&#123;options[:shake]&#125;)." if options[:shake].negative? || options[:shake] > 10
<span class='bg_yellow_nopad'>  options</span>
end
</pre>



<ol>
  <li>
    The default options are set in the highlighted hash.
    Default values are set for the <code>:shake</code> and <code>:loglevel</code> keys.
  </li>
  <li>
    When <code>parser.on</code> is passed the name of an option value in UPPER CASE,
    it creates an entry in the <code>options</code> hash with that name, in lower case.
    The above code shows the following examples:
    <ul>
      <li>
        <code>LOGLEVEL</code> provides a means for the user to specify a value to replace the default value of the
        <code>loglevel</code> entry in the <code>options</code> hash, which was initialized with the string value <code>'warning'</code>.
      </li>
      <li>
        <code>SHAKE</code>    provides a means for the user to specify a to replacement for the default value of the
        <code>shake</code>    entry in the <code>options</code> hash, which was initialized with the integer value <code>5</code>.
      </li>
      <li>
        <code>VERBOSE</code>  provides a means for the user to specify a string value for a new entry in the
        <code>options</code>  hash with the key <code>verbose</code>.
      </li>
      <li>
        <code>ZOOM</code>     provides a means for the user to specify a string value for a new entry in the
        <code>options</code>  hash with the key <code>zoom</code>.
      </li>
    </ul>
  </li>
  <li>
    <a href='https://docs.ruby-lang.org/en/master/OptionParser.html#method-i-order-21' target='_blank' rel="nofollow"><code>OptionParser.<wbr>order!</code></a>
    has the side effect that option keywords and key/value pairs
    that match <code>parser.on</code> statements are removed from <code>ARGV</code>.
  </li>
  <li>
    Ending the <code>end.order!</code> statement with <code>(into: options)</code>
    causes the parsed option key/value pairs to be added or updated in the hash called <code>options</code>.
  </li>
  <li>
    The parsed <code>options</code> are returned.
  </li>
</ol>
<!-- endregion -->


<!-- #region Parsing Positional Parameters -->
<h3 id="mandatory">Parsing Positional Parameters</h3>
<p>
  Let&rsquo;s see how <code>stabilize_video.rb</code> parses positional parameters:
</p>
<div class="codeLabel">stabilize_video.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id53948814cd3e"><button class='copyBtn' data-clipboard-target='#id53948814cd3e'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'colorator'
require_relative 'stabilize_video/version'
require_relative 'options'

# Require all Ruby files in 'lib/', except this file
Dir[File.join(__dir__, '*.rb')].each do |file|
  require file unless file.end_with?('/stabilize_video.rb')
end

def main
  options = parse_options
  help 'Video file name must be provided.' if ARGV.empty?
  help "Too many parameters specified.\n#&#123;ARGV&#125;" if ARGV.length > 1
  video_in = ARGV[0]
  video_out = "#&#123;File.dirname video_in&#125;/stabilized_#&#123;File.basename video_in&#125;"
  StablizeVideo.new(video_in, video_out, <span class='bg_yellow_nopad'>**options</span>).stabilize
end

main
</pre>



<p>
  Here are some notes to help you understand the above code:
</p>
<ul>
  <li>
    Usage of <a href='https://rubygems.org/gems/colorator' target='_blank' rel="nofollow"><code>colorator</code></a> or
    <a href='https://rubygems.org/gems/rainbow' target='_blank' rel="nofollow"><code>rainbow</code></a>
    to output colored strings helps readability but is not required.
  </li>
  <li>
    Because <code>Option&shy;Parser</code> removes each argument from <code>ARGV</code> that it recognizes,
    when it finishes, all that should be left on the command line are the positional parameters.
    The <code>main</code> method calls <code>parse_options</code>,
    which, as we know calls <code>Option&shy;Parser</code>,
    and then ensure that a mandatory filename parameter is provided.
  </li>
  <li>
    <code>parse_options</code> returns a hash of name/value pairs,
    which can optionally be passed when doubly dereferenced with two asterisks (<code>**options</code>),
    often referred to as the <i>double splat</i> operator.
    This is done in the highlighted code above when creating a new <code>StablizeVideo</code> instance.
  </li>
</ul>
<!-- endregion -->


<!-- #region warning -->
<h2 id="wam">Warning: AI Misinformation</h2>
<div class="alert rounded shadow">
  <p>
    Warning: AI-generated help from Google provides incorrect information about the double asterisk syntax.
    It says:
  </p>
  <div class="quote">
    The double asterisks cause each key/value pair in the <code>options</code> hash to be passed
    as a named argument to the <code>StablizeVideo.<wbr>initialize</code> method.
  </div>
  <p>
    The above is incorrect.
    Here is some code that shows exactly what happens:
  </p>
  <div class="codeLabel">deref_fun.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id1705085430b9"><button class='copyBtn' data-clipboard-target='#id1705085430b9'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def fun(**options)
  puts "fun options: #&#123;options&#125;"
  puts "fun options[:dry_run]: #&#123;options[:dry_run]&#125;"
  @dry_run = options.key?(:dry_run) ? options[:dry_run] : false
  puts "fun dry_run: #&#123;dry_run&#125;" # This line will raise an error because dry_run is not defined
rescue StandardError => e
  puts "Error in fun: #&#123;e.message&#125;"
end

my_options = &#123; dry_run: true &#125;
fun(**my_options)
puts "@dry_run: #&#123;@dry_run&#125;"
</pre>




  <p>
    Here is the output of the above code:
  </p>
  <!-- #region pre -->
  <div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id4f3e326021d3'><button class='copyBtn' data-clipboard-target='#id4f3e326021d3' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>ruby playground/deref_fun.rb
<span class='unselectable'>fun options: {:dry_run=>true}
fun options[:dry_run]: true
Error in fun: undefined local variable or method `dry_run' for main:Object
@dry_run: true </span></pre>

</div>

  <!-- endregion -->
</div>
<!-- endregion -->


<!-- #region Passing Options -->
<h3 id="passing_options">Passing Options</h3>
<p>
  In the following code, the optional values returned by <code>parse_options</code> are provided to the <code>StablizeVideo.<wbr>initialize</code> method.
  Once again, double asterisks are used.
</p>
<div class="codeLabel">Portion of stabilize.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id4c03131aabdc"><button class='copyBtn' data-clipboard-target='#id4c03131aabdc'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>  def initialize(video_in, video_out, <span class='bg_yellow_nopad'>**options</span>)
    @options   = options
    @loglevel  = "-loglevel #&#123;<span class='bg_yellow_nopad'>options[:loglevel]</span>&#125;"
    @loglevel += ' -stats' unless <span class='bg_yellow_nopad'>options[:loglevel]</span> == 'quiet'
    @shakiness = "shakiness=#&#123;<span class='bg_yellow_nopad'>options[:shake]</span>&#125;"
    @video_in  = MSUtil.expand_env video_in
    @video_out = MSUtil.expand_env video_out
    unless File.exist?(@video_in)
      printf "Error: file #&#123;@video_in&#125; does not exist.\n"
      exit 2
    end
    unless File.readable? @video_in
      printf "Error: #&#123;@video_in&#125; cannot be read.\n"
      exit 2
    end
    return unless File.exist?(@video_out) &amp;&amp; !<span class='bg_yellow_nopad'>options.key?(:overwrite)</span>

    printf "Error: #&#123;@video_out&#125; already exists.\n"
    exit 3
  end
</pre>



<p>
  Notice in the above code that:
</p>
<ul>
  <li>
    The value of the <code>-l</code> / <code>--loglevel</code> option is obtained from <code>options[:loglevel]</code></code>.
  </li>
  <li>
    The value of the <code>-s</code> / <code>--shake</code> option is obtained from <code>options[:shake]</code>.
  </li>
  <li>
    If the user specified the <code>-f</code> (<code>--overwrite</code>) option, that is detected by <code>options.key?(:overwrite)</code>.
  </li>
</ul>
<!-- endregion -->


<!-- #region Hand-written Help Text -->
<h3 id="help">Hand-written Help Text</h3>
<p>
  I find that using the automatically generated help text results in a more complex program for little gain,
  because there are so many moving parts to keep track of.
  Explicitly writing the <code>help</code> method is a more maintainable way of showing the user what they need to know.
</p>
<p>
  The <code>help</code> method below generates the help text, which might be preceded by an error message.
</p>
<div class="codeLabel">Portion of options.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="idf6903206cbd1"><button class='copyBtn' data-clipboard-target='#idf6903206cbd1'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def help(msg = nil)
  printf "Error: #&#123;msg&#125;\n\n".yellow unless msg.nil?
  msg = &lt;&lt;~END_HELP
    stabilize: Stabilizes a video using the FFmpeg vidstabdetect and vidstabtransform filters.

    Syntax: stabilize [Options] PATH_TO_VIDEO

    Options:
      -f Overwrite output file if present
      -h Show this help message
      -s Shakiness compensation 1..10 (default 5)
      -v Verbosity; one of: #&#123;VERBOSITY.join ', '&#125;
      -z Zoom percentage (computed if not specified)

    See:
      https://www.ffmpeg.org/ffmpeg-filters.html#vidstabdetect-1
      https://www.ffmpeg.org/ffmpeg-filters.html#toc-vidstabtransform-1
  END_HELP
  printf msg.cyan
  exit 1
end
</pre>



<p>
  A Ruby <a href='https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#:~:text=squiggly' target='_blank' rel="nofollow">squiggly heredoc</a>
  is used to store a multiline string as the help text.
</p>
<!-- endregion -->


<!-- #region Running the Program -->
<h3 id="run">Running the Program</h3>
<p>
  Now let's run the above program and view the generated help text:
</p>
<!-- #region -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id15ee00240b77'><button class='copyBtn' data-clipboard-target='#id15ee00240b77' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>stabilize -h
<span class='unselectable'>stabilize -h
  stabilize: Stabilizes a video using the FFmpeg vidstabdetect and vidstabtransform filters.

  Syntax: stabilize [Options] PATH_TO_VIDEO

  Options:
    -f Overwrite output file if present
    -h Show this help message
    -s Shakiness compensation 1..10 (default 5)
    -v Verbosity; one of: trace, debug, verbose, info, warning, error, fatal, panic, quiet
    -z Zoom percentage (computed if not specified)

  See:
    https://www.ffmpeg.org/ffmpeg-filters.html#vidstabdetect-1
    https://www.ffmpeg.org/ffmpeg-filters.html#toc-vidstabtransform-1 </span></pre>

</div>

<!-- endregion -->
<!-- endregion -->
<!-- endregion -->


<!-- #region Testing Argument Parsing -->
<h2 id="opi">Testing Argument Parsing</h2>
<!-- #region implicit -->
<p>
  As we have seen, Ruby passes an argument to the
  <a href='https://docs.ruby-lang.org/en/3.4/OptionParser.html' target='_blank' rel="nofollow"><code>Option&shy;Parser</code></a>
  block body.
  As a reminder, the following code shows the block body and the argument, called <code>parser</code>:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ida036b852cd33'><button class='copyBtn' data-clipboard-target='#ida036b852cd33' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>OptionParser.new do |parser|
  # OptionParser block body
end</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Designing Parsing Code for Testability -->
<h3 id="dpcft">Designing Parsing Code for Testability</h3>
<p>
  The argument provided to the <code>Option&shy;Parser</code> block body, called <code>parser</code>,
  is an <code>Option&shy;Parser</code> instance.
  There are two ways to supply a value for <code>ARGV</code> other than the default value that Ruby provides.

</p>
<ol>
  <li>
    The <code>Option&shy;Parser</code> provides methods called <code>parse</code>,
    <code>parse!</code>, <code>option</code> and <code>option!</code>.
    All of these methods accept an optional <code>argv</code> parameter.
  </li>
  <li>
    The first optional argument of each of these methods is the array of arguments to be parsed.
    The value of this argument defaults to <code>ARGV</code>, which
    the operating system passes to your Ruby program when it starts.
  </li>
</ol>
<p>
  The following Ruby code defines a method called <code>parse_options</code> that demonstrates a technique for
  command-line option parsing.
  Key features of this technique are:
</p>
<ol>
  <li>Default values can be established in a modular fashion</li>
  <li>Option parsing code is more modular, which makes it easier to understand</li>
  <li><code>Option&shy;Parser</code> functionality is not affected</li>
  <li>System-provided values can be overridden for testing</li>
  <li>Nothing extra required for full RSpec support</li>
</ol>
<p>
  I intend to use this latest iteration of the technique in all my scripts and CLI tools.
</p>

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id558b3182894b'><button class='copyBtn' data-clipboard-target='#id558b3182894b' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'

def parse_options(default_options, argv: ARGV)
  <span class='bg_yellow_nopad'>options = default_options</span>
  OptionParser.new do |parser|

    # Remaining parameter parsing code goes here
    # For example:
    parser.on '-v', '--verbose', FalseClass
  end.order! <span class='bg_yellow_nopad'>argv</span>, into: options
  options
end</pre>

</div>

<!-- endregion -->

<p>
  The <code>parse_options</code> method accepts one or two arguments:
  <code>default_options</code>, which is a hash containing default values for options,
  and the optional <code>argv</code> argument,
  which allows you to specify a custom array of arguments to parse instead
  of the default system <code>ARGV</code> provided by Ruby.
</p>
<p>
  The <code>parse_options</code> method body initializes a new automatic variable called <code>options</code>
  from the <code>default_options</code> parameter.
  A new <code>Option&shy;Parser</code> instance is then created called <code>parser</code>.
  Its scope is only within the <code>Option&shy;Parser</code> body.
  The odd syntax of this method body actually defines the parsing of the arguments,
  which stores the results in the <code>options</code> hash.
  If the parameter list does not specify an option, the default value remains intact;
  otherwise, the default value is overwritten.
</p>
<p>
  The highlighted line within the <code>Option&shy;Parser</code> body is important:
  this code tells the parser to use the optional <code>argv</code> array, if provided,
  as the source of command-line arguments.
  This technique is useful for testing and for programmatically specifying arguments.
</p>
<p>
  Define the options that your script accepts within the remainder of the <code>parser</code> block.
  The example recognizes a <code>-v</code> or <code>--verbose</code> flag.
  After instantiating the <code>Option&shy;Parser</code> instance called <code>parser</code>,
  the <code>end</code> closes the <code>Option&shy;Parser</code> constructor block, which finalizes the parsing process.
  The <code>order!</code> method is then called with an option called <code>into:</code>,
  followed by the name of the variable that will receive the hash resulting from the parsing operation that just completed.
</p>
<p>
  Finally, the <code>parse_options</code> method returns the populated <code>options</code> hash.
  It is up to the caller to handle any remaining arguments in <code>ARGV</code>.
</p>
<!-- endregion -->


<!-- #region Testing the Parsing Code with irb -->
<h3 id="ttpc">Testing the Parsing Code With <span class="code">irb</span></h3>
<p>
  The following example invocation demonstrates how to call <code>parse_options</code> with a set of default options
  and a custom argument String array, simulating command-line input.
</p>
<p>
  The demonstrated approach shows how to make option parsing flexible and testable.
  You can easily override the arguments without going through the
  pain of providing actual command lines to running instances of your code,
  or getting frustrated with Visual Studio Code&rsquo;s inability to accept ARGV tokens with embedded spaces.
</p>
<p>
  The following example shows how to use the <code>parse_options</code> method above.
  You might write code like this for unit tests of CLI user invocation.
  This example simulates a CLI being called with two positional parameters (<code>param1</code> and <code>param2</code>),
  and without an optional parameter (<code>-v</code> / <code>--verbose</code>) being provided.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Example of calling parse_options</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id7c108afe7d22'><button class='copyBtn' data-clipboard-target='#id7c108afe7d22' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>irb(main):001* </span>require 'optparse'<br>
<span class='unselectable'>irb(main):002* </span>default_options = {
<span class='unselectable'>irb(main):003* </span>  param1: 'value_1',
<span class='unselectable'>irb(main):004* </span>  param2: 'value_2'
<span class='unselectable'>irb(main):005> </span>}
<span class='unselectable'>=> {:param1=>"value_1", :param2=>"value_2"}<br>
irb(main):006> </span>my_argv = %w[param1 value_3 param2 value_4]
<span class='unselectable'>=> ["param1", "value_3", "param2", "value_4"] </span><br>
<span class='unselectable'>irb(main):006> </span> parse_options default_options, argv: my_argv
<span class='unselectable'>{:param1=>"value_1", :param2=>"value_2"} </span></pre>

</div>

<!-- endregion -->

<p>
  You can write RSpec unit tests that incorporate similar code.
  This allows you to test CLI argument parsing easily.
</p>
<!-- endregion -->
<!-- endregion -->


<!-- #region Testing with RSpec -->
<h2 id="rspectest">Testing with RSpec</h2>
<p>
  The following code shows an RSpec test that parses an argument as a Ruby <code>Time</code> instance.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idcf0a85c1b830'><button class='copyBtn' data-clipboard-target='#idcf0a85c1b830' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'
<span class='bg_yellow_nopad'>require 'optparse/time'</span>
require_relative 'spec_helper'

class NestedOptionParserTest
  RSpec.describe OptionParser do
    option_parser = described_class.new do |parser|
      parser.on('-t TIME', '--time=TIME', <span class='bg_yellow_nopad'>Time</span>)
      parser.on('-x', '--xray')
    end

    it 'initializes an OptionParser' do
      options = { time: '2020-02-12T00:00:00Z' } # Default value of all options
      option_parser.order! %w[-x -t 2025-07-01T12:00:00Z], into: options
      expect(options).to eq({ xray: true, time: Time.parse('2025-07-01T12:00:00Z') })
    end
  end
end</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Advanced Usage -->
<h2 id="advuse">Advanced Usage</h2>
<!-- #region implicit -->
<p>
  In Ruby, <code>proc</code>s are closures that capture variables from their surrounding context.
  If you are familiar with partial functions, they can be defined as <code>proc</code>s
  so they can be passed around and invoked later.
  This capability can be useful when defining <code>Option&shy;Parser</code> command-line option parsing.
</p>
<p>
  Ruby <code>proc</code>s can be provided to <code>OptionParser</code>,
  and command-line parsing would occur within them.
  However, it may not be obvious how to obtain the value of parsed options from a proc,
  especially if the proc is defined in a different binding context than one containing the
  accumulator (the <code>options</code> hash).
</p>
<p>
  To provide argument value outside the block, you can:
</p>
<ol>
  <li>Close over a variable (simple, but mixes parsing code with business logic)</li>
  <li>Pass in a container</li>
  <li>Encapsulate the logic in an object</li>
</ol>
<p>
  Let&rsquo;s look at each of these techniques in turn.
</p>
<!-- endregion -->


<!-- #region Close Over A Variable -->
<h3 id="coav">Close Over A Variable</h3>
<p>
  Using a proc to define the parsing for an option is comparatively simple to set up.
  Because only one lexical scope is involved, the accumulator (the <code>options</code> hash) must be defined
  in the same lexical scope as the parser procs.
</p>
<p>
  Constructing the proc in a method allows the proc to be reused because the lexical scope in force when the proc is
  constructed includes the method arguments.
</p>
<p>
  With this technique, the <code>proc</code> fills in the provided <code>options</code> hash
  without any lexical binding trickery.
  Because the <code>options</code> hash is passed by value,
  the caller can see the changes to the value that the proc might make.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide095a73471c0'><button class='copyBtn' data-clipboard-target='#ide095a73471c0' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'

def outdir_proc<span class='bg_yellow_nopad'>(options)</span>
  proc do |parser|
    parser.on('-o', '--out_dir=OUT_DIR') do |value|
      <span class='bg_yellow_nopad'>options[:out_dir]</span> = value
    end
  end
end

options = {}
parser  = OptionParser.new
<span class='bg_yellow_nopad'>outdir_proc(options).call(parser)</span>
parser.parse!(['-o', '/tmp/blah']) # Simulate ARGV
puts options # displays {:out_dir=>"/tmp/blah"}</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Multiple Procs -->
<h3 id="mp">Multiple Procs</h3>
<p>
  This approach allows for modular and reusable option definitions
  that can be easily combined in different ways.
</p>
<p>
  A separate <code>proc</code> is defined for each option or group of related options.
  Each <code>proc</code> adds parsing for one or more options to the parser;
  parsed values are stored in the <code>options</code> hash.
</p>
<ol>
  <li>
    Default values for options can be set in the <code>options</code> hash
    before calling the procs.
  </li>
  <li>
    Procs define how options are parsed and stored in the <code>options</code> hash.
    Procs are independent and cannot assume that any other proc has been called,
    so they must not depend on any other option values
    being present in the <code>options</code> hash.
  </li>
  <li>
    Procs can be called in any order when accumulating
    option values from the command line arguments
    into the options hash.
  </li>
  <li>
    Idempotency is important: calling a proc multiple times should not cause errors
    or unexpected behavior.
  </li>
  <li>
    After all <code>proc</code>s have been called, <code>parser.parse!</code> should be called
    to actually parse the command line arguments and populate the <code>options</code> hash
  </li>
</ol>
<p>
  Here’s a multi-option example using the Rails/Thor-style central <code>options</code>
  hash where multiple procs contribute their options independently but all populate the same hash.
  Note that each proc only references values passed to it,
  and default values are defined separately from the parsing code.
</p>
<div class="codeLabel">optionparser_many_procs.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="idad4ff2929b05"><button class='copyBtn' data-clipboard-target='#idad4ff2929b05'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'

# Define default values and accumulate option values into this Hash.
# The procs that follow are invoked such that option values
# accumulate into this variable.
options = &#123;
  dry_run: false,
  out_dir: Dir.home,
  verbose: false,
&#125;

# Proc for handling output directory
out_dir_proc = proc do |parser, opts|
  parser.on('-o', '--out_dir=OUT_DIR', 'Output directory') do |value|
    opts[:out_dir] = value
  end
end

# Proc for handling verbosity
verbose_proc = proc do |parser, opts|
  parser.on('-v', '--[no-]verbose', 'Run verbosely') do |value|
    opts[:verbose] = value
  end
end

# Proc for handling dry-run mode
dry_run_proc = proc do |parser, opts|
  parser.on('-n', '--dry-run', 'Do everything except execute') do |value|
    opts[:dry_run] = value
  end
end

# Build parser and apply all option procs
parser = OptionParser.new
[out_dir_proc, verbose_proc, dry_run_proc].each do |p|
  p.call(parser, options)
end

parser.parse!(['-o', '/tmp/test', '-v', 'very']) # Simulated command line arguments

puts "RubyOptions hash: #&#123;options.sort.to_h&#125;"
#   RubyOptions hash: &#123;:dry_run=> false, :out_dir=>"/tmp/test", :verbose=>true&#125;
</pre>



<!-- endregion -->


<!-- #region Encapsulate in a Class -->
<h3 id="encap">Encapsulate in a Class</h3>
<p>
  This version encapsulates the previous version in a class.
</p>
<div class="codeLabel">&dollar;nugem/playground/optionparser_class.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id9af538e32508"><button class='copyBtn' data-clipboard-target='#id9af538e32508'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'

class MyOptionParser
  attr_reader :parser, :options

  def initialize
    @options = &#123;&#125;
    @parser = OptionParser.new
    [dry_run_proc, out_dir_proc, verbose_proc].each do |p| # Build parser from option procs
      p.call(@parser, @options)
    end
  end

  # Proc for handling dry-run mode
  def dry_run_proc
    proc do |parser, opts|
      parser.on('-n', '--dry-run', 'Do everything except execute') do |value|
        opts[:dry_run] = value
      end
    end
  end

  # Build parser and apply all option procs
  def out_dir_proc
    proc do |parser, opts|
      parser.on('-o', '--out_dir=OUT_DIR') do |value|
        opts[:out_dir] = value
      end
    end
  end

  # Proc for handling verbosity
  def verbose_proc
    proc do |parser, opts|
      parser.on('-v', '--[no-]verbose', 'Run verbosely') do |value|
        opts[:verbose] = value
      end
    end
  end

  def parse(argv = ARGV)
    @parser.parse! argv
  end
end

my_option_parser = MyOptionParser.new

argv = ['-o', '/tmp/blah'] # Simulate ARGV
puts "Parsing #&#123;argv&#125;"
unparsed_options = my_option_parser.parse argv
puts "Parsed options: #&#123;my_option_parser.options&#125;"
puts "Unparsed options: #&#123;unparsed_options&#125;"

argv = ['-o', '/tmp/blah', 'x'] # Simulate ARGV
puts "\nParsing #&#123;argv&#125;"
unparsed_options = my_option_parser.parse argv
puts "Parsed options: #&#123;my_option_parser.options&#125;"
puts "Unparsed options: #&#123;unparsed_options&#125;"
</pre>



<p>
  Inside the <code>parser.on</code> block,
  the value of <code>OUT_DIR</code> is given as the block argument (e.g., <code>|out_dir|</code>).
</p>
<p>
  Note:
</p>
<ol>
  <li><code>options</code> is a hash that is passed around.</li>
  <li>
    Each option-defining proc knows nothing about where the hash lives;
    it just fills in the value for the provided key.
  </li>
  <li>
    After parsing, user-supplied options are stored in one place.
  </li>
</ol>
<!-- endregion -->
<!-- endregion -->


<!-- #region Composing Subcommand Parsers With a Common Option Parser -->
<h2 id="cspwcop">Composing Subcommand Parsers With a Common Option Parser</h2>
<p>
  This approach can be used to compose subcommand option parsers as well.
  For example, instead of defining a proc per option,
  you might define a proc for all options of each subcommand.
  Each subcommand proc would be invoked conditionally based on the first positional argument.
  The procs would accumulate the option values they parse to the central hash.
</p>
<p>
  Here is an example of how that might look:
</p>
<div class="codeLabel">optionparser_proc_fun.rb</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id4ae53e6f68df"><button class='copyBtn' data-clipboard-target='#id4ae53e6f68df'title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable numbered_line'>  1: </span>require 'optparse'
<span class='unselectable numbered_line'>  2: </span>
<span class='unselectable numbered_line'>  3: </span>argv = ['init', '-v', '-t', 'blah']
<span class='unselectable numbered_line'>  4: </span>subcommand = argv.shift
<span class='unselectable numbered_line'>  5: </span>if subcommand.nil?
<span class='unselectable numbered_line'>  6: </span>  puts 'Error: No subcommand provided. Subcommands are: init and deploy'
<span class='unselectable numbered_line'>  7: </span>  exit 1
<span class='unselectable numbered_line'>  8: </span>end
<span class='unselectable numbered_line'>  9: </span>options = &#123; verbose: false &#125; # Default value of options
<span class='unselectable numbered_line'> 10: </span>
<span class='unselectable numbered_line'> 11: </span>common_proc = proc do |parser, opts|
<span class='unselectable numbered_line'> 12: </span>  parser.on('-v', '--[no-]verbose', 'Run verbosely') do |value|
<span class='unselectable numbered_line'> 13: </span>    opts[:verbose] = value
<span class='unselectable numbered_line'> 14: </span>  end
<span class='unselectable numbered_line'> 15: </span>end
<span class='unselectable numbered_line'> 16: </span>
<span class='unselectable numbered_line'> 17: </span>subcommand_procs = &#123;
<span class='unselectable numbered_line'> 18: </span>  'init'   => proc do |parser, opts|
<span class='unselectable numbered_line'> 19: </span>    parser.on('-t', '--template=TEMPLATE', 'Template to use') do |value|
<span class='unselectable numbered_line'> 20: </span>      opts[:template] = value
<span class='unselectable numbered_line'> 21: </span>    end
<span class='unselectable numbered_line'> 22: </span>  end,
<span class='unselectable numbered_line'> 23: </span>  'deploy' => proc do |parser, opts|
<span class='unselectable numbered_line'> 24: </span>    parser.on('-e', '--env=ENV', 'Environment to deploy to') do |value|
<span class='unselectable numbered_line'> 25: </span>      opts[:env] = value
<span class='unselectable numbered_line'> 26: </span>    end
<span class='unselectable numbered_line'> 27: </span>  end,
<span class='unselectable numbered_line'> 28: </span>&#125;
<span class='unselectable numbered_line'> 29: </span>
<span class='unselectable numbered_line'> 30: </span>parser = OptionParser.new
<span class='unselectable numbered_line'> 31: </span>common_proc.call(parser, options)
<span class='unselectable numbered_line'> 32: </span>
<span class='unselectable numbered_line'> 33: </span># Subcommand-specific options
<span class='unselectable numbered_line'> 34: </span>if subcommand_procs.key?(subcommand)
<span class='unselectable numbered_line'> 35: </span>  subcommand_procs[subcommand].call(parser, options)
<span class='unselectable numbered_line'> 36: </span>else
<span class='unselectable numbered_line'> 37: </span>  puts "Unknown subcommand: #&#123;subcommand&#125;"
<span class='unselectable numbered_line'> 38: </span>  exit 1
<span class='unselectable numbered_line'> 39: </span>end
<span class='unselectable numbered_line'> 40: </span>
<span class='unselectable numbered_line'> 41: </span>parser.parse(argv) # Example args; replace with ARGV in real use
<span class='unselectable numbered_line'> 42: </span>puts "Subcommand '#&#123;subcommand&#125;' has options #&#123;options.inspect&#125;"
</pre>




<p>
  Lines 30-31 above are:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id25a446ba68de'><button class='copyBtn' data-clipboard-target='#id25a446ba68de' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>parser = OptionParser.new
common_proc.call(parser, options)</pre>

</div>

<!-- endregion -->
<p>
  The above is simple and clear.
  The following is more verbose and less clear,
  but it accomplishes the same purpose:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ide780dfb66bb1'><button class='copyBtn' data-clipboard-target='#ide780dfb66bb1' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>_option_parser = OptionParser.new do |parser|
  common_proc.call(parser, options)
end</pre>

</div>

<!-- endregion -->
<p>
  There are a lot of ways to compose <code>OptionParser</code> parsing.
</p>
<!-- endregion -->





<!-- #region Highlign -->
<h2 id="highline">Highline</h2>
<p>
  <code>Highline</code> is a gem that is often found in CLIs that are built with
  <code>Option&shy;Parser</code>.
  It now enjoys its very own article:
  <a href='/ruby/6650-highline.html'>Highline Gem and Workalike</a>.
</p>
<!-- endregion -->


<!-- #region Subcommand Options -->
<h2 id="subopt">Subcommand Options</h2>
<p class="clear">
  The documentation shows how to set up your code so subcommands can share common options,
  and also have options specific to certain subcommands.
  This is how most information on the interwebs says subcommand options should be organized:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Most commonly recommended form of subcommand options</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id6822fafd938f'><button class='copyBtn' data-clipboard-target='#id6822fafd938f' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>COMMAND [GLOBAL FLAGS] [SUB-COMMAND [SUB-COMMAND FLAGS]]</pre>

</div>

<!-- endregion -->

<p>
  For example, the <a href='/ruby/6800-nugem.html'><code>nugem</code></a>
  CLI has two subcommands: <code>ruby</code> and <code>jekyll</code>.
</p>
<ul>
  <li>
    The <code>--loglevel</code>, <code>--executable</code> and <code>--help</code> options are available for all subcommands.
  </li>
  <li>
    The <code>ruby</code> subcommand has no special options.
  </li>
  <li>
    The <code>--tags</code> and <code>--hooks</code> options are only for the <code>jekyll</code> subcommand.
  </li>
</ul>

<div class="alert rounded shadow">
  <p>
    The version of <code>nugem</code> described here is still under development.
    I am working on a
    <a href='https://github.com/mslinn/nugem/tree/wip' target='_blank' rel="nofollow">major new release</a>
    for <code>nugem</code> at present.
  </p>
  <span class='emoji big' style='float: left; margin-right: 5px;'>&#x1F44F;</span>
  <span class='emoji big' style='float: right; margin-left: 5px;'>&#x1F44F;</span>
  <p>
    This new release has taken 18 months;
    I decided to create (and test) lots of support gems for the new release.
    Now (2025-09-25) I about to start working on the last one.
    The new version is looking pretty nice.
    I am tempted to release the next generation Nugem soon.
  </p>
</div>

<p>
  Using the documentated way of implementing subcommand option parsing, the following syntaxes would be allowed:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Allowed syntaxes</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id077ce194a338'><button class='copyBtn' data-clipboard-target='#id077ce194a338' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>nugem --help
nugem ruby
nugem --loglevel info ruby
nugem --executable ruby
nugem --loglevel info --executable ruby
nugem jekyll
nugem jekyll --tags
nugem jekyll --tags --hooks
nugem --loglevel info jekyll --tags --hooks
nugem --loglevel info --executable jekyll --tags --hooks
... and others ...</pre>

</div>

<!-- endregion -->

<p class="pullQuote">
  I do not like this syntax.
</p>
<p style="margin-top: 1em;">
  The most commonly recommended approach requires that common options must precede the subcommand,
  and subcommand options must follow the subcommand.
  The following desirable syntaxes would therefore not be allowed:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable clear' data-lt-active='false'>Forbidden syntaxes</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id47c623fcd774'><button class='copyBtn' data-clipboard-target='#id47c623fcd774' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>nugem ruby --executable
nugem jekyll --executable
nugem --tags jekyll
nugem --tags jekyll --executable
nugem jekyll --executable --tags
... and others ...</pre>

</div>

<!-- endregion -->

<p>
  Here is a rough outline of how to implement this type of syntax:
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id204c63a0fa62'><button class='copyBtn' data-clipboard-target='#id204c63a0fa62' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>global = OptionParser.new do |opts|
  # on statements for flags common to all commands go here
end

subcommands = {
  'foo' => OptionParser.new do |opts|
    # on statements for the foo command go here
  end
  'baz' => OptionParser.new do |opts|
    # on statements for the baz command go here
  end
}

global.order!
subcommands[ARGV.shift].order!</pre>

</div>

<!-- endregion -->
<p style="margin-top: 2em;">
  The last two lines of the above code show how <code>global.order!</code>
  parses all options in the first element of <code>ARGV</code> until the first token
  that does not start with a dash and removes them from <code>ARGV</code>.
</p>
<p>
  The next line, <code>subcommands[ARGV.shift].order!</code>,
  parses and removes all options after the subcommand.
</p>
<p class="clear">
  I prefer to order options before positional parameters
  and to be able to specify options in any order,
  anywhere on the command line.
  However, this makes parsing multiple subcommands difficult.
</p>
<!-- endregion -->


<!-- #region A More Practical Approach -->
<h2 id="morepractical">A More Practical Approach</h2>
<p>
  A more practical approach would be to mandate that the first parameter must be the subcommand name,
  followed by options and any other positional parameters.
</p>
<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Forbidden syntaxes</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idd709458de578'><button class='copyBtn' data-clipboard-target='#idd709458de578' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>nugem ruby --executable
nugem ruby --executable
nugem jekyll --tags
nugem jekyll --tags --executable
nugem jekyll --executable --tags
... and others ...</pre>

</div>

<!-- endregion -->
<p>
  <code>Option&shy;Parser</code> <code>on</code> statements typically match specific flags or options.
  However, to match all options, regardless of their name or format,
  write the <code>on</code> statement as a custom order block:
</p>

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Separating keywords from options</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id3c3e3b3a8934'><button class='copyBtn' data-clipboard-target='#id3c3e3b3a8934' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def separate_keywords_from_options
  @keywords      = ARGV.reject { |x| x.start_with? '-' }
  @argv = ARGV.select { |x| x.start_with? '-' }
end</pre>

</div>

<!-- endregion -->

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Common option parsing</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id3818e0b17f3a'><button class='copyBtn' data-clipboard-target='#id3818e0b17f3a' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def parse_common_options(argv)
  OptionParser.new do |parser|
    parser.default_argv = argv
    parser.on '-e', '--executables', 'Include an executable'
    parser.on '-v', '--verbose', FalseClass
  end.order! into: @options
end</pre>

</div>

<!-- endregion -->

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Subcommand option parsing</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='ideba4c3e461b9'><button class='copyBtn' data-clipboard-target='#ideba4c3e461b9' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def parse_subcommand_ruby(argv)
  OptionParser.new do |parser|
    parser.default_argv = argv
    parser.on '-j', '--jump', 'Jump for joy'
    parser.on '-s', '--skip', 'Skip around'
  end.order! into: @options
end</pre>

</div>

<!-- endregion -->

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>process_keywords</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id4ca377773261'><button class='copyBtn' data-clipboard-target='#id4ca377773261' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>def process_keywords(keywords)
  puts "Keywords are: #{keywords.join ', '}"
end</pre>

</div>

<!-- endregion -->

<!-- #region pre -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Ruby code</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id8668a3c42c19'><button class='copyBtn' data-clipboard-target='#id8668a3c42c19' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'optparse'

@argv = []
@options = {}
separate_keywords_from_options
parse_common_options @argv
parse_subcommand_ruby @argv
puts "Options: #{@options}"
puts "keywords: #{@keywords}"
process_keywords @keywords</pre>

</div>

<!-- endregion -->
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><category term="Ruby" /><summary type="html"><![CDATA[OptionParser.]]></summary></entry><entry><title type="html">Rubocop Settings for Visual Studio Code</title><link href="https://www.mslinn.com/ruby/5100-rubocop-vscode.html" rel="alternate" type="text/html" title="Rubocop Settings for Visual Studio Code" /><published>2023-08-28T00:00:00-04:00</published><updated>2023-08-28T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/5100-rubocop-vscode</id><content type="html" xml:base="https://www.mslinn.com/ruby/5100-rubocop-vscode.html"><![CDATA[<!-- #region Rubocop Visual Studio Plugin -->
<h2 id="vscode">Rubocop Visual Studio Plugin</h2>
<p>
  <a href='https://rubocop.org/' target='_blank' rel="nofollow">RuboCop</a>
  is a Ruby code style checker (linter) and formatter based on the community-driven Ruby Style Guide.
  There is a Rubocop <a href='https://marketplace.visualstudio.com/items?itemName=misogi.ruby-rubocop' target='_blank' rel="nofollow">Visual Studio Code plugin</a> that works well.
</p>
<!-- endregion -->


<!-- #region Rubocop Project Configuration -->
<h2 id="project">Rubocop Project Configuration</h2>
<p>
  Copying the following lines into <code>.vscode/settings.json</code> makes Rubocop work in Visual Studio Code.
</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>settings.json</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id5eb5bea263c6'><button class='copyBtn' data-clipboard-target='#id5eb5bea263c6' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>"ruby.rubocop.useBundler": true,
"ruby.rubocop.configFilePath": "./.rubocop.yml"</pre>

</div>

<!-- endregion -->


<!-- #region Rubocop Workspace Configuration -->
<h2 id="workspace">Rubocop Workspace Configuration</h2>
<p>
  The above settings could also be located in a
  <a href='https://code.visualstudio.com/docs/editor/workspaces' target='_blank' rel="nofollow"><code>.code-workspace</code> file</a>:
</p>

<!-- #region  -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>workspace.code-workspace</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id6e1c59024577'><button class='copyBtn' data-clipboard-target='#id6e1c59024577' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>{
  "folders": [
    {
      "path": "~/work/myProject"
    },
  ],
  "settings": {
    "ruby.rubocop.useBundler": true,
    "ruby.rubocop.configFilePath": "./.rubocop.yml"
  }
}</pre>

</div>

<!-- endregion -->
<!-- endregion -->


<!-- #region Rubocop Settings -->
<h2 id="workspace">Rubocop Settings</h2>
<p>
  The following is the Rubocop configuration file (<code>.rubocop.yml</code>) that I use for this Jekyll website.
  It should be located within the top-level directory of a Ruby or Jekyll project.
</p>
<div class="codeLabel">.rubocop.yml</div>
<pre data-lt-active="false" class="pre_tag maxOneScreenHigh copyContainer" id="id62c00e837201">require:
  - rubocop-jekyll
  - rubocop-md
  - rubocop-performance
  - rubocop-rake
  - rubocop-rspec

inherit_gem:
  rubocop-jekyll: .rubocop.yml

AllCops:
  Exclude:
    - _site/**/*
    - binstub/**/*
    - Gemfile*
    - exe/**/*
    - jekyll/**/*
    - vendor/**/*
  NewCops: enable
  TargetRubyVersion: 2.6

Gemspec/RequireMFA:
  Enabled: false

Jekyll/NoPutsAllowed:
  Enabled: false # Some of my Ruby code is plugins, other Ruby code is not, so this rule is a PITA

Naming/FileName:
  Exclude:
    - _bin/**/*

Layout/HashAlignment:
  EnforcedColonStyle: table
  EnforcedHashRocketStyle: table

Layout/LeadingCommentSpace:
  Exclude:
    - _bin/**/*

Layout/LineLength:
  Max: 150

Layout/FirstHashElementIndentation:
  Enabled: false

Layout/MultilineMethodCallIndentation:
  Enabled: false

Metrics/AbcSize:
  Max: 40

Metrics/BlockLength:
  Max: 50

Metrics/CyclomaticComplexity:
  Max: 15

Metrics/MethodLength:
  Max: 30

Metrics/PerceivedComplexity:
  Max: 20

Style/Alias:
  Exclude:
    - _plugins/symlink_watcher.rb
    - blog/bin/avImport

Style/Documentation:
  Enabled: false

Style/FrozenStringLiteralComment:
  Enabled: false

Style/HashSyntax:
  EnforcedStyle: ruby19
  EnforcedShorthandSyntax: consistent

Style/PercentLiteralDelimiters:
  Enabled: false

Style/RegexpLiteral:
  Enabled: false

Style/StringLiterals:
  Enabled: false

Style/StringLiteralsInInterpolation:
  Enabled: false

Style/TrailingCommaInArrayLiteral:
  Enabled: false

Style/TrailingCommaInHashLiteral:
  EnforcedStyleForMultiline: comma
</pre>



<!-- endregion -->


<h2 id="about" class="clear">About the Author</h2>
<img
  class="right rounded shadow"
  src="/blog/images/svRuby/sdforum_ruby_690x565.webp"
  style="height: auto; width: 25%;">
<p>
  I, Mike Slinn, have been working with Ruby for a long time now.
  Back in 2005, I was the product marketing manager at CodeGear
  (the company was formerly known as Borland) for their
  <a href='https://www.infoq.com/news/2007/05/codegear-ror-ide/'>3rd Rail IDE</a>.
  3rd Rail supported Ruby and Ruby on Rails at launch.
</p>
<p>
  In 2006, I co-chaired the Silicon Valley Ruby Conference on behalf of the SD Forum in Silicon Valley.
  As you can see, I have the t-shirt.
  I was the sole chairman of the 2007 Silicon Valley Ruby Conference.
</p>
<p>
  Several court cases have come my way over the years
  in my capacity as a software expert witness.
  The court cases featured questions about IP misappropriation for Ruby on Rails programs.
  You can read about <a href="/expertArticles/">my experience as a software expert</a>
  if that interests you.
</p>
<p>
  I currently enjoy writing <a href="/jekyll_plugins/">Jekyll plugins</a> in Ruby for this website and others,
  as well as Ruby utilities.
</p>]]></content><author><name>Mike Slinn</name></author><category term="Ruby" /><category term="Visual Studio Code" /><summary type="html"><![CDATA[Rubocop configuration and settings for Visual Studio Code projects.]]></summary></entry><entry><title type="html">Graceful Crash Exit From Ruby</title><link href="https://www.mslinn.com/ruby/2200-crash-exit.html" rel="alternate" type="text/html" title="Graceful Crash Exit From Ruby" /><published>2023-08-11T00:00:00-04:00</published><updated>2023-08-11T00:00:00-04:00</updated><id>https://www.mslinn.com/ruby/2200-crash-exit</id><content type="html" xml:base="https://www.mslinn.com/ruby/2200-crash-exit.html"><![CDATA[<!-- #region intro -->
<p>
  Ever want to stop your Ruby program without generating a stack trace?
  It is simple enough to do if you control the execution environment,
  but if your program is loaded by another program, then the loading program can trap your attempts to exit.
</p>
<p>
  Jekyll traps exceptions thrown by its plugins.
  If you attempt to call <a href='https://www.rubydoc.info/stdlib/core/Kernel:abort' target='_blank' rel="nofollow"><code>abort</code></a> or
  <a href='https://www.rubydoc.info/stdlib/core/Kernel:exit' target='_blank' rel="nofollow"><code>exit</code></a>,
  those calls will work,
  but there is nothing you can do to prevent a long stack trace from being issued.
  This is not a pleasant experience for users.
</p>
<!-- endregion -->


<!-- #region process.kill -->
<h2 id="kill"><span class="code">Process.kill</span></h2>
<p>
  You might try calling
  <a href='https://docs.ruby-lang.org/en/master/Process.html#method-c-kill' target='_blank' rel="nofollow"><code>Process.kill</code></a>,
  like this:
</p>
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id25bbbdda3366'><button class='copyBtn' data-clipboard-target='#id25bbbdda3366' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>Process.kill('KILL', Process.pid) # Like kill -9 in bash
# or:
Process.kill('SEGV', Process.pid) # Like kill -11 in bash</pre>

</div>

<p>
  Unfortunately, not only does this generate a stack trace, but it also dumps lots of other information.
  This is worse than just calling <code>abort</code>.
</p>
<!-- endregion -->


<!-- #region exec -->
<h2 id="exec">Kernel.exec</h2>
<p>
  The only way I found that worked was to terminate the process by loading another process
  using <a href='https://rubydoc.info/stdlib/core/Kernel:exec' target='_blank' rel="nofollow"><code>Kernel:exec</code></a>.
</p>
<p>
  The following method prints an error message in red
  using <a href='https://github.com/octopress/colorator' target='_blank' rel="nofollow"><code>colorator</code></a>,
  then replaces the Jekyll process with the bash command, <code>echo ''</code>.
</p>
<!-- #region -->
<div class="jekyll_pre" >
<div class='codeLabel unselectable' data-lt-active='false'>Shell</div>
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id52887b86cd14'><button class='copyBtn' data-clipboard-target='#id52887b86cd14' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>require 'colorator'

# If a Jekyll plugin needs to crash exit, and stop Jekyll, call this method.
# It does not generate a stack trace.
# This method does not return because the process is abruptly terminated.
#
# @param error StandardError or a subclass of StandardError is required
#
# Do not raise the error before calling this method, just create it via 'new', like this:
# exit_without_stack_trace StandardError.new('This is my error message')
#
# If you want to call this method from a handler method, the default index for the backtrace array must be specified.
# The default backtrace index is 1, which means the calling method.
# To specify the calling method's caller, pass in 2, like this:
# exit_without_stack_trace StandardError.new('This is my error message'), 2
def exit_without_stack_trace(error, caller_index = 1)
  raise error
rescue StandardError => e
  file, line_number, caller = e.backtrace[caller_index].split(':')
  caller = caller.tr('`', "'")
  warn "#{self.class} died with a '#{error.message}' #{caller} on line #{line_number} of #{file}".red
  exec "echo ''"
end</pre>

</div>

<!-- endregion -->
<!-- endregion -->]]></content><author><name>Mike Slinn</name></author><category term="Jekyll" /><category term="Ruby" /><summary type="html"><![CDATA[Crash exit from Ruby without a stack trace.]]></summary></entry></feed>