bashly 0.8.1 → 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: 4be359964fc41fae59f83eae85c594a4e0edec41e0cc5752fec25850b7b7ca43
4
- data.tar.gz: 62023932e59ae5b9856a9617f9148a7e28bfab5bca5cebca98d3fefbf401ae21
3
+ metadata.gz: bc4a83c90c751c1df692c55db4f7335619c927cb86a30de4fcb7eef5115710a1
4
+ data.tar.gz: 343ede4e31e7a0833b6335a9690c9ecbe060da1983d829cfbdf197bbfe5dc640
5
5
  SHA512:
6
- metadata.gz: a0e222c6ead53cfef4597653d9ce51b6e9fffd1a3556acd72eff5447b9a203371a0097cd42f4058405cd473e7f0c55f4f9a448d45d3b1af73898b74a79706df4
7
- data.tar.gz: 8362eff89fccaad8c9a1f370270e33426d0089bbe131eec4fc55f586413afaa556caa81e62389daefb400941122db96fa1f8b4e04d3780a4d45ea88060dfdc1e
6
+ metadata.gz: 3b5873adb5c173a4676ae071f6d83b11975053addd8b39e50613375032d441b227d6f27c4ad860719bdf22e37f7ce8882217e5c622c607dcff048fe7dfdaab18
7
+ data.tar.gz: c01a6a3c20aee19338c3cddfbac0254d7316c5b9aa8f2423bbd6ba65746b439e1ce7e3eec2f2654087c3a6b55388fdeca645dfe786e4adcac97997f1a0f778e4
@@ -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,17 +30,50 @@ 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
38
+ Settings.env = args['--env'] if args['--env']
39
+ @watching = args['--watch']
40
+
41
+ generate
42
+ watch if watching
43
+ end
44
+
45
+ private
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
32
64
  with_valid_config do
33
- Settings.env = args['--env'] if args['--env']
34
65
  quiet_say "creating !txtgrn!production!txtrst! version" if Settings.production?
35
66
  generate_all_files
36
- quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script"
67
+ quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script" unless watching
37
68
  end
38
69
  end
39
70
 
40
- private
71
+ def reset
72
+ @config = nil
73
+ @config_validator = nil
74
+ @command = nil
75
+ @script = nil
76
+ end
41
77
 
42
78
  def quiet_say(message)
43
79
  say message unless args['--quiet']
@@ -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)
@@ -41,6 +41,11 @@ module Bashly
41
41
  "#{key} must be a boolean or a string"
42
42
  end
43
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
+
44
49
  def assert_arg(key, value)
45
50
  assert_hash key, value, Script::Argument.option_keys
46
51
  assert_string "#{key}.name", value['name']
@@ -117,6 +122,7 @@ module Bashly
117
122
 
118
123
  assert_boolean "#{key}.private", value['private']
119
124
  assert_boolean "#{key}.default", value['default']
125
+ assert_expose "#{key}.expose", value['expose']
120
126
  assert_version "#{key}.version", value['version']
121
127
  assert_catch_all "#{key}.catch_all", value['catch_all']
122
128
  assert_string_or_array "#{key}.alias", value['alias']
@@ -141,11 +147,16 @@ module Bashly
141
147
  refute repeatable_arg, "#{key}.catch_all makes no sense with repeatable arg (#{repeatable_arg})"
142
148
  end
143
149
 
150
+ if value['expose']
151
+ assert value['commands'], "#{key}.expose makes no sense without commands"
152
+ end
153
+
144
154
  if key == "root"
145
155
  refute value['alias'], "#{key}.alias makes no sense"
146
156
  refute value['group'], "#{key}.group makes no sense"
147
157
  refute value['default'], "#{key}.default makes no sense"
148
158
  refute value['private'], "#{key}.private makes no sense"
159
+ refute value['expose'], "#{key}.expose makes no sense"
149
160
  else
150
161
  refute value['version'], "#{key}.version makes no sense"
151
162
  refute value['extensible'], "#{key}.extensible makes no sense"
@@ -2,14 +2,13 @@ module Bashly
2
2
  module Script
3
3
  class Command < Base
4
4
  include Completions::Command
5
- include CommandScopes
6
5
 
7
6
  class << self
8
7
  def option_keys
9
8
  @option_keys ||= %i[
10
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
13
  private version
15
14
  short
@@ -18,6 +17,9 @@ module Bashly
18
17
  end
19
18
  end
20
19
 
20
+ attr_accessor :parent_command
21
+ attr_writer :parents
22
+
21
23
  # Returns the name to be used as an action.
22
24
  # - If it is the root command, the action is "root"
23
25
  # - Else, it is all the parents, except the first one (root) joined
@@ -32,6 +34,16 @@ module Bashly
32
34
  [name] + alt
33
35
  end
34
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
+
35
47
  # Returns an array of alternative aliases if any
36
48
  def alt
37
49
  # DEPRECATION 0.8.0
@@ -57,14 +69,79 @@ module Bashly
57
69
  @catch_all ||= CatchAll.from_config options['catch_all']
58
70
  end
59
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
+
60
102
  # Returns an array of the Commands
61
103
  def commands
62
104
  return [] unless options["commands"]
63
105
  options["commands"].map do |options|
64
- options['parents'] = parents + [name]
65
- options['parent_command'] = self
66
- Command.new options
106
+ result = Command.new options
107
+ result.parents = parents + [name]
108
+ result.parent_command = self
109
+ result
110
+ end
111
+ end
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
67
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
68
145
  end
69
146
 
70
147
  # Returns an array of EnvironmentVariables
@@ -100,32 +177,24 @@ module Bashly
100
177
  parents.any? ? (parents + [name]).join(' ') : name
101
178
  end
102
179
 
103
- # Reads a file from the userspace (Settings.source_dir) and returns
104
- # its contents.
105
- # If the file is not found, returns a string with a hint.
106
- def load_user_file(file, placeholder: true)
107
- path = "#{Settings.source_dir}/#{file}"
108
-
109
- content = if File.exist? path
110
- File.read(path).remove_front_matter
111
- elsif placeholder
112
- %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 }
113
184
  else
