dev-ui 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6e7b92fe38bc670b2bb8081569bcbe455d77fdbe
4
+ data.tar.gz: 92af92c2bf2a12df2024c45c34815d796fe9d46d
5
+ SHA512:
6
+ metadata.gz: 2f895bdd2ed2abb4803a03a575688fef49799797a3242b2a9b9449a71832c329f899f68d200254849824e76b9db9f23f8c42b258d83afbe1b059a9a9c40c72bb
7
+ data.tar.gz: 51e15314102eb1d5dbd1a9c2e959a88369d26617b9a4a91682b80dd627dc2dd3736d20156b467a2639d069d5662e31c235c85614ac4a3e0678c8c0d4a9623f26
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ *.gem
2
+ build
3
+ .vagrant
4
+ .DS_Store
5
+ .bundle
6
+
7
+ .starscope.db
8
+ cscope.out
9
+ tags
10
+
11
+ .byebug_history
12
+
13
+ .rubocop-*
14
+ doc
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ inherit_from:
2
+ - http://shopify.github.io/ruby-style-guide/rubocop.yml
3
+
4
+ AllCops:
5
+ Exclude:
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
12
+
13
+ # This doesn't take into account retrying from an exception
14
+ Lint/HandleExceptions:
15
+ Enabled: false
16
+
17
+ # allow String.new to create mutable strings
18
+ Style/EmptyLiteral:
19
+ Enabled: false
20
+
21
+ # allow the use of globals which makes sense in a CLI app like this
22
+ Style/GlobalVars:
23
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.15.0
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # NOTE: These are development-only dependencies
2
+ source "https://rubygems.org"
3
+
4
+ group :development, :test do
5
+ gem 'rubocop'
6
+ gem 'byebug'
7
+ gem 'method_source'
8
+ end
9
+
10
+ group :test do
11
+ gem 'mocha', require: false
12
+ gem 'minitest', '>= 5.0.0', require: false
13
+ gem 'minitest-reporters', require: false
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ ansi (1.5.0)
5
+ ast (2.3.0)
6
+ builder (3.2.3)
7
+ byebug (9.0.6)
8
+ metaclass (0.0.4)
9
+ method_source (0.8.2)
10
+ minitest (5.10.2)
11
+ minitest-reporters (1.1.14)
12
+ ansi
13
+ builder
14
+ minitest (>= 5.0)
15
+ ruby-progressbar
16
+ mocha (1.2.1)
17
+ metaclass (~> 0.0.1)
18
+ parallel (1.11.2)
19
+ parser (2.4.0.0)
20
+ ast (~> 2.2)
21
+ powerpack (0.1.1)
22
+ rainbow (2.2.2)
23
+ rake
24
+ rake (12.0.0)
25
+ rubocop (0.49.1)
26
+ parallel (~> 1.10)
27
+ parser (>= 2.3.3.1, < 3.0)
28
+ powerpack (~> 0.1)
29
+ rainbow (>= 1.99.1, < 3.0)
30
+ ruby-progressbar (~> 1.7)
31
+ unicode-display_width (~> 1.0, >= 1.0.1)
32
+ ruby-progressbar (1.8.1)
33
+ unicode-display_width (1.2.1)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ byebug
40
+ method_source
41
+ minitest (>= 5.0.0)
42
+ minitest-reporters
43
+ mocha
44
+ rubocop
45
+
46
+ BUNDLED WITH
47
+ 1.15.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Burke Libbey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ ```ruby
2
+ require 'dev/ui'
3
+
4
+ Dev::UI::Frame.open('{{*}} {{bold:a}}', color: :green) do
5
+ Dev::UI::Frame.open('{{i}} b', color: :magenta) do
6
+ Dev::UI::Frame.open('{{?}} c', color: :cyan) do
7
+ sg = Dev::UI::SpinGroup.new
8
+ sg.add('wow') { sleep(2.5) }
9
+ sg.add('such spin') { sleep(1.6) }
10
+ sg.add('many glyph') { sleep(2.0) }
11
+ sg.wait
12
+ end
13
+ end
14
+ Dev::UI::Frame.divider('{{v}} lol')
15
+ puts 'words'
16
+ sg = Dev::UI::SpinGroup.new
17
+ sg.add('more spins') { sleep(0.5) ; raise 'oh no' }
18
+ sg.wait
19
+ end
20
+ ```
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dev/ui"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/testunit ADDED
@@ -0,0 +1,20 @@
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
data/dev-ui.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "dev/ui/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dev-ui"
8
+ spec.version = Dev::UI::VERSION
9
+ spec.authors = ["Burke Libbey"]
10
+ spec.email = ["burke@libbey.me"]
11
+
12
+ spec.summary = %q{Terminal UI framework}
13
+ spec.description = %q{Terminal UI framework}
14
+ spec.homepage = "https://github.com/shopify/dev-ui"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.15"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ end
data/dev.yml ADDED
@@ -0,0 +1,8 @@
1
+ up:
2
+ - homebrew:
3
+ - ruby: 2.3.3
4
+ - bundler
5
+
6
+ commands:
7
+ test: bin/testunit
8
+ style: "bundle exec rubocop -D"
data/lib/dev/ui.rb ADDED
@@ -0,0 +1,85 @@
1
+ module Dev
2
+ module UI
3
+ autoload :ANSI, 'dev/ui/ansi'
4
+ autoload :Glyph, 'dev/ui/glyph'
5
+ autoload :Color, 'dev/ui/color'
6
+ autoload :Box, 'dev/ui/box'
7
+ autoload :Frame, 'dev/ui/frame'
8
+ autoload :InteractivePrompt, 'dev/ui/interactive_prompt'
9
+ autoload :Progress, 'dev/ui/progress'
10
+ autoload :Prompt, 'dev/ui/prompt'
11
+ autoload :Terminal, 'dev/ui/terminal'
12
+ autoload :Formatter, 'dev/ui/formatter'
13
+ autoload :Spinner, 'dev/ui/spinner'
14
+
15
+
16
+ # TODO: this, better
17
+ SpinGroup = Spinner::SpinGroup
18
+
19
+ # TODO: test
20
+ def self.glyph(handle)
21
+ Dev::UI::Glyph.lookup(handle)
22
+ end
23
+
24
+ # TODO: test
25
+ def self.resolve_color(input)
26
+ case input
27
+ when Symbol
28
+ Dev::UI::Color.lookup(input)
29
+ else
30
+ input
31
+ end
32
+ end
33
+
34
+ def self.confirm(question)
35
+ Dev::UI::Prompt.confirm(question)
36
+ end
37
+
38
+ def self.ask(question, **kwargs)
39
+ Dev::UI::Prompt.ask(question, **kwargs)
40
+ end
41
+
42
+ def self.resolve_text(input)
43
+ return input if input.nil?
44
+ Dev::UI::Formatter.new(input).format
45
+ end
46
+
47
+ def self.fmt(input, enable_color: true)
48
+ Dev::UI::Formatter.new(input).format(enable_color: enable_color)
49
+ end
50
+
51
+ def self.frame(*args, &block)
52
+ Dev::UI::Frame.open(*args, &block)
53
+ end
54
+
55
+ def self.spinner(*args, &block)
56
+ Dev::UI::Spinner.spin(*args, &block)
57
+ end
58
+
59
+ def self.with_frame_color(color, &block)
60
+ Dev::UI::Frame.with_frame_color_override(color, &block)
61
+ end
62
+
63
+ def self.log_output_to(path)
64
+ if Dev::UI::StdoutRouter.duplicate_output_to
65
+ raise "multiple logs not allowed"
66
+ end
67
+ Dev::UI::StdoutRouter.duplicate_output_to = File.open(path, 'w')
68
+ yield
69
+ ensure
70
+ f = Dev::UI::StdoutRouter.duplicate_output_to
71
+ f.close
72
+ Dev::UI::StdoutRouter.duplicate_output_to = nil
73
+ end
74
+
75
+ def self.raw
76
+ prev = Thread.current[:no_devui_frame_inset]
77
+ Thread.current[:no_devui_frame_inset] = true
78
+ yield
79
+ ensure
80
+ Thread.current[:no_devui_frame_inset] = prev
81
+ end
82
+ end
83
+ end
84
+
85
+ require 'dev/ui/stdout_router'
@@ -0,0 +1,74 @@
1
+ require 'dev/ui'
2
+
3
+ module Dev
4
+ module UI
5
+ module ANSI
6
+ ESC = "\x1b"
7
+
8
+ # ANSI escape sequences (like \x1b[31m) have zero width.
9
+ # when calculating the padding width, we must exclude them.
10
+ def self.printing_width(str)
11
+ strip_codes(str).size
12
+ end
13
+
14
+ def self.strip_codes(str)
15
+ str.gsub(/\x1b\[[\d;]+[A-z]|\r/, '')
16
+ end
17
+
18
+ def self.control(args, cmd)
19
+ ESC + "[" + args + cmd
20
+ end
21
+
22
+ # https://en.wikipedia.org/wiki/ANSI_escape_code#graphics
23
+ def self.sgr(params)
24
+ control(params.to_s, 'm')
25
+ end
26
+
27
+ # Cursor Movement
28
+
29
+ def self.cursor_up(n = 1)
30
+ return '' if n.zero?
31
+ control(n.to_s, 'A')
32
+ end
33
+
34
+ def self.cursor_down(n = 1)
35
+ return '' if n.zero?
36
+ control(n.to_s, 'B')
37
+ end
38
+
39
+ def self.cursor_forward(n = 1)
40
+ return '' if n.zero?
41
+ control(n.to_s, 'C')
42
+ end
43
+
44
+ def self.cursor_back(n = 1)
45
+ return '' if n.zero?
46
+ control(n.to_s, 'D')
47
+ end
48
+
49
+ # Cursor Visibility
50
+
51
+ def self.show_cursor
52
+ control('', "?25h")
53
+ end
54
+
55
+ def self.hide_cursor
56
+ control('', "?25l")
57
+ end
58
+
59
+ # Line Handling
60
+
61
+ def self.next_line
62
+ cursor_down + control('1', 'G')
63
+ end
64
+
65
+ def self.previous_line
66
+ cursor_up + control('1', 'G')
67
+ end
68
+
69
+ def self.end_of_line
70
+ control("\033[", 'C')
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/dev/ui/box.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'dev/ui'
2
+
3
+ module Dev
4
+ module UI
5
+ module Box
6
+ module Heavy
7
+ VERT = '┃'
8
+ HORZ = '━'
9
+ DIV = "┣"
10
+ TL = '┏'
11
+ BL = '┗'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ require 'dev/ui'
2
+
3
+ module Dev
4
+ module UI
5
+ class Color
6
+ attr_reader :sgr, :name, :code
7
+ def initialize(sgr, name)
8
+ @sgr = sgr
9
+ @code = Dev::UI::ANSI.sgr(sgr)
10
+ @name = name
11
+ end
12
+
13
+ RED = new('31', :red)
14
+ GREEN = new('32', :green)
15
+ YELLOW = new('33', :yellow)
16
+ # default blue is low-contrast against black in some default terminal color scheme
17
+ BLUE = new('94', :blue) # 9x = high-intensity fg color x
18
+ MAGENTA = new('35', :magenta)
19
+ CYAN = new('36', :cyan)
20
+ RESET = new('0', :reset)
21
+ BOLD = new('1', :bold)
22
+ WHITE = new('97', :white)
23
+
24
+ MAP = {
25
+ red: RED,
26
+ green: GREEN,
27
+ yellow: YELLOW,
28
+ blue: BLUE,
29
+ magenta: MAGENTA,
30
+ cyan: CYAN,
31
+ reset: RESET,
32
+ bold: BOLD,
33
+ }.freeze
34
+
35
+ class InvalidColorName < ArgumentError
36
+ def initialize(name)
37
+ @name = name
38
+ end
39
+
40
+ def message
41
+ keys = Color.available.map(&:inspect).join(',')
42
+ "invalid color: #{@name.inspect} " \
43
+ "-- must be one of Dev::UI::Color.available (#{keys})"
44
+ end
45
+ end
46
+
47
+ def self.lookup(name)
48
+ MAP.fetch(name)
49
+ rescue KeyError
50
+ raise InvalidColorName, name
51
+ end
52
+
53
+ def self.available
54
+ MAP.keys
55
+ end
56
+ end
57
+ end
58
+ end