bashly 0.7.10 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 471a0ae8e462a056c196ebf128f02f3f30ff3a100c10aff2d2c010757d6d942e
4
- data.tar.gz: d78cf8473f40b241ee412a00d819be124a244acda1f487f20e105dee1cba9842
3
+ metadata.gz: bc4a83c90c751c1df692c55db4f7335619c927cb86a30de4fcb7eef5115710a1
4
+ data.tar.gz: 343ede4e31e7a0833b6335a9690c9ecbe060da1983d829cfbdf197bbfe5dc640
5
5
  SHA512:
6
- metadata.gz: 77617bc6668796730c67d33621d497a8a454a6208d7f42d7c28ecb26e940da2e4edee6b3aefd8fc98b301d8480a13f14c1f6b292ff7f4d29b6030e601cdc1b08
7
- data.tar.gz: d7b851826c9dde19201f2fb48b5f4b5ff0cd9a2421f4770ed3fdbc1c340943b9d0c3b3703b26646e46dfb012dca0c2a10728c568786ac4c381ab1431dc191695
6
+ metadata.gz: 3b5873adb5c173a4676ae071f6d83b11975053addd8b39e50613375032d441b227d6f27c4ad860719bdf22e37f7ce8882217e5c622c607dcff048fe7dfdaab18
7
+ data.tar.gz: c01a6a3c20aee19338c3cddfbac0254d7316c5b9aa8f2423bbd6ba65746b439e1ce7e3eec2f2654087c3a6b55388fdeca645dfe786e4adcac97997f1a0f778e4
@@ -6,10 +6,28 @@ module Bashly
6
6
  class Base < MisterBin::Command
7
7
  include AssetHelper
8
8
 
9
+ def config
10
+ @config ||= Config.new "#{Settings.source_dir}/bashly.yml"
11
+ end
12
+
13
+ def config_validator
14
+ @config_validator ||= ConfigValidator.new config
15
+ end
16
+
9
17
  def validate_config
10
- config = Config.new "#{Settings.source_dir}/bashly.yml"
11
- validator = ConfigValidator.new config
12
- validator.validate
18
+ config_validator.validate
19
+ end
20
+
21
+ def with_valid_config
22
+ validate_config
23
+ yield
24
+ show_deprecations
25
+ end
26
+
27
+ def show_deprecations
28
+ return if config_validator.deprecations.empty? or ENV['BASHLY_HIDE_DEPRECATIONS']
29
+ messages = "\n" + config_validator.deprecations.map(&:message).join("\n\n") + "\n\n"
30
+ say! messages
13
31
  end
14
32
  end
15
33
  end
@@ -1,3 +1,5 @@
1
+ require 'filewatcher'
2
+
1
3
  module Bashly
2
4
  module Commands
3
5
  class Generate < Base
@@ -9,7 +11,8 @@ module Bashly
9
11
  option "-f --force", "Overwrite existing files"
10
12
  option "-q --quiet", "Disable on-screen progress report"
11
13
  option "-u --upgrade", "Upgrade all added library functions"
12
- option "-w --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
14
+ option "-w --watch", "Watch the source directory for changes and regenerate on change"
15
+ option "-r --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
13
16
  option "-e --env ENV", "Force the generation environment (see BASHLY_ENV)"
14
17
 
15
18
  environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
@@ -27,23 +30,61 @@ module Bashly
27
30
 
28
31
  example "bashly generate --force"
29
32
  example "bashly generate --wrap my_function"
33
+ example "bashly g -uw"
34
+
35
+ attr_reader :watching
30
36
 
31
37
  def run
32
- validate_config
33
38
  Settings.env = args['--env'] if args['--env']
34
- quiet_say "creating !txtgrn!production!txtrst! version" if Settings.production?
35
- create_user_files
36
- upgrade_libs if args['--upgrade']
37
- create_master_script
38
- quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script"
39
+ @watching = args['--watch']
40
+
41
+ generate
42
+ watch if watching
39
43
  end
40
44
 
41
45
  private
42
46
 
