bashly 0.2.2 → 0.2.3

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: 33745534735d1a93f143a68939db55de17d618457b6873e948149317671f772d
4
- data.tar.gz: 4ab52cd6f154cce246454d924fb898e8a092b1d7bb6e171dc9ff95d8dccc46c2
3
+ metadata.gz: d40ad6e14ab17c88a939cea3be81f4b72fe8383021478d94c93328c4842fb395
4
+ data.tar.gz: 4cb84c229411360da7633dd4ad4a15bb53bfdf49fba81e82aecbf88ec3422d72
5
5
  SHA512:
6
- metadata.gz: f15b8e87c30fcb1bbec774ac101ef3f3225313df181bc78ab1786baa833e0c972ac01c4f451d20c2661c8383d3924fce182f737aa4f0579edfd27e3f006fa511
7
- data.tar.gz: 7c51c2fd0b9564ba1be0e6a7b71b01176bbdc936da9d5382479ecbffee7671dd06e3c3c6172647b1c40d0d09d03ab347fecfe5f7464d1e0b90a4b49094116a88
6
+ metadata.gz: cb14645de6f614ce24393820591f6917bd92a7c41c48f14456b789183648c8031653c0304f8e59a2c43fecd7a4f0ed0e9ab4cf6a5092f0e5bd742de157af7b0c
7
+ data.tar.gz: b1b9fca567512ade4957a9b484d853b871f2c2edbefb267fc0c8fdbea8e5c2b1c5792cc011e04b60d07dc9e09b62996e18c826fd67902d28549e505bce8c30ce
data/README.md CHANGED
@@ -119,36 +119,42 @@ Configuration Reference
119
119
 
120
120
  ### Command options
121
121
 
122
- With the exception of `version` and `commands` (which define subcommands),
123
- everything else in this section is suitable both for the main script, and for
124
- any subcommand you define using `commands`.
122
+ Unless otherwise specified, these definitiona can be used for both the root
123
+ command and subcommands (under the `commands` definition).
125
124
 
126
125
  ```yaml
127
126
  # The name of the script or subcommand
128
127
  name: myscript
129
128
 
129
+ # An additional, optional name - usually used to denote a one letter
130
+ # variation of the command name
131
+ # Applicable only in subcommands
132
+ short: m
133
+
130
134
  # The header text to display when using --help
131
135
  # This can have multiple lines. In this case, the first line will be used as
132
136
  # summary wherever appropriate.
133
137
  help: a sample script generated with bashly
134
138
 
135
139
  # The string to display when using --version
140
+ # Applicable only in the main command
136
141
  version: 0.1.0
137
142
 
138
143
  # Specify an array of examples to show when using --help
144
+ # Each example can have multiple lines.
139
145
  examples:
140
146
  - myscript download
141
147
  - myscript download --force
142
148
 
143
149
  # Specify an array of environment variables needed by your script
144
150
  # This is used purely for displaying in the help text (when using --help)
151
+ # The help for each variable can have multiple lines.
145
152
  environment_variable:
146
153
  VARIABLE_NAME: Variable help text
147
154
 
148
155
  # Specify the array of subcommands to generate.
149
156
  # Each subcommand will have its own args and flags.
150
- # If this is provided, then you cannot provide flags or args for the main
151
- # script.
157
+ # If this is provided, you cannot provide flags or args for the main script.
152
158
  commands:
153
159
  - ...
154
160
 
@@ -185,8 +191,7 @@ bash function.
185
191
  name: user
186
192
 
187
193
  # The message to display when using --help.
188
- # This can have multiple lines, but it is recommended to keep lines shorter
189
- # than ~70 characters, to avoid text wrapping in narrower terminals.
194
+ # This can have multiple lines.
190
195
  help: Username to use for logging in
191
196
 
192
197
  # Specify if this argument is required.
@@ -216,8 +221,7 @@ long: --output
216
221
  short: -o
217
222
 
218
223
  # The text to display when using --help
219
- # This can have multiple lines, but it is recommended to keep lines shorter
220
- # than ~70 characters, to avoid text wrapping in narrower terminals.
224
+ # This can have multiple lines.
221
225
  help: Specify the output directory
222
226
 
223
227
  # If the flag requires an argument, specify its name here.
@@ -227,6 +231,13 @@ arg: directory
227
231
  required: true
228
232
  ```
