bashly 1.0.7 → 1.1.0

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/lib/bashly/cli.rb +1 -0
  4. data/lib/bashly/commands/completions.rb +1 -1
  5. data/lib/bashly/commands/render.rb +103 -0
  6. data/lib/bashly/concerns/validation_helpers.rb +1 -1
  7. data/lib/bashly/config.rb +0 -2
  8. data/lib/bashly/extensions/string.rb +17 -1
  9. data/lib/bashly/extensions/yaml.rb +4 -1
  10. data/lib/bashly/libraries/config/config.sh +74 -94
  11. data/lib/bashly/libraries/ini/ini.sh +110 -0
  12. data/lib/bashly/libraries/libraries.yml +40 -1
  13. data/lib/bashly/libraries/render/mandoc/README.md +45 -0
  14. data/lib/bashly/libraries/render/mandoc/mandoc.gtx +196 -0
  15. data/lib/bashly/libraries/render/mandoc/render.rb +34 -0
  16. data/lib/bashly/libraries/render/mandoc/summary.txt +1 -0
  17. data/lib/bashly/libraries/render/markdown/README.md +42 -0
  18. data/lib/bashly/libraries/render/markdown/markdown.gtx +186 -0
  19. data/lib/bashly/libraries/render/markdown/render.rb +23 -0
  20. data/lib/bashly/libraries/render/markdown/summary.txt +1 -0
  21. data/lib/bashly/refinements/compose_refinements.rb +0 -2
  22. data/lib/bashly/render_context.rb +30 -0
  23. data/lib/bashly/render_source.rb +71 -0
  24. data/lib/bashly/script/base.rb +2 -1
  25. data/lib/bashly/script/command.rb +17 -0
  26. data/lib/bashly/version.rb +1 -1
  27. data/lib/bashly/views/command/dependencies_filter.gtx +1 -1
  28. data/lib/bashly/views/command/footer.gtx +1 -1
  29. data/lib/bashly/views/command/parse_requirements_while.gtx +1 -1
  30. data/lib/bashly/views/command/usage.gtx +2 -2
  31. data/lib/bashly/views/command/usage_commands.gtx +2 -2
  32. data/lib/bashly.rb +56 -11
  33. metadata +41 -9
