<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Postmodern's Blog</title>
  <link href="http://postmodern.github.io/atom.xml" rel="self" />
  <link href="http://postmodern.github.io/" />
  <updated>2025-05-27T03:21:40+00:00</updated>
  <id>http://postmodern.github.io/</id>
  
  <entry>
    <title>How to add a man page to your Ruby project</title>
    <link href="http://postmodern.github.io/blog/2023/12/05/how-to-add-a-man-page-to-your-ruby-project.html" />
    <updated>2023-12-05T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2023/12/05/how-to-add-a-man-page-to-your-ruby-project.html</id>
    <content type="html">&lt;p&gt;If you have ever used the &lt;a href=&quot;https://git-scm.com/docs/git&quot;&gt;git&lt;/a&gt; command-line utility, you will be pleasantly
surprised that if you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone --help&lt;/code&gt; it automatically displays the man
page for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt; instead of the usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; output.&lt;/p&gt;

&lt;p&gt;This blog post will show you how to add the same functionality to your Ruby
command-line utility.&lt;/p&gt;

&lt;h2 id=&quot;introducing-kramdown-man&quot;&gt;Introducing kramdown-man&lt;/h2&gt;

&lt;p&gt;Man pages are written in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff typesetting markup language&lt;/a&gt;, which
uses macro tags that look like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.PP&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\fB&lt;/code&gt;. Needless to say writing &lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff&lt;/a&gt;
by hand is not fun. Instead, we will use the &lt;a href=&quot;https://github.com/postmodern/kramdown-man#readme&quot;&gt;kramdown-man&lt;/a&gt; gem to generate
the &lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff&lt;/a&gt; man page from a similar looking pure markdown man page.&lt;/p&gt;

&lt;h2 id=&quot;step-1-add-kramdown-man&quot;&gt;Step 1: Add kramdown-man&lt;/h2&gt;

&lt;p&gt;Add &lt;a href=&quot;https://github.com/postmodern/kramdown-man#readme&quot;&gt;kramdown-man&lt;/a&gt; to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;kramdown-man&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 1.0&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-2-write-the-markdown-man-page&quot;&gt;Step 2: Write the markdown man page&lt;/h2&gt;

&lt;p&gt;First create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man/&lt;/code&gt; directory in your project.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;man
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then write the markdown man page, which should be named like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man/mycli.1.md&lt;/code&gt;.
The number in the file name indicates the man page Section number
(Section 1 is for General Commands, Section 3 is for Library Functions).&lt;/p&gt;

&lt;p&gt;Use &lt;a href=&quot;https://github.com/postmodern/kramdown-man/blob/main/man/kramdown-man.1.md&quot;&gt;kramdown-man&lt;/a&gt;’s own man page as an example for how
to structure your markdown man page. It should look roughly like this:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# mycli 1 &quot;2024-01-01&quot; MyCLI &quot;User Manuals&quot;&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## NAME&lt;/span&gt;

mycli - Does things and stuff

&lt;span class=&quot;gu&quot;&gt;## SYNOPSIS&lt;/span&gt;

&lt;span class=&quot;sb&quot;&gt;`mycli`&lt;/span&gt; [&lt;span class=&quot;ge&quot;&gt;*options*&lt;/span&gt;] &lt;span class=&quot;ge&quot;&gt;*ARG1*&lt;/span&gt; [&lt;span class=&quot;ge&quot;&gt;*ARG2*&lt;/span&gt;]

&lt;span class=&quot;gu&quot;&gt;## DESCRIPTION&lt;/span&gt;

The &lt;span class=&quot;sb&quot;&gt;`mycli`&lt;/span&gt; utility does things and stuff. Bla bla bla bla.

&lt;span class=&quot;gu&quot;&gt;## ARGUMENTS&lt;/span&gt;

&lt;span class=&quot;ge&quot;&gt;*ARG1*&lt;/span&gt;
: This is a required argument.

&lt;span class=&quot;ge&quot;&gt;*ARG2*&lt;/span&gt;
: This is an optional argument

&lt;span class=&quot;gu&quot;&gt;## OPTIONS&lt;/span&gt;

&lt;span class=&quot;sb&quot;&gt;`-f`&lt;/span&gt;, &lt;span class=&quot;sb&quot;&gt;`--flag`&lt;/span&gt; &lt;span class=&quot;ge&quot;&gt;*VALUE*&lt;/span&gt;
: This is an option flag that takes a &lt;span class=&quot;ge&quot;&gt;*VALUE*&lt;/span&gt; argument.

&lt;span class=&quot;sb&quot;&gt;`-h`&lt;/span&gt;, &lt;span class=&quot;sb&quot;&gt;`--help`&lt;/span&gt;
: Prints the help information for the command.

&lt;span class=&quot;gu&quot;&gt;## EXAMPLES&lt;/span&gt;

Example command description:&lt;span class=&quot;sb&quot;&gt;

    $ mycli --flag file.txt

&lt;/span&gt;&lt;span class=&quot;gu&quot;&gt;## AUTHOR&lt;/span&gt;

Your Name &lt;span class=&quot;nv&quot;&gt;&amp;lt;you@example.com&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## SEE ALSO&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bash(1)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;man:bash.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;other-man-page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;(other-man-page.1.md)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To preview how the markdown man page will be rendered, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kramdown-man&lt;/code&gt;
command:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kramdown-man man/mycli.1.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;markdown-man-page-layout-explained&quot;&gt;Markdown Man Page Layout Explained&lt;/h3&gt;

&lt;h4 id=&quot;header&quot;&gt;Header&lt;/h4&gt;

&lt;p&gt;The first line will be used for the man page’s header and footer
lines. It has the following format:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# mycli 1 &quot;2024-01-01&quot; MyCLI &quot;User Manuals&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;# mycli&lt;/code&gt; - The command name.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; - The section number.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2024-01-01&lt;/code&gt; - The date the man page is being written, in the format
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyCLI&lt;/code&gt; - The project’s name.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;User Manuals&quot;&lt;/code&gt; - The man page section name.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;sections&quot;&gt;Sections&lt;/h4&gt;

&lt;p&gt;Man pages typically have the follow main sections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NAME&lt;/code&gt; - The command name and a short definition.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SYNOPSIS&lt;/code&gt; - The command’s usage, showing order of arguments.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DESCRIPTION&lt;/code&gt; - A more detailed description of the command.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARGUMENTS&lt;/code&gt; - Defines the purpose of each argument.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OPTIONS&lt;/code&gt; - Defines the purpose of each option flag and it’s usage.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXAMPLES&lt;/code&gt; - Show common examples of running the command.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTHORS&lt;/code&gt; - List the authors of the command.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEE ALSO&lt;/code&gt; - Link to other man pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;argument--option-lists&quot;&gt;ARGUMENT / OPTION LISTS&lt;/h4&gt;

&lt;p&gt;Argument and option definitions must be defined using
&lt;a href=&quot;https://kramdown.gettalong.org/quickref.html#definition-lists&quot;&gt;markdown definition lists&lt;/a&gt; (hence the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;: &lt;/code&gt; before the summary) for them to be
properly indented.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*ARG1*
: Definition goes here.

*ARG2*
: Definition goes here.

  Multiple paragraphs may be given.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;command-usage-syntax&quot;&gt;COMMAND USAGE SYNTAX&lt;/h4&gt;

