tty 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|