bashly 0.8.1 → 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: 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