@@ -0,0 +1,196 @@
1
+ # Reference: https://linux.die.net/man/5/pandoc_markdown
2
+
3
+ if version
4
+ > % {{ full_name.to_hyphen }}(1) Version {{ version }} | {{ summary }}
5
+ else
6
+ > % {{ full_name.to_hyphen }}(1) | {{ summary }}
7
+ end
8
+
9
+ > % {{ x_mandoc_authors&.for_manpage }}
10
+ > % {{ Date.today.strftime "%B %Y" }}
11
+ >
12
+
13
+ > NAME
14
+ > ==================================================
15
+ >
16
+ > **{{ full_name }}** - {{ summary }}
17
+ >
18
+
19
+ > SYNOPSIS
20
+ > ==================================================
21
+ >
22
+ > {{ usage_string.gsub(/^#{full_name}/, "**#{full_name}**") }}
23
+ >
24
+
25
+ > DESCRIPTION
26
+ > ==================================================
27
+ >
28
+ > {{ help.for_manpage }}
29
+ >
30
+
31
+ if default
32
+ > - *Default Command*
33
+ end
34
+ if alt.any?
35
+ > - Alias: **{{ alt.join ', ' }}**
36
+ end
37
+ if extensible
38
+ if extensible.is_a? String
39
+ > - Extensible: **{{ extensible }}**
40
+ else
41
+ > - *Extensible*
42
+ end
43
+ end
44
+ >
45
+
46
+ if public_commands.any?
47
+ grouped_commands.each do |group, commands|
48
+ > {{ group.gsub(/:$/, '').upcase }}
49
+ > ==================================================
50
+ >
51
+ commands.each do |subcommand|
52
+ > {{ subcommand.full_name }}
53
+ > --------------------------------------------------
54
+ >
55
+ > {{ subcommand.summary.for_manpage }}
56
+ >
57
+ end
58
+ >
59
+ end
60
+ end
61
+
62
+ if args.any?
63
+ > ARGUMENTS
64
+ > ==================================================
65
+ >
66
+ args.each do |arg|
67
+ > {{ arg.name.upcase }}
68
+ > --------------------------------------------------
69
+ >
70
+ > {{ arg.help.for_manpage }}
71
+ >
72
+ if arg.required
73
+ > - *Required*
74
+ end
75
+ if arg.repeatable
76
+ > - *Repeatable*
77
+ end
78
+ if arg.default
79
+ > - Default Value: **{{ arg.default }}**
80
+ end
81
+ if arg.allowed
82
+ > - Allowed Values: **{{ arg.allowed.join(', ') }}**
83
+ end
84
+ >
85
+ end
86
+
87
+ if catch_all.label && catch_all.help
88
+ > {{ catch_all.label }}
89
+ > --------------------------------------------------
90
+ >
91
+ if catch_all.help
92
+ > {{ catch_all.help.for_manpage }}
93
+ >
94
+ end
95
+ >
96
+
97
+ if catch_all.required?
98
+ > - *Required*
99
+ >
100
+ end
101
+ end
102
+ end
103
+
104
+ if flags.any?
105
+ > OPTIONS
106
+ > ==================================================
107
+ >
108
+ flags.each do |flag|
109
+ > {{ flag.usage_string }}
110
+ > --------------------------------------------------
111
+ >
112
+ > {{ flag.help.for_manpage }}
113
+ >
114
+
115
+ if flag.required
116
+ > - *Required*
117
+ end
118
+ if flag.repeatable
119
+ > - *Repeatable*
120
+ end
121
+ if flag.default
122
+ > - Default Value: **{{ flag.default }}**
123
+ end
124
+ if flag.allowed
125
+ > - Allowed Values: **{{ flag.allowed.join(', ') }}**
126
+ end
127
+ if flag.conflicts
128
+ > - Conflicts With: **{{ flag.conflicts.join(', ') }}**
129
+ end
130
+ >
131
+ end
132
+ end
133
+
134
+ if dependencies.any?
135
+ > DEPENDENCIES
136
+ > ==================================================
137
+ >
138
+ dependencies.each do |dependency|
139
+ > {{ dependency.commands.join ', ' }}
140
+ > --------------------------------------------------
141
+ >
142
+ if dependency.help
143
+ > {{ dependency.help.for_manpage }}
144
+ >
145
+ end
146
+ >
147
+ end
148
+ end
149
+
150
+ if public_environment_variables.any?
151
+ > ENVIRONMENT VARIABLES
152
+ > ==================================================
153
+ >
154
+ public_environment_variables.each do |environment_variable|
155
+ > {{ environment_variable.name.upcase }}
156
+ > --------------------------------------------------
157
+ >
158
+ > {{ environment_variable.help.for_manpage }}
159
+ >
160
+
161
+ if environment_variable.required
162
+ > - *Required*
163
+ end
164
+ if environment_variable.default
165
+ > - Default Value: **{{ environment_variable.default }}**
166
+ end
167
+ >
168
+ end
169
+ end
170
+
171
+ if examples
172
+ > EXAMPLES
173
+ > ==================================================
174
+ >
175
+ > ~~~
176
+ examples.each do |example|
177
+ > {{ example.for_manpage }}
178
+ >
179
+ end
180
+ > ~~~
181
+ >
182
+ end
183
+
184
+ if public_commands.any? || parents.any?
185
+ > SEE ALSO
186
+ > ==================================================
187
+ >
188
+ if parents.any?
189
+ > **{{ parents.first }}**(1)
190
+ end
191
+ = public_commands.map { |x| "**#{x.full_name.to_hyphen}**(1)" }.join ', '
192
+ >
193
+ end
194
+
195
+ = x_mandoc_footer&.for_manpage
196
+ >
@@ -0,0 +1,34 @@
1
+ # render script - mandoc
2
+ require 'gtx'
3
+
4
+ # Load the GTX template
5
+ template = "#{source}/mandoc.gtx"
6
+ gtx = GTX.load_file template
7
+
8
+ # Define a reusable code block for rendering a man page for a command
9
+ save_manpage = lambda { |command|
10
+ mdfile = "#{target}/#{command.full_name.tr(' ', '-')}.md"
11
+ manfile = "#{target}/#{command.full_name.tr(' ', '-')}.1"
12
+ save mdfile, gtx.parse(command)
13
+
14
+ # The pandoc command that creates a manpage from markdown
15
+ cmd = %[pandoc -f markdown-smart -s --to man "#{mdfile}" > "#{manfile}"]
16
+ success = system cmd
17
+ raise "Failed running pandoc\nMake sure the following command succeeds and try again:\n\n #{cmd}" unless success
18
+
19
+ say "g`saved` #{manfile}"
20
+ }
21
+
22
+ # Render the main command
23
+ save_manpage.call command
24
+
25
+ # Render all subcommands
26
+ command.deep_commands.reject(&:private).each do |subcommand|
27
+ save_manpage.call subcommand
28
+ end
29
+
30
+ # Show one of the files if requested
31
+ if show
32
+ file = "#{target}/#{show}"
33
+ system "man #{file}" if File.exist?(file)
34
+ end
@@ -0,0 +1 @@
1
+ Render man pages for your script
@@ -0,0 +1,42 @@
1
+ # Render markdown
2
+
3
+ Render markdown documents for your script.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ # Generate all documents to the ./docs directory
9
+ $ bashly render :markdown docs
10
+
11
+ # Generate on change, and show one of the files
12
+ $ bashly render :markdown docs --watch --show index.md
13
+ ```
14
+
15
+ ## Supported custom definitions
16
+
17
+ Add these definitions to your `bashly.yml` to render them in your
18
+ markdown:
19
+
20
+ ### Footer: `x_markdown_footer`
21
+
22
+ Add additional sections to your man pages. This field is expected
23
+ to be in markdown format.
24
+
25
+ #### Example
26
+
27
+ ```yaml
28
+ x_markdown_footer: |-
29
+ # ISSUE TRACKER
30
+
31
+ Report issues at <https://github.com/lanalang/smallville>
32
+ ```
33
+
34
+ ## Markdown server
35
+
36
+ In order to view your markdown files in a browser, you can use the
37
+ [Madness markdown server](https://madness.dannyb.co/):
38
+
39
+ ```bash
40
+ $ gem install madness
41
+ $ madness server docs
42
+ ```
@@ -0,0 +1,186 @@
1
+ # === Header
2
+
3
+ > # {{ full_name }}
4
+ >
5
+ > {{ help.for_markdown }}
6
+ >
7
+
8
+ attributes = version || alt.any? || default || extensible
9
+
10
+ if attributes
11
+ > | Attributes | &nbsp;
12
+ > |------------------|-------------
13
+ if version
14
+ > | Version: | {{ version }}
15
+ end
16
+ if alt.any?
17
+ > | Alias: | {{ alt.join ', ' }}
18
+ end
19
+ if default
20
+ > | Default Command: | ✓ Yes
21
+ end
22
+ if extensible
23
+ > | Extensible: | {{ extensible.is_a?(String) ? extensible : "✓ Yes" }}
24
+ end
25
+ >
26
+ end
27
+
28
+ # === Usage
29
+
30
+ > ## Usage
31
+ >
32
+ > ```bash
33
+ > {{ usage_string.for_markdown }}
34
+ > ```
35
+ >
36
+
37
+ # === Examples
38
+
39
+ if examples
40
+ > ## Examples
41
+ >
42
+ examples.each do |example|
43
+ > ```bash
44
+ > {{ example }}
45
+ > ```
46
+ >
47
+ end
48
+ end
49
+
50
+ # === Dependencies
51
+
52
+ if dependencies.any?
53
+ > ## Dependencies
54
+ >
55
+ dependencies.each do |dependency|
56
+ > #### *{{ dependency.commands.join ', ' }}*
57
+ >
58
+ > {{ dependency.help&.for_markdown }}
59
+ >
60
+ end
61
+ end
62
+
63
+ # === Environment Variables
64
+
65
+ if public_environment_variables.any?
66
+ > ## Environment Variables
67
+ >
68
+ public_environment_variables.each do |environment_variable|
69
+ attributes = environment_variable.required || environment_variable.default
70
+
71
+ > #### *{{ environment_variable.name.upcase }}*
72
+ >
73
+ > {{ environment_variable.help.for_markdown }}
74
+ >
75
+
76
+ if attributes
77
+ > | Attributes | &nbsp;
78
+ > |-----------------|-------------
79
+ if environment_variable.required
80
+ > | Required: | ✓ Yes
81
+ end
82
+ if environment_variable.default
83
+ > | Default Value: | {{ environment_variable.default }}
84
+ end
85
+ >
86
+ end
87
+ end
88
+ end
89
+
90
+ # === Commands
91
+
92
+ if commands.any?
93
+ grouped_commands.each do |group, commands|
94
+ > ## {{ group.gsub(/:$/, '') }}
95
+ >
96
+ commands.each do |subcommand|
97
+ > - [{{ subcommand.name }}]({{ subcommand.full_name.gsub(' ', '%20') }}) - {{ subcommand.summary.for_markdown }}
98
+ end
99
+ >
100
+ end
101
+ end
102
+
103
+ # === Arguments
104
+
105
+ if args.any?
106
+ > ## Arguments
107
+ >
108
+ args.each do |arg|
109
+ attributes = arg.required || arg.repeatable || arg.default || arg.allowed
110
+
111
+ > #### *{{ arg.name.upcase }}*
112
+ >
113
+ > {{ arg.help.for_markdown }}
114
+ >
115
+
116
+ if attributes
117
+ > | Attributes | &nbsp;
118
+ > |-----------------|-------------
119
+ if arg.required
120
+ > | Required: | ✓ Yes
121
+ end
122
+ if arg.repeatable
123
+ > | Repeatable: | ✓ Yes
124
+ end
125
+ if arg.default
126
+ > | Default Value: | {{ arg.default }}
127
+ end
128
+ if arg.allowed
129
+ > | Allowed Values: | {{ arg.allowed.join(', ') }}
130
+ end
131
+ >
132
+ end
133
+ end
134
+
135
+ if catch_all.label && catch_all.help
136
+ > #### *{{ catch_all.label }}*
137
+ >
138
+ > {{ catch_all.help&.for_markdown }}
139
+ >
140
+ if catch_all.required?
141
+ > | Attributes | &nbsp;
142
+ > |------------|-------------
143
+ > | Required: | ✓ Yes
144
+ >
145
+ end
146
+ end
147
+ end
148
+
149
+ # === Flags
150
+
151
+ if flags.any?
152
+ > ## Options
153
+ >
154
+ flags.each do |flag|
155
+ attributes = flag.required || flag.repeatable || flag.default || flag.allowed || flag.conflicts
156
+
157
+ > #### *{{ flag.usage_string }}*
158
+ >
159
+ > {{ flag.help.for_markdown }}
160
+ >
161
+
162
+ if attributes
163
+ > | Attributes | &nbsp;
164
+ > |-----------------|-------------
165
+ if flag.required
166
+ > | Required: | ✓ Yes
167
+ end
168
+ if flag.repeatable
169
+ > | Repeatable: | ✓ Yes
170
+ end
171
+ if flag.default
172
+ > | Default Value: | {{ flag.default }}
173
+ end
174
+ if flag.allowed
175
+ > | Allowed Values: | {{ flag.allowed.join(', ') }}
176
+ end
177
+ if flag.conflicts
178
+ > | Conflicts With: | *{{ flag.conflicts.join(', ') }}*
179
+ end
180
+ >
181
+ end
182
+ end
183
+ end
184
+
185
+ = x_markdown_footer&.for_manpage
186
+ >
@@ -0,0 +1,23 @@
1
+ # render script - markdown
2
+ require 'gtx'
3
+
4
+ # for previewing only (not needed for rendering)
5
+ require 'tty-markdown'
6
+
7
+ # Load the GTX template
8
+ template = "#{source}/markdown.gtx"
9
+ gtx = GTX.load_file template
10
+
11
+ # Render the file for the main command
12
+ save "#{target}/index.md", gtx.parse(command)
13
+
14
+ # Render a file for each subcommand
15
+ command.deep_commands.reject(&:private).each do |subcommand|
16
+ save "#{target}/#{subcommand.full_name}.md", gtx.parse(subcommand)
17
+ end
18
+
19
+ # Show one of the files if requested
20
+ if show
21
+ file = "#{target}/#{show}"
22
+ puts TTY::Markdown.parse_file(file) if File.exist?(file)
23
+ end
@@ -0,0 +1 @@
1
+ Render markdown documents for your script
@@ -1,5 +1,3 @@
1
- require 'yaml'
2
-
3
1
  module ComposeRefinements
4
2
  refine Hash do
5
3
  def compose(keyword = 'import')
@@ -0,0 +1,30 @@
1
+ require 'date' # for use by template render scripts
2
+ require 'colsole'
3
+
4
+ module Bashly
5
+ class RenderContext
6
+ include Colsole
7
+
8
+ attr_reader :source, :target, :show
9
+ attr_writer :config
10
+
11
+ def initialize(source:, target:, show: nil)
12
+ @source = source
13
+ @target = target
14
+ @show = show
15
+ end
16
+
17
+ def config
18
+ @config ||= Config.new Settings.config_path
19
+ end
20
+
21
+ def command
22
+ @command ||= Script::Command.new config
23
+ end
24
+
25
+ def save(filename, content)
26
+ File.deep_write filename, content
27
+ say "g`saved` #{filename}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,71 @@
1
+ module Bashly
2
+ class RenderSource
3
+ attr_reader :selector
4
+
5
+ class << self
6
+ include AssetHelper
7
+
8
+ def internal
9
+ @internal ||= internal_dirs.to_h do |dir|
10
+ selector = File.basename(dir).to_sym
11
+ [selector, new(selector)]
12
+ end
13
+ end
14
+
15
+ def internal_dirs
16
+ @internal_dirs ||= Dir["#{internal_root}/*"].select { |x| File.directory? x }
17
+ end
18
+
19
+ def internal_root
20
+ asset('libraries/render')
21
+ end
22
+ end
23
+
24
+ def initialize(selector)
25
+ @selector = selector
26
+ end
27
+
28
+ def render(target, show: nil)
29
+ context = RenderContext.new source: path, target: target, show: show
30
+ context.instance_eval render_script
31
+ end
32
+
33
+ def internal?
34
+ selector.is_a? Symbol
35
+ end
36
+
37
+ def path
38
+ internal? ? "#{internal_root}/#{selector}" : selector
39
+ end
40
+
41
+ def exist?
42
+ Dir.exist? path
43
+ end
44
+
45
+ def summary
46
+ File.readlines(summary_file)[0].chomp
47
+ end
48
+
49
+ def readme
50
+ File.read readme_file if File.exist? readme_file
51
+ end
52
+
53
+ private
54
+
55
+ def render_script
56
+ @render_script ||= File.read "#{path}/render.rb"
57
+ end
58
+
59
+ def internal_root
60
+ self.class.internal_root
61
+ end
62
+
63
+ def summary_file
64
+ "#{path}/summary.txt"
65
+ end
66
+
67
+ def readme_file
68
+ "#{path}/README.md"
69
+ end
70
+ end
71
+ end
@@ -35,7 +35,8 @@ module Bashly
35
35
  end
36
36
 
37
37
  def respond_to_missing?(method_name, include_private = false)
38
- self.class.option_keys.include?(method_name) || super
38
+ self.class.option_keys.include?(method_name) ||
39
+ method_name.to_s.start_with?('x_') || super
39
40
  end
40
41
  end
41
42
  end
@@ -200,6 +200,23 @@ module Bashly
200
200
  end
201
201
  end
202
202
 
203
+ # Returns subcommands by group
204
+ def grouped_commands
205
+ result = {}
206
+
207
+ public_commands.each do |command|
208
+ result[command.group_string] ||= []
209
+ result[command.group_string] << command
210
+ next unless command.expose
211
+
212
+ command.public_commands.each do |subcommand|
213
+ result[command.group_string] << subcommand
214
+ end
215
+ end
216
+
217
+ result
218
+ end
219
+
203
220
  # Returns a mode identifier
204
221
  def mode
205
222
  @mode ||= if global_flags? then :global_flags
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = '1.0.7'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -7,7 +7,7 @@ if dependencies.any?
7
7
  > else
8
8
  > printf "{{ strings[:missing_dependency] % { dependency: dependency.name } }}\n" >&2
9
9
  if dependency.help
10
- > printf "%s\n" "{{ dependency.help }}" >&2
10
+ > printf "%s\n" "{{ dependency.help.sanitize_for_print }}" >&2
11
11
  end
12
12
  > exit 1
13
13
  > fi
@@ -1,5 +1,5 @@
1
1
  = view_marker
2
2
 
3
- > printf "{{ footer.gsub("\n", '\n').gsub('"', '\"') }}\n"
3
+ > printf "{{ footer.sanitize_for_print }}\n"
4
4
  > echo
5
5
  >
@@ -27,7 +27,7 @@ if catch_all.enabled?
27
27
  > ;;
28
28
 
29
29
  else
30
- > printf "<%= strings[:invalid_flag] %>\n" "$key" >&2
30
+ > printf "{{ strings[:invalid_flag] }}\n" "$key" >&2
31
31
  > exit 1
32
32
  > ;;
33
33
 
@@ -4,7 +4,7 @@
4
4
  > if [[ -n $long_usage ]]; then
5
5
 
6
6
  if summary == help
7
- > printf "{{ caption_string }}\n"
7
+ > printf "{{ caption_string.sanitize_for_print }}\n"
8
8
  > echo
9
9
  else
10
10
  > printf "{{ full_name }}\n"
@@ -14,7 +14,7 @@ else
14
14
  end
15
15
 
16
16
  > else
17
- > printf "{{ caption_string }}\n"
17
+ > printf "{{ caption_string.sanitize_for_print }}\n"
18
18
  > echo
19
19
  > fi
20
20
  >
@@ -7,9 +7,9 @@ command_help_data.each do |group, commands|
7
7
 
8
8
  commands.each do |command, info|
9
9
  if info[:help_only]
10
- > [[ -n $long_usage ]] && printf " %s {{ info[:summary] }}\n" "{{ command.ljust(maxlen).color(:command) }}"
10
+ > [[ -n $long_usage ]] && printf " %s {{ info[:summary].sanitize_for_print }}\n" "{{ command.ljust(maxlen).color(:command) }}"
11
11
  else
12
- > printf " %s {{ info[:summary] }}\n" "{{ command.ljust(maxlen).color(:command) }}"
12
+ > printf " %s {{ info[:summary].sanitize_for_print }}\n" "{{ command.ljust(maxlen).color(:command) }}"
13
13
  end
14
14
  end
15
15