114
- ''
185
+ strings[:commands]
115
186
  end
116
-
117
- Settings.production? ? content : "#{view_marker path}\n#{content}"
118
- end
119
-
120
- # Returns the Command instance of the direct parent
121
- def parent_command
122
- options['parent_command']
123
187
  end
124
188
 
125
189
  # Returns an array of all parents. For example, the command
126
190
  # "docker container run" will have [docker, container] as its parents
127
191
  def parents
128
- options['parents'] || []
192
+ @parents ||= []
193
+ end
194
+
195
+ # Returns only commands that are not private
196
+ def public_commands
197
+ commands.reject &:private
129
198
  end
130
199
 
131
200
  # Returns true if one of the args is repeatable
@@ -133,6 +202,21 @@ module Bashly
133
202
  args.select(&:repeatable).any?
134
203
  end
135
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
+
136
220
  # Returns true if this is the root command (no parents)
137
221
  def root_command?
138
222
  parents.empty?
@@ -143,6 +227,15 @@ module Bashly
143
227
  flags.select { |f| f.short == flag }.any?
144
228
  end
145
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
+
146
239
  # Returns a constructed string suitable for Usage pattern
147
240
  def usage_string
148
241
  result = [full_name]
@@ -162,6 +255,15 @@ module Bashly
162
255
  @user_lib ||= Dir["#{Settings.full_lib_dir}/**/*.sh"].sort
163
256
  end
164
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
165
267
  end
166
268
  end
167
269
  end
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = "0.8.1"
2
+ VERSION = "0.8.2"
3
3
  end
@@ -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.8.1
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-05-22 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
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.4.1
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.4.1
40
+ version: 0.4.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mister_bin
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
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'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
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,7 +98,6 @@ 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
90
103
  - lib/bashly/concerns/validation_helpers.rb
@@ -200,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
213
  - !ruby/object:Gem::Version
201
214
  version: '0'
202
215
  requirements: []
203
- rubygems_version: 3.3.3
216
+ rubygems_version: 3.3.14
204
217
  signing_key:
205
218
  specification_version: 4
206
219
  summary: Bash Command Line Tool Generator
@@ -1,83 +0,0 @@
1
- module Bashly
2
- # This is a `Command` concern responsible for providing additional scopes.
3
- module CommandScopes
4
- # Returns an array of all full names (including aliases and aliases of
5
- # parents)
6
- def all_full_names
7
- if parent_command
8
- parent_command.all_full_names.product(aliases).map { |a| a.join ' ' }
9
- else
10
- aliases
11
- end
12
- end
13
-
14
- # Returns a full list of the Command names and aliases combined
15
- def command_aliases
16
- commands.map(&:aliases).flatten
17
- end
18
-
19
- # Returns only the names of the Commands
20
- def command_names
21
- commands.map &:name
22
- end
23
-
24
- # Returns a flat array containing all the commands in this tree.
25
- # This includes self + children + grandchildres + ...
26
- def deep_commands
27
- result = []
28
- commands.each do |command|
29
- result << command
30
- if command.commands.any?
31
- result += command.deep_commands
32
- end
33
- end
34
- result
35
- end
36
-
37
- # If any of this command's subcommands has the default option set to
38
- # true, this default command will be returned, nil otherwise.
39
- def default_command
40
- commands.find { |c| c.default }
41
- end
42
-
43
- # Returns an array of all the default Args
44
- def default_args
45
- args.select &:default
46
- end
47
-
48
- # Returns an array of all the default Environment Variables
49
- def default_environment_variables
50
- environment_variables.select &:default
51
- end
52
-
53
- # Returns an array of all the default Flags
54
- def default_flags
55
- flags.select &:default
56
- end
57
-
58
- # Returns an array of all the required Arguments
59
- def required_args
60
- args.select &:required
61
- end
62
-
63
- # Returns an array of all the required EnvironmentVariables
64
- def required_environment_variables
65
- environment_variables.select &:required
66
- end
67
-
68
- # Returns an array of all the required Flags
69
- def required_flags
70
- flags.select &:required
71
- end
72
-
73
- # Returns an array of all the args with a whitelist
74
- def whitelisted_args
75
- args.select &:allowed
76
- end
77
-
78
- # Returns an array of all the flags with a whitelist arg
79
- def whitelisted_flags
80
- flags.select &:allowed
81
- end
82
- end
83
- end