tty 0.7.0 → 0.8.0
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/.rspec +2 -2
- data/.travis.yml +15 -4
- data/CHANGELOG.md +22 -0
- data/Gemfile +4 -7
- data/README.md +648 -58
- data/appveyor.yml +3 -4
- data/exe/teletype +18 -0
- data/lib/tty.rb +7 -4
- data/lib/tty/cli.rb +140 -0
- data/lib/tty/cmd.rb +132 -0
- data/lib/tty/commands/add.rb +321 -0
- data/lib/tty/commands/new.rb +256 -0
- data/lib/tty/gemspec.rb +30 -0
- data/lib/tty/licenses.rb +34 -0
- data/lib/tty/path_helpers.rb +38 -0
- data/lib/tty/plugins.rb +20 -12
- data/lib/tty/templater.rb +54 -0
- data/lib/tty/templates/add/command.rb.tt +31 -0
- data/lib/tty/templates/add/gitkeep.tt +1 -0
- data/lib/tty/templates/add/namespace.rb.tt +17 -0
- data/lib/tty/templates/add/spec/integration/command_spec.rb.tt +20 -0
- data/lib/tty/templates/add/spec/integration/sub_command_spec.rb.tt +16 -0
- data/lib/tty/templates/add/spec/unit/command_spec.rb.tt +15 -0
- data/lib/tty/templates/add/spec/unit/sub_command_spec.rb.tt +15 -0
- data/lib/tty/templates/add/test/integration/command_test.rb.tt +23 -0
- data/lib/tty/templates/add/test/integration/sub_command_test.rb.tt +19 -0
- data/lib/tty/templates/add/test/unit/command_test.rb.tt +16 -0
- data/lib/tty/templates/add/test/unit/sub_command_test.rb.tt +16 -0
- data/lib/tty/templates/new/agplv3_LICENSE.txt.tt +555 -0
- data/lib/tty/templates/new/apache_LICENSE.txt.tt +157 -0
- data/lib/tty/templates/new/bsd2_LICENSE.txt.tt +22 -0
- data/lib/tty/templates/new/bsd3_LICENSE.txt.tt +26 -0
- data/lib/tty/templates/new/exe/newcli.tt +18 -0
- data/lib/tty/templates/new/gitkeep.tt +1 -0
- data/lib/tty/templates/new/gplv2_LICENSE.txt.tt +255 -0
- data/lib/tty/templates/new/gplv3_LICENSE.txt.tt +543 -0
- data/lib/tty/templates/new/lgplv3_LICENSE.txt.tt +143 -0
- data/lib/tty/templates/new/lib/newcli/cli.rb.tt +24 -0
- data/lib/tty/templates/new/lib/newcli/command.rb.tt +124 -0
- data/lib/tty/templates/new/mit_LICENSE.txt.tt +20 -0
- data/lib/tty/templates/new/mplv2_LICENSE.txt.tt +277 -0
- data/lib/tty/version.rb +1 -1
- data/spec/fixtures/foo-0.0.1.gemspec +4 -4
- data/spec/integration/add_desc_args_spec.rb +341 -0
- data/spec/integration/add_force_spec.rb +98 -0
- data/spec/integration/add_namespaced_spec.rb +291 -0
- data/spec/integration/add_spec.rb +535 -0
- data/spec/integration/add_subcommand_spec.rb +259 -0
- data/spec/integration/new_author_spec.rb +19 -0
- data/spec/integration/new_license_spec.rb +39 -0
- data/spec/integration/new_namespaced_spec.rb +228 -0
- data/spec/integration/new_spec.rb +354 -0
- data/spec/integration/new_test_spec.rb +21 -0
- data/spec/integration/start_spec.rb +21 -0
- data/spec/spec_helper.rb +47 -16
- data/spec/unit/gemspec_spec.rb +17 -0
- data/spec/{tty/plugins/load_spec.rb → unit/plugins/activate_spec.rb} +2 -4
- data/spec/unit/plugins/load_from_spec.rb +28 -0
- data/spec/{tty → unit}/plugins/plugin/load_spec.rb +1 -3
- data/spec/{tty → unit}/plugins/plugin/new_spec.rb +1 -3
- data/spec/{tty → unit}/tty_spec.rb +1 -3
- data/tty.gemspec +25 -15
- metadata +186 -49
- data/spec/tty/plugins/find_spec.rb +0 -20
- data/tasks/metrics/cane.rake +0 -14
- data/tasks/metrics/flog.rake +0 -17
- data/tasks/metrics/heckle.rake +0 -15
- data/tasks/metrics/reek.rake +0 -13
data/appveyor.yml
CHANGED
@@ -9,7 +9,6 @@ test_script:
|
|
9
9
|
- bundle exec rake ci
|
10
10
|
environment:
|
11
11
|
matrix:
|
12
|
-
- ruby_version: "193"
|
13
12
|
- ruby_version: "200"
|
14
13
|
- ruby_version: "200-x64"
|
15
14
|
- ruby_version: "21"
|
@@ -18,6 +17,6 @@ environment:
|
|
18
17
|
- ruby_version: "22-x64"
|
19
18
|
- ruby_version: "23"
|
20
19
|
- ruby_version: "23-x64"
|
21
|
-
|
22
|
-
|
23
|
-
- ruby_version: "
|
20
|
+
- ruby_version: "24"
|
21
|
+
- ruby_version: "24-x64"
|
22
|
+
- ruby_version: "25-x64"
|
data/exe/teletype
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 'tty/cli'
|
7
|
+
|
8
|
+
Signal.trap('INT') do
|
9
|
+
warn("\n#{caller.join("\n")}: interrupted")
|
10
|
+
exit(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
TTY::CLI.start
|
15
|
+
rescue TTY::CLI::Error => err
|
16
|
+
puts "ERROR: #{err.message}"
|
17
|
+
exit 1 #err.status
|
18
|
+
end
|
data/lib/tty.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'tty/cli'
|
4
|
+
require_relative 'tty/plugins'
|
5
|
+
require_relative 'tty/version'
|
6
6
|
|
7
7
|
module TTY
|
8
|
+
GEMSPEC_PATH = ::File.expand_path("#{::File.dirname(__FILE__)}/../tty.gemspec")
|
9
|
+
|
8
10
|
class << self
|
9
11
|
def included(base)
|
10
12
|
base.send :extend, ClassMethods
|
@@ -25,4 +27,5 @@ module TTY
|
|
25
27
|
extend ClassMethods
|
26
28
|
end # TTY
|
27
29
|
|
28
|
-
TTY.plugins.
|
30
|
+
TTY.plugins.load_from(TTY::GEMSPEC_PATH, /tty-(.*)|pastel/)
|
31
|
+
TTY.plugins.activate
|
data/lib/tty/cli.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
require_relative 'licenses'
|
7
|
+
|
8
|
+
module TTY
|
9
|
+
# Main CLI runner
|
10
|
+
# @api public
|
11
|
+
class CLI < Thor
|
12
|
+
extend TTY::Licenses
|
13
|
+
|
14
|
+
# Error raised by this runner
|
15
|
+
Error = Class.new(StandardError)
|
16
|
+
|
17
|
+
no_commands do
|
18
|
+
def self.logo(banner)
|
19
|
+
<<-EOS
|
20
|
+
┏━━━┓
|
21
|
+
┏━┳╋┳┳━┻━━┓
|
22
|
+
┣━┫┗┫┗┳┳┳━┫
|
23
|
+
┃ ┃┏┫┏┫┃┃★┃ #{banner}
|
24
|
+
┃ ┗━┻━╋┓┃ ┃
|
25
|
+
┗━━━━━┻━┻━┛
|
26
|
+
EOS
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.top_banner
|
30
|
+
require 'pastel'
|
31
|
+
pastel = Pastel.new
|
32
|
+
pastel.red(logo('Terminal apps toolkit'))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.executable_name
|
36
|
+
::File.basename($PROGRAM_NAME)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class_option :"no-color", type: :boolean, default: false,
|
41
|
+
desc: 'Disable colorization in output'
|
42
|
+
class_option :"dry-run", type: :boolean, aliases: ['-r'],
|
43
|
+
desc: 'Run but do not make any changes'
|
44
|
+
class_option :debug, type: :boolean, default: false,
|
45
|
+
desc: 'Run in debug mode'
|
46
|
+
|
47
|
+
def self.help(*)
|
48
|
+
puts top_banner
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'add COMMAND [SUBCOMMAND] [OPTIONS]', 'Add a command to the application'
|
53
|
+
long_desc <<-D
|
54
|
+
The `teletype add` will create a new command and place it into
|
55
|
+
appropriate structure in the cli app.
|
56
|
+
|
57
|
+
Example:
|
58
|
+
teletype add config --desc 'Set and get configuration options'
|
59
|
+
|
60
|
+
This generates a command in app/commands/config.rb
|
61
|
+
|
62
|
+
You can also add subcommands
|
63
|
+
|
64
|
+
Example:
|
65
|
+
teletype add config server
|
66
|
+
|
67
|
+
This generates a command in app/commands/config/server.rb
|
68
|
+
D
|
69
|
+
method_option :args, type: :array, aliases: '-a', default: [],
|
70
|
+
desc: 'List command argument names',
|
71
|
+
banner: 'arg1 arg2'
|
72
|
+
method_option :desc, aliases: '-d', desc: "Describe command's purpose"
|
73
|
+
method_option :force, type: :boolean, aliases: '-f',
|
74
|
+
desc: 'Overwrite existing command'
|
75
|
+
method_option :help, type: :boolean,
|
76
|
+
aliases: '-h', desc: 'Display usage information'
|
77
|
+
method_option :test, type: :string, aliases: '-t',
|
78
|
+
desc: 'Generate a test setup',
|
79
|
+
banner: 'rspec', enum: %w(rspec minitest)
|
80
|
+
def add(*names)
|
81
|
+
if options[:help]
|
82
|
+
invoke :help, ['add']
|
83
|
+
elsif names.size < 1
|
84
|
+
fail Error, "'teletype add' was called with no arguments\n" \
|
85
|
+
"Usage: 'teletype add COMMAND_NAME'"
|
86
|
+
else
|
87
|
+
require_relative 'commands/add'
|
88
|
+
TTY::Commands::Add.new(names, options).execute
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'new PROJECT_NAME [OPTIONS]', 'Create a new command line app skeleton'
|
93
|
+
long_desc <<-D
|
94
|
+
The 'teletype new' command creates a new command line application
|
95
|
+
with a default directory structure and configuration at the
|
96
|
+
specified path.
|
97
|
+
|
98
|
+
The PROJECT_NAME will be the name for the directory that includes all the
|
99
|
+
files and be the default binary name.
|
100
|
+
|
101
|
+
Example:
|
102
|
+
teletype new cli_app
|
103
|
+
D
|
104
|
+
method_option :author, type: :array, aliases: '-a',
|
105
|
+
desc: 'Author(s) of this library',
|
106
|
+
banner: 'name1 name2'
|
107
|
+
method_option :ext, type: :boolean, default: false,
|
108
|
+
desc: 'Generate a boilerpalate for C extension'
|
109
|
+
method_option :coc, type: :boolean, default: true,
|
110
|
+
desc: 'Generate a code of conduct file'
|
111
|
+
method_option :force, type: :boolean, aliases: '-f',
|
112
|
+
desc: 'Overwrite existing files'
|
113
|
+
method_option :help, aliases: '-h', type: :boolean,
|
114
|
+
desc: 'Display usage information'
|
115
|
+
method_option :license, type: :string, default: 'mit', banner: 'mit',
|
116
|
+
aliases: '-l', desc: 'Generate a license file',
|
117
|
+
enum: licenses.keys.concat(['custom'])
|
118
|
+
method_option :test, type: :string, default: 'rspec',
|
119
|
+
aliases: '-t', desc: 'Generate a test setup',
|
120
|
+
banner: 'rspec', enum: %w(rspec minitest)
|
121
|
+
def new(app_name = nil)
|
122
|
+
if options[:help]
|
123
|
+
invoke :help, ['new']
|
124
|
+
elsif app_name.nil?
|
125
|
+
fail Error, "'teletype new' was called with no arguments\n" \
|
126
|
+
"Usage: 'teletype new PROJECT_NAME'"
|
127
|
+
else
|
128
|
+
require_relative 'commands/new'
|
129
|
+
TTY::Commands::New.new(app_name, options).execute
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
desc 'version', 'TTY version'
|
134
|
+
def version
|
135
|
+
require_relative 'version'
|
136
|
+
puts "v#{TTY::VERSION}"
|
137
|
+
end
|
138
|
+
map %w(--version -v) => :version
|
139
|
+
end # CLI
|
140
|
+
end # TTY
|
data/lib/tty/cmd.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
require_relative 'path_helpers'
|
7
|
+
|
8
|
+
module TTY
|
9
|
+
class Cmd
|
10
|
+
extend Forwardable
|
11
|
+
include PathHelpers
|
12
|
+
|
13
|
+
def_delegators :command, :run
|
14
|
+
|
15
|
+
def_delegators 'Thor::Util', :snake_case
|
16
|
+
|
17
|
+
# Execute this command
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def execute(*)
|
21
|
+
raise(
|
22
|
+
NotImplementedError,
|
23
|
+
"#{self.class}##{__method__} must be implemented"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# The external commands runner
|
28
|
+
#
|
29
|
+
# @see http://www.rubydoc.info/gems/tty-command
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def command(**options)
|
33
|
+
require 'tty-command'
|
34
|
+
TTY::Command.new(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# The cursor movement
|
38
|
+
#
|
39
|
+
# @see http://www.rubydoc.info/gems/tty-cursor
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
def cursor
|
43
|
+
require 'tty-cursor'
|
44
|
+
TTY::Cursor
|
45
|
+
end
|
46
|
+
|
47
|
+
# Open a file or text in the user's preferred editor
|
48
|
+
#
|
49
|
+
# @see http://www.rubydoc.info/gems/tty-editor
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def editor
|
53
|
+
require 'tty-editor'
|
54
|
+
TTY::Editor
|
55
|
+
end
|
56
|
+
|
57
|
+
# File manipulation utility methods
|
58
|
+
#
|
59
|
+
# @see http://www.rubydoc.info/gems/tty-file
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def generator
|
63
|
+
require 'tty-file'
|
64
|
+
TTY::File
|
65
|
+
end
|
66
|
+
|
67
|
+
# Terminal output paging
|
68
|
+
#
|
69
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
def pager(**options)
|
73
|
+
require 'tty-pager'
|
74
|
+
TTY::Pager.new(options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Terminal platform and OS properties
|
78
|
+
#
|
79
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
def platform
|
83
|
+
require 'tty-platform'
|
84
|
+
TTY::Platform.new
|
85
|
+
end
|
86
|
+
|
87
|
+
# The interactive prompt
|
88
|
+
#
|
89
|
+
# @see http://www.rubydoc.info/gems/tty-prompt
|
90
|
+
#
|
91
|
+
# @api public
|
92
|
+
def prompt(**options)
|
93
|
+
require 'tty-prompt'
|
94
|
+
TTY::Prompt.new(options)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get terminal screen properties
|
98
|
+
#
|
99
|
+
# @see http://www.rubydoc.info/gems/tty-screen
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
def screen
|
103
|
+
require 'tty-screen'
|
104
|
+
TTY::Screen
|
105
|
+
end
|
106
|
+
|
107
|
+
# The unix which utility
|
108
|
+
#
|
109
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
110
|
+
#
|
111
|
+
# @api public
|
112
|
+
def which(*args)
|
113
|
+
require 'tty-which'
|
114
|
+
TTY::Which.which(*args)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Check if executable exists
|
118
|
+
#
|
119
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
120
|
+
#
|
121
|
+
# @api public
|
122
|
+
def exec_exist?(*args)
|
123
|
+
require 'tty-which'
|
124
|
+
TTY::Which.exist?(*args)
|
125
|
+
end
|
126
|
+
|
127
|
+
def constantinize(str)
|
128
|
+
str.gsub(/-[_-]*(?![_-]|$)/) { "::" }
|
129
|
+
.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase}
|
130
|
+
end
|
131
|
+
end # Cmd
|
132
|
+
end # TTY
|
@@ -0,0 +1,321 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
require_relative '../cmd'
|
7
|
+
require_relative '../templater'
|
8
|
+
|
9
|
+
module TTY
|
10
|
+
module Commands
|
11
|
+
class Add < TTY::Cmd
|
12
|
+
include PathHelpers
|
13
|
+
|
14
|
+
attr_reader :app_name
|
15
|
+
|
16
|
+
attr_reader :cmd_name
|
17
|
+
|
18
|
+
attr_reader :subcmd_name
|
19
|
+
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
def initialize(cmd_names, options)
|
23
|
+
@cmd_name = cmd_names[0]
|
24
|
+
@subcmd_name = cmd_names[1]
|
25
|
+
@app_path = relative_path_from(root_path, root_path)
|
26
|
+
@app_name = name_from_path(root_path)
|
27
|
+
@options = options
|
28
|
+
|
29
|
+
@templater = Templater.new('add', @app_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def namespaced_path
|
33
|
+
app_name.tr('-', '/')
|
34
|
+
end
|
35
|
+
|
36
|
+
def template_context
|
37
|
+
opts = OpenStruct.new
|
38
|
+
opts[:cmd_options] = cmd_options
|
39
|
+
opts[:cmd_object_parts] = cmd_object_parts
|
40
|
+
opts[:cmd_desc_args] = cmd_desc_args
|
41
|
+
opts[:cmd_desc] = cmd_desc
|
42
|
+
opts[:app_indent] = app_indent
|
43
|
+
opts[:cmd_indent] = cmd_indent
|
44
|
+
opts[:cmd_path] = "#{namespaced_path}/commands/#{cmd_name_path}"
|
45
|
+
opts[:subcmd_path] = subcmd_name &&
|
46
|
+
"#{namespaced_path}/commands/#{cmd_name_path}/#{subcmd_name_path}"
|
47
|
+
opts[:cmd_name_constantinized] = cmd_name_constantinized
|
48
|
+
opts[:subcmd_name_constantinized] = subcmd_name && subcmd_name_constantinized
|
49
|
+
opts[:app_name_underscored] = app_name_underscored
|
50
|
+
opts[:cmd_name_underscored] = cmd_name_underscored
|
51
|
+
opts[:subcmd_name_underscored] = subcmd_name && subcmd_name_underscored
|
52
|
+
opts[:app_constantinized_parts] = app_name_constantinized.split('::')
|
53
|
+
opts[:cmd_constantinized_parts] = cmd_constantinized_parts
|
54
|
+
opts[:cmd_file_path] = cmd_file_path
|
55
|
+
opts
|
56
|
+
end
|
57
|
+
|
58
|
+
def file_options
|
59
|
+
opts = {}
|
60
|
+
opts[:force] = true if options['force']
|
61
|
+
opts[:color] = false if options['no-color']
|
62
|
+
opts
|
63
|
+
end
|
64
|
+
|
65
|
+
def execute(input: $stdin, output: $stdout)
|
66
|
+
validate_cmd_name(cmd_name)
|
67
|
+
|
68
|
+
test_dir = (options["test"] == 'rspec') || ::Dir.exist?('spec') ? 'spec' : 'test'
|
69
|
+
cli_file = "lib/#{namespaced_path}/cli.rb"
|
70
|
+
cli_content = ::File.read(cli_file)
|
71
|
+
cmd_file = "lib/#{namespaced_path}/commands/#{cmd_name_path}.rb"
|
72
|
+
cmd_template_path = "lib/#{namespaced_path}/templates/#{cmd_name_path}"
|
73
|
+
|
74
|
+
cmd_integ_test_file = "#{test_dir}/integration/#{cmd_name_path}_#{test_dir}.rb"
|
75
|
+
cmd_unit_test_file = "#{test_dir}/unit/#{cmd_name_path}_#{test_dir}.rb"
|
76
|
+
|
77
|
+
if !subcmd_present?
|
78
|
+
@templater.add_mapping(
|
79
|
+
"#{test_dir}/integration/command_#{test_dir}.rb.tt",
|
80
|
+
"#{test_dir}/integration/#{cmd_name_path}_#{test_dir}.rb")
|
81
|
+
@templater.add_mapping("#{test_dir}/unit/command_#{test_dir}.rb.tt",
|
82
|
+
"#{test_dir}/unit/#{cmd_name_path}_#{test_dir}.rb")
|
83
|
+
@templater.add_mapping('command.rb.tt', cmd_file)
|
84
|
+
@templater.add_mapping('gitkeep.tt', "#{cmd_template_path}/.gitkeep")
|
85
|
+
@templater.generate(template_context, file_options)
|
86
|
+
|
87
|
+
if !cmd_exists?(cli_content)
|
88
|
+
match = cmd_matches.find { |m| cli_content =~ m }
|
89
|
+
generator.inject_into_file(
|
90
|
+
cli_file, "\n#{cmd_template}",
|
91
|
+
{after: match}.merge(file_options))
|
92
|
+
end
|
93
|
+
else
|
94
|
+
subcmd_file = "lib/#{namespaced_path}/commands/#{cmd_name_path}/#{subcmd_name_path}.rb"
|
95
|
+
subcmd_template_path = "lib/#{namespaced_path}/templates/#{cmd_name_path}/#{subcmd_name_path}"
|
96
|
+
unless ::File.exists?(cmd_integ_test_file)
|
97
|
+
@templater.add_mapping(
|
98
|
+
"#{test_dir}/integration/command_#{test_dir}.rb.tt",
|
99
|
+
cmd_integ_test_file)
|
100
|
+
end
|
101
|
+
unless ::File.exists?(cmd_unit_test_file)
|
102
|
+
@templater.add_mapping(
|
103
|
+
"#{test_dir}/unit/#{cmd_name_path}_#{test_dir}.rb",
|
104
|
+
cmd_unit_test_file
|
105
|
+
)
|
106
|
+
end
|
107
|
+
@templater.add_mapping(
|
108
|
+
"#{test_dir}/integration/sub_command_#{test_dir}.rb.tt",
|
109
|
+
"#{test_dir}/integration/#{cmd_name_path}/#{subcmd_name_path}_#{test_dir}.rb")
|
110
|
+
@templater.add_mapping(
|
111
|
+
"#{test_dir}/unit/sub_command_#{test_dir}.rb.tt",
|
112
|
+
"#{test_dir}/unit/#{cmd_name_path}/#{subcmd_name_path}_#{test_dir}.rb"
|
113
|
+
)
|
114
|
+
unless ::File.exists?(cmd_file) # namespace already present
|
115
|
+
@templater.add_mapping('namespace.rb.tt', cmd_file)
|
116
|
+
end
|
117
|
+
@templater.add_mapping('command.rb.tt', subcmd_file)
|
118
|
+
@templater.add_mapping('gitkeep.tt', "#{subcmd_template_path}/.gitkeep")
|
119
|
+
@templater.generate(template_context, file_options)
|
120
|
+
|
121
|
+
if !subcmd_registered?(cli_content)
|
122
|
+
match = register_subcmd_matches.find { |m| cli_content =~ m }
|
123
|
+
generator.inject_into_file(
|
124
|
+
cli_file, "\n#{register_subcmd_template}",
|
125
|
+
{after: match}.merge(file_options))
|
126
|
+
end
|
127
|
+
|
128
|
+
content = ::File.read(cmd_file)
|
129
|
+
if !subcmd_exists?(content)
|
130
|
+
match = subcmd_matches.find {|m| content =~ m }
|
131
|
+
generator.inject_into_file(
|
132
|
+
cmd_file, "\n#{subcmd_template}",
|
133
|
+
{after: match}.merge(file_options))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def subcmd_present?
|
139
|
+
!subcmd_name.nil?
|
140
|
+
end
|
141
|
+
|
142
|
+
def subcmd_registered?(content)
|
143
|
+
content =~%r{\s*require_relative 'commands/#{cmd_name_path}'}
|
144
|
+
end
|
145
|
+
|
146
|
+
def subcmd_exists?(content)
|
147
|
+
content =~ %r{\s*def #{subcmd_name_underscored}.*}
|
148
|
+
end
|
149
|
+
|
150
|
+
def cmd_exists?(content)
|
151
|
+
content =~ %r{\s*def #{cmd_name_underscored}.*}
|
152
|
+
end
|
153
|
+
|
154
|
+
# Matches for inlining command defition in template
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def cmd_matches
|
158
|
+
[
|
159
|
+
%r{def version.*?:version\n}m,
|
160
|
+
%r{def version.*?#{app_indent} end\n}m,
|
161
|
+
%r{class CLI < Thor\n}
|
162
|
+
]
|
163
|
+
end
|
164
|
+
|
165
|
+
def subcmd_matches
|
166
|
+
[
|
167
|
+
%r{namespace .*?\n},
|
168
|
+
%r{class .*? < Thor\n}
|
169
|
+
]
|
170
|
+
end
|
171
|
+
|
172
|
+
def register_subcmd_matches
|
173
|
+
[
|
174
|
+
%r{require_relative .*?\nregister .*?\n}m
|
175
|
+
].concat(cmd_matches)
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def cmd_template
|
181
|
+
<<-EOS
|
182
|
+
#{app_indent}#{cmd_indent}desc '#{cmd_name_underscored}#{cmd_desc_args}', '#{cmd_desc}'
|
183
|
+
#{app_indent}#{cmd_indent}method_option :help, aliases: '-h', type: :boolean,
|
184
|
+
#{app_indent}#{cmd_indent} desc: 'Display usage information'
|
185
|
+
#{app_indent}#{cmd_indent}def #{cmd_name_underscored}(#{cmd_args.join(', ')})
|
186
|
+
#{app_indent}#{cmd_indent} if options[:help]
|
187
|
+
#{app_indent}#{cmd_indent} invoke :help, ['#{cmd_name_underscored}']
|
188
|
+
#{app_indent}#{cmd_indent} else
|
189
|
+
#{app_indent}#{cmd_indent} require_relative 'commands/#{cmd_name_path}'
|
190
|
+
#{app_indent}#{cmd_indent} #{cmd_object_parts.join('::')}.new(#{cmd_options.join(', ')}).execute
|
191
|
+
#{app_indent}#{cmd_indent} end
|
192
|
+
#{app_indent}#{cmd_indent}end
|
193
|
+
EOS
|
194
|
+
end
|
195
|
+
|
196
|
+
def register_subcmd_template
|
197
|
+
<<-EOS
|
198
|
+
#{app_indent} require_relative 'commands/#{cmd_name_path}'
|
199
|
+
#{app_indent} register #{cmd_object_parts[0..-2].join('::')}, '#{cmd_name_underscored}', '#{cmd_name_underscored} [SUBCOMMAND]', '#{cmd_desc}'
|
200
|
+
EOS
|
201
|
+
end
|
202
|
+
|
203
|
+
def subcmd_template
|
204
|
+
<<-EOS
|
205
|
+
#{app_indent}#{cmd_indent}desc '#{subcmd_name_underscored}#{cmd_desc_args}', '#{cmd_desc}'
|
206
|
+
#{app_indent}#{cmd_indent}method_option :help, aliases: '-h', type: :boolean,
|
207
|
+
#{app_indent}#{cmd_indent} desc: 'Display usage information'
|
208
|
+
#{app_indent}#{cmd_indent}def #{subcmd_name_underscored}(#{cmd_args.join(', ')})
|
209
|
+
#{app_indent}#{cmd_indent} if options[:help]
|
210
|
+
#{app_indent}#{cmd_indent} invoke :help, ['#{subcmd_name_underscored}']
|
211
|
+
#{app_indent}#{cmd_indent} else
|
212
|
+
#{app_indent}#{cmd_indent} require_relative '#{cmd_name_path}/#{subcmd_name_path}'
|
213
|
+
#{app_indent}#{cmd_indent} #{cmd_object_parts.join('::')}.new(#{cmd_options.join(', ')}).execute
|
214
|
+
#{app_indent}#{cmd_indent} end
|
215
|
+
#{app_indent}#{cmd_indent}end
|
216
|
+
EOS
|
217
|
+
end
|
218
|
+
|
219
|
+
def cmd_desc_args
|
220
|
+
return '' unless @options[:args].any?
|
221
|
+
' ' + @options[:args].map do |arg|
|
222
|
+
if arg.start_with?('*')
|
223
|
+
arg[1..-1].upcase + '...'
|
224
|
+
elsif arg.include?('=')
|
225
|
+
"[#{arg.split('=')[0].strip}]"
|
226
|
+
else
|
227
|
+
arg
|
228
|
+
end.upcase
|
229
|
+
end.join(' ')
|
230
|
+
end
|
231
|
+
|
232
|
+
def cmd_desc
|
233
|
+
@options[:desc].nil? ? 'Command description...' : @options[:desc]
|
234
|
+
end
|
235
|
+
|
236
|
+
def cmd_args
|
237
|
+
@options[:args].empty? ? ['*'] : @options[:args]
|
238
|
+
end
|
239
|
+
|
240
|
+
def cmd_options
|
241
|
+
@options[:args].map do |arg|
|
242
|
+
if arg.start_with?('*')
|
243
|
+
arg[1..-1]
|
244
|
+
elsif arg.include?('=')
|
245
|
+
arg.split('=')[0].strip
|
246
|
+
else
|
247
|
+
arg
|
248
|
+
end
|
249
|
+
end + ['options']
|
250
|
+
end
|
251
|
+
|
252
|
+
def app_indent
|
253
|
+
' ' * app_name_constantinized.split('::').size
|
254
|
+
end
|
255
|
+
|
256
|
+
def cmd_indent
|
257
|
+
' ' * cmd_constantinized_parts.size
|
258
|
+
end
|
259
|
+
|
260
|
+
def cmd_object_parts
|
261
|
+
[
|
262
|
+
app_name_constantinized,
|
263
|
+
'Commands',
|
264
|
+
cmd_name && cmd_name_constantinized,
|
265
|
+
subcmd_name && subcmd_name_constantinized
|
266
|
+
].compact
|
267
|
+
end
|
268
|
+
|
269
|
+
def cmd_constantinized_parts
|
270
|
+
[
|
271
|
+
cmd_name && constantinize(cmd_name),
|
272
|
+
subcmd_name && constantinize(subcmd_name)
|
273
|
+
].compact
|
274
|
+
end
|
275
|
+
|
276
|
+
def validate_cmd_name(cmd_name)
|
277
|
+
# TODO: check if command has correct name
|
278
|
+
end
|
279
|
+
|
280
|
+
def app_name_constantinized
|
281
|
+
constantinize(app_name)
|
282
|
+
end
|
283
|
+
|
284
|
+
def app_name_underscored
|
285
|
+
snake_case(app_name)
|
286
|
+
end
|
287
|
+
|
288
|
+
def cmd_name_constantinized
|
289
|
+
constantinize(cmd_name)
|
290
|
+
end
|
291
|
+
|
292
|
+
def cmd_name_underscored
|
293
|
+
snake_case(cmd_name)
|
294
|
+
end
|
295
|
+
|
296
|
+
def cmd_name_path
|
297
|
+
cmd_name_underscored.tr('-', '/')
|
298
|
+
end
|
299
|
+
|
300
|
+
def cmd_file_path
|
301
|
+
'../' * cmd_constantinized_parts.size + 'command'
|
302
|
+
end
|
303
|
+
|
304
|
+
def subcmd_name_underscored
|
305
|
+
snake_case(subcmd_name)
|
306
|
+
end
|
307
|
+
|
308
|
+
def subcmd_name_constantinized
|
309
|
+
constantinize(subcmd_name)
|
310
|
+
end
|
311
|
+
|
312
|
+
def subcmd_name_path
|
313
|
+
subcmd_name_underscored.tr('-', '/')
|
314
|
+
end
|
315
|
+
|
316
|
+
def spec_root
|
317
|
+
Pathname.new('spec')
|
318
|
+
end
|
319
|
+
end # Add
|
320
|
+
end # Commands
|
321
|
+
end # TTY
|