dev-ui 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -7
- data/Gemfile.lock +10 -11
- data/README.md +13 -1
- data/Rakefile +19 -0
- data/dev-ui.gemspec +4 -4
- data/dev.yml +8 -1
- data/lib/dev/ui.rb +1 -2
- data/lib/dev/ui/formatter.rb +2 -2
- data/lib/dev/ui/frame.rb +16 -16
- data/lib/dev/ui/prompt.rb +21 -6
- data/lib/dev/ui/prompt/interactive_options.rb +169 -0
- data/lib/dev/ui/spinner/spin_group.rb +1 -1
- data/lib/dev/ui/terminal.rb +1 -1
- data/lib/dev/ui/version.rb +1 -1
- metadata +7 -5
- data/bin/testunit +0 -20
- data/lib/dev/ui/interactive_prompt.rb +0 -150
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 32b6a7470b4f74f2448604df24a809d4dd5b6176
         | 
| 4 | 
            +
              data.tar.gz: 47da2a0c8007c5232d7e447ba74406586fa740f4
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 51d64a4852b8b4981c778be9d1e0bbbd98bbe58a18917af5fcf2c05f2f53494380726a9ddb1c48e10481afbeac7ea99a9101562cb9b5464fcbddeef2f4debb40
         | 
| 7 | 
            +
              data.tar.gz: 3749f235fc1b4e30ac3836aec348f8848172ef6d4ae35299c30b6dfc291caab6e4dca9afa891d5bde83c5e3d21dacb736f34c95d370af5a1d900e407cae86813
         | 
    
        data/.rubocop.yml
    CHANGED
    
    | @@ -2,13 +2,7 @@ inherit_from: | |
| 2 2 | 
             
              - http://shopify.github.io/ruby-style-guide/rubocop.yml
         | 
| 3 3 |  | 
| 4 4 | 
             
            AllCops:
         | 
| 5 | 
            -
               | 
| 6 | 
            -
                - 'vendor/**/*'
         | 
| 7 | 
            -
              TargetRubyVersion: 2.0
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            # This doesn't understand that <<~ doesn't exist in 2.0
         | 
| 10 | 
            -
            Style/IndentHeredoc:
         | 
| 11 | 
            -
              Enabled: false
         | 
| 5 | 
            +
              TargetRubyVersion: 2.1
         | 
| 12 6 |  | 
| 13 7 | 
             
            # This doesn't take into account retrying from an exception
         | 
