bashly 0.7.10 → 0.8.2

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 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