bashly 1.0.8 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b82e5ead650cc1774cf3870af91b9046b1e5f69f05484d833f72dd4090eab091
4
- data.tar.gz: 26626b09b79382d2d4530241b5517ed04d3e4a27c004130adadad3b27421f100
3
+ metadata.gz: d333139a3555fdc50cdb3ddf21119712fcada4ab837f5703f29529f346c6ad24
4
+ data.tar.gz: 0a40e42bfa542ed5d4f8d59792aa2a19518ae1c9098db0309f9349db391c74b2
5
5
  SHA512:
6
- metadata.gz: 9e3b78d5975c92954347be94d567f28d240d628e080fb3d136382cc4f8fa1ae81827057ad3b61ac144b5ab930c3f5353128f9d6068b983d0e2e77ac996d4ab0d
7
- data.tar.gz: 2595428ca74b3ada25619fc7839f99993f98bcda169b47fcda84c82b279ebcaa1882864d2d842c67f05ca1ca946f2f64867d6905892f2ff5f5f5107f32f49037
6
+ metadata.gz: e601e098208e0a5de186279e67c001ff58c44536f09e5cea6815722e9bef3ec3dc1b257c1b9ca2267e94977f05514916a66f03721c88c5a795cab93e8eadb20d
7
+ data.tar.gz: 670bcae80903cdabc5d76b5139b1c6d80b1ee3ca33af03078a340a847c4ef1385ca74ca08b2b0a274845f0be5ecfc7ecd9822575b8f8ea8cb3592b821df22760
data/README.md CHANGED
@@ -71,7 +71,8 @@ Bashly is responsible for:
71
71
  - **Config file management** (INI format).
72
72
  - **YAML parsing**.
73
73
  - **Bash completions**.
74
- - and more.
74
+ - *and more*.
75
+ - Auto-generating **markdown and man page documentation** for your script.
75
76
 
76
77
  ## Contributing / Support
77
78
 
data/lib/bashly/cli.rb CHANGED
@@ -16,6 +16,7 @@ module Bashly
16
16
  runner.route 'add', to: Commands::Add
17
17
  runner.route 'doc', to: Commands::Doc
18
18
  runner.route 'completions', to: Commands::Completions
19
+ runner.route 'render', to: Commands::Render
19
20
  runner.route 'shell', to: Commands::Shell unless ENV['BASHLY_SHELL']
20
21
 
21
22
  runner
@@ -0,0 +1,103 @@
1
+ require 'filewatcher'
2
+ require 'tty-markdown'
3
+
4
+ module Bashly
5
+ module Commands
6
+ class Render < Base
7
+ help 'Render the bashly data structure using cutsom templates'
8
+
9
+ usage 'bashly render SOURCE TARGET [--watch --show PATH]'
10
+ usage 'bashly render SOURCE --about'
11
+ usage 'bashly render --list'
12
+ usage 'bashly render (-h|--help)'
13
+
14
+ param 'SOURCE', <<~HELP
15
+ An ID to an internal templates source, or a path to a custom templates directory.
16
+
17
+ A leading colon (:) denotes an internal ID (see `--list`).
18
+ HELP
19
+
20
+ param 'TARGET', 'Output directory'
21
+
22
+ option '-w --watch', 'Watch bashly.yml and the templates source for changes and render on change'
23
+ option '-s --show PATH', <<~USAGE
24
+ After rendering, show the result generated in PATH.
25
+
26
+ The provided PATH is treated as relative TARGET.
27
+
28
+ Note that this works only if the template source supports it.
29
+ USAGE
30
+
31
+ option '-l --list', 'Show list of built-in templates'
32
+ option '-a --about', 'Show information about a given templates source'
33
+
34
+ example 'bashly render --list'
35
+ example 'bashly render :markdown --about'
36
+ example 'bashly render :markdown docs --watch'
37
+ example 'bashly render :markdown docs --show "cli-download.1"'
38
+ example 'bashly render /path/to/templates ./out_path'
39
+
40
+ attr_reader :watching, :target, :source
41
+
42
+ def run
43
+ if args['--list'] then show_list
44
+ elsif args['--about'] then show_about
45
+ else
46
+ start_render
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def show_list
53
+ RenderSource.internal.each_value do |source|
54
+ say "g`:#{source.selector.to_s.ljust 10}` #{source.summary}"
55
+ end
56
+ end
57
+
58
+ def show_about
59
+ puts TTY::Markdown.parse(render_source.readme)
60
+ end
61
+
62
+ def start_render
63
+ @target = args['TARGET']
64
+ @watching = args['--watch']
65
+
66
+ render
67
+ watch if watching
68
+ end
69
+
70
+ def render
71
+ render_source.render target, show: args['--show']
72
+ end
73
+
74
+ def watch
75
+ say "g`watching`\n"
76
+
77
+ Filewatcher.new(watchables).watch do
78
+ render
79
+ say "g`waiting`\n"
80
+ end
81
+ end
82
+
83
+ def render_source
84
+ @render_source ||= begin
85
+ source = RenderSource.new selector
86
+ raise "Invalid render source: #{args['SOURCE']}" unless source.exist?
87
+
88
+ source
89
+ end
90
+ end
91
+
92
+ def selector
93
+ return args['SOURCE'] unless args['SOURCE'].start_with? ':'
94
+
95
+ args['SOURCE'][1..].to_sym
96
+ end
97
+
98
+ def watchables
99
+ @watchables ||= [Settings.config_path, render_source.path]
100
+ end
101
+ end
102
+ end
103
+ end
@@ -40,7 +40,7 @@ module Bashly
40
40
 