47
+ def watch
48
+ quiet_say "!txtgrn!watching!txtrst! #{Settings.source_dir}\n"
49
+
50
+ Filewatcher.new([Settings.source_dir]).watch do
51
+ reset
52
+ generate
53
+
54
+ rescue Bashly::ConfigurationError => e
55
+ say! "!undred!#{e.class}!txtrst!\n#{e.message}"
56
+
57
+ ensure
58
+ quiet_say "!txtgrn!waiting\n"
59
+
60
+ end
61
+ end
62
+
63
+ def generate
64
+ with_valid_config do
65
+ quiet_say "creating !txtgrn!production!txtrst! version" if Settings.production?
66
+ generate_all_files
67
+ quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script" unless watching
68
+ end
69
+ end
70
+
71
+ def reset
72
+ @config = nil
73
+ @config_validator = nil
74
+ @command = nil
75
+ @script = nil
76
+ end
77
+
43
78
  def quiet_say(message)
44
79
  say message unless args['--quiet']
45
80
  end
46
81
 
82
+ def generate_all_files
83
+ create_user_files
84
+ upgrade_libs if args['--upgrade']
85
+ create_master_script
86
+ end
87
+
47
88
  def upgrade_libs
48
89
  generated_files.each do |file|
49
90
  content = File.read file
@@ -128,10 +169,6 @@ module Bashly
128
169
  "#{Settings.target_dir}/#{command.name}"
129
170
  end
130
171
 
131
- def config
132
- @config ||= Config.new "#{Settings.source_dir}/bashly.yml"
133
- end
134
-
135
172
  def command
136
173
  @command ||= Script::Command.new config
137
174
  end
@@ -9,10 +9,11 @@ module Bashly
9
9
  environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
10
10
 
11
11
  def run
12
- config = Config.new "#{Settings.source_dir}/bashly.yml"
13
- command = Script::Command.new(config)
14
- script = Script::Wrapper.new command
15
- puts script.code
12
+ with_valid_config do
13
+ command = Script::Command.new config
14
+ script = Script::Wrapper.new command
15
+ puts script.code
16
+ end
16
17
  end
17
18
  end
18
19
  end
@@ -10,7 +10,13 @@ module Bashly
10
10
 
11
11
  def run
12
12
  validate_config
13
- say "!txtgrn!OK"
13
+ show_deprecations
14
+ deprecations = config_validator.deprecations
15
+ if deprecations.empty?
16
+ say "!txtgrn!OK"
17
+ else
18
+ say "!txtred!WARNING!txtrst! Found #{deprecations.count} deprecations"
19
+ end
14
20
  end
15
21
  end
16
22
  end
@@ -1,51 +1,76 @@
1
1
  require 'completely'
2
2
 
3
3
  module Bashly
4
- # This is a `Command` concern responsible for providing bash completion data
4
+ # This is a `Command` and `Flag` concern responsible for providing bash
5
+ # completion data
5
6
  module Completions
6
- def completion_data(with_version: true)
7
- result = { full_name => completion_words(with_version: with_version) }
8
-
9
- commands.each do |command|
10
- result.merge! command.completion_data(with_version: false)
11
- end
7
+ module Flag
8
+ def completion_data(command_full_name)
9
+ result = {}
10
+ comps = allowed || completions
12
11
 
13
- result
14
- end
12
+ if comps
13
+ aliases.each do |name|
14
+ result["#{command_full_name}*#{name}"] = comps
15
+ end
16
+ end
15
17
 
16
- def completion_script
17
- completion_generator.script
18
+ result
19
+ end
18
20
  end
19
21
 
20
- def completion_function(name = nil)
21
- completion_generator.wrapper_function(name)
22
- end
22
+ module Command
23
+ def completion_data(with_version: true)
24
+ result = {}
23
25
 
24
- private
26
+ all_full_names.each do |name|
27
+ result[name] = completion_words(with_version: with_version)
28
+ flags.each do |flag|
29
+ result.merge! flag.completion_data(name)
30
+ end
31
+ end
32
+
33
+ commands.each do |command|
34
+ result.merge! command.completion_data(with_version: false)
35
+ end
25
36
 
26
- def completion_generator
27
- Completely::Completions.new(completion_data)
28
- end
37
+ result
38
+ end
29
39
 
30
- def completion_flag_names
31
- flags.map(&:name) + flags.map(&:short)
32
- end
40
+ def completion_script
41
+ completion_generator.script
42
+ end
33
43
 
34
- def completion_allowed_args
35
- flags.map(&:allowed).flatten + args.map(&:allowed).flatten
36
- end
44
+ def completion_function(name = nil)
45
+ completion_generator.wrapper_function(name)
46
+ end
37
47
 
