bashly 1.0.8 → 1.1.0

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