41
41
  return unless keys
42
42
 
43
- invalid_keys = value.keys.map(&:to_sym) - keys
43
+ invalid_keys = (value.keys.map(&:to_sym) - keys).reject { |k| k.start_with? 'x_' }
44
44
  assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join ', '}"
45
45
  end
46
46
 
data/lib/bashly/config.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'yaml'
2
-
3
1
  module Bashly
4
2
  # A convenience class to use either a hash or a filename as a configuration
5
3
  # source.
@@ -1,6 +1,18 @@
1
1
  class String
2
2
  def sanitize_for_print
3
- gsub("\n", '\\n').gsub('"', '\"')
3
+ gsub("\n", '\\n').gsub('"', '\"').gsub('`', '\\\\`')
4
+ end
5
+
6
+ def for_markdown
7
+ gsub('<', '\\<').gsub('>', '\\>').nl2br
8
+ end
9
+
10
+ def for_manpage
11
+ gsub('<', '\\<').gsub('>', '\\>').gsub('`', '**').gsub(" \n", "\n\n")
12
+ end
13
+
14
+ def nl2br
15
+ gsub("\n", " \n")
4
16
  end
5
17
 
6
18
  def indent(offset)
@@ -13,6 +25,10 @@ class String
13
25
  gsub(/(.)([A-Z])/, '\1_\2').gsub(/[- ]/, '_').downcase
14
26
  end
15
27
 
28
+ def to_hyphen
29
+ tr(' ', '-').gsub(/([a-z])([A-Z])/, '\1-\2').downcase
30
+ end
31
+
16
32
  def to_path
17
33
  tr(' ', '/').downcase
18
34
  end
@@ -1,9 +1,12 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
1
4
  module YAML
2
5
  # We trust our loaded YAMLs
3
6
  # This patch is due to https://bugs.ruby-lang.org/issues/17866
4
7
  # StackOverflow: https://stackoverflow.com/questions/71191685/visit-psych-nodes-alias-unknown-alias-default-psychbadalias/71192990#71192990
5
8
  class << self
6
- alias load unsafe_load
9
+ alias load unsafe_load if YAML.respond_to? :unsafe_load
7
10
 
8
11
  def load_erb_file(path)
9
12
  YAML.load ERB.new(File.read(path)).result
@@ -56,6 +56,37 @@ lib:
56
56
  - source: "lib/sample_function.sh"
57
57
  target: "%{user_lib_dir}/sample_function.%{user_ext}"
58
58
 