38
- def completion_words(with_version: false)
39
- trivial_flags = %w[--help -h]
40
- trivial_flags += %w[--version -v] if with_version
41
- all = (
42
- command_names + trivial_flags +
43
- completion_flag_names + completion_allowed_args
44
- )
48
+ private
45
49
 
46
- all += completions if completions
47
- all.compact.uniq.sort
48
- end
50
+ def completion_generator
51
+ Completely::Completions.new(completion_data)
52
+ end
53
+
54
+ def completion_flag_names
55
+ flags.map(&:name) + flags.map(&:short)
56
+ end
57
+
58
+ def completion_allowed_args
59
+ args.map(&:allowed).flatten
60
+ end
61
+
62
+ def completion_words(with_version: false)
63
+ trivial_flags = %w[--help -h]
64
+ trivial_flags += %w[--version -v] if with_version
65
+ all = (
66
+ command_aliases + trivial_flags +
67
+ completion_flag_names + completion_allowed_args
68
+ )
49
69
 
70
+ all += completions if completions
71
+ all.compact.uniq.sort
72
+ end
73
+
74
+ end
50
75
  end
51
- end
76
+ end
@@ -18,6 +18,22 @@ module Bashly
18
18
  "# #{id}" unless Settings.production?
19
19
  end
20
20
 
21
+ # Reads a file from the userspace (Settings.source_dir) and returns
22
+ # its contents. If the file is not found, returns a string with a hint.
23
+ def load_user_file(file, placeholder: true)
24
+ path = "#{Settings.source_dir}/#{file}"
25
+
26
+ content = if File.exist? path
27
+ File.read(path).remove_front_matter
28
+ elsif placeholder
29
+ %q[echo "error: cannot load file"]
30
+ else
31
+ ''
32
+ end
33
+
34
+ Settings.production? ? content : "#{view_marker path}\n#{content}"
35
+ end
36
+
21
37
  private
22
38
 
23
39
  def view_path(view)
@@ -0,0 +1,75 @@
1
+ module Bashly
2
+ # This is a `ConfigValidator` concern responsible for providing basic
3
+ # assertion methods.
4
+ module ValidationHelpers
5
+
6
+ def deprecations
7
+ @deprecations ||= []
8
+ end
9
+
10
+ protected
11
+
12
+ def assert(valid, message)
13
+ raise ConfigurationError, message unless valid
14
+ end
15
+
16
+ def refute(invalid, message)
17
+ assert !invalid, message
18
+ end
19
+
20
+ def deprecate(key, **options)
21
+ deprecations.push Deprecation.new(key, **options)
22
+ end
23
+
24
+ def assert_string(key, value)
25
+ assert value.is_a?(String), "#{key} must be a string"
26
+ end
27
+
28
+ def assert_optional_string(key, value)
29
+ assert_string key, value if value
30
+ end
31
+
32
+ def assert_boolean(key, value)
33
+ assert [true, false, nil].include?(value), "#{key} must be a boolean"
34
+ end
35
+
36
+ def assert_array(key, value, of: nil)
37
+ return unless value
38
+ assert value.is_a?(Array), "#{key} must be an array"
39
+ if of
40
+ value.each_with_index do |val, i|
41
+ send "assert_#{of}".to_sym, "#{key}[#{i}]", val
42
+ end
43
+ end
44
+ end
45
+
46
+ def assert_hash(key, value, whitelist = nil)
47
+ assert value.is_a?(Hash), "#{key} must be a hash"
48
+
49
+ if whitelist
50
+ invalid_keys = value.keys.map(&:to_sym) - whitelist
51
+ assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join(', ')}"
52
+ end
53
+ end
54
+
55
+ def assert_uniq(key, value, array_keys)
56
+ return unless value
57
+ array_keys = [array_keys] unless array_keys.is_a? Array
58
+ list = []
59
+ array_keys.each do |array_key|
60
+ list += value.map { |c| c[array_key] }.compact.flatten
61
+ end
62
+
63
+ nonuniqs = list.nonuniq
64
+ assert nonuniqs.empty?, "#{key} contains non-unique elements (#{nonuniqs.join ', '}) in #{array_keys.join ' or '}"
65
+ end
66
+
67
+ def assert_string_or_array(key, value)
68
+ return unless value
69
+ assert [Array, String].include?(value.class),
70
+ "#{key} must be a string or an array"
71
+
72
+ assert_array key, value, of: :string if value.is_a? Array
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,7 @@
1
1
  module Bashly