229
233
 
234
+
235
+ Real World Examples
236
+ --------------------------------------------------
237
+
238
+ - [Rush][rush] - a Personal Package Manager
239
+
240
+
230
241
  Contributing / Support
231
242
  --------------------------------------------------
232
243
 
@@ -234,4 +245,4 @@ If you experience any issue, have a question or a suggestion, or if you wish
234
245
  to contribute, feel free to [open an issue][issues].
235
246
 
236
247
  [issues]: https://github.com/DannyBen/bashly/issues
237
-
248
+ [rush]: https://github.com/DannyBen/rush-cli
@@ -36,8 +36,9 @@ module Bashly
36
36
  end
37
37
 
38
38
  def create_all_command_files
39
- command.commands.each do |subcommand|
40
- file = "#{Settings.source_dir}/#{subcommand.full_name.to_underscore}_command.sh"
39
+ command.deep_commands.each do |subcommand|
40
+ next if subcommand.commands.any?
41
+ file = "#{Settings.source_dir}/#{subcommand.filename}"
41
42
  content = subcommand.render :default_script
42
43
  create_file file, content
43
44
  end
@@ -1,3 +1,5 @@
1
+ require 'erb'
2
+
1
3
  module Bashly
2
4
  module Renderable
3
5
  def render(view)
@@ -1,20 +1,27 @@
1
- require 'erb'
2
- require 'ostruct'
3
-
4
1
  class String
2
+ def escape_newlines
3
+ gsub "\n", "\\n"
4
+ end
5
+
5
6
  def indent(offset)
6
7
  return self unless offset > 0
7
8
  split("\n").indent(offset).join("\n")
8
9
  end
9
10
 
10
- def to_underscore!
11
- gsub!(/(.)([A-Z])/,'\1_\2')
12
- gsub!(' ', '_')
13
- downcase!
11
+ def to_underscore
12
+ gsub(/(.)([A-Z])/,'\1_\2').gsub(' ', '_').downcase
14
13
  end
15
14
 
