diff --git a/README.md b/README.md index e9f7cd0f..374f6069 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,13 @@ $ gem install bashly ``` +Prerequisites +-------------------------------------------------- + +The bash scripts generated by bashly require bash 4 or higher due to heavy +use of associative arrays. + + What is Bashly -------------------------------------------------- @@ -159,6 +166,7 @@ bash function. `name` | The name of the argument. `help` | The message to display when using `--help`. Can have multiple lines. `required` | Specify if this argument is required. Note that once you define an optional argument (without required: true) then you cannot define required arguments after it. +`default` | The value to use in case it is not provided by the user. Implies that this argument is optional. ### Flag options @@ -173,6 +181,7 @@ short form). `help` | The text to display when using `--help`. Can have multiple lines. `arg` | If the flag requires an argument, specify its name here. `required` | Specify if this flag is required. +`default` | The value to use in case it is not provided by the user. Implies that this flag is optional, and only makes sense when the flag has an argument. #### Special handling for -v and -h diff --git a/examples/colors/colorly b/examples/colors/colorly index 753b0b04..579d73ec 100644 --- a/examples/colors/colorly +++ b/examples/colors/colorly @@ -145,6 +145,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/command-default/ftp b/examples/command-default/ftp index 1e1de2f1..353d5073 100644 --- a/examples/command-default/ftp +++ b/examples/command-default/ftp @@ -209,6 +209,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -263,6 +264,7 @@ ftp_upload_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -317,6 +319,7 @@ ftp_download_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/command-groups/ftp b/examples/command-groups/ftp index 9e0434e6..d3183cef 100644 --- a/examples/command-groups/ftp +++ b/examples/command-groups/ftp @@ -282,6 +282,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -336,6 +337,7 @@ ftp_download_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -390,6 +392,7 @@ ftp_upload_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -432,6 +435,7 @@ ftp_login_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -474,6 +478,7 @@ ftp_logout_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/commands-nested/cli b/examples/commands-nested/cli index c07864e0..1f0a5e50 100644 --- a/examples/commands-nested/cli +++ b/examples/commands-nested/cli @@ -352,6 +352,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -419,6 +420,7 @@ cli_dir_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -473,6 +475,7 @@ cli_dir_list_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -533,6 +536,7 @@ cli_dir_remove_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -600,6 +604,7 @@ cli_file_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -654,6 +659,7 @@ cli_file_show_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -708,6 +714,7 @@ cli_file_edit_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/commands/cli b/examples/commands/cli index 34ecd48e..4dfe1579 100644 --- a/examples/commands/cli +++ b/examples/commands/cli @@ -242,6 +242,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -305,6 +306,7 @@ cli_download_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -388,6 +390,7 @@ cli_upload_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/config-ini/configly b/examples/config-ini/configly index 7e68278a..f2039c79 100644 --- a/examples/config-ini/configly +++ b/examples/config-ini/configly @@ -382,6 +382,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -447,6 +448,7 @@ configly_set_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -501,6 +503,7 @@ configly_get_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -543,6 +546,7 @@ configly_list_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/custom-includes/download b/examples/custom-includes/download index 5e398c95..fd13170a 100644 --- a/examples/custom-includes/download +++ b/examples/custom-includes/download @@ -117,6 +117,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/custom-strings/download b/examples/custom-strings/download index e72fa21c..2054d10d 100644 --- a/examples/custom-strings/download +++ b/examples/custom-strings/download @@ -133,6 +133,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/default-values/README.md b/examples/default-values/README.md new file mode 100644 index 00000000..8f16217a --- /dev/null +++ b/examples/default-values/README.md @@ -0,0 +1,7 @@ +Minimal Example +================================================== + +This example was generated with: + + $ bashly init --minimal + $ bashly generate diff --git a/examples/default-values/convert b/examples/default-values/convert new file mode 100644 index 00000000..f8218fdc --- /dev/null +++ b/examples/default-values/convert @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +# This script was generated by bashly (https://github.com/DannyBen/bashly) +# Modifying it manually is not recommended + +# :command.root_command +root_command() { + # :src/root_command.sh + echo "# this file is located in 'src/root_command.sh'" + echo "# you can edit it freely and regenerate (it will not be overwritten)" + inspect_args +} + +# :command.version_command +version_command() { + echo "$version" +} + +# :command.usage +convert_usage() { + if [[ -n $long_usage ]]; then + printf "convert - Sample application using default arguments and flags\n" + echo + else + printf "convert - Sample application using default arguments and flags\n" + echo + fi + + printf "Usage:\n" + printf " convert [SOURCE] [options]\n" + printf " convert --help | -h\n" + printf " convert --version | -v\n" + echo + + if [[ -n $long_usage ]]; then + printf "Options:\n" + # :command.usage_fixed_flags + echo " --help, -h" + printf " Show this help\n" + echo + echo " --version, -v" + printf " Show version number\n" + echo + # :command.usage_flags + # :flag.usage + echo " --format, -f FORMAT" + printf " Format to convert to\n" + printf " Default: png\n" + echo + # :command.usage_args + printf "Arguments:\n" + + # :argument.usage + echo " SOURCE" + printf " Files to convert\n" + printf " Default: *.jpg\n" + echo + + # :command.usage_examples + printf "Examples:\n" + + printf " convert *.bmp\n" + printf " convert --format jpg\n" + printf " convert *.bmp --format jpg\n" + echo + + fi +} + +# :command.inspect_args +inspect_args() { + echo args: + for k in "${!args[@]}"; do echo "- \${args[$k]} = ${args[$k]}"; done +} + +# :command.command_functions + +# :command.parse_requirements +parse_requirements() { + # :command.fixed_flag_filter + case "$1" in + --version | -v ) + version_command + exit + ;; + + --help | -h ) + long_usage=yes + convert_usage + exit 1 + ;; + + esac + # :command.environment_variables_filter + # :command.dependencies_filter + # :command.command_filter + action="root" + # :command.required_args_filter + # :command.required_flags_filter + # :command.parse_requirements_while + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + # :flag.case + --format | -f ) + if [[ $2 && $2 != -* ]]; then + args[--format]="$2" + shift + shift + else + printf "%s\n" "--format requires an argument: --format, -f FORMAT" + exit 1 + fi + ;; + + + -* ) + printf "invalid option: %s\n" "$key" + exit 1 + ;; + + * ) + # :command.parse_requirements_case + if [[ ! ${args[source]} ]]; then + args[source]=$1 + shift + else + printf "invalid argument: %s\n" "$key" + exit 1 + fi + ;; + + esac + done + # :command.default_assignments + [[ -n ${args[source]} ]] || args[source]="*.jpg" + [[ -n ${args[--format]} ]] || args[--format]="png" +} + +# :command.initialize +initialize() { + version="0.1.0" + long_usage='' + set -e + + # :src/initialize.sh + # Code here runs inside the initialize() function + # Use it for anything that you need to run before any other function, like + # setting environment vairables: + # CONFIG_FILE=settings.ini + # + # Feel free to empty (but not delete) this file. +} + +# :command.run +run() { + declare -A args + parse_requirements "$@" + + if [[ ${args[--version]} ]]; then + version_command + elif [[ ${args[--help]} ]]; then + long_usage=yes + convert_usage + elif [[ $action == "root" ]]; then + root_command + fi +} + +initialize +run "$@" diff --git a/examples/default-values/src/bashly.yml b/examples/default-values/src/bashly.yml new file mode 100644 index 00000000..ab9dcd80 --- /dev/null +++ b/examples/default-values/src/bashly.yml @@ -0,0 +1,20 @@ +name: convert +help: Sample application using default arguments and flags +version: 0.1.0 + +args: +- name: source + help: Files to convert + default: "*.jpg" + +flags: +- long: --format + short: -f + arg: format + help: Format to convert to + default: png + +examples: +- convert *.bmp +- convert --format jpg +- convert *.bmp --format jpg diff --git a/examples/default-values/src/initialize.sh b/examples/default-values/src/initialize.sh new file mode 100644 index 00000000..f2dbc52c --- /dev/null +++ b/examples/default-values/src/initialize.sh @@ -0,0 +1,6 @@ +# Code here runs inside the initialize() function +# Use it for anything that you need to run before any other function, like +# setting environment vairables: +# CONFIG_FILE=settings.ini +# +# Feel free to empty (but not delete) this file. diff --git a/examples/default-values/src/root_command.sh b/examples/default-values/src/root_command.sh new file mode 100644 index 00000000..91e64307 --- /dev/null +++ b/examples/default-values/src/root_command.sh @@ -0,0 +1,3 @@ +echo "# this file is located in 'src/root_command.sh'" +echo "# you can edit it freely and regenerate (it will not be overwritten)" +inspect_args diff --git a/examples/default-values/test.sh b/examples/default-values/test.sh new file mode 100644 index 00000000..8e830ab2 --- /dev/null +++ b/examples/default-values/test.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +rm -f ./src/*.sh + +set -x + +bashly generate + +./convert +./convert -h +./convert *.bmp +./convert -f pdf +./convert *.pdf --format gif \ No newline at end of file diff --git a/examples/dependencies/cli b/examples/dependencies/cli index 3320da66..7bf17fb2 100644 --- a/examples/dependencies/cli +++ b/examples/dependencies/cli @@ -181,6 +181,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -231,6 +232,7 @@ cli_download_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -273,6 +275,7 @@ cli_upload_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/docker-like/docker b/examples/docker-like/docker index e010d42f..5d40441e 100644 --- a/examples/docker-like/docker +++ b/examples/docker-like/docker @@ -300,6 +300,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -367,6 +368,7 @@ docker_container_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -421,6 +423,7 @@ docker_container_run_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -475,6 +478,7 @@ docker_container_stop_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -535,6 +539,7 @@ docker_image_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -577,6 +582,7 @@ docker_image_ls_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/environment-variables/cli b/examples/environment-variables/cli index 088240c7..484ec3e4 100644 --- a/examples/environment-variables/cli +++ b/examples/environment-variables/cli @@ -161,6 +161,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -207,6 +208,7 @@ cli_verify_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/git-like/git b/examples/git-like/git index 36894744..47e21f4a 100644 --- a/examples/git-like/git +++ b/examples/git-like/git @@ -197,6 +197,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -239,6 +240,7 @@ git_status_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -299,6 +301,7 @@ git_commit_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/minimal/download b/examples/minimal/download index 10d128dc..e17f026d 100644 --- a/examples/minimal/download +++ b/examples/minimal/download @@ -55,7 +55,7 @@ download_usage() { # :argument.usage echo " TARGET" - printf " Target filename (default: same as source)\n" + printf " Target filename\n Default: same as source\n" echo # :command.usage_examples @@ -137,6 +137,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/minimal/src/bashly.yml b/examples/minimal/src/bashly.yml index 58119ca0..c8860b33 100644 --- a/examples/minimal/src/bashly.yml +++ b/examples/minimal/src/bashly.yml @@ -7,7 +7,7 @@ args: required: true help: URL to download from - name: target - help: "Target filename (default: same as source)" + help: "Target filename\nDefault: same as source" flags: - long: --force diff --git a/examples/minus-v/cli b/examples/minus-v/cli index fc9d7bcd..d58e2a4f 100644 --- a/examples/minus-v/cli +++ b/examples/minus-v/cli @@ -120,6 +120,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/multiline/multi b/examples/multiline/multi index 2721e519..eff418f0 100644 --- a/examples/multiline/multi +++ b/examples/multiline/multi @@ -218,6 +218,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -271,6 +272,7 @@ multi_multiline_parse_requirements() { esac done + # :command.default_assignments } # :command.parse_requirements @@ -318,6 +320,7 @@ multi_regular_parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/examples/yaml/yaml b/examples/yaml/yaml index 39e6cc9e..7aafe899 100644 --- a/examples/yaml/yaml +++ b/examples/yaml/yaml @@ -197,6 +197,7 @@ parse_requirements() { esac done + # :command.default_assignments } # :command.initialize diff --git a/lib/bashly/models/command.rb b/lib/bashly/models/command.rb index 0f9483bc..be929621 100644 --- a/lib/bashly/models/command.rb +++ b/lib/bashly/models/command.rb @@ -55,12 +55,22 @@ def deep_commands result end + # Returns an array of all the default Args + def default_args + args.select &:default + end + # If any of this command's subcommands has the default option set to # true, this default command will be returned, nil otherwise. def default_command commands.find { |c| c.default } end + # Returns an array of all the default Flags + def default_flags + flags.select &:default + end + # Returns an array of EnvironmentVariables def environment_variables return [] unless options["environment_variables"] diff --git a/lib/bashly/templates/strings.yml b/lib/bashly/templates/strings.yml index f91a82f9..4b0f1bb5 100644 --- a/lib/bashly/templates/strings.yml +++ b/lib/bashly/templates/strings.yml @@ -14,6 +14,7 @@ group: "%{group} Commands:" command_shortcut: "Shortcut: %{short}" default_command_summary: "%{summary} (default)" required: "(required)" +default: "Default: %{value}" # Fixed flags help text help_flag_text: Show this help diff --git a/lib/bashly/views/argument/usage.erb b/lib/bashly/views/argument/usage.erb index b432d90a..7fb4595d 100644 --- a/lib/bashly/views/argument/usage.erb +++ b/lib/bashly/views/argument/usage.erb @@ -1,4 +1,7 @@ # :argument.usage echo " <%= name.upcase %>" printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n" +<%- if default -%> +printf " <%= strings[:default] % { value: default } -%>\n" +<%- end -%> echo diff --git a/lib/bashly/views/command/default_assignments.erb b/lib/bashly/views/command/default_assignments.erb new file mode 100644 index 00000000..e324095d --- /dev/null +++ b/lib/bashly/views/command/default_assignments.erb @@ -0,0 +1,7 @@ +# :command.default_assignments +<%- default_args.each do |arg| -%> +[[ -n ${args[<%= arg.name %>]} ]] || args[<%= arg.name %>]="<%= arg.default %>" +<%- end -%> +<%- default_flags.each do |flag| -%> +[[ -n ${args[<%= flag.long %>]} ]] || args[<%= flag.long %>]="<%= flag.default %>" +<%- end -%> diff --git a/lib/bashly/views/command/parse_requirements.erb b/lib/bashly/views/command/parse_requirements.erb index fbaf84aa..dbb83b43 100644 --- a/lib/bashly/views/command/parse_requirements.erb +++ b/lib/bashly/views/command/parse_requirements.erb @@ -11,6 +11,7 @@ parse_requirements() { <%= render(:required_args_filter).indent 2 %> <%= render(:required_flags_filter).indent 2 %> <%= render(:parse_requirements_while).indent 2 %> +<%= render(:default_assignments).indent 2 %> } <%- commands.each do |command| %> diff --git a/lib/bashly/views/flag/usage.erb b/lib/bashly/views/flag/usage.erb index ac4088bc..2f7e3161 100644 --- a/lib/bashly/views/flag/usage.erb +++ b/lib/bashly/views/flag/usage.erb @@ -1,4 +1,7 @@ # :flag.usage echo " <%= usage_string extended: true %>" printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n" +<%- if default -%> +printf " <%= strings[:default] % { value: default } -%>\n" +<%- end -%> echo diff --git a/spec/approvals/examples/default-values b/spec/approvals/examples/default-values new file mode 100644 index 00000000..a43cf565 --- /dev/null +++ b/spec/approvals/examples/default-values @@ -0,0 +1,59 @@ ++ bashly generate +creating user files in src +created src/initialize.sh +created src/root_command.sh +created ./convert +run ./convert --help to test your bash script ++ ./convert +# this file is located in 'src/root_command.sh' +# you can edit it freely and regenerate (it will not be overwritten) +args: +- ${args[source]} = *.jpg +- ${args[--format]} = png ++ ./convert -h +convert - Sample application using default arguments and flags + +Usage: + convert [SOURCE] [options] + convert --help | -h + convert --version | -v + +Options: + --help, -h + Show this help + + --version, -v + Show version number + + --format, -f FORMAT + Format to convert to + Default: png + +Arguments: + SOURCE + Files to convert + Default: *.jpg + +Examples: + convert *.bmp + convert --format jpg + convert *.bmp --format jpg + ++ ./convert '*.bmp' +# this file is located in 'src/root_command.sh' +# you can edit it freely and regenerate (it will not be overwritten) +args: +- ${args[source]} = *.bmp +- ${args[--format]} = png ++ ./convert -f pdf +# this file is located in 'src/root_command.sh' +# you can edit it freely and regenerate (it will not be overwritten) +args: +- ${args[source]} = *.jpg +- ${args[--format]} = pdf ++ ./convert '*.pdf' --format gif +# this file is located in 'src/root_command.sh' +# you can edit it freely and regenerate (it will not be overwritten) +args: +- ${args[source]} = *.pdf +- ${args[--format]} = gif diff --git a/spec/approvals/examples/minimal b/spec/approvals/examples/minimal index 95d4ce9c..a896a5a0 100644 --- a/spec/approvals/examples/minimal +++ b/spec/approvals/examples/minimal @@ -30,7 +30,8 @@ Arguments: URL to download from TARGET - Target filename (default: same as source) + Target filename + Default: same as source Examples: download example.com diff --git a/spec/bashly/models/command_spec.rb b/spec/bashly/models/command_spec.rb index a65d3946..c49aaf78 100644 --- a/spec/bashly/models/command_spec.rb +++ b/spec/bashly/models/command_spec.rb @@ -90,6 +90,15 @@ end end + describe '#default_arguments' do + let(:fixture) { :default_values } + + it "returns an array of only the Argument objects that have default" do + expect(subject.default_args.size).to eq 1 + expect(subject.default_args.first.name).to eq "files" + end + end + describe '#default_command' do let(:fixture) { :default_command } @@ -99,6 +108,16 @@ end end + describe '#default_flags' do + let(:fixture) { :default_values } + + it "returns an array of only the Flags objects that have default" do + expect(subject.default_flags.size).to eq 1 + expect(subject.default_flags.first.long).to eq "--format" + end + end + + describe '#environment_cariables' do it "returns an array of EnvironemntVariable objects" do expect(subject.environment_variables).to be_an Array diff --git a/spec/fixtures/models/commands.yml b/spec/fixtures/models/commands.yml index 287df363..7cb6756c 100644 --- a/spec/fixtures/models/commands.yml +++ b/spec/fixtures/models/commands.yml @@ -78,3 +78,13 @@ long: --hello - short: -v long: --vorld + +:default_values: + args: + - name: files + default: "*.jpg" + flags: + - short: -f + long: --format + arg: format + default: png