2
2
  class ConfigValidator
3
+ include ValidationHelpers
4
+
3
5
  attr_reader :data
4
6
 
5
7
  def initialize(data)
@@ -12,45 +14,6 @@ module Bashly
12
14
 
13
15
  private
14
16
 
15
- def assert(valid, message)
16
- raise ConfigurationError, message unless valid
17
- end
18
-
19
- def refute(invalid, message)
20
- assert !invalid, message
21
- end
22
-
23
- def assert_string(key, value)
24
- assert value.is_a?(String), "#{key} must be a string"
25
- end
26
-
27
- def assert_optional_string(key, value)
28
- assert_string key, value if value
29
- end
30
-
31
- def assert_boolean(key, value)
32
- assert [true, false, nil].include?(value), "#{key} must be a boolean"
33
- end
34
-
35
- def assert_array(key, value, of: nil)
36
- return unless value
37
- assert value.is_a?(Array), "#{key} must be an array"
38
- if of
39
- value.each_with_index do |val, i|
40
- send "assert_#{of}".to_sym, "#{key}[#{i}]", val
41
- end
42
- end
43
- end
44
-
45
- def assert_hash(key, value, whitelist = nil)
46
- assert value.is_a?(Hash), "#{key} must be a hash"
47
-
48
- if whitelist
49
- invalid_keys = value.keys.map(&:to_sym) - whitelist
50
- assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join(', ')}"
51
- end
52
- end
53
-
54
17
  def assert_version(key, value)
55
18
  return unless value
56
19
  assert [String, Integer, Float].include?(value.class),
@@ -78,6 +41,11 @@ module Bashly
78
41
  "#{key} must be a boolean or a string"
79
42
  end
80
43
 
44
+ def assert_expose(key, value)
45
+ return unless value
46
+ assert [true, false, nil, 'always'].include?(value), "#{key} must be a boolean, or the string 'always'"
47
+ end
48
+
81
49
  def assert_arg(key, value)
82
50
  assert_hash key, value, Script::Argument.option_keys
83
51
  assert_string "#{key}.name", value['name']
@@ -98,6 +66,8 @@ module Bashly
98
66
  assert_hash key, value, Script::Flag.option_keys
99
67
  assert value['short'] || value['long'], "#{key} must have at least one of long or short name"
100
68
 
69
+ refute value['allowed'] && value['completions'], "#{key} cannot have both allowed and completions"
70
+
101
71
  assert_optional_string "#{key}.long", value['long']
102
72
  assert_optional_string "#{key}.short", value['short']
103
73
  assert_optional_string "#{key}.help", value['help']
@@ -109,6 +79,7 @@ module Bashly
109
79
  assert_boolean "#{key}.required", value['required']
110
80
  assert_array "#{key}.allowed", value['allowed'], of: :string
111
81
  assert_array "#{key}.conflicts", value['conflicts'], of: :string
82
+ assert_array "#{key}.completions", value['completions'], of: :string
112
83
 
113
84
  assert value['long'].match(/^--[a-zA-Z0-9_\-]+$/), "#{key}.long must be in the form of '--name'" if value['long']
114
85
  assert value['short'].match(/^-[a-zA-Z0-9]$/), "#{key}.short must be in the form of '-n'" if value['short']
@@ -123,6 +94,10 @@ module Bashly
123
94
  if value['allowed']
124
95
  assert value['arg'], "#{key}.allowed does not make sense without arg"
125
96
  end
97
+
98
+ if value['completions']
99
+ assert value['arg'], "#{key}.completions does not make sense without arg"
100
+ end
126
101
  end
127
102
 
128
103
  def assert_env_var(key, value)
@@ -140,7 +115,6 @@ module Bashly
140
115
  refute value['commands'] && value['flags'], "#{key} cannot have both commands and flags"
141
116
 
142
117
  assert_string "#{key}.name", value['name']
143
- assert_optional_string "#{key}.short", value['short']
144
118
  assert_optional_string "#{key}.help", value['help']
145
119
  assert_optional_string "#{key}.footer", value['footer']
146
120
  assert_optional_string "#{key}.group", value['group']
@@ -148,8 +122,10 @@ module Bashly
148
122
 
149
123
  assert_boolean "#{key}.private", value['private']