16
- def to_underscore
17
- dup.tap { |s| s.to_underscore! }
15
+ def wrap(length = 80)
16
+ strip!
17
+ split("\n").collect! do |line|
18
+ if line.length > length
19
+ line.gsub!(/([^\s]{#{length}})([^\s$])/, "\\1 \\2")
20
+ line.gsub(/(.{1,#{length}})(\s+|$)/, "\\1\n").rstrip
21
+ else
22
+ line
23
+ end
24
+ end * "\n"
18
25
  end
19
26
 
20
27
  end
@@ -1,18 +1,9 @@
1
1
  module Bashly
2
2
  module Models
3
3
  class Argument < Base
4
- def optional
5
- !required
6
- end
7
-
8
4
  def usage_string
9
5
  required ? name.upcase : "[#{name.upcase}]"
10
6
  end
11
-
12
- def summary
13
- return "" unless help
14
- help.split("\n").first
15
- end
16
7
  end
17
8
  end
18
9
  end
@@ -26,13 +26,24 @@ module Bashly
26
26
  verify if respond_to? :verify
27
27
  end
28
28
 
29
+ def optional
30
+ !required
31
+ end
32
+
33
+ def summary
34
+ help.empty? ? "" : help.split("\n").first
35
+ end
36
+
37
+ def help
38
+ options['help'] ||= ''
39
+ end
40
+
29
41
  def method_missing(method_name, *arguments, &block)
30
42
  key = method_name.to_s
31
43
  respond_to?(method_name) ? options[key] : super
32
44
  end
33
45
 
34
46
  def respond_to?(method_name, include_private = false)
35
- # options.has_key?(method_name.to_s) || super
36
47
  OPTION_KEYS.include?(method_name) || super
37
48
  end
38
49
  end
@@ -1,6 +1,15 @@
1
1
  module Bashly
2
2
  module Models
3
3
  class Command < Base
4
+ # Returns the name to be used as an action.
5
+ # - If it is the root command, the action is "root"
6
+ # - Else, it is all the parents, except the first tone (root) joined
7
+ # by space. For example, for a command like "docker container run"
8
+ # the action name is "container run".
9
+ def action_name
10
+ parents.any? ? (parents[1..-1] + [name]).join(' ') : "root"
11
+ end
12
+
4
13
  # Returns all the possible aliases for this command
5
14
  def aliases
6
15
  short ? [name, short] : [name]
@@ -28,11 +37,30 @@ module Bashly
28
37
  def commands
29
38
  return [] unless options["commands"]
30
39
  options["commands"].map do |options|
31
- options['parent_name'] = full_name
40
+ options['parents'] = parents + [name]
32
41
  command = Command.new options
33
42
  end
34
43
  end
35
44
 
45
+ # Returns a flat array containing all the commands in this tree.
46
+ # This includes self + children + grandchildres + ...
47
+ def deep_commands
48
+ result = []
49
+ commands.each do |command|
50
+ result << command
51
+ if command.commands.any?
52
+ result += command.deep_commands
53
+ end
54
+ end
55
+ result
56
+ end
57
+
58
+ # Returns the bash filename that is expected to hold the user code
59
+ # for this command
60
+ def filename
61
+ "#{action_name.to_underscore}_command.sh"
62
+ end
63
+
36
64
  # Returns an array of Flags
37
65
  def flags
38
66
  return [] unless options["flags"]
@@ -41,10 +69,15 @@ module Bashly
41
69
  end
42
70
  end
43
71
 
72
+ # Returns a unique name, suitable to be used in a bash function
73
+ def function_name
74
+ full_name.to_underscore
75
+ end
76
+
44
77
  # Returns the name of the command, including its parent name (in case
45
78
  # this is a subcommand)
46
79
  def full_name
47
- parent_name ? "#{parent_name} #{name}" : name
80
+ parents.any? ? (parents + [name]).join(' ') : name
48
81
  end
49
82
 
50
83
  # Reads a file from the userspace (Settings.source_dir) and returns
@@ -52,7 +85,7 @@ module Bashly
52
85
  # If the file is not found, returns a string with a hint.
53
86
  def load_user_file(file, placeholder: true)
54
87
  path = "#{Settings.source_dir}/#{file}"
55
- default_content = placeholder ? "# error: cannot load file" : ''
88
+ default_content = placeholder ? "echo \"error: cannot load file\"" : ''
56
89
 
57
90
  content = if File.exist? path
58
91
  File.read path
@@ -63,6 +96,12 @@ module Bashly
63
96
  "# :#{path}\n#{content}"
64
97
  end
65
98
 
99
+ # Returns an array of all parents. For example, the command
100
+ # "docker container run" will have [docker, container] as its parents
101
+ def parents
102
+ options['parents'] || []
103
+ end
104
+
66
105
  # Returns an array of all the required Arguments
67
106
  def required_args
68
107
  args.select &:required
@@ -73,10 +112,9 @@ module Bashly
73
112
  flags.select &:required
74
113
  end
75
114
 
76
- # Returns the first line of the help message
77
- def summary
78
- return "" unless help
79
- help.split("\n").first
115
+ # Returns trus if this is the root command (no parents)
116
+ def root_command?
117
+ parents.empty?
80
118
  end
81
119
 
82
120
  # Returns a constructed string suitable for Usage pattern
@@ -109,10 +147,6 @@ module Bashly
109
147
  if args.any? or flags.any?
110
148
  raise ConfigurationError, "Error in the !txtgrn!#{full_name}!txtrst! command.\nThe !txtgrn!commands!txtrst! key cannot be at the same level as the !txtgrn!args!txtrst! or !txtgrn!flags!txtrst! keys."
111
149
  end
112
-
113
- if parent_name
114
- raise ConfigurationError, "Error in the !txtgrn!#{full_name}!txtrst! command.\nNested commands are not supported."
115
- end
116
150
  end
117
151
 
118
152
  end
@@ -11,15 +11,6 @@ module Bashly
11
11
  end
12
12
  end
13
13
 
14
- def optional
15
- !required
16
- end
17
-
18
- def summary
19
- return "" unless help
20
- help.split("\n").first
21
- end
22
-
23
14
  def usage_string(extended: false)
24
15
  result = [aliases.join(", ")]
25
16
  result << arg.upcase if arg
@@ -100,7 +100,6 @@ config_show() {
100
100
  # done
101
101
  #
102
102
  config_keys() {
103
- key=$1
104
103
  regex="^(.*)\s*="
105
104
 
106
105
  config_init
@@ -112,5 +111,5 @@ config_keys() {
112
111
  keys+=("$key")
113
112
  fi
114
113
  done < "$CONFIG_FILE"
115
- echo ${keys[@]}
114
+ echo "${keys[@]}"
116
115
  }
@@ -7,8 +7,8 @@ examples: "Examples:"
7
7
  environment_variables: "Environment Variables:"
8
8
 
9
9
  # Fixed flags help text
10
- show_this_help: Show this help
11
- show_version_number: Show version number
10
+ help_flag_text: Show this help
11
+ version_flag_text: Show version number
12
12
 
13
13
  # Error messages
14
14
  flag_requires_an_argument: "%{long} requires an argument: %{usage}"
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -1,4 +1,4 @@
1
1
  # :argument.usage
2
2
  echo " <%= name.upcase %>"
3
- echo " <%= summary %>"
3
+ echo -e "<%= help.wrap(76).indent(4).escape_newlines %>"
4
4
  echo
@@ -10,17 +10,17 @@ case $action in
10
10
  <%= command.aliases.join " | " %> )
11
11
  action="<%= command.name %>"
12
12
  shift
13
- <%= command.name %>_parse_args "$@"
13
+ <%= command.function_name %>_parse_args "$@"
14
14
  shift $#
15
15
  ;;
16
16
 
17
17
  <%- end -%>
18
18
  * )
19
- <%= name %>_usage
19
+ <%= function_name %>_usage
20
20
  exit 1
21
21
  ;;
22
22
 
23
23
  esac
24
24
  <%- else -%>
25
- action=root
25
+ action="<%= action_name %>"
26
26
  <%- end -%>
@@ -1,4 +1,4 @@
1
1
  # :command.command_functions
2
- <%- commands.each do |command| -%>
3
- <%= command.render :function %>
2
+ <%- deep_commands.each do |command| -%>
3
+ <%= command.render :function unless command.commands.any? %>
4
4
  <%- end -%>
@@ -1,4 +1,4 @@
1
- echo "# this file is located in '<%= Settings.source_dir %>/<%= full_name.to_underscore %>_command.sh'"
1
+ echo "# this file is located in '<%= "#{Settings.source_dir}/#{filename}" %>'"
2
2
  echo "# code for '<%= full_name %>' goes here"
3
3
  echo "# you can edit it freely and regenerate (it will not be overwritten)"
4
4
  inspect_args
@@ -1,13 +1,13 @@
1
1
  # :command.fixed_flag_filter
2
2
  case "$1" in
3
- --version | -v )
3
+ --version )
4
4
  version_command
5
5
  exit 1
6
6
  ;;
7
7
 
8
8
  --help | -h )
9
9
  long_usage=yes
10
- <%= full_name.to_underscore %>_usage
10
+ <%= function_name %>_usage
11
11
  exit 1
12
12
  ;;
13
13
 
@@ -1,4 +1,4 @@
1
1
  # :command.function
2
- <%= full_name.to_underscore %>_command() {
3
- <%= load_user_file("#{full_name.to_underscore}_command.sh").indent 2 %>
2
+ <%= function_name %>_command() {
3
+ <%= load_user_file(filename).indent 2 %>
4
4
  }
@@ -1,5 +1,9 @@
1
1
  # :command.parse_args
2
+ <%- if root_command? -%>
2
3
  parse_args() {
4
+ <%- else -%>
5
+ <%= function_name %>_parse_args() {
6
+ <%- end -%>
3
7
  <%= render(:fixed_flags_filter).indent 2 %>
4
8
  <%= render(:command_filter).indent 2 %>
5
9
  <%= render(:required_args_filter).indent 2 %>
@@ -8,5 +12,5 @@ parse_args() {
8
12
  }
9
13
 
10
14
  <%- commands.each do |command| %>
11
- <%= command.render 'parse_args_secondary' %>
15
+ <%= command.render 'parse_args' %>
12
16
  <%- end -%>
@@ -4,13 +4,13 @@ run() {
4
4
  parse_args "$@"
5
5
 
6
6
  <%- condition = "if" -%>
7
- <%- commands.each do |command| -%>
8
- <%= condition %> [[ $action == "<%= command.name %>" ]]; then
7
+ <%- deep_commands.each do |command| -%>
8
+ <%= condition %> [[ $action == "<%= command.action_name %>" ]]; then
9
9
  if [[ ${args[--help]} ]]; then
10
10
  long_usage=yes
11
- <%= command.full_name.to_underscore %>_usage
11
+ <%= command.function_name %>_usage
12
12
  else
13
- <%= command.full_name.to_underscore %>_command
13
+ <%= command.function_name %>_command
14
14
  fi
15
15
  <% condition = "elif" %>
16
16
  <%- end -%>
@@ -1,9 +1,29 @@
1
1
  # :command.usage
2
- <%= full_name.to_underscore %>_usage() {
3
- echo -e "<%= caption_string %>"
4
- echo
2
+ <%= function_name %>_usage() {
3
+ if [[ -n $long_usage ]]; then
4
+ <%- if summary == help -%>
5
+ echo -e "<%= caption_string %>"
6
+ echo
7
+ <%- else -%>
8
+ echo -e "<%= full_name %>"
9
+ echo
10
+ echo -e "<%= help.wrap(78).indent(2).escape_newlines %>"
11
+ echo
12
+ <%- end -%>
13
+ else
14
+ echo -e "<%= caption_string %>"
15
+ echo
16
+ fi
5
17
  echo -e "<%= strings[:usage] %>"
6
18
  echo -e " <%= usage_string %>"
19
+ <%- if commands.any? -%>
20
+ echo -e " <%= full_name %> [command] --help | -h"
21
+ <%- else -%>
22
+ echo -e " <%= full_name %> --help | -h"
23
+ <%- end -%>
24
+ <%- if root_command? -%>
25
+ echo -e " <%= full_name %> --version"
26
+ <%- end -%>
7
27
  echo
8
28
  <%= render(:usage_commands).indent 2 if commands.any? %>
9
29
 
@@ -2,6 +2,6 @@
2
2
  echo -e "<%= strings[:environment_variables] %>"
3
3
  <%- environment_variables.each do |name, description| -%>
4
4
  echo " <%= name %>"
5
- echo " <%= description %>"
5
+ echo -e "<%= description.wrap(76).indent(4).escape_newlines %>"
6
6
  <%- end -%>
7
7
  echo
@@ -1,6 +1,7 @@
1
1
  # :command.usage_examples
2
2
  echo -e "<%= strings[:examples] %>"
3
+
3
4
  <%- examples.each do |example| -%>
4
- echo " <%= example %>"
5
+ echo -e "<%= example.wrap(78).indent(2).escape_newlines %>"
5
6
  <%- end -%>
6
7
  echo
@@ -1,7 +1,9 @@
1
1
  # :command.usage_fixed_flags
2
2
  echo " --help, -h"
3
- echo -e " <%= strings[:show_this_help] %>"
3
+ echo -e " <%= strings[:help_flag_text] %>"
4
4
  echo
5
+ <%- if root_command? -%>
5
6
  echo " --version"
6
- echo -e " <%= strings[:show_version_number] %>"
7
+ echo -e " <%= strings[:version_flag_text] %>"
7
8
  echo
9
+ <%- end -%>
@@ -1,4 +1,4 @@
1
1
  # :flag.usage
2
2
  echo " <%= usage_string extended: true %>"
3
- echo " <%= summary %>"
3
+ echo -e "<%= help.wrap(76).indent(4).escape_newlines %>"
4
4
  echo
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.2.2
4
+ version: 0.2.3
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: 2019-11-22 00:00:00.000000000 Z
11
+ date: 2019-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -101,7 +101,6 @@ files:
101
101
  - lib/bashly/views/command/master_script.erb
102
102
  - lib/bashly/views/command/parse_args.erb
103
103
  - lib/bashly/views/command/parse_args_case.erb
104
- - lib/bashly/views/command/parse_args_secondary.erb
105
104
  - lib/bashly/views/command/parse_args_while.erb
106
105
  - lib/bashly/views/command/required_args_filter.erb
107
106
  - lib/bashly/views/command/required_flags_filter.erb
@@ -1,7 +0,0 @@
1
- # :command.parse_args_secondary
2
- <%= name %>_parse_args() {
3
- <%= render(:fixed_flags_filter).indent 2 %>
4
- <%= render(:required_args_filter).indent 2 %>
5
- <%= render(:required_flags_filter).indent 2 %>
6
- <%= render(:parse_args_while).indent 2 %>
7
- }