Andrew Schutt

Thor: a toolkit for building powerful command-line interfaces

What is Thor?

What is Thor?

Thor is a simple and efficient tool for building self documenting command line utilities.

Why Thor?

Why Thor?

Getting Started

Getting Started

    gem install thor
   

    class Hello < Thor
        desc "odinson", "Prince of Asgard"
        def odinson
            puts "It is I Thor!"
        end
    end
   

    class Hello < Thor
        desc "odinson", "Prince of Asgard"
        def odinson
            puts "It is I Thor!"
        end
    end
   

    class Hello < Thor
        desc "odinson", "Prince of Asgard"
        def odinson
            puts "It is I Thor!"
        end
    end
   

    class Hello < Thor
        desc "odinson", "Prince of Asgard"
        def odinson
            puts "It is I Thor!"
        end
    end
   

    thor hello:odinson

   
   
  It is I Thor!

    thor list

    thor list

    hello
    #----
    thor hello:odinson # Prince of Asgard

    thor help [COMMAND]

    thor help hello:odinson

    Usage:
      thor hello:odinson

    Prince of Asgard

Long Description

   

    class Hello < Thor
        desc "odinson", "Prince of Asgard"
        long_desc <<-LONGDESC
            `hello:odinson` will print out a lovely message
             from Thor odinson himself.
    
            The usage for this command is just invocation
            using Thor.
    
            `thor hello:odinson`
    
            No additional option are available (yet!)
        LONGDESC
        def odinson
            puts "It is I Thor!"
        end
    end

    thor help hello:odinson
    
    Usage:
      thor hello:odinson
    
    
    Description:
      `hello:odinson` will print out a lovely message from Thor odinson himself.
    
      The usage for this command is just invocation using Thor.
    
      `thor hello:odinson`
    
      No additional option are available (yet!)

Arguments

   

    class Hello < Thor
        desc "odinson", "says hello"
        def odinson(name)
            puts "It is I Thor! Hello #{name}"
        end
    end
   

    class Hello < Thor
        desc "odinson", "says hello"
        def odinson(name)
            puts "It is I Thor! Hello #{name}"
        end
    end

Task Options

Options

   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :verbose
      def odinson
          verbose_mode = options[:verbose]
          if verbose_mode
              puts "It is I Thor! Odinson! Prince of Asgard!"
          else
              puts "It is I Thor!"
          end
      end
  end

Options

   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :verbose
      def odinson
          verbose_mode = options[:verbose]
          if verbose_mode
              puts "It is I Thor! Odinson! Prince of Asgard!"
          else
              puts "It is I Thor!"
          end
      end
  end
    thor help hello:odinson

    Usage:
      thor hello:odinson

    Options:
      [--verbose=VERBOSE]  

    Prince of Asgard
  
  thor hello:odinson --verbose

Options for the options

  • :desc
  • :aliases
  • :banner
  • :default
  • :lazy_default
  • :required
  • :type
    • :string, :hash, :array, :numeric, or :boolean
  • :enum
:desc
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :verbose, 
                    :desc => "display Thor's full name"
      def odinson
          verbose_mode = options[:verbose]
          if verbose_mode
              puts "It is I Thor! Odinson! Prince of Asgard!"
          else
              puts "It is I Thor!"
          end
      end
  end
:aliases
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :verbose, 
                    :desc => "display Thor's full name",
                    :aliases => "-v"
      def odinson
          verbose_mode = options[:verbose]
          if verbose_mode
              puts "It is I Thor! Odinson! Prince of Asgard!"
          else
              puts "It is I Thor!"
          end
      end
  end
:banner
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :verbose, 
                    :desc => "display Thor's full name",
                    :banner => "to use this option append --verbose"
      def odinson
          verbose_mode = options[:verbose]
          if verbose_mode
              puts "It is I Thor! Odinson! Prince of Asgard!"
          else
              puts "It is I Thor!"
          end
      end
  end
:default
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :hello_to, 
                    :desc => "who to say hello to",
                    :default => "stranger"
      def odinson
          hello_to = options[:hello_to]
          puts "It is I Thor! Hello #{hello_to}"
      end
  end
:lazy_default
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :hello_to, 
                    :desc => "who to say hello to",
                    :lazy_default => "stranger"
      def odinson
          hello_to = options[:hello_to]
          puts "It is I Thor! Hello #{hello_to}"
      end
  end
:required
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :hello_to, 
                    :desc => "who to say hello to",
                    :required => true
      def odinson
          hello_to = options[:hello_to]
          puts "It is I Thor! Hello #{hello_to}"
      end
  end
:type
  • :boolean
    
    • --option=true
  • :string
    • --option=value
  • :numeric
    • --option=1234
  • :array
    • --option=one two three
  • :hash
    • --option=name:string age:integer