150
124
  assert_boolean "#{key}.default", value['default']
125
+ assert_expose "#{key}.expose", value['expose']
151
126
  assert_version "#{key}.version", value['version']
152
127
  assert_catch_all "#{key}.catch_all", value['catch_all']
128
+ assert_string_or_array "#{key}.alias", value['alias']
153
129
  assert_extensible "#{key}.extensible", value['extensible']
154
130
 
155
131
  assert_array "#{key}.args", value['args'], of: :arg
@@ -161,20 +137,35 @@ module Bashly
161
137
  assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
162
138
  assert_array "#{key}.examples", value['examples'], of: :string
163
139
 
140
+ assert_uniq "#{key}.commands", value['commands'], ['name', 'alias']
141
+ assert_uniq "#{key}.flags", value['flags'], 'long'
142
+ assert_uniq "#{key}.flags", value['flags'], 'short'
143
+ assert_uniq "#{key}.args", value['args'], 'name'
144
+
164
145
  if value['catch_all'] and value['args']
165
146
  repeatable_arg = value['args'].select { |a| a['repeatable'] }.first&.dig 'name'
166
147
  refute repeatable_arg, "#{key}.catch_all makes no sense with repeatable arg (#{repeatable_arg})"
167
148
  end
168
149
 
150
+ if value['expose']
151
+ assert value['commands'], "#{key}.expose makes no sense without commands"
152
+ end
153
+
169
154
  if key == "root"
170
- refute value['short'], "#{key}.short makes no sense"
155
+ refute value['alias'], "#{key}.alias makes no sense"
171
156
  refute value['group'], "#{key}.group makes no sense"
172
157
  refute value['default'], "#{key}.default makes no sense"
173
158
  refute value['private'], "#{key}.private makes no sense"
159
+ refute value['expose'], "#{key}.expose makes no sense"
174
160
  else
175
161
  refute value['version'], "#{key}.version makes no sense"
176
162
  refute value['extensible'], "#{key}.extensible makes no sense"
177
163
  end
164
+
165
+ # DEPRECATION 0.8.0
166
+ if value['short']
167
+ deprecate "#{key}.short", replacement: "alias", reference: "https://github.com/DannyBen/bashly/pull/220"
168
+ end
178
169
  end
179
170
  end
180
171
  end
@@ -0,0 +1,25 @@
1
+ module Bashly
2
+ class Deprecation
3
+ attr_reader :old, :replacement, :reference
4
+
5
+ def initialize(old, replacement: nil, reference: nil)
6
+ @old, @replacement, @reference = old, replacement, reference
7
+ end
8
+
9
+ def message
10
+ result = ["Deprecation Warning:", "!txtred!#{old}!txtrst! is deprecated"]
11
+ result.push "use !txtgrn!#{replacement}!txtrst! instead" if replacement
12
+ result.push "see !undblu!#{reference}!txtrst!" if reference
13
+
14
+ result.map { |line| "!txtred!▐!txtrst! #{line}"}.join("\n")
15
+ end
16
+
17
+ def to_h
18
+ {
19
+ old: old,
20
+ replacement: replacement,
21
+ reference: reference
22
+ }
23
+ end
24
+ end
25
+ end
@@ -4,4 +4,9 @@ class Array
4
4
  indentation = " " * offset
5
5
  map { |line| "#{indentation}#{line}" }
6
6
  end
7
+
8
+ def nonuniq
9
+ tally.select { |key, count| count > 1 }.keys
10
+ end
11
+
7
12
  end
@@ -1,21 +1,25 @@
1
1
  module Bashly
2
2
  module Script
3
3
  class Command < Base
4
- include Completions
5
- include CommandScopes
4
+ include Completions::Command
6
5
 
7
6
  class << self
8
7
  def option_keys
9
8
  @option_keys ||= %i[
10
- args catch_all commands completions
9
+ alias args catch_all commands completions
11
10
  default dependencies environment_variables examples
12
- extensible filename filters flags
11
+ extensible expose filename filters flags
13
12
  footer group help name
14
- private short version
13
+ private version
14
+ short
15
15
  ]
16
+ # DEPRECATION 0.8.0
16
17
  end
17
18
  end
18
19
 
20
+ attr_accessor :parent_command
21
+ attr_writer :parents
22
+
19
23
  # Returns the name to be used as an action.
20
24
  # - If it is the root command, the action is "root"
