bashly 0.2.2 → 0.2.3

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