:enum
   

  class Hello < Thor
      desc "odinson", "Prince of Asgard"
      method_option :hello_to, 
                    :desc => "who to say hello to",
                    :enum => ['Loki', 'Hela', 'Hulk']
      def odinson
          hello_to = options[:hello_to]
          puts "It is I Thor! Hello #{hello_to}"
      end
  end
   

   class Hello < Thor
     desc "odinson", "Prince of Asgard"
     method_option :verbose, desc: "display full message from thor",
                             aliases: "-v",
                             default: false,
                             type: :boolean
     method_option :family, desc: "display message for Thor family member",
                            aliases: "-f",
                            banner: "<family member name>",
                            default: "brother",
                            type: :string,
                            enum: ["Loki", "Hela", "Odin"]
     method_option :no_name, desc: "display message without Thor addressing anyone",
                             aliases: "-nn",
                             banner: "Thor addesses no one"
     def odinson(name)
         hello_to = options[:family] ? option[:family] : name
         if option[:no_name]
           puts "Hello! It is I Thor!"
         else
           if options[:verbose]
             puts "Hello #{name}. It is I Thor! Son of Odin and Prince of Asgard!"
           else
             puts "Hello #{name}. It is I Thor!"
           end
         end
     end
   end
   

   class Hello < Thor
     desc "odinson", "Prince of Asgard"
     method_option :verbose, desc: "display full message from thor",
                             aliases: "-v",
                             default: false,
                             type: :boolean
     method_option :family, desc: "display message for Thor family member",
                            aliases: "-f",
                            banner: "<family member name>",
                            default: "brother",
                            type: :string,
                            enum: ["Loki", "Hela", "Odin"]
     method_option :no_name, desc: "display message without Thor addressing anyone",
                             aliases: "-nn",
                             banner: "Thor addesses no one"
     def odinson(name)
         hello_to = options[:family] ? option[:family] : name
         if option[:no_name]
           puts "Hello! It is I Thor!"
         else
           if options[:verbose]
             puts "Hello #{name}. It is I Thor! Son of Odin and Prince of Asgard!"
           else
             puts "Hello #{name}. It is I Thor!"
           end
         end
     end
   end

    thor help hello:odinson

    Usage:
      thor hello:odinson

    Options:
      -v, [--verbose], [--no-verbose]        # display full message from thor
      -f, [--family=<family member name>]    # display message for Thor family member
                                         # Default: brother
                                         # Possible values: Loki, Hela, Odin
      -nn, [--no-name=Thor addesses no one]  # display message without Thor addressing anyone

    Prince of Asgard

Class Options

class_option
   

  class_option :yelling, type: :boolean,
                         default: false,
                         desc: "engage yelling mode"
class_option
  
  class Volume < Thor
    class_option :yelling, :type => :boolean,
                           :default => false,
                           :aliases => "-y"

    desc "yell", "yell it!"
    def yell
        msg = "I am not yelling! You're yelling!"
        if options[:yelling]
          puts msg.upcase
        end
        puts msg
    end

    desc "whisper", "shhhh"
    def whisper
        if options[:yelling]
          msg = "please stop yelling. whisper time"
        else
          msg = "shhh, whisper, whisper"
        end
        puts msg
    end
  end

    thor help volume:yell
Usage:
  thor volume:whisper

Options:
  -y, [--yelling], [--no-yelling] 

shhhh

    thor help volume:whisper
Usage:
  thor volume:yell

Options:
  -y, [--yelling], [--no-yelling] 

yell it!

Task Actions

Actions

   

  class Quinjet < Thor
    include Thor::Actions
    desc "authenticate", "authenticates for quinjet access"
    def authenticate
      authenticated = false
      run("say -v Moira Welcome. Authentication required.")
      while !authenticated
        username = ask "Welcome. Authentication required."
        if username.upcase == "POINT BREAK"
          authenticated = true
          run("say -v Moira Welcome Point Break.")
        else
          run("say -v Moira Access Denied.")
        end
      end
    end
  end

Actions

   

  class Quinjet < Thor
    include Thor::Actions
    desc "authenticate", "authenticates for quinjet access"
    def authenticate
      authenticated = false
      run("say -v Moira Welcome. Authentication required.")
      while !authenticated
        username = ask "Welcome. Authentication required."
        if username.upcase == "POINT BREAK"
          authenticated = true
          run("say -v Moira Welcome Point Break.")
        else
          run("say -v Moira Access Denied.")
        end
      end
    end
  end

Actions

   

  class Quinjet < Thor
    include Thor::Actions
    desc "authenticate", "authenticates for quinjet access"
    def authenticate
      authenticated = false
      run("say -v Moira Welcome. Authentication required.")
      while !authenticated
        username = ask "Welcome. Authentication required."
        if username.upcase == "POINT BREAK"
          authenticated = true
          run("say -v Moira Welcome Point Break.")
        else
          run("say -v Moira Access Denied.")
        end
      end
    end
  end

Actions

  • :say
  • :ask
  • :yes?
  • :no?
  • :add_file, :remove_file, :copy_file
  • :template
  • :directory
  • :inside
  • :run
  • and more
    

Subcommands

Subcommands

 
  module GitCLI
    class Remote < Thor
      desc "add <name> <url>", "Adds a remote"
      def add(name, url)
        # implement git remote add
      end
 
      desc "rename <old> <new>", "Rename the remote"
      def rename(old, new)
        # implement git remote rename
      end
    end
 
    class Git < Thor
      desc "remote SUBCOMMAND ...ARGS", "manage set repositories"
      subcommand "remote", Remote
    end
  end

System Wide

  thor install [.THOR FILE]

Thank you!

Thor CLI

By Andrew Schutt

Thor CLI

a presentation for Iowa Ruby Brigade around the Thor CLI tool with an example of creating a Magic the Gathering search CLI tool

  • 1,207