21
25
  # - Else, it is all the parents, except the first one (root) joined
@@ -27,7 +31,25 @@ module Bashly
27
31
 
28
32
  # Returns all the possible aliases for this command
29
33
  def aliases
30
- short ? [name, short] : [name]
34
+ [name] + alt
35
+ end
36
+
37
+ # Returns an array of all full names (including aliases and aliases of
38
+ # parents)
39
+ def all_full_names
40
+ if parent_command
41
+ parent_command.all_full_names.product(aliases).map { |a| a.join ' ' }
42
+ else
43
+ aliases
44
+ end
45
+ end
46
+
47
+ # Returns an array of alternative aliases if any
48
+ def alt
49
+ # DEPRECATION 0.8.0
50
+ options['alias'] ||= options['short']
51
+ return [] unless options["alias"]
52
+ options['alias'].is_a?(String) ? [options['alias']] : options['alias']
31
53
  end
32
54
 
33
55
  # Returns an array of Arguments
@@ -47,15 +69,81 @@ module Bashly
47
69
  @catch_all ||= CatchAll.from_config options['catch_all']
48
70
  end
49
71
 
72
+ # Returns a full list of the Command names and aliases combined
73
+ def command_aliases
74
+ commands.map(&:aliases).flatten
75
+ end
76
+
77
+ # Returns a data structure for displaying subcommands help
78
+ def command_help_data
79
+ result = {}
80
+
81
+ public_commands.each do |command|
82
+ result[command.group_string] ||= {}
83
+ result[command.group_string][command.name] = { summary: command.summary_string }
84
+ next unless command.expose
85
+
86
+ command.public_commands.each do |subcommand|
87
+ result[command.group_string]["#{command.name} #{subcommand.name}"] = {
88
+ summary: subcommand.summary_string,
89
+ help_only: command.expose != 'always'
90
+ }
91
+ end
92
+ end
93
+
94
+ result
95
+ end
96
+
97
+ # Returns only the names of the Commands
98
+ def command_names
99
+ commands.map &:name
100
+ end
101
+
50
102
  # Returns an array of the Commands
51
103
  def commands
52
104
  return [] unless options["commands"]
53
105
  options["commands"].map do |options|
54
- options['parents'] = parents + [name]
55
- Command.new options
106
+ result = Command.new options
107
+ result.parents = parents + [name]
108
+ result.parent_command = self
109
+ result
56
110
  end
57
111
  end
58
112
 
113
+ # Returns a flat array containing all the commands in this tree.
114
+ # This includes self + children + grandchildres + ...
115
+ def deep_commands
116
+ result = []
117
+ commands.each do |command|
118
+ result << command
119
+ if command.commands.any?
120
+ result += command.deep_commands
121
+ end
122
+ end
123
+ result
124
+ end
125
+
126
+ # If any of this command's subcommands has the default option set to
127
+ # true, this default command will be returned, nil otherwise.
128
+ def default_command
129
+ commands.find { |c| c.default }
130
+ end
131
+
132
+ # Returns an array of all the default Args
133
+ def default_args
134
+ args.select &:default
135
+ end
136
+
137
+ # Returns an array of all the default Environment Variables
138
+ def default_environment_variables
139
+ environment_variables.select &:default
140
+ end
141
+
142
+ # Returns an array of all the default Flags
143
+ def default_flags
144
+ flags.select &:default
145
+ end
146
+
59
147
  # Returns an array of EnvironmentVariables
60
148
  def environment_variables
61
149
  return [] unless options["environment_variables"]
@@ -89,27 +177,24 @@ module Bashly
89
177
  parents.any? ? (parents + [name]).join(' ') : name
90
178
  end
91
179
 
92
- # Reads a file from the userspace (Settings.source_dir) and returns
93
- # its contents.
94
- # If the file is not found, returns a string with a hint.
95
- def load_user_file(file, placeholder: true)
96
- path = "#{Settings.source_dir}/#{file}"
97
-
98
- content = if File.exist? path
99
- File.read(path).remove_front_matter
100
- elsif placeholder
101
- %q[echo "error: cannot load file"]
180
+ # Returns the string for the group caption
181
+ def group_string
182
+ if group
183
+ strings[:group] % { group: group }
102
184
  else
103
- ''
185
+ strings[:commands]
104
186
  end
105
-
106
- Settings.production? ? content : "#{view_marker path}\n#{content}"
107
187
  end