59
+ render_markdown:
60
+ help: Copy the markdown templates to your project, allowing you to customize the markdown documentation output.
61
+ skip_src_check: true
62
+ files:
63
+ - source: "render/markdown/README.md"
64
+ target: "templates/markdown/README.md"
65
+ - source: "render/markdown/markdown.gtx"
66
+ target: "templates/markdown/markdown.gtx"
67
+ - source: "render/markdown/render.rb"
68
+ target: "templates/markdown/render.rb"
69
+ post_install_message: |
70
+ Generate your markdown documentation by running:
71
+
72
+ m`$ bashly render templates/markdown docs`
73
+
74
+ render_mandoc:
75
+ help: Copy the mandoc templates to your project, allowing you to customize the man documentation output.
76
+ skip_src_check: true
77
+ files:
78
+ - source: "render/mandoc/README.md"
79
+ target: "templates/mandoc/README.md"
80
+ - source: "render/mandoc/mandoc.gtx"
81
+ target: "templates/mandoc/mandoc.gtx"
82
+ - source: "render/mandoc/render.rb"
83
+ target: "templates/mandoc/render.rb"
84
+ post_install_message: |
85
+ Note that this template requires pandoc.
86
+ Generate your man pages by running:
87
+
88
+ m`$ bashly render templates/mandoc docs`
89
+
59
90
  settings:
60
91
  help: Copy a sample settings.yml file to your project, allowing you to customize some bashly options.
61
92
  skip_src_check: true