| 14 8 | 
             
            Lint/HandleExceptions:
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                dev-ui (0.1. | 
| 4 | 
            +
                dev-ui (0.1.2)
         | 
| 5 5 |  | 
| 6 6 | 
             
            GEM
         | 
| 7 7 | 
             
              remote: https://rubygems.org/
         | 
| @@ -20,22 +20,21 @@ GEM | |
| 20 20 | 
             
                  ruby-progressbar
         | 
| 21 21 | 
             
                mocha (1.2.1)
         | 
| 22 22 | 
             
                  metaclass (~> 0.0.1)
         | 
| 23 | 
            -
                parallel (1. | 
| 24 | 
            -
                parser (2.4.0. | 
| 25 | 
            -
                  ast (~> 2. | 
| 23 | 
            +
                parallel (1.12.0)
         | 
| 24 | 
            +
                parser (2.4.0.2)
         | 
| 25 | 
            +
                  ast (~> 2.3)
         | 
| 26 26 | 
             
                powerpack (0.1.1)
         | 
| 27 | 
            -
                rainbow ( | 
| 28 | 
            -
                  rake
         | 
| 27 | 
            +
                rainbow (3.0.0)
         | 
| 29 28 | 
             
                rake (10.5.0)
         | 
| 30 | 
            -
                rubocop (0. | 
| 29 | 
            +
                rubocop (0.52.0)
         | 
| 31 30 | 
             
                  parallel (~> 1.10)
         | 
| 32 | 
            -
                  parser (>= 2. | 
| 31 | 
            +
                  parser (>= 2.4.0.2, < 3.0)
         | 
| 33 32 | 
             
                  powerpack (~> 0.1)
         | 
| 34 | 
            -
                  rainbow (>=  | 
| 33 | 
            +
                  rainbow (>= 2.2.2, < 4.0)
         | 
| 35 34 | 
             
                  ruby-progressbar (~> 1.7)
         | 
| 36 35 | 
             
                  unicode-display_width (~> 1.0, >= 1.0.1)
         | 
| 37 | 
            -
                ruby-progressbar (1. | 
| 38 | 
            -
                unicode-display_width (1. | 
| 36 | 
            +
                ruby-progressbar (1.9.0)
         | 
| 37 | 
            +
                unicode-display_width (1.3.0)
         | 
| 39 38 |  | 
| 40 39 | 
             
            PLATFORMS
         | 
| 41 40 | 
             
              ruby
         | 
    
        data/README.md
    CHANGED
    
    | @@ -47,7 +47,6 @@ Prompt user with options and ask them to choose. Can answer using arrow keys, nu | |
| 47 47 |  | 
| 48 48 | 
             
            ```ruby
         | 
| 49 49 | 
             
            Dev::UI.ask('What language/framework do you use?', options: %w(rails go ruby python))
         | 
| 50 | 
            -
            Dev::UI::InteractivePrompt.call(%w(rails go ruby python))
         | 
| 51 50 | 
             
            ```
         | 
| 52 51 |  | 
| 53 52 | 
             
            
         | 
| @@ -150,3 +149,16 @@ end | |
| 150 149 | 
             
            Output:
         | 
| 151 150 |  | 
| 152 151 | 
             
            
         | 
| 152 | 
            +
             | 
| 153 | 
            +
            ## Development
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            - Run tests using `bundle exec rake test`. All code should be tested.
         | 
| 156 | 
            +
            - No code, outside of development and tests needs, should use dependencies. This is a self contained library
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            ## Contributing
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/dev-ui.
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            ## License
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            The code is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            require 'dev/ui'
         | 
| 2 | 
            +
            require 'rake/testtask'
         | 
| 3 | 
            +
            require 'rubocop/rake_task'
         | 
| 4 | 
            +
            require 'bundler/gem_tasks'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            TEST_ROOT = File.expand_path('../test', __FILE__)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Rake::TestTask.new do |t|
         | 
| 9 | 
            +
              t.libs += ["test"]
         | 
| 10 | 
            +
              t.test_files = FileList[File.join(TEST_ROOT, '**', '*_test.rb')]
         | 
| 11 | 
            +
              t.verbose = false
         | 
| 12 | 
            +
              t.warning = false
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            RuboCop::RakeTask.new(:style) do |t|
         | 
| 16 | 
            +
              t.options = ['--display-cop-names']
         | 
| 17 | 
            +
            end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            task default: [:test]
         | 
    
        data/dev-ui.gemspec
    CHANGED
    
    | @@ -6,11 +6,11 @@ require "dev/ui/version" | |
| 6 6 | 
             
            Gem::Specification.new do |spec|
         | 
| 7 7 | 
             
              spec.name          = "dev-ui"
         | 
| 8 8 | 
             
              spec.version       = Dev::UI::VERSION
         | 
| 9 | 
            -
              spec.authors       = ["Burke Libbey", "Julian Nadeau"]
         | 
| 10 | 
            -
              spec.email         = ["burke.libbey@shopify.com", "julian.nadeau@shopify.com"]
         | 
| 9 | 
            +
              spec.authors       = ["Burke Libbey", "Julian Nadeau", "Lisa Ugray"]
         | 
| 10 | 
            +
              spec.email         = ["burke.libbey@shopify.com", "julian.nadeau@shopify.com", "lisa.ugray@shopify.com"]
         | 
| 11 11 |  | 
| 12 | 
            -
              spec.summary       =  | 
| 13 | 
            -
              spec.description   =  | 
| 12 | 
            +
              spec.summary       = 'Terminal UI framework'
         | 
| 13 | 
            +
              spec.description   = 'Terminal UI framework'
         | 
| 14 14 | 
             
              spec.homepage      = "https://github.com/shopify/dev-ui"
         | 
| 15 15 | 
             
              spec.license       = "MIT"
         | 
| 16 16 |  | 
    
        data/dev.yml
    CHANGED
    
    
    
        data/lib/dev/ui.rb
    CHANGED
    
    | @@ -5,7 +5,6 @@ module Dev | |
| 5 5 | 
             
                autoload :Color,              'dev/ui/color'
         | 
| 6 6 | 
             
                autoload :Box,                'dev/ui/box'
         | 
| 7 7 | 
             
                autoload :Frame,              'dev/ui/frame'
         | 
| 8 | 
            -
                autoload :InteractivePrompt,  'dev/ui/interactive_prompt'
         | 
| 9 8 | 
             
                autoload :Progress,           'dev/ui/progress'
         | 
| 10 9 | 
             
                autoload :Prompt,             'dev/ui/prompt'
         | 
| 11 10 | 
             
                autoload :Terminal,           'dev/ui/terminal'
         | 
| @@ -140,7 +139,7 @@ module Dev | |
| 140 139 | 
             
                  yield
         | 
| 141 140 | 
             
                ensure
         | 
| 142 141 | 
             
                  if file_descriptor = Dev::UI::StdoutRouter.duplicate_output_to
         | 
| 143 | 
            -
                    file_descriptor.close | 
| 142 | 
            +
                    file_descriptor.close
         | 
| 144 143 | 
             
                    Dev::UI::StdoutRouter.duplicate_output_to = nil
         | 
| 145 144 | 
             
                  end
         | 
| 146 145 | 
             
                end
         | 
    
        data/lib/dev/ui/formatter.rb
    CHANGED
    
    
    
        data/lib/dev/ui/frame.rb
    CHANGED
    
    | @@ -29,7 +29,7 @@ module Dev | |
| 29 29 | 
             
                    # * +:timing+ - How long did the frame content take? Invalid for blockless. Defaults to true for the block form
         | 
| 30 30 | 
             
                    #
         | 
| 31 31 | 
             
                    # ==== Example
         | 
| 32 | 
            -
                    # | 
| 32 | 
            +
                    #
         | 
| 33 33 | 
             
                    # ===== Block Form (Assumes +Dev::UI::StdoutRouter.enable+ has been called)
         | 
| 34 34 | 
             
                    #
         | 
| 35 35 | 
             
                    #   Dev::UI::Frame.open('Open') { puts 'hi' }
         | 
| @@ -38,7 +38,7 @@ module Dev | |
| 38 38 | 
             
                    #   ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
         | 
| 39 39 | 
             
                    #   ┃ hi
         | 
| 40 40 | 
             
                    #   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (0.0s) ━━
         | 
| 41 | 
            -
                    # | 
| 41 | 
            +
                    #
         | 
| 42 42 | 
             
                    # ===== Blockless Form
         | 
| 43 43 | 
             
                    #
         | 
| 44 44 | 
             
                    #   Dev::UI::Frame.open('Open')
         | 
| @@ -46,7 +46,7 @@ module Dev | |
| 46 46 | 
             
                    # Output:
         | 
| 47 47 | 
             
                    #   ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
         | 
| 48 48 | 
             
                    #
         | 
| 49 | 
            -
                    # | 
| 49 | 
            +
                    #
         | 
| 50 50 | 
             
                    def open(
         | 
| 51 51 | 
             
                      text,
         | 
| 52 52 | 
             
                      color: DEFAULT_FRAME_COLOR,
         | 
| @@ -80,7 +80,7 @@ module Dev | |
| 80 80 | 
             
                      begin
         | 
| 81 81 | 
             
                        success = false
         | 
| 82 82 | 
             
                        success = yield
         | 
| 83 | 
            -
                      rescue | 
| 83 | 
            +
                      rescue
         | 
| 84 84 | 
             
                        closed = true
         | 
| 85 85 | 
             
                        t_diff = timing ? (Time.now.to_f - t_start) : nil
         | 
| 86 86 | 
             
                        close(failure_text, color: :red, elapsed: t_diff)
         | 
| @@ -118,7 +118,7 @@ module Dev | |
| 118 118 | 
             
                    # Output:
         | 
| 119 119 | 
             
                    #   ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
         | 
| 120 120 | 
             
                    #
         | 
| 121 | 
            -
                    # | 
| 121 | 
            +
                    #
         | 
| 122 122 | 
             
                    def close(text, color: DEFAULT_FRAME_COLOR, elapsed: nil)
         | 
| 123 123 | 
             
                      color = Dev::UI.resolve_color(color)
         | 
| 124 124 |  | 
| @@ -188,7 +188,7 @@ module Dev | |
| 188 188 | 
             
                      pfx
         | 
| 189 189 | 
             
                    end
         | 
| 190 190 |  | 
| 191 | 
            -
                    # Override a color for a given thread. | 
| 191 | 
            +
                    # Override a color for a given thread.
         | 
| 192 192 | 
             
                    #
         | 
| 193 193 | 
             
                    # ==== Attributes
         | 
| 194 194 | 
             
                    #
         | 
| @@ -202,7 +202,7 @@ module Dev | |
| 202 202 | 
             
                      Thread.current[:devui_frame_color_override] = prev
         | 
| 203 203 | 
             
                    end
         | 
| 204 204 |  | 
| 205 | 
            -
                    # The width of a prefix given the number of Frames in the stack | 
| 205 | 
            +
                    # The width of a prefix given the number of Frames in the stack
         | 
| 206 206 | 
             
                    #
         | 
| 207 207 | 
             
                    def prefix_width
         | 
| 208 208 | 
             
                      w = FrameStack.items.size
         | 
| @@ -254,15 +254,15 @@ module Dev | |
| 254 254 | 
             
                      # Jumping around the line can cause some unwanted flashes
         | 
| 255 255 | 
             
                      o << Dev::UI::ANSI.hide_cursor
         | 
| 256 256 |  | 
| 257 | 
            -
                      if is_ci
         | 
| 258 | 
            -
             | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 | 
            -
             | 
| 265 | 
            -
             | 
| 257 | 
            +
                      o << if is_ci
         | 
| 258 | 
            +
                             # In CI, we can't use absolute horizontal positions because of timestamps.
         | 
| 259 | 
            +
                             # So we move around the line by offset from this cursor position.
         | 
| 260 | 
            +
                             Dev::UI::ANSI.cursor_save
         | 
| 261 | 
            +
                           else
         | 
| 262 | 
            +
                             # Outside of CI, we reset to column 1 so that things like ^C don't
         | 
| 263 | 
            +
                             # cause output misformatting.
         | 
| 264 | 
            +
                             "\r"
         | 
| 265 | 
            +
                           end
         | 
| 266 266 |  | 
| 267 267 | 
             
                      o << color.code
         | 
| 268 268 | 
             
                      o << Dev::UI::Box::Heavy::HORZ * termwidth # draw a full line
         | 
    
        data/lib/dev/ui/prompt.rb
    CHANGED
    
    | @@ -4,6 +4,9 @@ require 'readline' | |
| 4 4 | 
             
            module Dev
         | 
| 5 5 | 
             
              module UI
         | 
| 6 6 | 
             
                module Prompt
         | 
| 7 | 
            +
                  autoload :InteractiveOptions,  'dev/ui/prompt/interactive_options'
         | 
| 8 | 
            +
                  private_constant :InteractiveOptions
         | 
| 9 | 
            +
             | 
| 7 10 | 
             
                  class << self
         | 
| 8 11 | 
             
                    # Ask a user a question with either free form answer or a set of answers
         | 
| 9 12 | 
             
                    # Do not use this method for yes/no questions. Use +confirm+
         | 
| @@ -12,7 +15,7 @@ module Dev | |
| 12 15 | 
             
                    # * Handles free form answers (options are nil)
         | 
| 13 16 | 
             
                    # * Handles default answers for free form text
         | 
| 14 17 | 
             
                    # * Handles file auto completion for file input
         | 
| 15 | 
            -
                    # * Handles interactively choosing answers using + | 
| 18 | 
            +
                    # * Handles interactively choosing answers using +InteractiveOptions+
         | 
| 16 19 | 
             
                    #
         | 
| 17 20 | 
             
                    # https://user-images.githubusercontent.com/3074765/33799822-47f23302-dd01-11e7-82f3-9072a5a5f611.png
         | 
| 18 21 | 
             
                    #
         | 
| @@ -22,7 +25,7 @@ module Dev | |
| 22 25 | 
             
                    #
         | 
| 23 26 | 
             
                    # ==== Options
         | 
| 24 27 | 
             
                    #
         | 
| 25 | 
            -
                    # * +:options+ - Options to ask the user. Will use + | 
| 28 | 
            +
                    # * +:options+ - Options to ask the user. Will use +InteractiveOptions+ to do so
         | 
| 26 29 | 
             
                    # * +:default+ - The default answer to the question (e.g. they just press enter and don't input anything)
         | 
| 27 30 | 
             
                    # * +:is_file+ - Tells the input to use file auto-completion (tab completion)
         | 
| 28 31 | 
             
                    # * +:allow_empty+ - Allows the answer to be empty
         | 
| @@ -62,8 +65,21 @@ module Dev | |
| 62 65 | 
             
                        puts_question(question)
         | 
| 63 66 | 
             
                      end
         | 
| 64 67 |  | 
| 65 | 
            -
                       | 
| 68 | 
            +
                      # Present the user with options
         | 
| 69 | 
            +
                      if options
         | 
| 70 | 
            +
                        resp = InteractiveOptions.call(options)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                        # Clear the line, and reset the question to include the answer
         | 
| 73 | 
            +
                        print(ANSI.previous_line + ANSI.end_of_line + ' ')
         | 
| 74 | 
            +
                        print(ANSI.cursor_save)
         | 
| 75 | 
            +
                        print(' ' * Dev::UI::Terminal.width)
         | 
| 76 | 
            +
                        print(ANSI.cursor_restore)
         | 
| 77 | 
            +
                        puts_question("#{question} (You chose: {{italic:#{resp}}})")
         | 
| 66 78 |  | 
| 79 | 
            +
                        return resp
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                      # Ask a free form question
         | 
| 67 83 | 
             
                      loop do
         | 
| 68 84 | 
             
                        line = readline(is_file: is_file)
         | 
| 69 85 |  | 
| @@ -83,12 +99,11 @@ module Dev | |
| 83 99 | 
             
                    #
         | 
| 84 100 | 
             
                    # ==== Example Usage:
         | 
| 85 101 | 
             
                    #
         | 
| 86 | 
            -
                    #  | 
| 102 | 
            +
                    # Confirmation question
         | 
| 87 103 | 
             
                    #   Dev::UI::Prompt.confirm('Is the sky blue?')
         | 
| 88 104 | 
             
                    #
         | 
| 89 105 | 
             
                    def confirm(question)
         | 
| 90 | 
            -
                       | 
| 91 | 
            -
                      InteractivePrompt.call(%w(yes no)) == 'yes'
         | 
| 106 | 
            +
                      ask(question, options: %w(yes no)) == 'yes'
         | 
| 92 107 | 
             
                    end
         | 
| 93 108 |  | 
| 94 109 | 
             
                    private
         | 
| @@ -0,0 +1,169 @@ | |
| 1 | 
            +
            require 'io/console'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dev
         | 
| 4 | 
            +
              module UI
         | 
| 5 | 
            +
                module Prompt
         | 
| 6 | 
            +
                  class InteractiveOptions
         | 
| 7 | 
            +
                    # Prompts the user with options
         | 
| 8 | 
            +
                    # Uses an interactive session to allow the user to pick an answer
         | 
| 9 | 
            +
                    # Can use arrows, y/n, numbers (1/2), and vim bindings to control
         | 
| 10 | 
            +
                    #
         | 
| 11 | 
            +
                    # https://user-images.githubusercontent.com/3074765/33797984-0ebb5e64-dcdf-11e7-9e7e-7204f279cece.gif
         | 
| 12 | 
            +
                    #
         | 
| 13 | 
            +
                    # ==== Example Usage:
         | 
| 14 | 
            +
                    #
         | 
| 15 | 
            +
                    # Ask an interactive question
         | 
| 16 | 
            +
                    #   Dev::UI::Prompt::InteractiveOptions.call(%w(rails go python))
         | 
| 17 | 
            +
                    #
         | 
| 18 | 
            +
                    def self.call(options)
         | 
| 19 | 
            +
                      list = new(options)
         | 
| 20 | 
            +
                      options[list.call - 1]
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    # Initializes a new +InteractiveOptions+
         | 
| 24 | 
            +
                    # Usually called from +self.call+
         | 
| 25 | 
            +
                    #
         | 
| 26 | 
            +
                    # ==== Example Usage:
         | 
| 27 | 
            +
                    #
         | 
| 28 | 
            +
                    #   Dev::UI::Prompt::InteractiveOptions.new(%w(rails go python))
         | 
| 29 | 
            +
                    #
         | 
| 30 | 
            +
                    def initialize(options)
         | 
| 31 | 
            +
                      @options = options
         | 
| 32 | 
            +
                      @active = 1
         | 
| 33 | 
            +
                      @marker = '>'
         | 
| 34 | 
            +
                      @answer = nil
         | 
| 35 | 
            +
                      @state = :root
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    # Calls the +InteractiveOptions+ and asks the question
         | 
| 39 | 
            +
                    # Usually used from +self.call+
         | 
| 40 | 
            +
                    #
         | 
| 41 | 
            +
                    def call
         | 
| 42 | 
            +
                      Dev::UI.raw { print(ANSI.hide_cursor) }
         | 
| 43 | 
            +
                      while @answer.nil?
         | 
| 44 | 
            +
                        render_options
         | 
| 45 | 
            +
                        wait_for_user_input
         | 
| 46 | 
            +
                        reset_position
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                      clear_output
         | 
| 49 | 
            +
                      @answer
         | 
| 50 | 
            +
                    ensure
         | 
| 51 | 
            +
                      Dev::UI.raw do
         | 
| 52 | 
            +
                        print(ANSI.show_cursor)
         | 
| 53 | 
            +
                        puts(ANSI.previous_line + ANSI.end_of_line)
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    private
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    def reset_position
         | 
| 60 | 
            +
                      # This will put us back at the beginning of the options
         | 
| 61 | 
            +
                      # When we redraw the options, they will be overwritten
         | 
| 62 | 
            +
                      Dev::UI.raw do
         | 
| 63 | 
            +
                        num_lines.times { print(ANSI.previous_line) }
         | 
| 64 | 
            +
                        print(ANSI.previous_line + ANSI.end_of_line + "\n")
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    def clear_output
         | 
| 69 | 
            +
                      Dev::UI.raw do
         | 
| 70 | 
            +
                        # Write over all lines with whitespace
         | 
| 71 | 
            +
                        num_lines.times { puts(' ' * Dev::UI::Terminal.width) }
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
                      reset_position
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    def num_lines
         | 
| 77 | 
            +
                      # @options will be an array of questions but each option can be multi-line
         | 
| 78 | 
            +
                      # so to get the # of lines, you need to join then split
         | 
| 79 | 
            +
                      joined_options = @options.join("\n")
         | 
| 80 | 
            +
                      joined_options.split("\n").reject(&:empty?).size
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    ESC = "\e"
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    def up
         | 
| 86 | 
            +
                      @active = @active - 1 >= 1 ? @active - 1 : @options.length
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    def down
         | 
| 90 | 
            +
                      @active = @active + 1 <= @options.length ? @active + 1 : 1
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    def select_n(n)
         | 
| 94 | 
            +
                      @active = n
         | 
| 95 | 
            +
                      @answer = n
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                    def select_bool(char)
         | 
| 99 | 
            +
                      return unless (@options - %w(yes no)).empty?
         | 
| 100 | 
            +
                      opt = @options.detect { |o| o.start_with?(char) }
         | 
| 101 | 
            +
                      @active = @options.index(opt) + 1
         | 
| 102 | 
            +
                      @answer = @options.index(opt) + 1
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    # rubocop:disable Style/WhenThen,Layout/SpaceBeforeSemicolon
         | 
| 106 | 
            +
                    def wait_for_user_input
         | 
| 107 | 
            +
                      char = read_char
         | 
| 108 | 
            +
                      case @state
         | 
| 109 | 
            +
                      when :root
         | 
| 110 | 
            +
                        case char
         | 
| 111 | 
            +
                        when ESC                       ; @state = :esc
         | 
| 112 | 
            +
                        when 'k'                       ; up
         | 
| 113 | 
            +
                        when 'j'                       ; down
         | 
| 114 | 
            +
                        when ('1'..@options.size.to_s) ; select_n(char.to_i)
         | 
| 115 | 
            +
                        when 'y', 'n'                  ; select_bool(char)
         | 
| 116 | 
            +
                        when " ", "\r", "\n"           ; @answer = @active # <enter>
         | 
| 117 | 
            +
                        when "\u0003"                  ; raise Interrupt   # Ctrl-c
         | 
| 118 | 
            +
                        end
         | 
| 119 | 
            +
                      when :esc
         | 
| 120 | 
            +
                        case char
         | 
| 121 | 
            +
                        when '[' ; @state = :esc_bracket
         | 
| 122 | 
            +
                        else     ; raise Interrupt # unhandled escape sequence.
         | 
| 123 | 
            +
                        end
         | 
| 124 | 
            +
                      when :esc_bracket
         | 
| 125 | 
            +
                        @state = :root
         | 
| 126 | 
            +
                        case char
         | 
| 127 | 
            +
                        when 'A' ; up
         | 
| 128 | 
            +
                        when 'B' ; down
         | 
| 129 | 
            +
                        else     ; raise Interrupt # unhandled escape sequence.
         | 
| 130 | 
            +
                        end
         | 
| 131 | 
            +
                      end
         | 
| 132 | 
            +
                    end
         | 
| 133 | 
            +
                    # rubocop:enable Style/WhenThen,Layout/SpaceBeforeSemicolon
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    def read_char
         | 
| 136 | 
            +
                      raw_tty! { $stdin.getc.chr }
         | 
| 137 | 
            +
                    rescue IOError
         | 
| 138 | 
            +
                      "\e"
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    def raw_tty!
         | 
| 142 | 
            +
                      if ENV['TEST'] || !$stdin.tty?
         | 
| 143 | 
            +
                        yield
         | 
| 144 | 
            +
                      else
         | 
| 145 | 
            +
                        $stdin.raw { yield }
         | 
| 146 | 
            +
                      end
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    def render_options
         | 
| 150 | 
            +
                      @options.each_with_index do |choice, index|
         | 
| 151 | 
            +
                        num = index + 1
         | 
| 152 | 
            +
                        message = "  #{num}."
         | 
| 153 | 
            +
                        message += choice.split("\n").map { |l| " {{bold:#{l}}}" }.join("\n")
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                        if num == @active
         | 
| 156 | 
            +
                          message = message.split("\n").map.with_index do |l, idx|
         | 
| 157 | 
            +
                            idx == 0 ? "{{blue:> #{l.strip}}}" : "{{blue:>#{l.strip}}}"
         | 
| 158 | 
            +
                          end.join("\n")
         | 
| 159 | 
            +
                        end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                        Dev::UI.with_frame_color(:blue) do
         | 
| 162 | 
            +
                          puts Dev::UI.fmt(message)
         | 
| 163 | 
            +
                        end
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
              end
         | 
| 169 | 
            +
            end
         | 
    
        data/lib/dev/ui/terminal.rb
    CHANGED
    
    
    
        data/lib/dev/ui/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,16 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dev-ui
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.1. | 
| 4 | 
            +
              version: 0.1.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Burke Libbey
         | 
| 8 8 | 
             
            - Julian Nadeau
         | 
| 9 | 
            +
            - Lisa Ugray
         | 
| 9 10 | 
             
            autorequire: 
         | 
| 10 11 | 
             
            bindir: exe
         | 
| 11 12 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2017-12- | 
| 13 | 
            +
            date: 2017-12-13 00:00:00.000000000 Z
         | 
| 13 14 | 
             
            dependencies:
         | 
| 14 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 16 | 
             
              name: bundler
         | 
| @@ -57,6 +58,7 @@ description: Terminal UI framework | |
| 57 58 | 
             
            email:
         | 
| 58 59 | 
             
            - burke.libbey@shopify.com
         | 
| 59 60 | 
             
            - julian.nadeau@shopify.com
         | 
| 61 | 
            +
            - lisa.ugray@shopify.com
         | 
| 60 62 | 
             
            executables: []
         | 
| 61 63 | 
             
            extensions: []
         | 
| 62 64 | 
             
            extra_rdoc_files: []
         | 
| @@ -68,8 +70,8 @@ files: | |
| 68 70 | 
             
            - Gemfile.lock
         | 
| 69 71 | 
             
            - LICENSE.txt
         | 
| 70 72 | 
             
            - README.md
         | 
| 73 | 
            +
            - Rakefile
         | 
| 71 74 | 
             
            - bin/console
         | 
| 72 | 
            -
            - bin/testunit
         | 
| 73 75 | 
             
            - dev-ui.gemspec
         | 
| 74 76 | 
             
            - dev.yml
         | 
| 75 77 | 
             
            - lib/dev/ui.rb
         | 
| @@ -79,9 +81,9 @@ files: | |
| 79 81 | 
             
            - lib/dev/ui/formatter.rb
         | 
| 80 82 | 
             
            - lib/dev/ui/frame.rb
         | 
| 81 83 | 
             
            - lib/dev/ui/glyph.rb
         | 
| 82 | 
            -
            - lib/dev/ui/interactive_prompt.rb
         | 
| 83 84 | 
             
            - lib/dev/ui/progress.rb
         | 
| 84 85 | 
             
            - lib/dev/ui/prompt.rb
         | 
| 86 | 
            +
            - lib/dev/ui/prompt/interactive_options.rb
         | 
| 85 87 | 
             
            - lib/dev/ui/spinner.rb
         | 
| 86 88 | 
             
            - lib/dev/ui/spinner/async.rb
         | 
| 87 89 | 
             
            - lib/dev/ui/spinner/spin_group.rb
         | 
| @@ -108,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 108 110 | 
             
                  version: '0'
         | 
| 109 111 | 
             
            requirements: []
         | 
| 110 112 | 
             
            rubyforge_project: 
         | 
| 111 | 
            -
            rubygems_version: 2. | 
| 113 | 
            +
            rubygems_version: 2.5.2.1
         | 
| 112 114 | 
             
            signing_key: 
         | 
| 113 115 | 
             
            specification_version: 4
         | 
| 114 116 | 
             
            summary: Terminal UI framework
         | 
    
        data/bin/testunit
    DELETED
    
    | @@ -1,20 +0,0 @@ | |
| 1 | 
            -
            #!/usr/bin/env ruby --disable-gems
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            root = File.expand_path('../..', __FILE__)
         | 
| 4 | 
            -
            DEV_TEST_ROOT = root + '/test'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            $LOAD_PATH.unshift(DEV_TEST_ROOT)
         | 
| 7 | 
            -
             | 
| 8 | 
            -
            def test_files
         | 
| 9 | 
            -
              Dir.glob(DEV_TEST_ROOT + "/**/*_test.rb")
         | 
| 10 | 
            -
            end
         | 
| 11 | 
            -
             | 
| 12 | 
            -
            if ARGV.empty?
         | 
| 13 | 
            -
              test_files.each { |f| require(f) }
         | 
| 14 | 
            -
              exit 0
         | 
| 15 | 
            -
            end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
            # A list of files is presumed to be specified
         | 
| 18 | 
            -
            ARGV.each do |a|
         | 
| 19 | 
            -
              require a.sub(%r{^test/}, '').sub(%r{^misc/gatekeeper/test/}, '')
         | 
| 20 | 
            -
            end
         | 
| @@ -1,150 +0,0 @@ | |
| 1 | 
            -
            require 'io/console'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Dev
         | 
| 4 | 
            -
              module UI
         | 
| 5 | 
            -
                class InteractivePrompt
         | 
| 6 | 
            -
                  # Prompts the user with options
         | 
| 7 | 
            -
                  # Uses an interactive session to allow the user to pick an answer
         | 
| 8 | 
            -
                  # Can use arrows, y/n, numbers (1/2), and vim bindings to control
         | 
| 9 | 
            -
                  #
         | 
| 10 | 
            -
                  # https://user-images.githubusercontent.com/3074765/33797984-0ebb5e64-dcdf-11e7-9e7e-7204f279cece.gif
         | 
| 11 | 
            -
                  #
         | 
| 12 | 
            -
                  # ==== Example Usage:
         | 
| 13 | 
            -
                  #
         | 
| 14 | 
            -
                  # Ask an interactive question
         | 
| 15 | 
            -
                  #   Dev::UI::InteractivePrompt.call(%w(rails go python))
         | 
| 16 | 
            -
                  #
         | 
| 17 | 
            -
                  def self.call(options)
         | 
| 18 | 
            -
                    list = new(options)
         | 
| 19 | 
            -
                    options[list.call - 1]
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                  # Initializes a new +InteractivePrompt+
         | 
| 23 | 
            -
                  # Usually called from +self.call+
         | 
| 24 | 
            -
                  #
         | 
| 25 | 
            -
                  # ==== Example Usage:
         | 
| 26 | 
            -
                  #
         | 
| 27 | 
            -
                  #   Dev::UI::InteractivePrompt.new(%w(rails go python))
         | 
| 28 | 
            -
                  #
         | 
| 29 | 
            -
                  def initialize(options)
         | 
| 30 | 
            -
                    @options = options
         | 
| 31 | 
            -
                    @active = 1
         | 
| 32 | 
            -
                    @marker = '>'
         | 
| 33 | 
            -
                    @answer = nil
         | 
| 34 | 
            -
                    @state = :root
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                  # Calls the +InteractivePrompt+ and asks the question
         | 
| 38 | 
            -
                  # Usually used from +self.call+
         | 
| 39 | 
            -
                  #
         | 
| 40 | 
            -
                  def call
         | 
| 41 | 
            -
                    Dev::UI.raw { print(ANSI.hide_cursor) }
         | 
| 42 | 
            -
                    while @answer.nil?
         | 
| 43 | 
            -
                      render_options
         | 
| 44 | 
            -
                      wait_for_user_input
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                      # This will put us back at the beginning of the options
         | 
| 47 | 
            -
                      # When we redraw the options, they will be overwritten
         | 
| 48 | 
            -
                      Dev::UI.raw do
         | 
| 49 | 
            -
                        num_lines = @options.join("\n").split("\n").reject(&:empty?).size
         | 
| 50 | 
            -
                        num_lines.times { print(ANSI.previous_line) }
         | 
| 51 | 
            -
                        print(ANSI.previous_line + ANSI.end_of_line + "\n")
         | 
| 52 | 
            -
                      end
         | 
| 53 | 
            -
                    end
         | 
| 54 | 
            -
                    render_options
         | 
| 55 | 
            -
                    @answer
         | 
| 56 | 
            -
                  ensure
         | 
| 57 | 
            -
                    Dev::UI.raw do
         | 
| 58 | 
            -
                      print(ANSI.show_cursor)
         | 
| 59 | 
            -
                      puts(ANSI.previous_line + ANSI.end_of_line)
         | 
| 60 | 
            -
                    end
         | 
| 61 | 
            -
                  end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                  private
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                  ESC = "\e"
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                  def up
         | 
| 68 | 
            -
                    @active = @active - 1 >= 1 ? @active - 1 : @options.length
         | 
| 69 | 
            -
                  end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                  def down
         | 
| 72 | 
            -
                    @active = @active + 1 <= @options.length ? @active + 1 : 1
         | 
| 73 | 
            -
                  end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                  def select_n(n)
         | 
| 76 | 
            -
                    @active = n
         | 
| 77 | 
            -
                    @answer = n
         | 
| 78 | 
            -
                  end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                  def select_bool(char)
         | 
| 81 | 
            -
                    return unless (@options - %w(yes no)).empty?
         | 
| 82 | 
            -
                    opt = @options.detect { |o| o.start_with?(char) }
         | 
| 83 | 
            -
                    @active = @options.index(opt) + 1
         | 
| 84 | 
            -
                    @answer = @options.index(opt) + 1
         | 
| 85 | 
            -
                  end
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                  # rubocop:disable Style/WhenThen,Layout/SpaceBeforeSemicolon
         | 
| 88 | 
            -
                  def wait_for_user_input
         | 
| 89 | 
            -
                    char = read_char
         | 
| 90 | 
            -
                    case @state
         | 
| 91 | 
            -
                    when :root
         | 
| 92 | 
            -
                      case char
         | 
| 93 | 
            -
                      when ESC                       ; @state = :esc
         | 
| 94 | 
            -
                      when 'k'                       ; up
         | 
| 95 | 
            -
                      when 'j'                       ; down
         | 
| 96 | 
            -
                      when ('1'..@options.size.to_s) ; select_n(char.to_i)
         | 
| 97 | 
            -
                      when 'y', 'n'                  ; select_bool(char)
         | 
| 98 | 
            -
                      when " ", "\r", "\n"           ; @answer = @active # <enter>
         | 
| 99 | 
            -
                      when "\u0003"                  ; raise Interrupt   # Ctrl-c
         | 
| 100 | 
            -
                      end
         | 
| 101 | 
            -
                    when :esc
         | 
| 102 | 
            -
                      case char
         | 
| 103 | 
            -
                      when '[' ; @state = :esc_bracket
         | 
| 104 | 
            -
                      else     ; raise Interrupt # unhandled escape sequence.
         | 
| 105 | 
            -
                      end
         | 
| 106 | 
            -
                    when :esc_bracket
         | 
| 107 | 
            -
                      @state = :root
         | 
| 108 | 
            -
                      case char
         | 
| 109 | 
            -
                      when 'A' ; up
         | 
| 110 | 
            -
                      when 'B' ; down
         | 
| 111 | 
            -
                      else     ; raise Interrupt # unhandled escape sequence.
         | 
| 112 | 
            -
                      end
         | 
| 113 | 
            -
                    end
         | 
| 114 | 
            -
                  end
         | 
| 115 | 
            -
                  # rubocop:enable Style/WhenThen,Layout/SpaceBeforeSemicolon
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                  def read_char
         | 
| 118 | 
            -
                    raw_tty! { $stdin.getc.chr }
         | 
| 119 | 
            -
                  rescue IOError
         | 
| 120 | 
            -
                    "\e"
         | 
| 121 | 
            -
                  end
         | 
| 122 | 
            -
             | 
| 123 | 
            -
                  def raw_tty!
         | 
| 124 | 
            -
                    if ENV['TEST'] || !$stdin.tty?
         | 
| 125 | 
            -
                      yield
         | 
| 126 | 
            -
                    else
         | 
| 127 | 
            -
                      $stdin.raw { yield }
         | 
| 128 | 
            -
                    end
         | 
| 129 | 
            -
                  end
         | 
| 130 | 
            -
             | 
| 131 | 
            -
                  def render_options
         | 
| 132 | 
            -
                    @options.each_with_index do |choice, index|
         | 
| 133 | 
            -
                      num = index + 1
         | 
| 134 | 
            -
                      message = "  #{num}."
         | 
| 135 | 
            -
                      message += choice.split("\n").map { |l| " {{bold:#{l}}}" }.join("\n")
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                      if num == @active
         | 
| 138 | 
            -
                        message = message.split("\n").map.with_index do |l, idx|
         | 
| 139 | 
            -
                          idx == 0 ? "{{blue:> #{l.strip}}}" : "{{blue:>#{l.strip}}}"
         | 
| 140 | 
            -
                        end.join("\n")
         | 
| 141 | 
            -
                      end
         | 
| 142 | 
            -
             | 
| 143 | 
            -
                      Dev::UI.with_frame_color(:blue) do
         | 
| 144 | 
            -
                        puts Dev::UI.fmt(message)
         | 
| 145 | 
            -
                      end
         | 
| 146 | 
            -
                    end
         | 
| 147 | 
            -
                  end
         | 
| 148 | 
            -
                end
         | 
| 149 | 
            -
              end
         | 
| 150 | 
            -
            end
         |