108
188
 
109
189
  # Returns an array of all parents. For example, the command
110
190
  # "docker container run" will have [docker, container] as its parents
111
191
  def parents
112
- options['parents'] || []
192
+ @parents ||= []
193
+ end
194
+
195
+ # Returns only commands that are not private
196
+ def public_commands
197
+ commands.reject &:private
113
198
  end
114
199
 
115
200
  # Returns true if one of the args is repeatable
@@ -117,6 +202,21 @@ module Bashly
117
202
  args.select(&:repeatable).any?
118
203
  end
119
204
 
205
+ # Returns an array of all the required Arguments
206
+ def required_args
207
+ args.select &:required
208
+ end
209
+
210
+ # Returns an array of all the required EnvironmentVariables
211
+ def required_environment_variables
212
+ environment_variables.select &:required
213
+ end
214
+
215
+ # Returns an array of all the required Flags
216
+ def required_flags
217
+ flags.select &:required
218
+ end
219
+
120
220
  # Returns true if this is the root command (no parents)
121
221
  def root_command?
122
222
  parents.empty?
@@ -127,6 +227,15 @@ module Bashly
127
227
  flags.select { |f| f.short == flag }.any?
128
228
  end
129
229
 
230
+ # Returns the summary string
231
+ def summary_string
232
+ if default
233
+ strings[:default_command_summary] % { summary: summary }
234
+ else
235
+ summary
236
+ end
237
+ end
238
+
130
239
  # Returns a constructed string suitable for Usage pattern
131
240
  def usage_string
132
241
  result = [full_name]
@@ -146,6 +255,15 @@ module Bashly
146
255
  @user_lib ||= Dir["#{Settings.full_lib_dir}/**/*.sh"].sort
147
256
  end
148
257
 
258
+ # Returns an array of all the args with a whitelist
259
+ def whitelisted_args
260
+ args.select &:allowed
261
+ end
262
+
263
+ # Returns an array of all the flags with a whitelist arg
264
+ def whitelisted_flags
265
+ flags.select &:allowed
266
+ end
149
267
  end
150
268
  end
151
269
  end
@@ -1,11 +1,13 @@
1
1
  module Bashly
2
2
  module Script
3
3
  class Flag < Base
4
+ include Completions::Flag
5
+
4
6
  class << self
5
7
  def option_keys
6
8
  @option_keys ||= %i[
7
- allowed arg conflicts default help long repeatable required
8
- short validate
9
+ allowed arg completions conflicts default help long repeatable
10
+ required short validate
9
11
  ]
10
12
  end
11
13
  end
@@ -8,7 +8,7 @@ environment_variables:
8
8
 
9
9
  commands:
10
10
  - name: download
11
- short: d
11
+ alias: d
12
12
  help: Download a file
13
13
 
14
14
  args:
@@ -32,7 +32,7 @@ commands:
32
32
  help: Set the default location to download to
33
33
 
34
34
  - name: upload
35
- short: u
35
+ alias: u
36
36
  help: Upload a file
37
37
  args:
38
38
  - name: source
@@ -11,7 +11,7 @@ environment_variables: "Environment Variables:"
11
11
  group: "%{group} Commands:"
12
12
 
13
13
  # Usage helpers
14
- command_shortcut: "Shortcut: %{short}"
14
+ command_alias: "Alias: %{alias}"
15
15
  default_command_summary: "%{summary} (default)"
16
16
  required: "(required)"
17
17
  repeatable: "(repeatable)"
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = "0.7.10"
2
+ VERSION = "0.8.2"
3
3
  end
@@ -15,8 +15,8 @@
15
15
  echo
16
16
  fi
17
17
 
18
- <%- if short -%>
19
- printf "<%= strings[:command_shortcut] % { short: short } %>\n"
18
+ <%- if alt&.any? -%>
19
+ printf "<%= strings[:command_alias] % { alias: alt.join(', ') } %>\n"
20
20
  echo
21
21
  <%- end -%>
22
22
 
@@ -1,18 +1,13 @@
1
1
  <%= view_marker %>