@@ -0,0 +1,45 @@
1
+ # Render mandoc
2
+
3
+ Render man pages for your script.
4
+
5
+ Note that this renderer will render specially formatted markdown documents and
6
+ will then use [pandoc](https://command-not-found.com/pandoc) to convert them.
7
+
8
+ ## Usage
9
+
10
+ ```bash
11
+ # Generate all man pages to the ./docs directory
12
+ $ bashly render :mandoc docs
13
+
14
+ # Generate on change, and show one of the files
15
+ $ bashly render :mandoc docs --watch --show cli-download.1
16
+ ```
17
+
18
+ ## Supported custom definitions
19
+
20
+ Add these definitions to your `bashly.yml` to render them in your
21
+ markdown:
22
+
23
+ ### Footer: `x_mandoc_footer`
24
+
25
+ Add additional sections to your man pages. This field is expected
26
+ to be in markdown format.
27
+
28
+ #### Example
29
+
30
+ ```yaml
31
+ x_mandoc_footer: |-
32
+ # ISSUE TRACKER
33
+
34
+ Report issues at <https://github.com/lanalang/smallville>
35
+ ```
36
+
37
+ ### Authors: `x_mandoc_authors`
38
+
39
+ Add an authors string to your man pages.
40
+
41
+ #### Example
42
+
43
+ ```yaml
44
+ x_mandoc_authors: Lana Lang
45
+ ```
@@ -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.8'
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
 
data/lib/bashly.rb CHANGED
@@ -1,16 +1,61 @@
1
- require 'requires'
2
-
3
1
  if ENV['BYEBUG']
4
2
  require 'byebug'
5
3
  require 'lp'
6
4
  end
7
5
 
8
- requires 'bashly/concerns'
9
- requires 'bashly/extensions'
10
- requires 'bashly/settings'
11
- requires 'bashly/exceptions'
12
- requires 'bashly/refinements'
13
- requires 'bashly/script/base'
14
- requires 'bashly/commands/base'
15
- requires 'bashly/libraries/base'
16
- requires 'bashly'
6
+ require 'bashly/extensions/array'
7
+ require 'bashly/extensions/file'
8
+ require 'bashly/extensions/string'
9
+ require 'bashly/extensions/yaml'
10
+ require 'bashly/exceptions'
11
+
12
+ module Bashly
13
+ autoload :CLI, 'bashly/cli'
14
+ autoload :Config, 'bashly/config'
15
+ autoload :ConfigValidator, 'bashly/config_validator'
16
+ autoload :Library, 'bashly/library'
17
+ autoload :LibrarySource, 'bashly/library_source'
18
+ autoload :MessageStrings, 'bashly/message_strings'
19
+ autoload :RenderContext, 'bashly/render_context'
20
+ autoload :RenderSource, 'bashly/render_source'
21
+ autoload :VERSION, 'bashly/version'
22
+
23
+ autoload :AssetHelper, 'bashly/concerns/asset_helper'
24
+ autoload :Completions, 'bashly/concerns/completions'
25
+ autoload :ComposeRefinements, 'bashly/refinements/compose_refinements'
26
+ autoload :Renderable, 'bashly/concerns/renderable'
27
+ autoload :Settings, 'bashly/settings'
28
+ autoload :ValidationHelpers, 'bashly/concerns/validation_helpers'
29
+
30
+ module Script
31
+ autoload :Argument, 'bashly/script/argument'
32
+ autoload :Base, 'bashly/script/base'
33
+ autoload :CatchAll, 'bashly/script/catch_all'
34
+ autoload :Command, 'bashly/script/command'
35
+ autoload :Dependency, 'bashly/script/dependency'
36
+ autoload :EnvironmentVariable, 'bashly/script/environment_variable'
37
+ autoload :Flag, 'bashly/script/flag'
38
+ autoload :Wrapper, 'bashly/script/wrapper'
39
+ end
40
+
41
+ module Commands
42
+ autoload :Add, 'bashly/commands/add'
43
+ autoload :Base, 'bashly/commands/base'
44
+ autoload :Completions, 'bashly/commands/completions'
45
+ autoload :Doc, 'bashly/commands/doc'
46
+ autoload :Generate, 'bashly/commands/generate'
47
+ autoload :Init, 'bashly/commands/init'
48
+ autoload :Preview, 'bashly/commands/preview'
49
+ autoload :Render, 'bashly/commands/render'
50
+ autoload :Shell, 'bashly/commands/shell'
51
+ autoload :Validate, 'bashly/commands/validate'
52
+ end
53
+
54
+ module Libraries
55
+ autoload :Base, 'bashly/libraries/base'
56
+ autoload :CompletionsFunction, 'bashly/libraries/completions/completions_function'
57
+ autoload :CompletionsScript, 'bashly/libraries/completions/completions_script'
58
+ autoload :CompletionsYAML, 'bashly/libraries/completions/completions_yaml'
59
+ autoload :Help, 'bashly/libraries/help/help'
60
+ end
61
+ 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: 1.0.8
4
+ version: 1.1.0
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: 2023-08-11 00:00:00.000000000 Z
11
+ date: 2023-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -101,19 +101,19 @@ dependencies:
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0.7'
103
103
  - !ruby/object:Gem::Dependency
104
- name: requires
104
+ name: tty-markdown
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '1.0'
109
+ version: '0.7'
110
110
  type: :runtime
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '1.0'
116
+ version: '0.7'
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: psych
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -152,6 +152,7 @@ files:
152
152
  - lib/bashly/commands/generate.rb
153
153
  - lib/bashly/commands/init.rb
154
154
  - lib/bashly/commands/preview.rb
155
+ - lib/bashly/commands/render.rb
155
156
  - lib/bashly/commands/shell.rb
156
157
  - lib/bashly/commands/validate.rb
157
158
  - lib/bashly/completions/README.md
@@ -187,6 +188,14 @@ files:
187
188
  - lib/bashly/libraries/ini/ini.sh
188
189
  - lib/bashly/libraries/lib/sample_function.sh
189
190
  - lib/bashly/libraries/libraries.yml
191
+ - lib/bashly/libraries/render/mandoc/README.md
192
+ - lib/bashly/libraries/render/mandoc/mandoc.gtx
193
+ - lib/bashly/libraries/render/mandoc/render.rb
194
+ - lib/bashly/libraries/render/mandoc/summary.txt
195
+ - lib/bashly/libraries/render/markdown/README.md
196
+ - lib/bashly/libraries/render/markdown/markdown.gtx
197
+ - lib/bashly/libraries/render/markdown/render.rb
198
+ - lib/bashly/libraries/render/markdown/summary.txt
190
199
  - lib/bashly/libraries/settings/settings.yml
191
200
  - lib/bashly/libraries/strings/strings.yml
192
201
  - lib/bashly/libraries/test/approvals.bash
@@ -200,6 +209,8 @@ files:
200
209
  - lib/bashly/library_source.rb
201
210
  - lib/bashly/message_strings.rb
202
211
  - lib/bashly/refinements/compose_refinements.rb
212
+ - lib/bashly/render_context.rb
213
+ - lib/bashly/render_source.rb
203
214
  - lib/bashly/script/argument.rb
204
215
  - lib/bashly/script/base.rb
205
216
  - lib/bashly/script/catch_all.rb
@@ -289,7 +300,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
289
300
  - !ruby/object:Gem::Version
290
301
  version: '0'
291
302
  requirements: []
292
- rubygems_version: 3.4.18
303
+ rubygems_version: 3.3.26
293
304
  signing_key:
294
305
  specification_version: 4
295
306
  summary: Bash Command Line Tool Generator