boxybox 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +91 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +153 -0
- data/LICENSE.txt +20 -0
- data/README.md +157 -0
- data/Rakefile +16 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/boxybox.gemspec +70 -0
- data/exe/boxybox +18 -0
- data/ext/boxybox/boxybox.c +9 -0
- data/ext/boxybox/boxybox.h +6 -0
- data/ext/boxybox/extconf.rb +5 -0
- data/lib/boxybox.rb +8 -0
- data/lib/boxybox/cli.rb +41 -0
- data/lib/boxybox/command.rb +122 -0
- data/lib/boxybox/commands/.gitkeep +1 -0
- data/lib/boxybox/commands/boxes.rb +33 -0
- data/lib/boxybox/models/box_builder.rb +78 -0
- data/lib/boxybox/templates/.gitkeep +1 -0
- data/lib/boxybox/templates/boxes/.gitkeep +1 -0
- data/lib/boxybox/templates/boxes/example1.json +46 -0
- data/lib/boxybox/templates/boxes/example2.json +46 -0
- data/lib/boxybox/templates/boxes/example3.json +25 -0
- data/lib/boxybox/templates/boxes/example4.json +49 -0
- data/lib/boxybox/version.rb +5 -0
- metadata +458 -0
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require 'rake/extensiontask'
|
9
|
+
|
10
|
+
task build: :compile
|
11
|
+
|
12
|
+
Rake::ExtensionTask.new('boxybox') do |ext|
|
13
|
+
ext.lib_dir = 'lib/boxybox'
|
14
|
+
end
|
15
|
+
|
16
|
+
task default: %i[clobber compile spec]
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "boxybox"
|
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/setup
ADDED
data/boxybox.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'boxybox/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'boxybox'
|
9
|
+
spec.license = 'MIT'
|
10
|
+
spec.version = Boxybox::VERSION
|
11
|
+
spec.authors = ['Travis Carter']
|
12
|
+
spec.email = ['travis.carter79@outlook.com']
|
13
|
+
|
14
|
+
spec.summary = 'Box generator for an at home code challenge'
|
15
|
+
spec.description = 'Draws terminal ascii boxes, and boxes within boxes'
|
16
|
+
spec.homepage = 'https://github.com/phatjam98/boxybox'
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
22
|
+
|
23
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
24
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
25
|
+
spec.metadata['changelog_uri'] = spec.homepage
|
26
|
+
else
|
27
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
28
|
+
'public gem pushes.'
|
29
|
+
end
|
30
|
+
|
31
|
+
# Specify which files should be added to the gem when it is released.
|
32
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
33
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
34
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
35
|
+
end
|
36
|
+
spec.bindir = 'exe'
|
37
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
38
|
+
spec.require_paths = ['lib']
|
39
|
+
spec.extensions = ['ext/boxybox/extconf.rb']
|
40
|
+
spec.required_ruby_version = '>2.6'
|
41
|
+
|
42
|
+
spec.add_dependency 'pastel', '~> 0.7.2'
|
43
|
+
spec.add_dependency 'thor', '~> 0.20.0'
|
44
|
+
spec.add_dependency 'tty-box', '~> 0.5.0'
|
45
|
+
spec.add_dependency 'tty-color', '~> 0.5'
|
46
|
+
spec.add_dependency 'tty-command', '~> 0.9.0'
|
47
|
+
spec.add_dependency 'tty-config', '~> 0.3.2'
|
48
|
+
spec.add_dependency 'tty-cursor', '~> 0.7'
|
49
|
+
spec.add_dependency 'tty-editor', '~> 0.5'
|
50
|
+
spec.add_dependency 'tty-file', '~> 0.8.0'
|
51
|
+
spec.add_dependency 'tty-font', '~> 0.4.0'
|
52
|
+
spec.add_dependency 'tty-logger', '~> 0.2.0'
|
53
|
+
spec.add_dependency 'tty-markdown', '~> 0.6.0'
|
54
|
+
spec.add_dependency 'tty-pager', '~> 0.12'
|
55
|
+
spec.add_dependency 'tty-pie', '~> 0.3.0'
|
56
|
+
spec.add_dependency 'tty-platform', '~> 0.2'
|
57
|
+
spec.add_dependency 'tty-progressbar', '~> 0.17'
|
58
|
+
spec.add_dependency 'tty-prompt', '~> 0.19'
|
59
|
+
spec.add_dependency 'tty-screen', '~> 0.7'
|
60
|
+
spec.add_dependency 'tty-spinner', '~> 0.9'
|
61
|
+
spec.add_dependency 'tty-table', '~> 0.11.0'
|
62
|
+
spec.add_dependency 'tty-tree', '~> 0.3'
|
63
|
+
spec.add_dependency 'tty-which', '~> 0.4'
|
64
|
+
|
65
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
66
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
67
|
+
spec.add_development_dependency 'rake-compiler'
|
68
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
69
|
+
spec.add_development_dependency 'rubocop', '~> 0.89.0'
|
70
|
+
end
|
data/exe/boxybox
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib_path = File.expand_path('../lib', __dir__)
|
5
|
+
$:.unshift(lib_path) if !$:.include?(lib_path)
|
6
|
+
require 'boxybox/cli'
|
7
|
+
|
8
|
+
Signal.trap('INT') do
|
9
|
+
warn("\n#{caller.join("\n")}: interrupted")
|
10
|
+
exit(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Boxybox::CLI.start
|
15
|
+
rescue Boxybox::CLI::Error => err
|
16
|
+
puts "ERROR: #{err.message}"
|
17
|
+
exit 1
|
18
|
+
end
|
data/lib/boxybox.rb
ADDED
data/lib/boxybox/cli.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Boxybox
|
6
|
+
# Handle the application command line parsing
|
7
|
+
# and the dispatch to various command objects
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class CLI < Thor
|
11
|
+
# Error raised by this runner
|
12
|
+
Error = Class.new(StandardError)
|
13
|
+
|
14
|
+
desc 'version', 'boxybox version'
|
15
|
+
def version
|
16
|
+
require_relative 'version'
|
17
|
+
puts "v#{Boxybox::VERSION}"
|
18
|
+
end
|
19
|
+
map %w(--version -v) => :version
|
20
|
+
|
21
|
+
desc 'boxes [INPUT]', 'Accepts a config as defined in the README and prints ASCII boxes in the terminal'
|
22
|
+
method_option :help, aliases: '-h', type: :boolean,
|
23
|
+
desc: 'Display usage information'
|
24
|
+
method_option :file, aliases: '-f', type: :string,
|
25
|
+
desc: 'Specify a file path to use as the input'
|
26
|
+
method_option :example, aliases: '-e', type: :string,
|
27
|
+
desc: 'Select example 1 to 4',
|
28
|
+
banner: 'example1', enum: %w(example1 example2 example3 example4)
|
29
|
+
method_option :print_example, aliases: '-p', type: :boolean,
|
30
|
+
desc: 'Select the example json to output to terminal.'
|
31
|
+
|
32
|
+
def boxes(input = nil)
|
33
|
+
if options[:help]
|
34
|
+
invoke :help, ['boxes']
|
35
|
+
else
|
36
|
+
require_relative 'commands/boxes'
|
37
|
+
Boxybox::Commands::Boxes.new(input, options).execute
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Boxybox
|
6
|
+
# This is generated from the TTY application bootstrap and is used to access the commands available to this app
|
7
|
+
class Command
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :command, :run
|
11
|
+
|
12
|
+
# Execute this command
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
def execute(*)
|
16
|
+
raise(
|
17
|
+
NotImplementedError,
|
18
|
+
"#{self.class}##{__method__} must be implemented"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
# The external commands runner
|
23
|
+
#
|
24
|
+
# @see http://www.rubydoc.info/gems/tty-command
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def command(**options)
|
28
|
+
require 'tty-command'
|
29
|
+
TTY::Command.new(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# The cursor movement
|
33
|
+
#
|
34
|
+
# @see http://www.rubydoc.info/gems/tty-cursor
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def cursor
|
38
|
+
require 'tty-cursor'
|
39
|
+
TTY::Cursor
|
40
|
+
end
|
41
|
+
|
42
|
+
# Open a file or text in the user's preferred editor
|
43
|
+
#
|
44
|
+
# @see http://www.rubydoc.info/gems/tty-editor
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def editor
|
48
|
+
require 'tty-editor'
|
49
|
+
TTY::Editor
|
50
|
+
end
|
51
|
+
|
52
|
+
# File manipulation utility methods
|
53
|
+
#
|
54
|
+
# @see http://www.rubydoc.info/gems/tty-file
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
def generator
|
58
|
+
require 'tty-file'
|
59
|
+
TTY::File
|
60
|
+
end
|
61
|
+
|
62
|
+
# Terminal output paging
|
63
|
+
#
|
64
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def pager(**options)
|
68
|
+
require 'tty-pager'
|
69
|
+
TTY::Pager.new(options)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Terminal platform and OS properties
|
73
|
+
#
|
74
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def platform
|
78
|
+
require 'tty-platform'
|
79
|
+
TTY::Platform.new
|
80
|
+
end
|
81
|
+
|
82
|
+
# The interactive prompt
|
83
|
+
#
|
84
|
+
# @see http://www.rubydoc.info/gems/tty-prompt
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def prompt(**options)
|
88
|
+
require 'tty-prompt'
|
89
|
+
TTY::Prompt.new(options)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get terminal screen properties
|
93
|
+
#
|
94
|
+
# @see http://www.rubydoc.info/gems/tty-screen
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def screen
|
98
|
+
require 'tty-screen'
|
99
|
+
TTY::Screen
|
100
|
+
end
|
101
|
+
|
102
|
+
# The unix which utility
|
103
|
+
#
|
104
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def which(*args)
|
108
|
+
require 'tty-which'
|
109
|
+
TTY::Which.which(*args)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Check if executable exists
|
113
|
+
#
|
114
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
def exec_exist?(*args)
|
118
|
+
require 'tty-which'
|
119
|
+
TTY::Which.exist?(*args)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
#
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
require 'boxybox/models/box_builder'
|
5
|
+
|
6
|
+
module Boxybox
|
7
|
+
module Commands
|
8
|
+
class Boxes < Boxybox::Command
|
9
|
+
def initialize(input, options)
|
10
|
+
@input = input
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(input: $stdin, output: $stdout)
|
15
|
+
json_str = @input
|
16
|
+
|
17
|
+
if @options['file']
|
18
|
+
file_path = @options['file']
|
19
|
+
json_str = IO.read(file_path)
|
20
|
+
elsif @options['example']
|
21
|
+
file_path = "lib/boxybox/templates/boxes/#{@options['example']}.json"
|
22
|
+
json_str = IO.read(file_path)
|
23
|
+
output.puts json_str if @options['print_example']
|
24
|
+
end
|
25
|
+
|
26
|
+
boxes = BoxBuilder.create_from_json(json_str)
|
27
|
+
|
28
|
+
# Command logic goes here ...
|
29
|
+
output.puts boxes
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'tty-box'
|
5
|
+
|
6
|
+
# @author Travis Carter
|
7
|
+
# helper methods to create and merge terminal boxes
|
8
|
+
class BoxBuilder
|
9
|
+
SPACE = ' '
|
10
|
+
|
11
|
+
attr_accessor :direction, :children, :content
|
12
|
+
|
13
|
+
# @param [Object] config
|
14
|
+
# @return [BoxBuilder] new instance of BoxBuilder
|
15
|
+
def initialize(config)
|
16
|
+
@direction = config['direction']
|
17
|
+
@children = config['children']
|
18
|
+
@content = config['content']
|
19
|
+
end
|
20
|
+
|
21
|
+
# This kicks off the parsing of the input JSON and returns the nested boxes as a string
|
22
|
+
# @param [JSON] json_string input to create boxes
|
23
|
+
# @return [String] will return a multiline string of ascii boxes
|
24
|
+
def self.create_from_json(json_string)
|
25
|
+
config = JSON.parse(json_string)
|
26
|
+
box_builder = BoxBuilder.new(config['boxes'])
|
27
|
+
|
28
|
+
box_builder.create_boxes
|
29
|
+
end
|
30
|
+
|
31
|
+
# This is the recursive method that craws down the tree to create the boxes
|
32
|
+
# @return [Strings]
|
33
|
+
def create_boxes
|
34
|
+
unless @children.nil?
|
35
|
+
child_boxes = []
|
36
|
+
|
37
|
+
children.each { |child| child_boxes << BoxBuilder.new(child).create_boxes }
|
38
|
+
|
39
|
+
@content = if child_boxes.length > 1 && direction == 'horizontal'
|
40
|
+
BoxBuilder.merge_boxes(*child_boxes)
|
41
|
+
else
|
42
|
+
child_boxes.join("\n")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@content = make_box(@content)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Wrapper around the TTY::Box module. This will be a prime target for expanded style choices and using more of the
|
50
|
+
# customizations available within the TTY gem set
|
51
|
+
# @return [Strings] ascii box
|
52
|
+
def make_box(content)
|
53
|
+
TTY::Box.frame(content, padding: 1, border: :ascii)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [Array] box_strings boxes to be merged
|
57
|
+
# @return [String]
|
58
|
+
def self.merge_boxes(*box_strings)
|
59
|
+
box_arrays = {}
|
60
|
+
max_rows = 0
|
61
|
+
new_box = []
|
62
|
+
|
63
|
+
box_strings.each_with_index do |box, idx|
|
64
|
+
box_arrays[idx] = box.split("\n")
|
65
|
+
|
66
|
+
max_rows = max_rows > box_arrays[idx].length ? max_rows : box_arrays[idx].length
|
67
|
+
end
|
68
|
+
|
69
|
+
max_rows.times do |x|
|
70
|
+
new_line = ''
|
71
|
+
box_arrays.each_value { |box| new_line += box[x].nil? ? SPACE * box.first.length : box[x] }
|
72
|
+
|
73
|
+
new_box << new_line
|
74
|
+
end
|
75
|
+
|
76
|
+
new_box.join("\n")
|
77
|
+
end
|
78
|
+
end
|