2
- % unless commands.first.group
3
- printf "<%= strings[:commands] %>\n"
4
- % end
5
- % maxlen = command_names.map(&:size).max
6
- % commands.reject(&:private).each do |command|
7
- % summary = command.summary
8
- % summary = strings[:default_command_summary] % { summary: summary } if command.default
9
- % if command.group
10
- % if command.name == commands.first.name
11
- printf "<%= strings[:group] % { group: command.group } %>\n"
2
+ % maxlen = command_help_data.values.map(&:keys).flatten.map(&:size).max
3
+ % command_help_data.each do |group, commands|
4
+ printf "<%= group %>\n"
5
+ % commands.each do |command, info|
6
+ % if info[:help_only]
7
+ [[ -n $long_usage ]] && echo " <%= command.ljust maxlen %> <%= info[:summary] %>"
12
8
  % else
13
- printf "\n<%= strings[:group] % { group: command.group } %>\n"
14
- % end
9
+ echo " <%= command.ljust maxlen %> <%= info[:summary] %>"
15
10
  % end
16
- echo " <%= command.name.ljust maxlen %> <%= summary %>"
17
11
  % end
18
12
  echo
13
+ % end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bashly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.10
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Ben Shitrit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-29 00:00:00.000000000 Z
11
+ date: 2022-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.6'
19
+ version: '0.7'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.6'
26
+ version: '0.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: completely
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.3'
33
+ version: 0.4.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.3'
40
+ version: 0.4.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mister_bin
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.1'
61
+ version: '0.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: filewatcher
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.1'
62
76
  type: :runtime
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '0.1'
82
+ version: '1.1'
69
83
  description: Generate bash command line tools using YAML configuration
70
84
  email: db@dannyben.com
71
85
  executables:
@@ -84,11 +98,12 @@ files:
84
98
  - lib/bashly/commands/preview.rb
85
99
  - lib/bashly/commands/validate.rb
86
100
  - lib/bashly/concerns/asset_helper.rb
87
- - lib/bashly/concerns/command_scopes.rb
88
101
  - lib/bashly/concerns/completions.rb
89
102
  - lib/bashly/concerns/renderable.rb
103
+ - lib/bashly/concerns/validation_helpers.rb
90
104
  - lib/bashly/config.rb
91
105
  - lib/bashly/config_validator.rb
106
+ - lib/bashly/deprecation.rb
92
107
  - lib/bashly/exceptions.rb
93
108
  - lib/bashly/extensions/array.rb
94
109
  - lib/bashly/extensions/file.rb
@@ -198,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
213
  - !ruby/object:Gem::Version
199
214
  version: '0'
200
215
  requirements: []
201
- rubygems_version: 3.3.3
216
+ rubygems_version: 3.3.14
202
217
  signing_key:
203
218
  specification_version: 4
204
219
  summary: Bash Command Line Tool Generator
@@ -1,68 +0,0 @@
1
- module Bashly
2
- # This is a `Command` concern responsible for providing additional scopes.
3
- module CommandScopes
4
- # Returns only the names of the Commands
5
- def command_names
6
- commands.map &:name
7
- end
8
-
9
- # Returns a flat array containing all the commands in this tree.
10
- # This includes self + children + grandchildres + ...
11
- def deep_commands
12
- result = []
13
- commands.each do |command|
14
- result << command
15
- if command.commands.any?
16
- result += command.deep_commands
17
- end
18
- end
19
- result
20
- end
21
-
22
- # If any of this command's subcommands has the default option set to
23
- # true, this default command will be returned, nil otherwise.
24
- def default_command
25
- commands.find { |c| c.default }
26
- end
27
-
28
- # Returns an array of all the default Args
29
- def default_args
30
- args.select &:default
31
- end
32
-
33
- # Returns an array of all the default Environment Variables
34
- def default_environment_variables
35
- environment_variables.select &:default
36
- end
37
-
38
- # Returns an array of all the default Flags
39
- def default_flags
40
- flags.select &:default
41
- end
42
-
43
- # Returns an array of all the required Arguments
44
- def required_args
45
- args.select &:required
46
- end
47
-
48
- # Returns an array of all the required EnvironmentVariables
49
- def required_environment_variables
50
- environment_variables.select &:required
51
- end
52
-
53
- # Returns an array of all the required Flags
54
- def required_flags
55
- flags.select &:required
56
- end
57
-
58
- # Returns an array of all the args with a whitelist
59
- def whitelisted_args
60
- args.select &:allowed
61
- end
62
-
63
- # Returns an array of all the flags with a whitelist arg
64
- def whitelisted_flags
65
- flags.select &:allowed
66
- end
67
- end
68
- end