&lt;p&gt;Codespans indicate a literal word:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;sb&quot;&gt;`mycli`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Emphasis and all uppercase indicates a required argument:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;ge&quot;&gt;*ARG*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Square brackets around an argument indicates an optional argument.&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[&lt;span class=&quot;ge&quot;&gt;*ARG*&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Curly-braces with pipe separates indicates one of the arguments is required:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&lt;span class=&quot;ge&quot;&gt;*ARG1*&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\|&lt;/span&gt; &lt;span class=&quot;ge&quot;&gt;*ARG2*&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\|&lt;/span&gt; &lt;span class=&quot;ge&quot;&gt;*ARG3*&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;man-page-links&quot;&gt;Man Page Links&lt;/h4&gt;

&lt;p&gt;To link to other man pages in your project’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man/&lt;/code&gt; directory, use a regular
markdown link that links to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.1.md&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;other-man-page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;other-man-page.1.md&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will also generate a bolded man page reference which will look like
&lt;strong&gt;other-man-page&lt;/strong&gt;(1) in the displayed man page.&lt;/p&gt;

&lt;p&gt;To link to other man pages that are already installed on a system, use a
regular markdown link, but use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man:page-name.1&lt;/code&gt; URL with the man page
name &lt;em&gt;and&lt;/em&gt; section number:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bash(1)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;man:bash.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will generate a bolded man page reference which will look like &lt;strong&gt;bash&lt;/strong&gt;(1)
in the displayed man page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Firefox on Linux will actually recognize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man:&lt;/code&gt; URIs and open them
using Gnome’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yelp&lt;/code&gt; help browser.&lt;/p&gt;

&lt;h2 id=&quot;step-3-add-the-rake-task&quot;&gt;Step 3: Add the rake task&lt;/h2&gt;

&lt;p&gt;Now that we have written the markdown man page, we need to setup a rake task to
generate the &lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff&lt;/a&gt; formatted man page from the markdown man page.&lt;/p&gt;

&lt;p&gt;Add the following code to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;kramdown/man/task&apos;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Kramdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Man&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will define a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man&lt;/code&gt; rake task and define &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; dependencies between the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man/*.1&lt;/code&gt; output files and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man/*.1.md&lt;/code&gt; input files.&lt;/p&gt;

&lt;h2 id=&quot;step-4-generate-the-man-pages&quot;&gt;Step 4: Generate the man pages&lt;/h2&gt;

&lt;p&gt;To generate all man pages in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man/&lt;/code&gt; directory run:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rake man
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can then view the generated man pages using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man&lt;/code&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;man ./man/mycli.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-5-add-the-code&quot;&gt;Step 5: Add the code&lt;/h2&gt;

&lt;p&gt;In order for our CLI to automatically display the man page when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt;
option is given, we will need to add this bit of code to the &lt;a href=&quot;https://docs.ruby-lang.org/en/3.1/OptionParser.html&quot;&gt;OptionParser&lt;/a&gt;’s
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; option:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# The path to the man/mycli.1 generated man page&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MAN_PAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__dir__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;..&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;..&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;..&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;man&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mycli.1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;-h&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;--help&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Prints this kruft&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tty?&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;man&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MAN_PAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if $stdout.tty?&lt;/code&gt; check tests whether stdout is a TTY or being redirected
to a file or another command. If we are running in a real TTY terminal, then
display the man page. If we are not running in a real TTY terminal, then
print the usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; output. This is a polite thing to do, as users might
want to view the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; output through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;less&lt;/code&gt; or might dump it to a file using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help &amp;gt;mycli.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you don’t want to copy/paste the above code into all of your Ruby projects,
you can use the &lt;a href=&quot;https://github.com/postmodern/command_kit.rb#readme&quot;&gt;command_kit&lt;/a&gt; gem, which provides a &lt;a href=&quot;https://rubydoc.info/gems/command_kit/CommandKit/Help/Man&quot;&gt;CommandKit::Help::Man&lt;/a&gt;
module that adds the same functionality to a command class.&lt;/p&gt;

&lt;h2 id=&quot;step-6-package-your-man-page&quot;&gt;Step 6: Package your man page&lt;/h2&gt;

&lt;p&gt;Now that we have generated our &lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff&lt;/a&gt; man pages, we will want to add them to
either git or the gemspec’s files list. This way the generated
&lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff&lt;/a&gt; man page will be included in the packaged gem.&lt;/p&gt;

&lt;p&gt;If you prefer to not add the generated &lt;a href=&quot;https://en.wikipedia.org/wiki/Roff_(software)&quot;&gt;roff&lt;/a&gt; man page to git, you can manually
add it to the list of files in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;man/mycli.1&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then build and install your gem:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rake gem
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gem &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; ./pkg/mycli-0.1.0.gem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-7-test-it&quot;&gt;Step 7: Test It!&lt;/h2&gt;

&lt;p&gt;Now your command should display it’s own man page when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; is given:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mycli --help
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should see something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;example_man_page_screenshot.png&quot; alt=&quot;screenshot of the displayed man page&quot; /&gt;&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Solving Advent of Cyber 2023 Day 2 using Ruby</title>
    <link href="http://postmodern.github.io/blog/2023/12/02/solving-advent-of-cyber-day-2-using-ruby.html" />
    <updated>2023-12-02T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2023/12/02/solving-advent-of-cyber-day-2-using-ruby.html</id>
    <content type="html">&lt;p&gt;This year I decided to try my hand at the &lt;a href=&quot;https://tryhackme.com/room/adventofcyber2023&quot;&gt;Advent of Cyber&lt;/a&gt; challenges.&lt;/p&gt;

&lt;p&gt;The Day 2 challenge involves Data Science. We are given a &lt;a href=&quot;https://jupyter.org/&quot;&gt;Jupyter Notebook&lt;/a&gt;
file containing a table of log data showing ports scan events. Now the challenge
teaches you how to use &lt;a href=&quot;https://jupyter.org/&quot;&gt;Jupyter Notebook&lt;/a&gt; and Python, but we’re not going to
solve it using Python. We are going to solve it using only Ruby!&lt;/p&gt;

&lt;p&gt;While Python is very popular in the Data Science field, you can do Data Science
with Ruby. Ruby standard library comes with many useful methods, such as
&lt;a href=&quot;https://rubydoc.info/stdlib/core/Enumerable#map-instance_method&quot;&gt;map&lt;/a&gt;, &lt;a href=&quot;https://rubydoc.info/stdlib/core/Enumerable#select-instance_method&quot;&gt;select&lt;/a&gt;,
&lt;a href=&quot;https://rubydoc.info/stdlib/core/Enumerable#group_by-instance_method&quot;&gt;group_by&lt;/a&gt;, &lt;a href=&quot;https://rubydoc.info/stdlib/core/Enumerable#max_by-instance_method&quot;&gt;group_by&lt;/a&gt;, which allow you
to slice and dice large datasets.&lt;/p&gt;

&lt;p&gt;First, we will need to liberate the data from the Jupyter Notebook. To do this,
we open the Jupyter Notebook, navigate to the Table View, select all rows,
copy the rows, and paste into a text file.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PacketNumber	Timestamp	Source	Destination	Protocol
1	05:49.5	10.10.1.7	10.10.1.9	HTTP
2	05:50.3	10.10.1.10	10.10.1.3	TCP
3	06:10.3	10.10.1.1	10.10.1.2	HTTP
4	06:10.4	10.10.1.9	10.10.1.3	ICMP
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The rows will paste as Tab Separated Values (TSV). We will need to convert the
rows into Comma Separated Values (CSV). Converting from TSV to CSV is as simple 
as the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; substitution command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%s/\v\t/,/g&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-csv&quot;&gt;PacketNumber,Timestamp,Source,Destination,Protocol
1,05:49.5,10.10.1.7,10.10.1.9,HTTP
2,05:50.3,10.10.1.10,10.10.1.3,TCP
3,06:10.3,10.10.1.1,10.10.1.2,HTTP
4,06:10.4,10.10.1.9,10.10.1.3,ICMP
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Much better. Finally, we save the file to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data.csv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we will spawn an Interactive Ruby session using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irb&lt;/code&gt; with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;csv&lt;/code&gt;
library preloaded:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;irb &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; csv
irb&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;main&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:001&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we will load our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data.csv&lt;/code&gt; file into a variable:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;data.csv&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;headers: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we just have to answer the Day 2 questions using pure Ruby.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How many packets were captured (looking at the PacketNumber)?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PacketNumber&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;What IP address sent the most amount of traffic during the packet capture?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Source&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;What was the most frequent protocol?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Source&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, you don’t necessarily have to use Python for Data Science.
Ruby is more than capable of doing basic Data Science.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>How to use ActiveRecord in a library</title>
    <link href="http://postmodern.github.io/blog/2022/03/28/howto-use-activerecord-in-a-library.html" />
    <updated>2022-03-28T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2022/03/28/howto-use-activerecord-in-a-library.html</id>
    <content type="html">&lt;p&gt;Recently, I wanted to use &lt;a href=&quot;https://guides.rubyonrails.org/active_record_basics.html&quot;&gt;ActiveRecord&lt;/a&gt; in a library, &lt;em&gt;not&lt;/em&gt; in a Rails app or
a Rails engine. After doing a bit of Googling, I found this excellent
&lt;a href=&quot;https://www.devdungeon.com/content/ruby-activerecord-without-rails-tutorial&quot;&gt;tutorial&lt;/a&gt;. While it covered the basics, such as connecting to the database,
defining a model and a migration, it missed a few things. In this blog post I
will provide a more detailed example of how to connect to a default or
arbitrary database, “connect” multiple models to the database, and run a
directory of migrations if they have not already been ran.&lt;/p&gt;

&lt;h2 id=&quot;connecting-to-the-database&quot;&gt;Connecting to the Database&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;active_record&apos;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;adapter:  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sqlite3&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;database: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;database.sqlite3&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to connect to a separate database for tests, you can use an
in-memory sqlite3 database:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;adapter:  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sqlite3&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;database: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;:memory:&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;migrations&quot;&gt;Migrations&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/migrations/0001_create_authors_table.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateAuthorsTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;7.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:library_authors&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/migrations/0002_create_books_table.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateBooksTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;7.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:library_books&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/migrations/0003_create_book_authors_table.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateBookAuthorsTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;7.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:library_book_authors&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;references&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;ss&quot;&gt;foreign_key: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                              &lt;span class=&quot;ss&quot;&gt;to_table: :library_authors&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;references&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;foreign_key: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;ss&quot;&gt;to_table: :library_books&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:author_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:book_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; due to how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt; loads migration classes they must &lt;em&gt;not&lt;/em&gt; be
defined in a module namespace and must &lt;em&gt;not&lt;/em&gt; contain any all-uppercase acronym
words (ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateIpAddressesTable&lt;/code&gt; not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateIPAddressesTable&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id=&quot;running-migrations&quot;&gt;Running Migrations&lt;/h2&gt;

&lt;p&gt;ActiveRecord provides a &lt;a href=&quot;https://rubydoc.info/gems/activerecord/ActiveRecord/MigrationContext&quot;&gt;ActiveRecord::MigrationContext&lt;/a&gt; class
which can load and run migrations similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake db:migrate&lt;/code&gt;. This class can
be initialized with our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrations/&lt;/code&gt; directory path, which will load all
migrations within the directory and allow us to run the migrations.&lt;/p&gt;

&lt;p&gt;The next step is to define our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Migrations&lt;/code&gt; module which defines
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate_up&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;migrate_down&lt;/code&gt; methods which call the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.migrate&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.up&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.down&lt;/code&gt; methods on the initialized
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::MigrationContext&lt;/code&gt; object:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/migrations.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;active_record&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Library&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Migrations&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__dir__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;migrations&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;context&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MigrationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;models&quot;&gt;Models&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/author.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Library&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_name_prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;library_&apos;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;uniqueness: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:authorships&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;class_name: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;BookAuthor&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:books&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;through:    :authorships&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;inverse_of: :authors&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.table_name_prefix&lt;/code&gt; is required since ActiveRecord
is unable to infer the table prefix based on the module namespace.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/book_author.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Library&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BookAuthor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_name_prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;library_&apos;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;required: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;required: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; while the Internet says that all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;belongs_to&lt;/code&gt; associations are
required by default since Rails 5, when using ActiveRecord &lt;em&gt;outside&lt;/em&gt; of Rails
one must explicitly add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;required: true&lt;/code&gt; to them.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/book.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Library&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_name_prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;library_&apos;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;presence: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;ss&quot;&gt;uniqueness: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:book_authors&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:authors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;through:    :book_authors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;ss&quot;&gt;inverse_of: :books&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make it easy to load all models after establishing the database connection
(ActiveRecord does not allow you to define any model classes before calling
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.establish_connection&lt;/code&gt;), we also create a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models.rb&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library/models.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;author&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;book&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;book_author&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Library&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;MODELS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;BookAuthor&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;activerecordbaseconnection&quot;&gt;ActiveRecord::Base.connection&lt;/h3&gt;

&lt;p&gt;In order for your model’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inspect&lt;/code&gt; methods to work properly, the ActiveRecord
models need to be “connected” to the established database connection. This can
be done by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection&lt;/code&gt; on each model class.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Library&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;Library::Book (call &apos;Library::Book.connection&apos; to establish a connection)&quot;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Library&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Library&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;Library::Book(id: integer, title: string)&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; for some reason it appears that this extra step is not necessary if
the migrations are ran before loading the models.&lt;/p&gt;

&lt;p&gt;I am not really sure why this extra step is necessary, as it seems like
ActiveRecord could lazy-connect each model when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base#inspect&lt;/code&gt; is
called.&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting It All Together&lt;/h2&gt;

&lt;p&gt;Now we just need an entry-point method that can:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Connect to the database&lt;/li&gt;
  &lt;li&gt;Run migrations (if there are any pending migrations)&lt;/li&gt;
  &lt;li&gt;“connect” the models&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We shall call this entry-point method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Library.connect&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/library.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Library&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;DEFAULT_DATABASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;adapter:  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sqlite3&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;database: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/path/to/default/database.sqlite3&apos;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DEFAULT_DATABASE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;library/migrations&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Migrations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;migrate_up&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;library/models&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;MODELS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are interested, I have pushed the working code for this blog post to
&lt;a href=&quot;https://github.com/postmodern/example-activerecord-lib#readme&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Enumerating XKCD-style passwords with Ruby</title>
    <link href="http://postmodern.github.io/blog/2022/01/23/enumerating-xkcd-style-passwords-with-ruby.html" />
    <updated>2022-01-23T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2022/01/23/enumerating-xkcd-style-passwords-with-ruby.html</id>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;https://xkcd.com/936/&quot;&gt;XKCD comic #936&lt;/a&gt; it explains how to measure password
strength and how to come up with a more “secure” password. In this blog post I
will show you how to enumerate such XKCD-style passwords using &lt;a href=&quot;https://www.ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To do this we will use two libraries: &lt;a href=&quot;https://github.com/postmodern/wordlist.rb#readme&quot;&gt;wordlist&lt;/a&gt; and &lt;a href=&quot;https://github.com/postmodern/chars.rb#readme&quot;&gt;chars&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;wordlist chars
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/postmodern/wordlist.rb#readme&quot;&gt;wordlist&lt;/a&gt; library allows one to open/build wordlists, enumerate them,
combine multiple wordlists together, and even mutate each word in the wordlist.
The &lt;a href=&quot;https://github.com/postmodern/chars.rb#readme&quot;&gt;chars&lt;/a&gt; library allows working with common character sets, such as digits,
alpha-numeric, punctuation, etc.&lt;/p&gt;

&lt;h2 id=&quot;tr0ub4dor&quot;&gt;Tr0ub4dor&lt;/h2&gt;

&lt;p&gt;The first XKCD password (“Tr0ub4dor&amp;amp;3”) seems pretty difficult to guess, but
it’s actually quite easy to enumerate. The original word is “troubador”, with
some character/case substitutions, and one punctuation character along with one
number appended to the end. Chaining together
&lt;a href=&quot;https://github.com/postmodern/wordlist.rb#string-manipulation&quot;&gt;wordlist’s string manipulation methods&lt;/a&gt;
we can enumerate over every possible mutation of “troubador”.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wordlist&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;chars&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;base_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Wordlist&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;troubador&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;passwords&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;punctuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chars&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mutations&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/oa/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;o&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;4&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mutate_case&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;mutations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above code creates a list of literal words, then combines them with all
symbol characters and all digit characters, and finally it applies mutation
rules to mutate the case of letters and substitutes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;o&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s enumerate through all possible passwords. Let ‘er rip!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;troubador 0
Troubador 0
tRoubador 0
trOubador 0
troUbador 0
trouBador 0
troubAdor 0
troubaDor 0
troubadOr 0
troubadoR 0
TRoubador 0
TrOubador 0
TroUbador 0
TrouBador 0
TroubAdor 0
TroubaDor 0
TroubadOr 0
TroubadoR 0
tROubador 0
tRoUbador 0
...
TrOuBADOR!9
TroUBADOR!9
tROUBADOr!9
tROUBADoR!9
tROUBAdOR!9
tROUBaDOR!9
tROUbADOR!9
tROuBADOR!9
tRoUBADOR!9
trOUBADOR!9
TROUBADOr!9
TROUBADoR!9
TROUBAdOR!9
TROUBaDOR!9
TROUbADOR!9
TROuBADOR!9
TRoUBADOR!9
TrOUBADOR!9
tROUBADOR!9
TROUBADOR!9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;correcthorsebatterystapler&quot;&gt;correcthorsebatterystapler&lt;/h2&gt;

&lt;p&gt;The second XKCD password (“correcthorsebatterystapler”) is four random words
concatenated together. Assuming we have a wordlist of common English words,
all we have to do is combine that wordlist with itself four times, then call
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.each&lt;/code&gt; like in the previous example.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wordlist&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;common_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Wordlist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;common_words.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;passwords&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;common_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;passwords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above code opens a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.txt&lt;/code&gt; wordlist file, then creates a new wordlist object
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;passwords&lt;/code&gt;) that is simply the wordlist multiplied by itself four times;
in Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;**&lt;/code&gt; is the power operator.&lt;/p&gt;

&lt;p&gt;Let’s see if it works. Let ‘er rip!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aahedaahedaahedaahed
aahedaahedaahedaahing
aahedaahedaahedaalii
aahedaahedaahedaaliis
aahedaahedaahedaardvark
aahedaahedaahedaardvarks
aahedaahedaahedaardwolf
aahedaahedaahedaardwolves
aahedaahedaahedaargh
aahedaahedaahedaaron
aahedaahedaahedaaronic
aahedaahedaahedaaronical
aahedaahedaahedaaronite
aahedaahedaahedaaronitic
aahedaahedaahedaarrgh
aahedaahedaahedaarrghh
aahedaahedaahedaasvogel
aahedaahedaahedaasvogels
aahedaahedaahedababdeh
aahedaahedaahedababua
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While it definitely took much longer to enumerate over every four common English
words, it is still possible to easily enumerate over every possible password.&lt;/p&gt;

&lt;h2 id=&quot;bonus-challenge&quot;&gt;Bonus Challenge&lt;/h2&gt;

&lt;p&gt;What if we used different words for the first and second word, added random
punctuation in between the words, and appended multiple digits?&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wordlist&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;chars&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;wordlist1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Wordlist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;animals.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;wordlist2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Wordlist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;months.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;passwords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordlist1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;punctuation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wordlist2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strings_of_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;passwords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above code opens two wordlists for the first and second words, combines the
first wordlist with all punctuation characters, combines the second wordlist,
then combines all possible strings of lengths 1-4 of digit characters.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dog january0
dog january1
dog january2
dog january3
dog january4
dog january5
dog january6
dog january7
dog january8
dog january9
dog january00
dog january01
dog january02
dog january03
dog january04
dog january05
dog january06
dog january07
dog january08
dog january09
...
pony!december9980
pony!december9981
pony!december9982
pony!december9983
pony!december9984
pony!december9985
pony!december9986
pony!december9987
pony!december9988
pony!december9989
pony!december9990
pony!december9991
pony!december9992
pony!december9993
pony!december9994
pony!december9995
pony!december9996
pony!december9997
pony!december9998
pony!december9999
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-math&quot;&gt;The Math&lt;/h2&gt;

&lt;p&gt;Using &lt;a href=&quot;https://en.wikipedia.org/wiki/Combinatorics&quot;&gt;Combinatorics&lt;/a&gt;, if we wanted to calculate the total search space of a
password we would use the simple equation &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n ** k&lt;/code&gt;, where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; is the number of
possibilities and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k&lt;/code&gt; is the number of times they are repeated in succession.
Example: if a 32bit unsigned integer has 32 bits, and each bit has 2
possibilities (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;), then the total number of possible 32bit unsigned
integers is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2 ** 32&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Assuming a password of length 26 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;correcthorsebatterystapler&quot;.length # =&amp;gt; 26&lt;/code&gt;)
composed entirely of printable ASCII characters, and there are 100 printable
ASCII characters (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chars.printable.length # =&amp;gt; 100&lt;/code&gt;), that would give us a
search space of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;100 ** 26&lt;/code&gt; which is
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10000000000000000000000000000000000000000000000000000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now what if we were a cunning hacker (or pentester), and guessed that the
users password was probably four random English words? Then we would grab our
trusty common English words wordlist, which happens to be 171,000 words long.
This results in a search space of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;171_000 ** 4&lt;/code&gt;, which is
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;855036081000000000000&lt;/code&gt;; much smaller than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10000000000000000000000000000000000000000000000000000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The XKCD web comic made the assumption that a hacker (or pentester)
would only resort to enumerating through every possible bit in a password
string. Enumerating through every combination of bits of a 26 character long
password would result in a search space of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2 ** (26 * 8)&lt;/code&gt;
(1 char = 1 byte = 8 bits) or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;411376139330301510538742295639337626245683966408394965837152256&lt;/code&gt;. This
assumption is wrong as I have shown that one can use wordlists to reduce the
search space to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;855036081000000000000&lt;/code&gt; which is visibly less than both
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;411376139330301510538742295639337626245683966408394965837152256&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10000000000000000000000000000000000000000000000000000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The smaller search space is the obvious winner, as it means &lt;strong&gt;less work to
do&lt;/strong&gt; and ultimately &lt;strong&gt;less time spent&lt;/strong&gt;. Factor in the ever increasing speed of
technology, the fact that you can distribute password bruteforcing across
multiple IPs “in the cloud”, or how &lt;a href=&quot;https://hashcat.net/hashcat/&quot;&gt;HashCat&lt;/a&gt; supports GPU accelerated password
cracking using &lt;a href=&quot;https://www.khronos.org/opencl/&quot;&gt;OpenCL&lt;/a&gt; (not to mention &lt;a href=&quot;https://hackaday.com/2020/05/15/all-your-passwords-are-belong-to-fpga/&quot;&gt;FPGA&lt;/a&gt;s and &lt;a href=&quot;https://en.wikipedia.org/wiki/Application-specific_integrated_circuit&quot;&gt;ASIC&lt;/a&gt;s), and that further
reduces the time it would take to bruteforce or crack a password using
wordlists and common password patterns.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;As you can see, we can easily enumerate complex password patterns using the
&lt;a href=&quot;https://github.com/postmodern/wordlist.rb#readme&quot;&gt;wordlist&lt;/a&gt; and &lt;a href=&quot;https://github.com/postmodern/chars.rb#readme&quot;&gt;chars&lt;/a&gt; libraries, and it didn’t take that much code! This can
be incredibly powerful when combined with a login bruteforcing or password
cracker. By combining wordlists and character sets, instead of bruteforce
enumerating over every ASCII character, we can reduce the size
of the search space and thus reduce the time it takes to bruteforce/crack a
password; fewer passwords to check means less time spent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; Don’t get your security advice from a web comic. Use a password
manager that generates truly random passwords and turn on 2FA.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Introducing command_mapper</title>
    <link href="http://postmodern.github.io/blog/2021/11/25/introducing-command-mapper.html" />
    <updated>2021-11-25T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2021/11/25/introducing-command-mapper.html</id>
    <content type="html">&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Normally in Ruby if you need to write a method which accepts one or more
arguments and executes a command, you would write something like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;git_pull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;git pull origin &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, there are a few problems with the above code:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Does not validate the input
(ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull(nil)&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull(&quot;&quot;)&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull(true)&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull(false)&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull([1,2,3])&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull({1=&amp;gt;2})&lt;/code&gt;, etc).&lt;/li&gt;
  &lt;li&gt;Vulnerable to arbitrary &lt;a href=&quot;https://owasp.org/www-community/attacks/Command_Injection&quot;&gt;command injection&lt;/a&gt;
(ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull(&quot;;evil_command_here#&quot;)&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Vulnerable to arbitrary &lt;a href=&quot;https://staaldraad.github.io/post/2019-11-24-argument-injection/&quot;&gt;option injection&lt;/a&gt;
(ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull(&quot;--option-that-gives-an-attacker-control branch&quot;)&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better version of the above code might look like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;git_pull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[git pull origin]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Better. We have fixed the arbitrary command injection by passing multiple
arguments to &lt;a href=&quot;https://rubydoc.info/stdlib/core/Kernel#system-instance_method&quot;&gt;Kernel#system&lt;/a&gt;, which executes the command as its own sub-process,
not in a sub-shell. We also added very basic validations for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;branch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, those basic validations are not enough and the above code is still
vulnerable to &lt;a href=&quot;https://staaldraad.github.io/post/2019-11-24-argument-injection/&quot;&gt;option injection&lt;/a&gt; via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;branch&lt;/code&gt; or any additional argument that is
appended to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;args&lt;/code&gt;. While the above code might be suitable for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt;
where we only call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull&lt;/code&gt; with explicit literal values, if we were to pass
user input to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git_pull&lt;/code&gt;, possibly from say a web app, we would need stronger
input validations. It would take a lot of work to add support for all of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;’s other options and arguments, and add validations for each of them.&lt;/p&gt;

&lt;h2 id=&quot;enter-command_mapper&quot;&gt;Enter command_mapper&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper&quot;&gt;command_mapper&lt;/a&gt; is a new library for mapping external commands to Ruby classes.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;command_mapper/command&apos;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Represents the `grep` command&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Grep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CommandMapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Command&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;grep&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--extended-regexp&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--fixed-strings&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--basic-regexp&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--perl-regexp&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--regexp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: :patterns_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--ignore-case&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--no-ignore-case&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--word-regexp&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--line-regexp&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--null-data&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--no-messages&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--invert-match&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--version&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--help&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--max-count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--byte-offset&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--line-number&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--line-buffered&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--with-filename&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--no-filename&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--label&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--only-matching&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--quiet&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--binary-files&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--text&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-I&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: 	&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# FIXME: name&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--directories&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--devices&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--recursive&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--dereference-recursive&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--include&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--exclude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--exclude-from&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--exclude-dir&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--files-without-match&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--files-with-matches&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--count&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--initial-tab&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--null&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--before-context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--after-context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--group-separator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--no-group-separator&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--color&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: :optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;required: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--colour&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;equals: :optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;required: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--binary&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;argument&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:patterns&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;argument&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;required: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;repeats: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;type-system&quot;&gt;Type System&lt;/h2&gt;

&lt;p&gt;An observant reader will notice &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type: Num.new&lt;/code&gt; in the above example code.
All option values and arguments may have a type. All options and arguments
default to the &lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Str&quot;&gt;Str&lt;/a&gt; type. These types define their
own validation and formatting rules. The available types are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Str&quot;&gt;Str&lt;/a&gt;: string values&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Num&quot;&gt;Num&lt;/a&gt;: numeric values&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Hex&quot;&gt;Hex&lt;/a&gt;: hexadecimal values&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Map&quot;&gt;Map&lt;/a&gt;: maps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;no&lt;/code&gt;, or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enabled&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disabled&lt;/code&gt; (aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt=yes|no&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt=enabled|disabled&lt;/code&gt;
values).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Enum&quot;&gt;Enum&lt;/a&gt;: maps a finite set of Symbols to
a finite set of Strings (aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt={foo|bar|baz}&lt;/code&gt; values).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/List&quot;&gt;List&lt;/a&gt;: comma-separated list
(aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt VALUE,...&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/KeyValue&quot;&gt;KeyValue&lt;/a&gt;: maps a Hash or Array to key:value
Strings (aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt KEY:VALUE&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt KEY=VALUE&lt;/code&gt; values).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/KeyValueList&quot;&gt;KeyValueList&lt;/a&gt;: a key-value list
(aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt KEY:VALUE,...&lt;/code&gt; or  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--opt KEY=VALUE;...&lt;/code&gt; values).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/InputPath&quot;&gt;InputPath&lt;/a&gt;: a path to a pre-existing file or
directory&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/InputFile&quot;&gt;InputFile&lt;/a&gt;: a path to a pre-existing file&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/InputDir&quot;&gt;InputDir&lt;/a&gt;: a path to a pre-existing directory&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;custom-types&quot;&gt;Custom Types&lt;/h3&gt;

&lt;p&gt;Custom type classes can be defined by simply inheriting from
&lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Type&quot;&gt;CommandMapper::Types::Type&lt;/a&gt; then defining &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;validate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format&lt;/code&gt; instance
methods.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PortRange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CommandMapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Range&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;kind_of?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;port range can only contain Integers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;port range must be an Integer or a Range of Integers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Range&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then the custom type class can then be passed to any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type:&lt;/code&gt; keyword argument:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;required: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PortRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;running-commands&quot;&gt;Running Commands&lt;/h2&gt;

&lt;p&gt;Once a &lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Command&quot;&gt;CommandMapper::Command&lt;/a&gt; class has been defined, it can then map the
class’s attributes back to the command’s option flags, additional arguments,
or subcommands, and then safely executed via &lt;a href=&quot;https://rubydoc.info/stdlib/core/Kernel#system-instance_method&quot;&gt;system()&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ignore_case: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;file.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Commands can also be initialized with a block and executed:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ignore_case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;patterns&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;file&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;file.txt&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; that if a required argument does not have a value or if an invalid
value is given to an option or argument, a validation error will be raised:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;file.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:494:in `block in command_argv&apos;: argument patterns is required (CommandMapper::ArgumentRequired)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:490:in `each&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:490:in `command_argv&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:537:in `run_command&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:108:in `run&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Output from the command can also be captured (similar to &lt;kbd&gt;\`...\`&lt;/kbd&gt;):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ignore_case: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;file.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;...&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Output from the command can also be read via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IO.popen&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;popen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ignore_case: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;file.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; #&amp;lt;IO:...&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The command can also be ran under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; (see the &lt;a href=&quot;https://rubydoc.info/gems/command_mapper/CommandMapper/Sudo&quot;&gt;CommandMapper::Sudo&lt;/a&gt;
class):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/var/log/syslog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Password: &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, the command can even be safely embedded in another command string:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ignore_case: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;file.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; | less&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;security&quot;&gt;Security&lt;/h2&gt;

&lt;p&gt;In order to prevent arbitrary command injection, any special shell characters
in the command’s option or argument values will automatically be escaped using
&lt;a href=&quot;https://rubydoc.info/stdlib/shellwords/Shellwords&quot;&gt;Shellwords&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;;injected_command#&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;test.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;command_string&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;grep \\;injected_command\\# test.txt&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to prevent &lt;a href=&quot;https://staaldraad.github.io/post/2019-11-24-argument-injection/&quot;&gt;option injection&lt;/a&gt;, options will explicitly not
allow values that begin with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; character:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;--injected-option&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;test.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# /home/postmodern/code/command_mapper.rb/lib/command_mapper/option.rb:273:in `emit_option_flag_and_value&apos;: option label formatted value (&quot;--injected-option&quot;) cannot start with a &apos;-&apos; (CommandMapper::ValidationError)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/option.rb:164:in `argv&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:485:in `block in command_argv&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:483:in `each&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:483:in `command_argv&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:542:in `run_command&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 	from /home/postmodern/code/command_mapper.rb/lib/command_mapper/command.rb:108:in `run&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to prevent arbitrary &lt;a href=&quot;https://staaldraad.github.io/post/2019-11-24-argument-injection/&quot;&gt;option injection&lt;/a&gt; via additional arguments,
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--&lt;/code&gt; separator will be inserted between the options and the additional
arguments if any of the arguments starts with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; character. This will force
the CLI utility to stop parsing options after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--&lt;/code&gt; separator, and prevents
the CLI utility from parsing the additional arguments as option flags:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ignore_case: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;patterns: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;-foo&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;file: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;test.txt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;command_argv&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [&quot;grep&quot;, &quot;--ignore-case&quot;, &quot;--&quot;, &quot;-foo&quot;, &quot;test.txt&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;but-wait-theres-more&quot;&gt;But Wait, There’s More!&lt;/h2&gt;

&lt;p&gt;Now you might be thinking “gee that’s still a lot to type, must be tedious to
setup”, and you’d be right. That’s where &lt;a href=&quot;https://github.com/postmodern/command_mapper-gen.rb#readme&quot;&gt;command_mapper-gen&lt;/a&gt; comes in. The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command_mapper-gen&lt;/code&gt; CLI utility can parse a command’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; output and/or
man page, and automatically generate the above code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ command_mapper-gen grep
Failed to parse line in `grep --help`:

    -NUM                      same as --context=NUM

Failed to match sequence ((&apos;	&apos; / SPACES) OPTION &apos;,&apos;? ([ \\t]{1, } OPTION_SUMMARY)? !.) at line 1 char 5.

require &apos;command_mapper/command&apos;

#
# Represents the `grep` command
#
class Grep &amp;lt; CommandMapper::Command

  command &quot;grep&quot; do
    option &quot;--extended-regexp&quot;
    option &quot;--fixed-strings&quot;
    option &quot;--basic-regexp&quot;
    option &quot;--perl-regexp&quot;
    option &quot;--regexp&quot;, equals: true, value: true
    option &quot;--file&quot;, equals: true, value: true
    option &quot;--ignore-case&quot;
    option &quot;--no-ignore-case&quot;
    option &quot;--word-regexp&quot;
    option &quot;--line-regexp&quot;
    option &quot;--null-data&quot;
    option &quot;--no-messages&quot;
    option &quot;--invert-match&quot;
    option &quot;--version&quot;
    option &quot;--help&quot;
    option &quot;--max-count&quot;, equals: true, value: {type: Num.new}
    option &quot;--byte-offset&quot;
    option &quot;--line-number&quot;
    option &quot;--line-buffered&quot;
    option &quot;--with-filename&quot;
    option &quot;--no-filename&quot;
    option &quot;--label&quot;, equals: true, value: true
    option &quot;--only-matching&quot;
    option &quot;--quiet&quot;
    option &quot;--binary-files&quot;, equals: true, value: true
    option &quot;--text&quot;
    option &quot;-I&quot;, name: 	# FIXME: name
    option &quot;--directories&quot;, equals: true, value: true
    option &quot;--devices&quot;, equals: true, value: true
    option &quot;--recursive&quot;
    option &quot;--dereference-recursive&quot;
    option &quot;--include&quot;, equals: true, value: true
    option &quot;--exclude&quot;, equals: true, value: true
    option &quot;--exclude-from&quot;, equals: true, value: true
    option &quot;--exclude-dir&quot;, equals: true, value: true
    option &quot;--files-without-match&quot;, value: true
    option &quot;--files-with-matches&quot;
    option &quot;--count&quot;
    option &quot;--initial-tab&quot;
    option &quot;--null&quot;
    option &quot;--before-context&quot;, equals: true, value: {type: Num.new}
    option &quot;--after-context&quot;, equals: true, value: {type: Num.new}
    option &quot;--context&quot;, equals: true, value: {type: Num.new}
    option &quot;--group-separator&quot;, equals: true, value: true
    option &quot;--no-group-separator&quot;
    option &quot;--color&quot;, equals: :optional, value: {required: false}
    option &quot;--colour&quot;, equals: :optional, value: {required: false}
    option &quot;--binary&quot;

    argument :patterns
    argument :file, required: false, repeats: true
  end

end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; some keyword arguments are intentionally left blank with a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;# FIXME&lt;/code&gt; command because command_mapper-gen cannot infer the names of &lt;em&gt;every&lt;/em&gt;
option (ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-I&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id=&quot;importance-to-the-ruby-ecosystem&quot;&gt;Importance to the Ruby Ecosystem&lt;/h2&gt;

&lt;p&gt;Beyond providing a Ruby interface to external commands, and preventing
arbitrary command injection, &lt;a href=&quot;https://rubydoc.info/gems/command_mapper&quot;&gt;command_mapper&lt;/a&gt; and &lt;a href=&quot;https://github.com/postmodern/command_mapper-gen.rb#readme&quot;&gt;command_mapper-gen&lt;/a&gt; allows
Ruby to quickly interface with other CLI utilities written in other programming
language ecosystems that Ruby cannot bind to, such as &lt;a href=&quot;https://elixir-lang.org/&quot;&gt;Elixir&lt;/a&gt;, &lt;a href=&quot;https://go.dev/&quot;&gt;Go&lt;/a&gt;, &lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust&lt;/a&gt;,
&lt;a href=&quot;https://crystal-lang.org/&quot;&gt;Crystal&lt;/a&gt;, &lt;a href=&quot;https://nim-lang.org/&quot;&gt;Nim&lt;/a&gt;, or &lt;a href=&quot;https://ziglang.org/&quot;&gt;Zig&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using &lt;a href=&quot;https://rubydoc.info/gems/command_mapper&quot;&gt;command_mapper&lt;/a&gt; we can automate other CLI utilities, written in other
languages, parse their output or output files, all seamlessly from Ruby as if
you were calling another Ruby library.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>RubyGems Tasks</title>
    <link href="http://postmodern.github.io/blog/2012/05/22/rubygems-tasks.html" />
    <updated>2012-05-22T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2012/05/22/rubygems-tasks.html</id>
    <content type="html">&lt;div class=&quot;warning&quot;&gt;
  &lt;h3&gt;Warning: Controversial Content&lt;/h3&gt;

  &lt;p&gt;
  If you are morally or ethically opposed to building/releasing Ruby Gems
  with &lt;code&gt;rake&lt;/code&gt;, this blog post may anger you. If this is the case,
  you are advised to STOP READING and CLICK THE BACK BUTTON.
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Ever since we could release Ruby Gems, we have had Gem helpers that
could generate new projects and provided Rake tasks to automate the
building/publishing of Gems. The most popular of these Gem helpers were &lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt;
and &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt;. Both &lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt; and &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt; required a certain project layout and
imposed a certain workflow onto the developer.&lt;/p&gt;

&lt;p&gt;Sometime before the &lt;a href=&quot;http://yehudakatz.com/2010/09/30/bundler-as-simple-as-what-you-did-before/&quot;&gt;introduction&lt;/a&gt; of &lt;a href=&quot;http://gembundler.com&quot;&gt;Bundler&lt;/a&gt;, Yehuda Katz &lt;a href=&quot;http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/&quot;&gt;proposed&lt;/a&gt;
a radically simpler way of building Gems, using the &lt;a href=&quot;http://docs.rubygems.org/read/chapter/20&quot;&gt;gemspec&lt;/a&gt; file.
Developers could use the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem build&lt;/code&gt; command to build a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gem&lt;/code&gt; file
from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt;, and use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem push&lt;/code&gt; command to publish the Gem to
&lt;a href=&quot;https://rubygems.org/&quot;&gt;rubygems.org&lt;/a&gt;. This marked the start of an exodus of sorts,
away from using Gem helpers such as &lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt; and &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;all-you-need-is-a-gemspec&quot;&gt;All you need is a Gemspec?&lt;/h2&gt;

&lt;p&gt;With the advent of building gems from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt;, a vocal minority formed
within the Ruby community. They proclaimed that all one needs is a gemspec,
and that all other tools (&lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt;, &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt;, &lt;a href=&quot;http://gembundler.com&quot;&gt;Bundler&lt;/a&gt; and even &lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt;)
are now obsolete!&lt;/p&gt;

&lt;p&gt;Let us take a look at how they might release a Gem:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git status
$ git push
$ gem build my_project.gemspec
$ gem push my_project-1.2.3.gem
$ git tag 1.2.3
$ git push --tags
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It takes roughly &lt;strong&gt;six&lt;/strong&gt; commands to release a Gem to &lt;a href=&quot;https://rubygems.org/&quot;&gt;rubygems.org&lt;/a&gt;.
A developer must remember to run each of these six commands, in order,
every time they release a new version of their Gem. The possibility for human
error increases.&lt;/p&gt;

&lt;p&gt;In fact, I have found Gems that contained newer source-code than their Git
repository; because the developer forgot to commit or push the changes
before pushing the Gem. I have also found Git repositories with no tags,
making it difficult to review what exactly changed between versions.&lt;/p&gt;

&lt;p&gt;Now, let us see how developers release gems using &lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt; tasks:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ rake release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! That one command will run each of the above &lt;strong&gt;six&lt;/strong&gt; commands.
If any of the commands fail, the release process will halt.
If there are uncommitted changes, the release process will halt.
The possibility for human error has been greatly minimized.&lt;/p&gt;

&lt;h2 id=&quot;alternatives&quot;&gt;Alternatives&lt;/h2&gt;

&lt;p&gt;Having used &lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt;, &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt; and then &lt;a href=&quot;http://gembundler.com&quot;&gt;Bundler&lt;/a&gt;, I missed some of the
workflow provided by these Gem helpers. So I began searching for lightweight
alternatives.&lt;/p&gt;

&lt;p&gt;I looked at &lt;a href=&quot;http://rubygems.rubyforge.org/rubygems-update/Gem/PackageTask.html&quot;&gt;Gem::PackageTask&lt;/a&gt;, &lt;a href=&quot;https://github.com/sr/mg#readme&quot;&gt;MG&lt;/a&gt;, &lt;a href=&quot;https://github.com/svenfuchs/gem-release#readme&quot;&gt;gem release&lt;/a&gt; and even
&lt;a href=&quot;https://github.com/bundler/bundler/blob/master/lib/bundler/gem_helper.rb&quot;&gt;bundler/gem_helper&lt;/a&gt;. Unfortunately they all were missing various features.
&lt;a href=&quot;http://rubygems.rubyforge.org/rubygems-update/Gem/PackageTask.html&quot;&gt;Gem::PackageTask&lt;/a&gt; only built the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gem&lt;/code&gt; file, but did not check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;,
tag the release or push the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gem&lt;/code&gt;.
&lt;a href=&quot;https://github.com/sr/mg#readme&quot;&gt;MG&lt;/a&gt; also did not check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; before releasing,
did not tag releases, and only supports Git. &lt;a href=&quot;https://github.com/svenfuchs/gem-release#readme&quot;&gt;gem release&lt;/a&gt; came close,
but did not check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; before releasing, deleted the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gem&lt;/code&gt; file,
only supports Git and tried to do too much
(auto-magically bumping the version of your project for you).
&lt;a href=&quot;https://github.com/bundler/bundler/blob/master/lib/bundler/gem_helper.rb&quot;&gt;bundler/gem_helper&lt;/a&gt; came the closest, although while it defined &lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt; tasks
it did not leverage Rake’s powerful &lt;a href=&quot;http://jasonseifer.com/2010/04/06/rake-tutorial&quot;&gt;task =&amp;gt; dependency&lt;/a&gt; system.&lt;/p&gt;

&lt;p&gt;So I decided to cherry-pick all of the nice features from &lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt;, &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt;,
&lt;a href=&quot;https://github.com/sr/mg#readme&quot;&gt;MG&lt;/a&gt;, &lt;a href=&quot;https://github.com/svenfuchs/gem-release#readme&quot;&gt;gem release&lt;/a&gt; and &lt;a href=&quot;https://github.com/bundler/bundler/blob/master/lib/bundler/gem_helper.rb&quot;&gt;bundler/gem_helper&lt;/a&gt;, and leave out the
&lt;em&gt;opinionated&lt;/em&gt; features.&lt;/p&gt;

&lt;h2 id=&quot;rubygems-tasks&quot;&gt;rubygems-tasks&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/postmodern/rubygems-tasks#readme&quot;&gt;rubygems-tasks&lt;/a&gt; provides agnostic and unobtrusive Rake tasks for building,
installing and releasing Ruby Gems.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install rubygems-tasks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adding rubygems-tasks to an existing project is easy as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;rubygems/tasks&apos;
Gem::Tasks.new

$ rake -T
rake build    # Builds all packages
rake console  # Spawns an Interactive Ruby Console
rake install  # Installs all built gem packages
rake release  # Performs a release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Provides tasks to build, install and push gems to &lt;a href=&quot;https://rubygems.org/&quot;&gt;rubygems.org&lt;/a&gt;:
    &lt;ul&gt;
      &lt;li&gt;Loads all project metadata from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file.&lt;/li&gt;
      &lt;li&gt;Supports loading multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; files.&lt;/li&gt;
      &lt;li&gt;Supports pushing gems to alternate &lt;a href=&quot;https://github.com/rubygems/rubygems.org#readme&quot;&gt;Gemcutter&lt;/a&gt; servers.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Supports optionally building &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tar.gz&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zip&lt;/code&gt; archives.&lt;/li&gt;
  &lt;li&gt;Supports &lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt;, &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; and &lt;a href=&quot;http://subversion.tigris.org/&quot;&gt;SubVersion&lt;/a&gt; SCMs.
    &lt;ul&gt;
      &lt;li&gt;Supports creating &lt;a href=&quot;http://en.wikipedia.org/wiki/Pretty_Good_Privacy&quot;&gt;PGP&lt;/a&gt; signed Git/Mercurial tags.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Supports generating checksums of built packages:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new(:sign =&amp;gt; {:checksum =&amp;gt; true})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Supports generating &lt;a href=&quot;http://en.wikipedia.org/wiki/Pretty_Good_Privacy&quot;&gt;PGP&lt;/a&gt; signatures for built packages:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new(:sign =&amp;gt; {:pgp =&amp;gt; true})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Provides a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console&lt;/code&gt; task, for jumping right into your code.&lt;/li&gt;
  &lt;li&gt;Defines task aliases for users coming from &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt; or &lt;a href=&quot;http://www.zenspider.com/projects/hoe.html&quot;&gt;Hoe&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;ANSI coloured messages!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;anti-features&quot;&gt;Anti-Features&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Does not&lt;/strong&gt; parse project metadata from the README or the ChangeLog.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Does not&lt;/strong&gt; generate or modify your code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Does not&lt;/strong&gt; automatically commit changes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Does not&lt;/strong&gt; inject dependencies into gems.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Zero&lt;/strong&gt; dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;/h3&gt;

&lt;p&gt;Specifying an alternate Ruby Console to run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new do |tasks|
  tasks.console.command = &apos;pry&apos;
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enable pushing gems to an in-house &lt;a href=&quot;https://github.com/rubygems/rubygems.org#readme&quot;&gt;Gemcutter&lt;/a&gt; server:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new do |tasks|
  tasks.push.host = &apos;gems.company.com&apos;
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Disable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; task:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new(:push =&amp;gt; false)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enable building &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tar.gz&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zip&lt;/code&gt; archives:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new(:build =&amp;gt; {:tar =&amp;gt; true, :zip =&amp;gt; true})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enable Checksums and &lt;a href=&quot;http://en.wikipedia.org/wiki/Pretty_Good_Privacy&quot;&gt;PGP&lt;/a&gt; signatures for built packages:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Tasks.new(:sign =&amp;gt; {:checksum =&amp;gt; true, :pgp =&amp;gt; true})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Selectively defining tasks:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Gem::Build::Tar.new
Gem::SCM::Status.new
Gem::SCM::Tag.new(:format =&amp;gt; &apos;REL-%s&apos;)
Gem::Sign::Checksum.new
Gem::Console.new
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>You don't have to use Bundler to create new RubyGems</title>
    <link href="http://postmodern.github.io/blog/2012/05/20/you-dont-have-to-use-bundler-to-create-new-rubygems.html" />
    <updated>2012-05-20T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2012/05/20/you-dont-have-to-use-bundler-to-create-new-rubygems.html</id>
    <content type="html">&lt;div class=&quot;warning&quot;&gt;
  &lt;h3&gt;Warning: Controversial Content&lt;/h3&gt;

  &lt;p&gt;
  If you are morally or ethically opposed to using Project Generators
  and prefer to create RubyGems by hand,
  &lt;strong&gt;STOP READING AND CLICK THE BACK BUTTON&lt;/strong&gt;.
  &lt;/p&gt;

  &lt;p&gt;
  I am perfectly aware that one does not need any tools to create a RubyGem,
  and that all you really need is RubyGems and a &lt;kbd&gt;*.gemspec&lt;/kbd&gt;
  file. However, the majority of users do not have the time or patience
  to create each Ruby project from scratch. Thus developers have historically
  used project &lt;em&gt;generators&lt;/em&gt; such as Hoe, Jeweler and now Bundler.
  &lt;/p&gt;

  &lt;p&gt;
  If you are a diehard Bundler user, please read the &lt;em&gt;entire&lt;/em&gt; blog post.
  This blog post &lt;strong&gt;is not&lt;/strong&gt; putting Bundler down,
  nor is it posing a binary choice between Bundler and some new tool.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;enter-bundler&quot;&gt;Enter Bundler&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt; was initially created as a more robust way to &lt;a href=&quot;http://yehudakatz.com/2010/09/30/bundler-as-simple-as-what-you-did-before/&quot;&gt;manage dependencies
of Rails3 applications&lt;/a&gt;. Once Bundler was integrated
into the Rails3 generator templates, developers realized Bundler could also
be used to manage the dependencies of any large Ruby application or library.&lt;/p&gt;

&lt;p&gt;In order to help developers create projects with Bundler already setup,
&lt;a href=&quot;https://github.com/carlhuda/bundler/tree/master/lib/bundler/templates&quot;&gt;template files&lt;/a&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle gem&lt;/code&gt; command were added.
Since Bundler was the “new hotness” and developers were becoming increasingly
dissatisfied with &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler&lt;/a&gt;/&lt;a href=&quot;http://docs.seattlerb.org/hoe/&quot;&gt;Hoe&lt;/a&gt;, the community began to
&lt;a href=&quot;http://railscasts.com/episodes/245-new-gem-with-bundler&quot;&gt;cargo&lt;/a&gt; &lt;a href=&quot;https://github.com/radar/guides/blob/master/gem-development.md&quot;&gt;cult&lt;/a&gt; Bundler as the defacto way to create RubyGems.&lt;/p&gt;

&lt;h2 id=&quot;looking-back&quot;&gt;Looking Back&lt;/h2&gt;

&lt;p&gt;After having extensively used &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt; with &lt;a href=&quot;https://github.com/ronin-ruby/ronin/blob/master/Gemfile&quot;&gt;Ronin&lt;/a&gt;, to keep it’s many
&lt;a href=&quot;https://github.com/ronin-ruby/&quot;&gt;repositories&lt;/a&gt; in-sync with each other, I can definitely say Bundler
solved dependency management for large Ruby projects.
However, I began to question using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle gem&lt;/code&gt; to create new RubyGems.&lt;/p&gt;

&lt;p&gt;At first it made sense to provide a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle gem&lt;/code&gt; command, but project
generation is outside of the original scope of Bundler (dependency management).
Furthermore, adding Bundler to an existing project isn’t that difficult;
just add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Given that &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt;’s stated goal is to “manage an application’s dependencies”,
it doesn’t seem very pragmatic to use Bundler in libraries with
&lt;strong&gt;zero&lt;/strong&gt; or only &lt;strong&gt;one&lt;/strong&gt; runtime dependency.&lt;/p&gt;

&lt;p&gt;Bundler’s &lt;a href=&quot;https://github.com/carlhuda/bundler/tree/master/lib/bundler/templates&quot;&gt;template files&lt;/a&gt; are a bit spartan as well.
The &lt;a href=&quot;https://github.com/carlhuda/bundler/blob/master/lib/bundler/templates/newgem/Rakefile.tt&quot;&gt;Rakefile&lt;/a&gt; template does not include Rake tasks for &lt;a href=&quot;http://rdoc.rubyforge.org/&quot;&gt;RDoc&lt;/a&gt; or &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt;.
This omission might encourage developers to not write documentation or tests,
and rush to release.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle gem&lt;/code&gt; command isn’t very configurable either. If you want to generate
a project with &lt;a href=&quot;http://textile.sitemonks.com/&quot;&gt;Textile&lt;/a&gt; markup, &lt;a href=&quot;http://yardoc.org/&quot;&gt;YARD&lt;/a&gt; documentation, &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt; tests and
&lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt;, you are out of luck.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;span-18&quot; src=&quot;/images/2012/05/20/you-dont-have-to-use-bundler-to-create-new-rubygems/morpheus.jpg&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;enter-ore&quot;&gt;Enter Ore&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://github.com/ruby-ore/ore#readme&quot;&gt;Ore&lt;/a&gt; is a flexible Ruby project generator. Unlike other Ruby project
generators, Ore provides many built in templates and allows custom templates
to be installed from Git repositories.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install ore
$ mine my_project
Generating /home/postmodern/my_project
      create  lib
      create  lib/my_project
      create  spec
      create  .gitignore
      create  .rspec
      create  spec/my_project_spec.rb
      create  spec/spec_helper.rb
      create  .document
      create  my_project.gemspec
      create  ChangeLog.rdoc
      create  LICENSE.txt
      create  README.rdoc
      create  Rakefile
      create  lib/my_project/version.rb
      create  lib/my_project.rb
         run  git init from &quot;.&quot;
         run  git add . from &quot;.&quot;
         run  git commit -m &quot;Initial commit.&quot; from &quot;.&quot;
$ cd my_project/
$ rake -T
rake build         # Builds all packages
rake clobber_rdoc  # Remove RDoc HTML files
rake console       # Spawns an Interactive Ruby Console
rake install       # Installs all built gem packages
rake rdoc          # Build RDoc HTML files
rake release       # Performs a release
rake rerdoc        # Rebuild RDoc HTML files
rake spec          # Run RSpec code examples
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ore generates new Ruby projects with sensible defaults, such as
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file, &lt;a href=&quot;https://github.com/postmodern/rubygems-tasks#readme&quot;&gt;rubygems-tasks&lt;/a&gt;, &lt;a href=&quot;http://rdoc.rubyforge.org/&quot;&gt;RDoc&lt;/a&gt;, &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt;, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; file
and initializes the &lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;Ore also provides many different templates and options:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine --help
Usage:
  mine PATH

Options:
      [--gemspec-yml]               
      [--jeweler-tasks]             
      [--bundler]                   
      [--rubygems-tasks]            
                                    # Default: true
      [--yard]                      
      [--bundler-tasks]             
      [--hg]                        
      [--rspec]                     
                                    # Default: true
      [--rvmrc]                     
      [--gem-test]                  
      [--gem-package-task]          
      [--gemspec]                   
                                    # Default: true
      [--test-unit]                 
      [--git]                       
                                    # Default: true
      [--bin]                       
      [--rdoc]                      
                                    # Default: true
      [--markdown]                  
      [--textile]                   
  -T, [--templates=TEMPLATE [...]]  
  -n, [--name=NAME]                 
  -V, [--version=VERSION]           
                                    # Default: 0.1.0
  -s, [--summary=SUMMARY]           
                                    # Default: TODO: Summary
  -D, [--description=DESCRIPTION]   
                                    # Default: TODO: Description
  -a, [--authors=NAME [...]]        
                                    # Default: [&quot;postmodern&quot;]
  -e, [--email=EMAIL]               
  -U, [--homepage=HOMEPAGE]         
  -B, [--bug-tracker=BUG_TRACKER]   
  -L, [--license=LICENSE]           
                                    # Default: MIT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, Ore is completely configurable and supports:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; / &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; / &lt;a href=&quot;http://subversion.tigris.org/&quot;&gt;SubVersion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rvm.io/workflow/rvmrc/#project&quot;&gt;.rvmrc&lt;/a&gt; files&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/postmodern/rubygems-tasks#readme&quot;&gt;rubygems-tasks&lt;/a&gt; / &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler tasks&lt;/a&gt; / &lt;a href=&quot;https://github.com/technicalpickles/jeweler#readme&quot;&gt;Jeweler::Tasks&lt;/a&gt; /
&lt;a href=&quot;http://rubygems.rubyforge.org/rubygems-update/Gem/PackageTask.html&quot;&gt;Gem::PackageTask&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt; / &lt;a href=&quot;http://test-unit.rubyforge.org/&quot;&gt;Test::Unit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yardoc.org/&quot;&gt;YARD&lt;/a&gt; / &lt;a href=&quot;http://rdoc.rubyforge.org/&quot;&gt;RDoc&lt;/a&gt; documentation&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rdoc.rubyforge.org/&quot;&gt;RDoc&lt;/a&gt; / &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; / &lt;a href=&quot;http://textile.sitemonks.com/&quot;&gt;Textile&lt;/a&gt; markup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike other project generators, Ore focuses &lt;em&gt;only&lt;/em&gt; on project generation and
does not force a specific project layout or workflow upon the developer.
You can even generate projects with &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt;, &lt;a href=&quot;http://yardoc.org/&quot;&gt;YARD&lt;/a&gt; + &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; and
&lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; instead of &lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine my_project --bundler --yard --markdown --hg
Generating /home/postmodern/my_project
      create  lib
      create  lib/my_project
      create  spec
      create  .hgignore
      create  .document
      create  .yardopts
      create  Gemfile
      create  .rspec
      create  spec/my_project_spec.rb
      create  spec/spec_helper.rb
      create  my_project.gemspec
      create  ChangeLog.md
      create  LICENSE.txt
      create  README.md
      create  Rakefile
      create  lib/my_project/version.rb
      create  lib/my_project.rb
         run  hg init from &quot;.&quot;
         run  hg add . from &quot;.&quot;
         run  hg commit -m &quot;Initial commit.&quot; from &quot;.&quot;
$ cd my_project &amp;amp;&amp;amp; bundle install
$ rake spec
/home/postmodern/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec ./spec/my_project_spec.rb

MyProject
  should have a VERSION constant

Finished in 0.00593 seconds
1 example, 0 failures
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;think-outside-of-the-bundle&quot;&gt;Think Outside of the Bundle&lt;/h2&gt;

&lt;p&gt;Now that you have been introduced to &lt;a href=&quot;https://github.com/ruby-ore/ore#readme&quot;&gt;Ore&lt;/a&gt;, I hope you will at the very least
give it a try. I also hope you will understand that I am not
simply anti-Bundler / pro-Ore. Ore gives you the option of generating Ruby
projects with/without Bundler. The two tools are not mutually exclusive.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Reading RFID Cards on Linux (in the year 2012)</title>
    <link href="http://postmodern.github.io/blog/2012/04/04/reading-rfid-cards-on-linux.html" />
    <updated>2012-04-04T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2012/04/04/reading-rfid-cards-on-linux.html</id>
    <content type="html">&lt;p&gt;A while back &lt;a href=&quot;https://twitter.com/#!/tenderlove&quot;&gt;tenderlove&lt;/a&gt; &lt;a href=&quot;http://tenderlovemaking.com/2009/09/19/ruby-and-rfid-tags/&quot;&gt;blogged&lt;/a&gt; about using Ruby to interact with 
&lt;a href=&quot;http://en.wikipedia.org/wiki/Near_field_communication&quot;&gt;NFC&lt;/a&gt; &lt;a href=&quot;http://www.touchatag.com/&quot;&gt;tags&lt;/a&gt;. Near Field Communication (NFC) grew out of
the Radio Frequency Identification (&lt;a href=&quot;http://en.wikipedia.org/wiki/Radio-frequency_identification&quot;&gt;RFID&lt;/a&gt;) standard and has been used for
contact-less payment with SmartPhones. Although,
NFC tags are not yet widely deployed in the US, as opposed to Japan.
On the other hand, RFID &lt;em&gt;is&lt;/em&gt; widely deployed in the US, used in Payment systems,
Asset management, Inventory systems, Product tracking, ID cards,
Transportation tracking and even Passports. RFID is out there,
and in those tags may be interesting information.&lt;/p&gt;

&lt;p&gt;Not wanting to simply copy &lt;a href=&quot;https://twitter.com/#!/tenderlove&quot;&gt;tenderlove&lt;/a&gt; and purchase an NFC starter kit,
I went searching for a consumer grade RFID reader. I ended up purchasing
an &lt;a href=&quot;http://www.hidglobal.com/prod_detail.php?prod_id=274&quot;&gt;OmniKey 6321 USB&lt;/a&gt; reader/writer (ideal for mobile use) and
a couple blank &lt;a href=&quot;http://www.rfdump.org/&quot;&gt;Mifare Read/Write RFID cards&lt;/a&gt; (with 8K EEPROM!).
The next logical step is getting the reader working on Linux.&lt;/p&gt;

&lt;p&gt;Unless you have a serial RFID reader (or can setup a USB Serial device
with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;udev&lt;/code&gt;), the classic &lt;a href=&quot;http://www.rfdump.org/&quot;&gt;rfdump&lt;/a&gt; program is pretty much useless.
&lt;a href=&quot;http://openmrtd.org/projects/librfid/&quot;&gt;librfid&lt;/a&gt; does not appear to be maintained any longer.
Also, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;librfid-tool -S&lt;/code&gt; did not detect the OmniKey USB reader.
Google quickly found many tutorials for using &lt;a href=&quot;http://pcsclite.alioth.debian.org/&quot;&gt;pcsc-lite&lt;/a&gt; to interact with
SmartCard readers to read RFID tags.&lt;/p&gt;

&lt;h2 id=&quot;install-pcscd&quot;&gt;Install pcscd&lt;/h2&gt;

&lt;p&gt;On Debian:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# apt-get install pcscd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On Fedora:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# yum install pcsc-lite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;install-the-omnikey-6321-pcsc-driver&quot;&gt;Install the OmniKey 6321 PC/SC Driver&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Browser to &lt;a href=&quot;http://www.hidglobal.com/driverDownloads.php?techCat=19&amp;amp;prod_id=274&quot;&gt;www.hidglobal.com/driverDownloads.php?techCat=19&amp;amp;prod_id=274&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Select your Operating System. Choose “Linux” for 32bit or
“Linux x64” for 64bit.&lt;/li&gt;
  &lt;li&gt;Download the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifdokrfid_lnx&lt;/code&gt; tar archive file.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Extract the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifdokrfid_lnx&lt;/code&gt; tar archive:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ tar xzvf ifdokrfid_lnx*.tar.gz
$ cd ifdokrfid_lnx*/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Install the proprietary PC/SC Driver:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;start-pcscd&quot;&gt;Start pcscd&lt;/h2&gt;

&lt;p&gt;Plugin the OmniKey 6321 USB reader and start &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pcscd&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# pcscd -a -f -d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then you should see the following output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00000083 pcscdaemon.c:575:main() pcsc-lite 1.7.4 daemon ready.
00002003 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/002/001
00000196 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/002/001
00000189 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x047D, PID: 0x2043, path: /dev/bus/usb/002/002
00000174 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x047D, PID: 0x2043, path: /dev/bus/usb/002/002
00000176 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/002/001
00000180 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x046D, PID: 0xC069, path: /dev/bus/usb/002/003
00000207 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/003/001
00000184 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0001, path: /dev/bus/usb/003/001
00000190 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x076B, PID: 0x6321, path: /dev/bus/usb/003/002
00000081 hotplug_libudev.c:258:get_driver() Looking for a driver for VID: 0x076B, PID: 0x6321, path: /dev/bus/usb/003/002
00000056 hotplug_libudev.c:309:HPAddDevice() Adding USB device: OMNIKEY 6321
00000087 readerfactory.c:934:RFInitializeReader() Attempting startup of OMNIKEY 6321 (USB iClass Reader) 00 00 using /usr/lib64/pcsc/drivers/ifdokrfid_lnx_x64-2.10.0.1.bundle/Contents/Linux/ifdokrfid.so
00000423 readerfactory.c:824:RFBindFunctions() Loading IFD Handler 3.0
HID HID Global OMNIKEY RFID  X64 v2.10.0.1 
00304886 readerfactory.c:295:RFAddReader() Using the reader polling thread
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To test the reader, briefly place an RFID card near the reader then remove it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;99999999 eventhandler.c:372:EHStatusHandlerThread() powerState: POWER_STATE_POWERED
00000030 eventhandler.c:387:EHStatusHandlerThread() Card inserted into OMNIKEY 6321 (USB iClass Reader) 00 01
00000015 Card ATR: 3B 8F 80 01 80 4F 0C A0 00 00 03 06 03 00 03 00 00 00 00 68 
00462848 eventhandler.c:446:EHStatusHandlerThread() powerState: POWER_STATE_UNPOWERED
00305960 eventhandler.c:325:EHStatusHandlerThread() Card Removed From OMNIKEY 6321 (USB iClass Reader) 00 01
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>DIY Pagination with DataMapper</title>
    <link href="http://postmodern.github.io/blog/2012/04/04/diy-pagination-with-datamapper.html" />
    <updated>2012-04-04T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2012/04/04/diy-pagination-with-datamapper.html</id>
    <content type="html">&lt;p&gt;There are many pagination solutions out there. Of course there’s the venerable
&lt;a href=&quot;https://github.com/mislav/will_paginate#readme&quot;&gt;will_pagination&lt;/a&gt; and the much newer &lt;a href=&quot;https://github.com/amatsuda/kaminari#readme&quot;&gt;Kaminari&lt;/a&gt;. However, all of the
pagination solutions usually contain boiler-plate HTML. What if we only
want the pagination logic, maybe in a JSON API, without using Rails or
ActiveRecord, but instead &lt;a href=&quot;http://sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; and &lt;a href=&quot;http://datamapper.org/&quot;&gt;DataMapper&lt;/a&gt;. As it turns out,
DataMapper makes DIY pagination as simple as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@posts = Post[((page - 1) * per_page), per_page]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As a Sinatra helper method this would look like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def paginate(query)
  @page     = (params[:page] || 1).to_i
  @per_page = (params[:per_page] || 10).to_i

  query[((@page - 1) * @per_page), @per_page]
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, what if we want to know the total number of pages and records? Enter the
&lt;a href=&quot;https://github.com/postmodern/dm-chunked_query#readme&quot;&gt;dm-chunked_query&lt;/a&gt; gem, which provides convenience methods for querying
chunks of records:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;dm-chunked_query&apos;

def paginate(query)
  @page        = (params[:page] || 1).to_i
  @per_page    = (params[:per_page] || 10).to_i

  @pages       = query.chunks_of(per_page)
  @total_count = @pages.count
  @page_count  = @pages.length

  @pages[@page - 1]
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pagination is that simple.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Hexdump 0.2.x</title>
    <link href="http://postmodern.github.io/blog/2011/06/11/hexdump-0.2.html" />
    <updated>2011-06-11T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2011/06/11/hexdump-0.2.html</id>
    <content type="html">&lt;p&gt;Recently &lt;a href=&quot;http://github.com/mephux&quot;&gt;mephux&lt;/a&gt; began working on a
&lt;a href=&quot;http://github.com/mephux/hexdump.js#readme&quot;&gt;hexdump.js&lt;/a&gt; library
(&lt;a href=&quot;http://mephux.github.com/hexdump.js&quot;&gt;epic demo&lt;/a&gt;). Naturally, I thought
I should go back and improve my Ruby
&lt;a href=&quot;http://github.com/postmodern/hexdump#readme&quot;&gt;Hexdump&lt;/a&gt; library.&lt;/p&gt;

&lt;h2 id=&quot;hexdump-02x&quot;&gt;Hexdump 0.2.x&lt;/h2&gt;

&lt;p&gt;Hexdump now supports word-sizes and endianness. This is useful for dumping
packed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsigned int&lt;/code&gt;s:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hexdump.dump(&quot;\x12\x34\x00\x00\x42\x42&quot;, :word_size =&amp;gt; 2, :endian =&amp;gt; :big)
# 00000000  1234 0000 4242                           |ሴ䉂|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:word_size&lt;/code&gt; can have any value, and is not restricted to powers of 2.&lt;/p&gt;

&lt;p&gt;Now that Hexdump parses multi-byte words from data, displaying Unicode
characters was the next logical step:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hexdump.dump(&quot;\xb6\x80&quot; * 10, :word_size =&amp;gt; 2)
# 00000000  80b6 80b6 80b6 80b6 80b6 80b6 80b6 80b6  |肶肶肶肶肶肶肶肶|
# 00000010  80b6 80b6                                |肶肶|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, Hexdump received some performance tuning which cut benchmark times
in half on Ruby 1.9.2:&lt;/p&gt;

&lt;h3 id=&quot;before&quot;&gt;Before&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                                user     system      total        real
hexdump (block)             7.740000   0.030000   7.770000 (  8.138029)
hexdump                     9.590000   0.050000   9.640000 ( 10.178203)
hexdump width=256 (block)   7.280000   0.020000   7.300000 (  7.534507)
hexdump width=256           8.130000   0.030000   8.160000 (  8.342448)
hexdump ascii=true (block)  7.740000   0.030000   7.770000 (  7.958550)
hexdump ascii=true          9.550000   0.050000   9.600000 (  9.803758)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;after&quot;&gt;After&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                                 user     system      total        real
hexdump (block)              3.010000   0.010000   3.020000 (  3.529396)
hexdump                      5.430000   0.030000   5.460000 (  6.216174)
hexdump width=256 (block)    3.010000   0.020000   3.030000 (  3.308961)
hexdump width=256            4.700000   0.040000   4.740000 (  5.520189)
hexdump ascii=true (block)   3.050000   0.010000   3.060000 (  3.501436)
hexdump ascii=true           5.450000   0.040000   5.490000 (  6.352144)
hexdump word_size=2 (block)  7.420000   0.050000   7.470000 (  9.174734)
hexdump word_size=2          9.500000   0.070000   9.570000 ( 11.228204)
hexdump word_size=4 (block)  4.110000   0.030000   4.140000 (  4.849785)
hexdump word_size=4          5.380000   0.060000   5.440000 (  6.209022)
hexdump word_size=8 (block)  3.350000   0.070000   3.420000 (  4.147304)
hexdump word_size=8          4.430000   0.040000   4.470000 (  5.930758)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;a href=&quot;http://ruby-prof.rubyforge.org/&quot;&gt;ruby-prof&lt;/a&gt; and the
Rubinius Profiler (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rbx -Xprofile&lt;/code&gt;) I found that the majority of time spent
was in:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc#call&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#join&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Integer#chr&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String#%&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Optimizing out these excess method calls involved:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Frequently called lambdas were replaced with methods.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#join&lt;/code&gt; was replaced with incremental String concatenating code.&lt;/li&gt;
  &lt;li&gt;A lookup table of bytes and printable characters were added, to reduce
excess &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Integer#chr&lt;/code&gt; calls.&lt;/li&gt;
  &lt;li&gt;Interestingly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel#sprintf&lt;/code&gt; is slightly faster than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String#%&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;uses&quot;&gt;Uses&lt;/h2&gt;

&lt;p&gt;Both &lt;a href=&quot;http://github.com/mephux#readme&quot;&gt;hexdump.js&lt;/a&gt; and Ruby
&lt;a href=&quot;http://github.com/postmodern/hexdump#readme&quot;&gt;Hexdump&lt;/a&gt; aim to provide
similar features and behaviors. hexdump.js is definitely useful for when
you want to offload the work of Hexdumping to the Browser, or when writing
&lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt; Apps.&lt;/p&gt;

&lt;p&gt;The Ruby Hexdump library is more suited for Ruby CLI Apps, but can also be
used in Web Apps:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dump = []

Hexdump.dump(data) do |index,numeric,printable|
  dump &amp;lt;&amp;lt; [index, numeric, printable]
end

render :json =&amp;gt; dump
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Don't like WEBrick? Try net-http-server</title>
    <link href="http://postmodern.github.io/blog/2011/05/08/dont-like-webrick-try-net-http-server.html" />
    <updated>2011-05-08T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2011/05/08/dont-like-webrick-try-net-http-server.html</id>
    <content type="html">&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/postmodern/net-http-server&quot;&gt;net-http-server&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install net-http-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;pure-Ruby HTTP &lt;a href=&quot;https://github.com/postmodern/net-http-server/blob/master/lib/net/http/server/parser.rb&quot;&gt;Parser&lt;/a&gt; and &lt;a href=&quot;https://github.com/postmodern/net-http-server/blob/master/lib/net/http/server/daemon.rb&quot;&gt;Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/postmodern/net-http-server/tree/master/lib&quot;&gt;Small codebase&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Fast-ish&lt;/li&gt;
  &lt;li&gt;Rack-like API&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/postmodern/net-http-server/blob/master/lib/rack/handler/http.rb&quot;&gt;Rack Handler&lt;/a&gt; included&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rubydoc.info/gems/net-http-server/frames&quot;&gt;full YARD Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;webrick&quot;&gt;WEBrick&lt;/h2&gt;

&lt;p&gt;Some have said that the &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/webrick/rdoc/index.html&quot;&gt;WEBrick&lt;/a&gt;
HTTP Server is a &lt;a href=&quot;http://www.mikeperham.com/2010/11/22/the-ruby-stdlib-is-a-ghetto/&quot;&gt;Ghetto&lt;/a&gt;.
While WEBrick is &lt;strong&gt;very fast&lt;/strong&gt; for a pure-Ruby HTTP Server, the parsing
code is hand written and &lt;a href=&quot;https://github.com/ruby/ruby/blob/trunk/lib/webrick/httprequest.rb#L256-414&quot;&gt;difficult to read&lt;/a&gt;.
WEBrick is also one of the oldest Ruby HTTP Servers, but for some reason
lacks &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/webrick/rdoc/classes/WEBrick/HTTPServer.html&quot;&gt;documentation coverage&lt;/a&gt;.
Given the rise of &lt;a href=&quot;http://rack.rubyforge.org/&quot;&gt;Rack&lt;/a&gt;, middleware
and Rack applications, WEBricks API now seems &lt;a href=&quot;http://segment7.net/projects/ruby/WEBrick/servlets.html&quot;&gt;awkward&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-parser&quot;&gt;The Parser&lt;/h2&gt;

&lt;p&gt;When &lt;a href=&quot;http://kschiess.github.com/parslet/&quot;&gt;Parslet&lt;/a&gt;
(a pure Ruby PEG Parser library) was announced, I wondered how hard would it
be to write a HTTP Parser with Parslet. After researching the other
Ragel based HTTP Parsers (&lt;a href=&quot;https://github.com/macournoyer/thin/blob/master/ext/thin_parser/common.rl&quot;&gt;Thin&lt;/a&gt;
and &lt;a href=&quot;https://github.com/defunkt/unicorn/blob/master/ext/unicorn_http/unicorn_http_common.rl&quot;&gt;Unicorn&lt;/a&gt;)
and double checking &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616.html&quot;&gt;RFC 2616&lt;/a&gt;,
I suddenly had a &lt;a href=&quot;https://github.com/postmodern/net-http-server/blob/master/lib/net/http/server/parser.rb&quot;&gt;pure-Ruby HTTP Parser&lt;/a&gt;.
(in one file, that you can actually read!)&lt;/p&gt;

&lt;p&gt;I found that the way in which Parslet nested matches into Arrays of Hashes
resulted in data that looked very much like a Rack &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env&lt;/code&gt; Hash.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;net/http/server/parser&apos;

parser = Net::HTTP::Server::Parser.new
parser.parse(&quot;GET /path?x=1&amp;amp;y=2 HTTP/1.1\r\nCookie: xyz;123\r\n\r\n&quot;)
# =&amp;gt; {
#      :method=&amp;gt;&quot;GET&quot;,
#      :uri=&amp;gt;{:path=&amp;gt;&quot;path&quot;, :query=&amp;gt;&quot;x=1&amp;amp;y=2&quot;},
#      :version=&amp;gt;&quot;1.1&quot;,
#      :headers=&amp;gt;[{:name=&amp;gt;&quot;Cookie&quot;, :value=&amp;gt;&quot;xyz;123&quot;}]
#    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-daemon&quot;&gt;The Daemon&lt;/h2&gt;

&lt;p&gt;The next step was to write an actual Daemon that would:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Receive Connections&lt;/li&gt;
  &lt;li&gt;Parse HTTP Requests&lt;/li&gt;
  &lt;li&gt;Pass HTTP Requests to a Request Handler&lt;/li&gt;
  &lt;li&gt;Receive HTTP Responses from the Request Handler&lt;/li&gt;
  &lt;li&gt;Send HTTP Responses&lt;/li&gt;
  &lt;li&gt;Close Connections&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I settled on using the battle tested &lt;a href=&quot;http://rubydoc.info/stdlib/gserver/1.9.2/frames&quot;&gt;GServer&lt;/a&gt;
class to handle the Connections for
&lt;a href=&quot;https://github.com/postmodern/net-http-server/blob/master/lib/net/http/server/daemon.rb&quot;&gt;Nett:HTTP::Server::Daemon&lt;/a&gt;.
I also borrowed some ideas from Rack, such as passing
HTTP Requests via a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt; method and returning HTTP Responses as an Array
(containing the HTTP Status, Headers and Body).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;net/http/server&apos;
require &apos;pp&apos;

Net::HTTP::Server.run(:host =&amp;gt; &apos;127.0.0.1&apos;, :port =&amp;gt; 8080) do |request,socket|
  pp request

  [200, {&apos;Content-Type&apos; =&amp;gt; &apos;text/html&apos;}, [&apos;Hello World&apos;]]
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-rack-handler&quot;&gt;The Rack Handler&lt;/h2&gt;

&lt;p&gt;Given that the API was already very Rack-ish, writing a &lt;a href=&quot;https://github.com/postmodern/net-http-server/blob/master/lib/rack/handler/http.rb&quot;&gt;Rack handler&lt;/a&gt; on top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Net::HTTP::Server::Daemon&lt;/code&gt; was simple.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;rack/handler/http&apos;
require &apos;sinatra&apos;

class HelloWorld &amp;lt; Sinatra::Base

  get &apos;/&apos; do
    [200, {&apos;Content-Type&apos; =&amp;gt; &apos;text/html&apos;}, [&quot;Hello World&quot;]]
  end

end

Rack::Handler::HTTP.run HelloWorld, :Host =&amp;gt; &apos;localhost&apos;, :Port =&amp;gt; 1212
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h2&gt;

&lt;p&gt;By now your probably wondering, how fast is this pure Ruby HTTP Server?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;net/http/server&apos;

Net::HTTP::Server.run(:port =&amp;gt; 8080) do |request,socket|
  [200, {&apos;Content-Type&apos; =&amp;gt; &apos;text/html&apos;}, [&apos;Hello World&apos;]]
end

$ ab -n 4000 -c 4 http://localhost:8080/
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
...

Finished 4000 requests


Server Software:        
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      4
Time taken for tests:   73.405 seconds
Complete requests:      4000
Failed requests:        0
Write errors:           0
Total transferred:      220000 bytes
HTML transferred:       44000 bytes
Requests per second:    54.49 [#/sec] (mean)
Time per request:       73.405 [ms] (mean)
Time per request:       18.351 [ms] (mean, across all concurrent requests)
Transfer rate:          2.93 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       8
Processing:    24   73  31.5     62     236
Waiting:       24   72  31.4     60     236
Total:         25   73  31.5     62     236

Percentage of the requests served within a certain time (ms)
  50%     62
  66%     86
  75%     98
  80%    103
  90%    119
  95%    134
  98%    148
  99%    153
 100%    236 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Definitely not as fast as &lt;a href=&quot;http://code.macournoyer.com/thin/&quot;&gt;Thin&lt;/a&gt; or even
WEBrick, but not too bad considering its pure-Ruby and the size of the
code-base.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Typos in your Documentation?</title>
    <link href="http://postmodern.github.io/blog/2011/03/04/typos-in-your-documentation.html" />
    <updated>2011-03-04T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2011/03/04/typos-in-your-documentation.html</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;More likely than you think.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having good documentation (internal and external) can make or break bigger
projects. Good documentation helps skilled users/developers get up to
speed with your project. However, bad documentation will drive potential
users away. Typos are probably the most embarrassing thing to find in
documentation.&lt;/p&gt;

&lt;p&gt;One day I was fixing a trivial bug in my code for a new-user, and glanced
over the documentation. Low and behold I spot a typo right above the broken 
method. In this moment of embarrassment (in front of the new-user),
I said enough is enough, &lt;strong&gt;no more typos!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since I use &lt;a href=&quot;http://yardoc.org/&quot;&gt;YARD&lt;/a&gt; for all of my projects, I decided
to write a YARD plugin which would scan all of my documentation text using
the &lt;a href=&quot;http://hunspell.sourceforge.net/&quot;&gt;Hunspell&lt;/a&gt; spellchecking library
(via &lt;a href=&quot;http://github.com/postmodern/ffi-hunspell#readme&quot;&gt;ffi-hunspell&lt;/a&gt;).
A couple days later,
&lt;a href=&quot;https://github.com/postmodern/yard-spellcheck#readme&quot;&gt;yard-spellcheck&lt;/a&gt;
was printing typos with file-names, line-numbers and ANSI highlighting.&lt;/p&gt;

&lt;p&gt;As I suspected, I was able to find and fix a handful of typos in my own
libraries, thanks to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yard-spellcheck&lt;/code&gt;. Next, I started scanning larger
projects with YARD documentation. I found a couple typos in
&lt;a href=&quot;http://datamapper.org/&quot;&gt;DataMapper&lt;/a&gt; and even
&lt;a href=&quot;http://github.com/lsegal/yard#readme&quot;&gt;YARD&lt;/a&gt; itself (all are fixed now).&lt;/p&gt;

&lt;p&gt;The lesson of this short story is that typos are lurking everywhere; too
numerous and well hidden for human eyes to catch them all. Luckily, we
now have an automated-tool in the fight against typos. The Ruby Community
is notably obsessed with testing and quality of software, we should feel
the same way about our documentation.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install yard-spellcheck
$ cd my_project/
$ yard-spellcheck
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Introducing DeploYML</title>
    <link href="http://postmodern.github.io/blog/2010/11/21/introducing-deployml.html" />
    <updated>2010-11-21T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2010/11/21/introducing-deployml.html</id>
    <content type="html">&lt;p&gt;When Rails 3.0.0.beta.1 was first released, there was some confusion about
how to deploy a Rails3 app with this new &lt;a href=&quot;http://gembundler.com&quot;&gt;Bundler&lt;/a&gt;
thing. A few blog posts were written with some monkey patches for injecting
into &lt;a href=&quot;https://github.com/capistrano/capistrano/wiki/Documentation-v2.x&quot;&gt;Capistrano&lt;/a&gt;,
which enable early Bundler support. Unfortunately, Bundler was still very
new and none of the deployment tools had official support yet.&lt;/p&gt;

&lt;p&gt;Around the same time, I needed to deploy a Rails3 app and I did not want to
deal with the heavy weight of Capistrano; nor did I want to rely on a
monkey patch from a blog post. Of course the first thing I did was
look at the alternatives, which were
&lt;a href=&quot;http://rubyhitsquad.com/Vlad_the_Deployer.html&quot;&gt;Vlad The Deployer&lt;/a&gt; or
write a bash script that used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;. After trying to run
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vlad&lt;/code&gt; rake tasks, I hit some weird bugs with how it was calling
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;thin&lt;/code&gt;. Being on a dead-line and not wanting to wait on
Capistrano or Vlad, I thought it should be easy to create a simple
deployment utility with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;deployml&quot;&gt;DeploYML&lt;/h2&gt;

&lt;p&gt;Introducing &lt;a href=&quot;http://github.com/postmodern/deployml#readme&quot;&gt;DeploYML&lt;/a&gt;,
a simple deployment solution for Ruby / Rails projects that uses a
&lt;strong&gt;single YAML file&lt;/strong&gt;, &lt;a href=&quot;http://www.git-scm.com&quot;&gt;Git&lt;/a&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Configuring DeploYML requires at least &lt;strong&gt;two&lt;/strong&gt; things:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# config/deploy.yml
source: git@github.com:user/project.git
dest: deploy@www.example.com/var/www/site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then one can deploy the project using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deployml&lt;/code&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ deployml deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After making some changes, one can re-deploy the project:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ deployml redeploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;

&lt;p&gt;Of course, one will want to specify more information, such as what server
to run the project under:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;source: git@github.com:user/project.git
dest: deploy@www.example.com/var/www/site
server: apache
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or what options to run the server with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;source: git@github.com:user/project.git
dest: deploy@www.example.com/var/www/site
server:
  name: thin
  options:
    servers: 4
    deamonize: true
    socket: /var/run/thin.sock
    rackup: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or more importantly, what Framework or ORM the project uses:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;source: git@github.com:user/project.git
dest: deploy@www.example.com/var/www/site
framework: rails3
orm: datamapper
server: apache
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One can even specify multiple-environments:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# config/deploy.yml
source: git@github.com:user/project.git
framework: rails3
orm: datamapper

# config/deploy/staging.yml
dest: ssh://deploy@www.example.com/srv/staging
server:
  name: thin
  options:
    config: /etc/thin/staging.yml
    socket: /tmp/thin.staging.sock

# config/deploy/production.yml
dest: ssh://deploy@www.example.com/srv/project
server:
  name: thin
  options:
    config: /etc/thin/example.yml
    socket: /tmp/thin.example.sock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;administration&quot;&gt;Administration&lt;/h2&gt;

&lt;p&gt;DeploYML does more than just deploying, it also allows interacting with the
deployment server and deployed project.&lt;/p&gt;

&lt;p&gt;Need to quickly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; into the server as the deploy user?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ deployml ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Need to quickly execute a command remotely, within the directory of the
deployed project?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ deployml exec &apos;ps aux&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Need to execute a rake task remotely?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ deployml rake db:autoupgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;interested&quot;&gt;Interested?&lt;/h2&gt;

&lt;p&gt;Install it today:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install deployml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I originally started &lt;a href=&quot;https://github.com/postmodern/deployml/commits/f287b187f585eb03c0eb2a13b4149501b43d7b4d&quot;&gt;DeploYML&lt;/a&gt;
before &lt;a href=&quot;http://ruby5.envylabs.com/episodes/96-episode-94-july-16-2010/stories/812-rails3-deployments-with-inploy&quot;&gt;Imploy&lt;/a&gt;
was released, and just now got around to making a release. If you feel
Capistrano is too heavy, I encourage you to also checkout
&lt;a href=&quot;https://github.com/dcrec1/inploy&quot;&gt;Imploy&lt;/a&gt; for deploying Rails3 apps.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Mining RubyGems from Ore</title>
    <link href="http://postmodern.github.io/blog/2010/10/25/mining-rubygems-from-ore.html" />
    <updated>2010-10-25T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2010/10/25/mining-rubygems-from-ore.html</id>
    <content type="html">&lt;p&gt;Recently there has been some interesting discussion on the role of
Gem builders and gemspecs. &lt;a href=&quot;http://jeffkreeftmeijer.com/&quot;&gt;Jeff Kreeftmeijer&lt;/a&gt;
wrote about how easy it is to build a RubyGem using a hand-written
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file. With just a stand-alone &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file one can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Build RubyGems: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem build my-project.gemspec&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Publish RubyGems: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem push my-project-0.1.0.gem&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing worries me is the fact that you either have to specify
everything explicitly in the gemspec, or use inline &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; commands to query
the files of a project. Copying and pasting all this boilerplate code around
seems like a potential future maintenance hassle; also not very DRY.&lt;/p&gt;

&lt;p&gt;The argument for just a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file did get me thinking. I do find
myself regenerating the gemspec with
&lt;a href=&quot;http://github.com/technicalpickles/jeweler&quot;&gt;Jeweler&lt;/a&gt;, almost once per-day.
Also, these stand-alone gemspecs are pretty succinct. So when in doubt,
see how other languages solved the problem.&lt;/p&gt;

&lt;h2 id=&quot;code-vs-data&quot;&gt;Code vs. Data&lt;/h2&gt;

&lt;p&gt;I asked one of my Haskell friends how &lt;a href=&quot;http://www.haskell.org/cabal/&quot;&gt;Cabal&lt;/a&gt;
(the Haskell packager of choice) solves this problem. He pointed me
to the &lt;a href=&quot;http://hpaste.org/40862/serialistnet_cabal_file&quot;&gt;Cabal file&lt;/a&gt; of his
Haskell webapp &lt;a href=&quot;http://serialist.net/&quot;&gt;serialist.net&lt;/a&gt;. Cabal uses easy to
read, easy to parse and easy to edit &lt;strong&gt;plain-text&lt;/strong&gt;. This reminded me,
Code is for describing logic and flat-files are for describing static data.
The majority of the information in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file, is static data.&lt;/p&gt;

&lt;p&gt;Now I am starting to really question the whole reason for an explicit
gemspec. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file only exists to create a
&lt;a href=&quot;http://rubygems.rubyforge.org/rubygems-update/Gem/Specification.html&quot;&gt;Gem::Specification&lt;/a&gt;
object, which is then passed to &lt;a href=&quot;http://rubygems.rubyforge.org/rubygems-update/Gem/Builder.html&quot;&gt;Gem::Builder&lt;/a&gt;
or loaded by &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt;. Why are we writing Ruby
code that normally would only exist in-memory? The gemspec purists
state this is to allow things such as, dynamically loading the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERSION&lt;/code&gt;
constant from the project or dynamically listing files tracked by Git.
Although, both of these tasks can easily be automated by a library.&lt;/p&gt;

&lt;p&gt;So I wondered, why not have a small library that loads the project
information from a YAML file, fills in any missing information, and then
creates a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gem::Specification&lt;/code&gt; object. Furthermore, If we can call a
method and get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gem::Specification&lt;/code&gt; object back, we could place this in
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; file for both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem build&lt;/code&gt; and Bundler to make use of.&lt;/p&gt;

&lt;h2 id=&quot;introducing-ore&quot;&gt;Introducing Ore&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/ruby-ore/ore&quot;&gt;Ore&lt;/a&gt; allows you to define all project
information for a RubyGem in a &lt;strong&gt;single YAML file&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gemspec.yml&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;name: ore
version: 0.1.2
summary: Cut raw RubyGems from YAML
description:
  Ore is a simple RubyGem building solution. Ore handles the
  creation of Gem::Specification objects as well as building &apos;.gem&apos;
  files. Ore allows the developer to keep all of the project information
  in a single YAML file.

license: MIT
authors: Postmodern
email: postmodern.mod3@gmail.com
homepage: http://github.com/postmodern/ore
has_yard: true

dependencies:
  thor: ~&amp;gt; 0.14.3

development_dependencies:
  yard: ~&amp;gt; 0.6.1
  rspec: ~&amp;gt; 2.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With Ore, one can write their description as free-text, no more using
with awkward Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%Q{...}&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;Dependencies are listed in a YAML Hash, no more repeating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_dependency&lt;/code&gt;
or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_development_dependency&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ore can also infer missing information. If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;version&lt;/code&gt; is not specified,
Ore will search for and parse any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERSION&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERSION.yml&lt;/code&gt; files. Ore
can even slurp up any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERSION&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAJOR&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MINOR&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATCH&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUILD&lt;/code&gt;
constants from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;version.rb&lt;/code&gt; file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/&lt;/code&gt; directory. Also, notice
that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;files&lt;/code&gt; is not listed, this is because Ore can detect the project is
using Git, and list all files tracked by Git.&lt;/p&gt;

&lt;p&gt;For a complete reference of everything that may go into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gemspec.yml&lt;/code&gt;
file, and how Ore infers missing data, please see
&lt;a href=&quot;http://rubydoc.info/gems/ore-core/file/GemspecYML.md&quot;&gt;GemspecYML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building gems with Ore is easy as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ore
Successfully built RubyGem
Name: ore
Version: 0.1.2
File: ore-0.1.2.gem
$ ls pkg/
ore-0.1.2.gem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One can still get the traditional gemspec from Ore:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ore gemspec
# -*- encoding: utf-8 -*-

Gem::Specification.new do |s|
  s.name = %q{ore}
  s.version = &quot;0.1.2&quot;

  s.required_rubygems_version = Gem::Requirement.new(&quot;&amp;gt;= 0&quot;) if s.respond_to? :required_rubygems_version=
  s.authors = [&quot;Postmodern&quot;]
  s.date = %q{2010-10-25}
  s.default_executable = %q{ore}
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can even use Ore in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gemspec&lt;/code&gt; files:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# -*- encoding: utf-8 -*-

begin
  Ore::Specification.new do |gemspec|
    # custom logic here
  end
rescue NameError
  begin
    require &apos;ore/specification&apos;
    retry
  rescue LoadError
    STDERR.puts &quot;The &apos;my-project.gemspec&apos; file requires Ore.&quot;
    STDERR.puts &quot;Run `gem install ore-core` to install Ore.&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ore will still work with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem build&lt;/code&gt; and even Bundler.&lt;/p&gt;

&lt;h2 id=&quot;mining-rubygems-from-ore&quot;&gt;Mining RubyGems from Ore&lt;/h2&gt;

&lt;p&gt;Ore also comes with an extendable generator, for creating new projects:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine my-project
Generating /home/hal/my-project
  create  lib
  create  lib/my/project
  create  spec
  create  .rspec
  create  spec/my/project_spec.rb
  create  spec/spec_helper.rb
  create  .document
  create  .gitignore
  create  my-project.gemspec
  create  ChangeLog.rdoc
  create  LICENSE.txt
  create  README.rdoc
  create  Rakefile
  create  gemspec.yml
  create  lib/my/project/version.rb
  create  lib/my/project.rb
     run  git init from &quot;.&quot;
     run  git add . from &quot;.&quot;
     run  git commit -m &quot;Initial commit.&quot; from &quot;.&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine&lt;/code&gt; will generate an RDoc and test-unit project. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine&lt;/code&gt;
can also generate very customized projects:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine my-project --rspec --yard --markdown --bundler
Generating /home/hal/my-project
      create  lib
      create  lib/my/project
      create  spec
      create  .yardopts
      create  .rspec
      create  spec/my/project_spec.rb
      create  spec/spec_helper.rb
      create  Gemfile
      create  .document
      create  .gitignore
      create  my-project.gemspec
      create  ChangeLog.md
      create  LICENSE.txt
      create  README.md
      create  Rakefile
      create  gemspec.yml
      create  lib/my/project/version.rb
      create  lib/my/project.rb
         run  git init from &quot;.&quot;
         run  git add . from &quot;.&quot;
         run  git commit -m &quot;Initial commit.&quot; from &quot;.&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine&lt;/code&gt; simply renders Ore Templates. Unlike other generators which have
their logic hard-coded in Ruby, Ore Templates are simply
&lt;a href=&quot;http://github.com/ruby-ore/ore/tree/master/data/ore/templates/&quot;&gt;directories&lt;/a&gt;,
containing static and ERb files. One can make their own Ore template
by creating a directory, adding files and publishing a Git repository.&lt;/p&gt;

&lt;p&gt;Users can install custom Ore templates from Git repositories:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ore install http://github.com/user/awesometest.git
$ ore list
Builtin templates:
  base
  rspec
  test_unit
  yard
  bundler
  jeweler_tasks
  rdoc
  ore_tasks
Installed templates:
  awesometest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-T&lt;/code&gt; option to specify additional custom templates:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine my-project --yard --markdown -T awesometest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;workflow&quot;&gt;Workflow&lt;/h2&gt;

&lt;p&gt;By default Ore does not impose a workflow onto the developer. Even the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine&lt;/code&gt;
utility does not add any additional Rake tasks to new projects. This allows the
developer to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem build&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem push&lt;/code&gt;, or even use
&lt;a href=&quot;http://github.com/technicalpickles/jeweler&quot;&gt;Jeweler::Tasks&lt;/a&gt; with Ore.&lt;/p&gt;

&lt;p&gt;For those just wanting simple Rake tasks to build, push and tag releases, there
is &lt;a href=&quot;http://github.com/ruby-ore/ore-tasks&quot;&gt;ore-tasks&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ore::Tasks&lt;/code&gt; provides
the following tasks:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake build            # Only builds a Gem
rake console[script]  # Start IRB with all runtime dependencies loaded
rake gem              # Alias to the &apos;build&apos; task
rake install          # Builds and installs a Gem
rake push             # Builds and pushes a Gem
rake release          # Builds and Pushes a new Gem / Build, Tags and Pushe...
rake tag              # Tags a release and pushes the tag
rake version          # Displays the current version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To generate a project with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ore::Tasks&lt;/code&gt; included:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine my-project --ore-tasks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To generate a project with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jeweler::Tasks&lt;/code&gt; included:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mine my-project --jeweler-tasks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;dog-fooding&quot;&gt;Dog Fooding&lt;/h2&gt;

&lt;p&gt;In order to do real-world testing with Ore, I created
&lt;a href=&quot;http://github.com/ruby-ore/ore-example&quot;&gt;ore-example&lt;/a&gt; which uses Bundler,
RSpec2, YARD and Ore::Tasks.&lt;/p&gt;

&lt;p&gt;As of now, I have also migrated my
&lt;a href=&quot;http://github.com/postmodern/chars&quot;&gt;chars&lt;/a&gt; and
&lt;a href=&quot;http://github.com/postmodern/uri-query_params&quot;&gt;uri-query_params&lt;/a&gt;
libraries to Ore.&lt;/p&gt;

&lt;h2 id=&quot;interested&quot;&gt;Interested?&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install ore
$ mine the-future
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For questions or feedback, join &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#ruby-ore&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irc.freenode.net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All source-code is located on &lt;a href=&quot;http://github.com/ruby-ore&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ore is tested with &lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec2&lt;/a&gt; and has extensive
&lt;a href=&quot;http://yardoc.org&quot;&gt;YARD&lt;/a&gt; &lt;a href=&quot;http://rubydoc.info/gems/ore&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Introducing Combinatorics 0.2.0</title>
    <link href="http://postmodern.github.io/blog/2010/10/03/introducing-combinatorics-0.2.0.html" />
    <updated>2010-10-03T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2010/10/03/introducing-combinatorics-0.2.0.html</id>
    <content type="html">&lt;p&gt;I decided to take my &lt;a href=&quot;/2010/10/01/list-comprehensions-in-ruby.html&quot;&gt;List Comprehension&lt;/a&gt;
code with some other Combinatorics code I had floating around and release
&lt;a href=&quot;http://rubydoc.info/gems/combinatorics&quot;&gt;Combinatorics&lt;/a&gt; 0.2.0:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install combinatorics
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After getting specs on the List Comprehension code, it became easy to
refactor it and get the specs passing on Ruby 1.8.7, 1.9.2 and JRuby.
Unfortunately, &lt;a href=&quot;http://rubini.us/&quot;&gt;Rubinius&lt;/a&gt; does not yet support
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerator#next&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Combinatorics library also contains the
&lt;a href=&quot;http://rubydoc.info/gems/combinatorics/Combinatorics/PowerSet/Mixin#powerset-instance_method&quot;&gt;powerset&lt;/a&gt;
method, added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;combinatorics&apos;

[1,2,3].powerset
# =&amp;gt; [[],
      [3],
      [2],
      [2, 3],
      [1],
      [1, 3],
      [1, 2],
      [1, 2, 3]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In Combinatorics 0.2.0,
&lt;a href=&quot;http://rubydoc.info/gems/combinatorics/Range#%26-instance_method&quot;&gt;Range#&amp;amp;&lt;/a&gt;,
&lt;a href=&quot;http://rubydoc.info/gems/combinatorics/Range#upto-instance_method&quot;&gt;Range#upto&lt;/a&gt;
and &lt;a href=&quot;http://rubydoc.info/gems/combinatorics/Range#downto-instance_method&quot;&gt;Range#downto&lt;/a&gt;
were also added:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(1..50) &amp;amp; (20..100)
# =&amp;gt; (20..50)

(1..5).upto(2..10).to_a
# =&amp;gt; [1..5, 1..6, 1..7, 1..8, 1..9, 1..10,
      2..5, 2..6, 2..7, 2..8, 2..9, 2..10]

(2..10).downto(1..5).to_a
# =&amp;gt; [2..10, 2..9, 2..8, 2..7, 2..6, 2..5,
      1..10, 1..9, 1..8, 1..7, 1..6, 1..5]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Fork the &lt;a href=&quot;http://github.com/postmodern/combinatorics/#fork_box&quot;&gt;Combinatorics&lt;/a&gt;
library today, and add your own Combinatoric methods.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>List Comprehensions in Ruby</title>
    <link href="http://postmodern.github.io/blog/2010/10/01/list-comprehensions-in-ruby.html" />
    <updated>2010-10-01T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2010/10/01/list-comprehensions-in-ruby.html</id>
    <content type="html">&lt;p&gt;Recently I have been interested in &lt;a href=&quot;http://haskell.org/&quot;&gt;Haskell&lt;/a&gt; and been
skimming the whimsical (yet informative)
&lt;a href=&quot;http://learnyouahaskell.com/&quot;&gt;Learn a Haskell, for Great Good!&lt;/a&gt;.
One feature I really like from Haskell is their implementation of
list comprehensions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Prelude&amp;gt; [(x,y) | x &amp;lt;- [1..10], y &amp;lt;- [2..8]]
[(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),
(2,2),(2,3),(2,4),(2,5),(2,6),(2,7),(2,8),
(3,2),(3,3),(3,4),(3,5),(3,6),(3,7),(3,8),
(4,2),(4,3),(4,4),(4,5),(4,6),(4,7),(4,8),
(5,2),(5,3),(5,4),(5,5),(5,6),(5,7),(5,8),
(6,2),(6,3),(6,4),(6,5),(6,6),(6,7),(6,8),
(7,2),(7,3),(7,4),(7,5),(7,6),(7,7),(7,8),
(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8),
(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),
(10,2),(10,3),(10,4),(10,5),(10,6),(10,7),(10,8)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This list comprehension is essentially a Set definition using first-order
predicate logic, with universal quantifiers on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; (where the Set
contains the tuples composed of every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; and every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; from the ranges
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1..10&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2..8&lt;/code&gt;). Amazingly, the Haskell version looks almost exactly
like the Set definitions from my
&lt;a href=&quot;http://www.amazon.com/Discrete-Structures-Computability-Bartlett-Computer/dp/0763718432&quot;&gt;Discrete Structures, Logic, and Computability (2nd edition)&lt;/a&gt;
book, except without the curly-braces or universal quantifiers
(&lt;img src=&quot;http://upload.wikimedia.org/math/d/4/d/d4d49bead125261b226eaa867bd016ce.png&quot; alt=&quot;universal quantifier&quot; /&gt;).&lt;/p&gt;

&lt;p&gt;One does not have to learn functional programming and Haskell to use list
comprehensions, Python has them as well:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; [(x,y) for x in range(0,11) for y in range(2,9)]
[(0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8),
(1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8),
(2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8),
(3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8),
(4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7), (4, 8),
(5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7), (5, 8),
(6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7), (6, 8),
(7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 7), (7, 8),
(8, 2), (8, 3), (8, 4), (8, 5), (8, 6), (8, 7), (8, 8),
(9, 2), (9, 3), (9, 4), (9, 5), (9, 6), (9, 7), (9, 8),
(10, 2), (10, 3), (10, 4), (10, 5), (10, 6), (10, 7), (10, 8)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Granted, using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop syntax as constraints for the list
comprehension is not as sexy as the Haskell version, but it gets the job
done.&lt;/p&gt;

&lt;p&gt;Unfortunately, Ruby does not support list comprehensions, and only has
a couple methods for doing &lt;a href=&quot;http://en.wikipedia.org/wiki/Combinatorics&quot;&gt;Combinatorics&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#combination&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array#permutation&lt;/code&gt;).
So, I &lt;a href=&quot;http://gist.github.com/605891&quot;&gt;implemented list comprehensions&lt;/a&gt;
for the Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; class:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt; require &apos;./array_comprehension&apos;
=&amp;gt; true
&amp;gt;&amp;gt; [(1..10), (2..8)].comprehension.to_a
=&amp;gt; [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8],
    [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8],
    [3, 2], [3, 3], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8],
    [4, 2], [4, 3], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8],
    [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8],
    [6, 2], [6, 3], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8],
    [7, 2], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [7, 8],
    [8, 2], [8, 3], [8, 4], [8, 5], [8, 6], [8, 7], [8, 8],
    [9, 2], [9, 3], [9, 4], [9, 5], [9, 6], [9, 7], [9, 8],
    [10, 2], [10, 3], [10, 4], [10, 5], [10, 6], [10, 7], [10, 8]] 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; to quickly pass results to the given block, to not
stress the Garbage Collector with building a huge Arrays. The method also
returns an &lt;a href=&quot;http://rubydoc.info/docs/ruby-core/1.9.2/Enumerator&quot;&gt;Enumerator&lt;/a&gt;
object if no block is given, to fake Haskells lazy evaluation.&lt;/p&gt;

&lt;p&gt;Note, that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; may contain:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://rubydoc.info/docs/ruby-core/1.9.2/Enumerable&quot;&gt;Enumerable&lt;/a&gt; objects:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;gt;&amp;gt; [(&apos;a&apos;..&apos;f&apos;),(0..10).step(2)].comprehension.to_a
  =&amp;gt; [[&quot;A&quot;, 0], [&quot;A&quot;, 2], [&quot;A&quot;, 4], [&quot;A&quot;, 6], [&quot;A&quot;, 8], [&quot;A&quot;, 10],
      [&quot;B&quot;, 0], [&quot;B&quot;, 2], [&quot;B&quot;, 4], [&quot;B&quot;, 6], [&quot;B&quot;, 8], [&quot;B&quot;, 10],
      [&quot;C&quot;, 0], [&quot;C&quot;, 2], [&quot;C&quot;, 4], [&quot;C&quot;, 6], [&quot;C&quot;, 8], [&quot;C&quot;, 10],
      [&quot;D&quot;, 0], [&quot;D&quot;, 2], [&quot;D&quot;, 4], [&quot;D&quot;, 6], [&quot;D&quot;, 8], [&quot;D&quot;, 10],
      [&quot;E&quot;, 0], [&quot;E&quot;, 2], [&quot;E&quot;, 4], [&quot;E&quot;, 6], [&quot;E&quot;, 8], [&quot;E&quot;, 10],
      [&quot;F&quot;, 0], [&quot;F&quot;, 2], [&quot;F&quot;, 4], [&quot;F&quot;, 6], [&quot;F&quot;, 8], [&quot;F&quot;, 10]] 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Non-enumerable objects:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;gt;&amp;gt; [5,(6..9),10].comprehension.to_a
  =&amp;gt; [[5, 6, 10], [5, 7, 10], [5, 8, 10], [5, 9, 10]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Even other list comprehensions:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;gt;&amp;gt; syllable = ([&apos;a&apos;..&apos;z&apos;] * 2).comprehension
  &amp;gt;&amp;gt; word = [syllable] * 4
  &amp;gt;&amp;gt; number = (1..100)
  &amp;gt;&amp;gt; [*word, number].comprehension { |*s| puts s.join }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This code does require Ruby 1.9 since &lt;a href=&quot;http://rubydoc.info/docs/ruby-core/1.9.2/Enumerator#peek-instance_method&quot;&gt;Enumerator#peek&lt;/a&gt;
was not back-ported to Ruby 1.8.7.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ rvm install 1.9.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
  </entry>
  
  <entry>
    <title>Introducing OpenNamespace</title>
    <link href="http://postmodern.github.io/blog/2010/09/03/introducing-open-namespace.html" />
    <updated>2010-09-03T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2010/09/03/introducing-open-namespace.html</id>
    <content type="html">&lt;p&gt;I have always liked how frameworks such as Rails can
&lt;a href=&quot;http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html&quot;&gt;autoload&lt;/a&gt;
Classes. I wanted to provide similar behavior in
&lt;a href=&quot;http://ronin-ruby.github.com/&quot;&gt;Ronin&lt;/a&gt; and other frameworks, so I created
the &lt;a href=&quot;http://github.com/postmodern/open_namespace&quot;&gt;OpenNamespace&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://rubydoc.info/gems/open_namespace&quot;&gt;OpenNamespace&lt;/a&gt; allows namespaces
to require and find classes and modules from RubyGems. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenNamespace&lt;/code&gt;
you can make a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Plugins&lt;/code&gt; module able to load plugin modules/classes from
other gems.&lt;/p&gt;

&lt;p&gt;More specifically, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenNamespace&lt;/code&gt; does not need to know where
the files are, it just guesses the file path based on the constant name,
attempts to require it then finds the constant in the namespace.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install open_namespace
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;open_namespace&apos;

module Project
  module Plugins
    include OpenNamespace
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Explicitly load constants:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project::Plguins.require_const :foo_bar
# =&amp;gt; Project::Plugins::FooBar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Explicitly load constants with odd capitalization:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project::Plugins.require_const :tcp_session
# =&amp;gt; Project::Plugins::TCPSession
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Explicitly load constants via sub-paths:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project::Plguins.require_const &apos;templates/erb&apos;
# =&amp;gt; Project::Plugins::Templates::Erb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Implicitly load constants via const_missing:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project::Plugins::Other
# =&amp;gt; Project::Plugins::Other
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Introducing uri-query_params</title>
    <link href="http://postmodern.github.io/blog/2010/09/02/introducing-uri-query-params.html" />
    <updated>2010-09-02T00:00:00+00:00</updated>
    <author>
      <name>Postmodern</name>
      <email>postmodern.mod3@gmail.com</email>
    </author>
    <id>http://postmodern.github.io/blog/2010/09/02/introducing-uri-query-params.html</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://github.com/postmodern/uri-query_params&quot;&gt;uri-query_params&lt;/a&gt; is a new
library which allows accessing the individual parameters in the query
string of a HTTP URI in Ruby. The library
&lt;a href=&quot;http://en.wikipedia.org/wiki/Monkey_patch&quot;&gt;monkey-patches&lt;/a&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URI::HTTP&lt;/code&gt;
class to provide similar behavior to PHPs famous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_GET&lt;/code&gt; hash.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install uri-query_params
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uri-query_params&lt;/code&gt; you can, inspect the URI query_params:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &apos;uri/query_params&apos;

url = URI(&apos;http://www.google.com/search?hl=en&amp;amp;client=firefox-a&amp;amp;rls=org.mozilla%3Aen-US%3Aofficial&amp;amp;hs=1HY&amp;amp;q=bob+ross&amp;amp;btnG=Search&apos;)

url.query_params
# =&amp;gt; {&quot;btnG&quot;=&amp;gt;&quot;Search&quot;, &quot;hs&quot;=&amp;gt;&quot;1HY&quot;, &quot;rls&quot;=&amp;gt;&quot;org.mozilla:en-US:official&quot;, &quot;client&quot;=&amp;gt;&quot;firefox-a&quot;, &quot;hl&quot;=&amp;gt;&quot;en&quot;, &quot;q&quot;=&amp;gt;&quot;bob+ross&quot;}

url.query_params[&apos;q&apos;]
# =&amp;gt; &quot;bob+ross&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, set the URI query_params:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;url.query_params[&apos;q&apos;] = &apos;Upright Citizens Brigade&apos;
url.to_s
# =&amp;gt; &quot;http://www.google.com/search?btnG=Search&amp;amp;hs=1HY&amp;amp;rls=org.mozilla:en-US:official&amp;amp;client=firefox-a&amp;amp;hl=en&amp;amp;q=Upright%20Citizens%20Brigade&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Originally, the &lt;a href=&quot;http://rubydoc.info/gems/uri-query_params/URI/QueryParams/Mixin&quot;&gt;URI::QueryParams&lt;/a&gt;
mixin was developed for &lt;a href=&quot;http://ronin-ruby.github.com/&quot;&gt;Ronin&lt;/a&gt; and
&lt;a href=&quot;http://github.com/postmodern/gscraper/&quot;&gt;GScraper&lt;/a&gt;. I decided to split the
code out into a common library, to prevent conflicts between the two
separate versions.&lt;/p&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;
</content>
  </entry>
  
</feed>
