thor_enhance 0.4.0 → 0.5.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: 6298d393beb8a380215c08bc6fa78c97cfd29b3d804159f97b361628b5dd4707
4
- data.tar.gz: 15533218c1806a98998f3b66781a9543a22c231f60126c7b98b6a58bc5cf9cd6
3
+ metadata.gz: 8bd2c2b70158e1ab7a387dddfefde9c88c27c1809278ee05608dfca0f7de6484
4
+ data.tar.gz: eb3ed82f1b26e2239130ff9383971c7167975ac685815e08d48de42cf7eea74c
5
5
  SHA512:
6
- metadata.gz: a85cdd00c9939a3f0165fdb41950c9578b56d806cd42cfff90a7058e384fcf33d46ca9f2f79eea35524f305a791a57ee1167e38818d7587fe0b5115112945c84
7
- data.tar.gz: 38e8c731b8696c6abb82df29fdaabe2468e742ac69b222ac06796b989d0b6de119dfd56af82c6d2cf833522d06c2fdd0802cbb1f2fffce28b6c37c2ce62cb5d3
6
+ metadata.gz: 52f31daa639f2db023690b1c5b67ae261f0de382a1e03c9ad0a4801162e2ddef4127a942d8b57ad9a2dd72392a17953e63ad9ebfa913bf3e6a70d753bc668617
7
+ data.tar.gz: b504a6e6087671967755adf851386e0ad499d568a9f66ae561b2aee44b77a2b84c882ace9656136a10f2ed91ce77101023ee8e1ee9b2136d79f02dc428c1e63b
data/Gemfile CHANGED
@@ -9,4 +9,5 @@ gem "pry"
9
9
  gem "pry-byebug"
10
10
  gem "rspec", "~> 3.0"
11
11
  gem "rspec_junit_formatter"
12
- gem "simplecov", "~> 0.17.0", require: false
12
+ gem "simplecov", require: false
13
+ gem "ice_age"
data/Gemfile.lock CHANGED
@@ -1,18 +1,39 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- thor_enhance (0.3.0)
4
+ thor_enhance (0.5.0)
5
+ activesupport (>= 6)
5
6
  thor (~> 1.3)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ activesupport (7.1.2)
12
+ base64
13
+ bigdecimal
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ connection_pool (>= 2.2.5)
16
+ drb
17
+ i18n (>= 1.6, < 2)
18
+ minitest (>= 5.1)
19
+ mutex_m
20
+ tzinfo (~> 2.0)
21
+ base64 (0.2.0)
22
+ bigdecimal (3.1.4)
10
23
  byebug (11.1.3)
11
24
  coderay (1.1.3)
25
+ concurrent-ruby (1.2.2)
26
+ connection_pool (2.4.1)
12
27
  diff-lcs (1.5.0)
13
28
  docile (1.4.0)
14
- json (2.6.3)
29
+ drb (2.2.0)
30
+ ruby2_keywords
31
+ i18n (1.14.1)
32
+ concurrent-ruby (~> 1.0)
33
+ ice_age (0.2.0)
15
34
  method_source (1.0.0)
35
+ minitest (5.20.0)
36
+ mutex_m (0.2.0)
16
37
  pry (0.14.2)
17
38
  coderay (~> 1.1)
18
39
  method_source (~> 1.0)
@@ -34,23 +55,30 @@ GEM
34
55
  rspec-support (3.12.1)
35
56
  rspec_junit_formatter (0.6.0)
36
57
  rspec-core (>= 2, < 4, != 2.12.0)
37
- simplecov (0.17.1)
58
+ ruby2_keywords (0.0.5)
59
+ simplecov (0.22.0)
38
60
  docile (~> 1.1)
39
- json (>= 1.8, < 3)
40
- simplecov-html (~> 0.10.0)
41
- simplecov-html (0.10.2)
61
+ simplecov-html (~> 0.11)
62
+ simplecov_json_formatter (~> 0.1)
63
+ simplecov-html (0.12.3)
64
+ simplecov_json_formatter (0.1.4)
42
65
  thor (1.3.0)
66
+ tzinfo (2.0.6)
67
+ concurrent-ruby (~> 1.0)
43
68
 
44
69
  PLATFORMS
45
70
  aarch64-linux
71
+ arm64-darwin-22
72
+ arm64-darwin-23
46
73
 
47
74
  DEPENDENCIES
75
+ ice_age
48
76
  pry
49
77
  pry-byebug
50
78
  rspec (~> 3.0)
51
79
  rspec_junit_formatter
52
- simplecov (~> 0.17.0)
80
+ simplecov
53
81
  thor_enhance!
54
82
 
55
83
  BUNDLED WITH
56
- 2.4.22
84
+ 2.5.1
data/README.md CHANGED
@@ -31,10 +31,12 @@ Command option injection is very powerful. This allows add low level documentati
31
31
 
32
32
  [Command option documentation](docs/command.md)
33
33
 
34
- ### [Future Plans] Automatic ReadMe Generation
34
+ ### Automatic ReadMe Generation
35
35
  The beauty of ThorEnhance is that it forces all your documentation to live with the code. As your code changes, the documentation naturally changes with it.
36
36
 
37
- In the near future, we plan to allow for automatic readme generation based on the enhanced commands provided.
37
+ ThorEnhance can automatically generate your code bases Readme for you.
38
+
39
+ [Autogenerate Readme](docs/autogenerate/Readme.md)
38
40
 
39
41
 
40
42
  ### Initialization
data/bin/thor_enhance ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+ require "pry"
6
+
7
+ require "thor_enhance"
8
+
9
+ ThorEnhance.configure do |c|
10
+ c.readme_enhance!(required: true) do |r|
11
+ r.custom_header(:how_does_this_help, repeatable: true)
12
+ r.custom_header(:when_should_i_use_this, required: true, question: true)
13
+ end
14
+ end
15
+
16
+ require "thor_enhance/sample"
17
+
18
+ begin
19
+ ThorEnhance::Sample.start
20
+ rescue Interrupt => e
21
+ $stdout.puts "\nThanks for using Better Dependabot 👋\n"
22
+ end
@@ -0,0 +1,152 @@
1
+ # Autogenerate Readme
2
+
3
+ A core component of `ThorEnhance` is the ability to autogenerate Readmes for your thor commands. This allows the code and the readme to be in lockstep without the worry of the Readme getting out of date.
4
+
5
+ ## How does this work?
6
+ By building on top of [Command Method Injection](../command.md) and [Method Option Injection](../method_option.md), ThorEnhance allows Readme's to get autogenerated based on the description of the command and the methods.
7
+
8
+ ## How to use
9
+
10
+ ```ruby
11
+ # thor_enhance_config.rb
12
+ ThorEnhance.configure do |c|
13
+ c.readme_enhance!
14
+ end
15
+ ```
16
+
17
+ ## Default capabilities
18
+
19
+ ### Example Command Option
20
+ When `readme_enhance` is enabled, by default the `example`ew command option is available on for every command.
21
+
22
+ ```ruby
23
+ desc "sample", "This Sample command"
24
+ example "sample", desc: "Basic invocation of the useless command"
25
+ example "sample --boolean", desc: "Basic invocation of the useless command with a flag"
26
+ method_option :boolean, type: :boolean, desc: "Just a normal boolean"
27
+ def sample;end;
28
+ ```
29
+
30
+ The example command takes signature expects:
31
+ ```ruby
32
+ # remove basename and any submodules from command execution
33
+ example "command execution", desc: "Description of what the command does"
34
+ ```
35
+
36
+ By default, `example` is not required for every command. To make example required for every command, modify the configuration to
37
+ ```ruby
38
+ ThorEnhance.configure do |c|
39
+ c.readme_enhance! do |r|
40
+ r.example(required: true)
41
+ end
42
+ end
43
+ ```
44
+
45
+ ### Header Command Option
46
+ When `readme_enhance` is enabled, by default a the `header` option is available on every command.
47
+
48
+ ```ruby
49
+ desc "sample", "This Sample command"
50
+ header name: "When should I use this?", desc: "To add additional commentary to your command"
51
+ header name: "I can have many headers", desc: "Headers can be different per command"
52
+ def sample;end;
53
+ ```
54
+
55
+ By default, `header` is not required for every command. It is an additive command that is customizable per command
56
+
57
+ ### Title Command Option
58
+ When `readme_enhance` is enabled, by default a the `title` option is available on every command.
59
+
60
+ This optional command option gets used for the Readme title. Otherwise, the method name gets used
61
+
62
+ ```ruby
63
+ desc "sample", "This Sample command"
64
+ title ""
65
+ def sample;end;
66
+ ```
67
+
68
+ By default, `title` is not required for every command. To make example required for every command, modify the configuration to
69
+ ```ruby
70
+ ThorEnhance.configure do |c|
71
+ c.readme_enhance! do |r|
72
+ r.title(required: true)
73
+ end
74
+ end
75
+ ```
76
+
77
+ ### Readme Method Option
78
+
79
+ When `readme_enhance` is enabled, by default `readme` flag is available on every option.
80
+
81
+ ```ruby
82
+ desc "sample", "This Sample command"
83
+ method_option :boolean, type: :boolean, desc: "Just a normal boolean", readme: :important
84
+ def sample;end;
85
+ ```
86
+
87
+ The Default options for the value of `:readme` are [`:important`, `:advanced`, `:skip`].
88
+
89
+ All methods flagged with `:important` will show up higher in the readme. Anything labled with `:skip`, will not show up on the readme.
90
+
91
+
92
+ By default, `readme` is not required for every method.
93
+
94
+ ```ruby
95
+ # This will require all method_options to have the `readme` flag
96
+ ThorEnhance.configure do |c|
97
+ c.readme_enhance! do |r|
98
+ r.readme(required: true)
99
+ end
100
+ end
101
+ ```
102
+
103
+ ```ruby
104
+ # This will options without the `readme` flag to a group of :empty on the readme
105
+ ThorEnhance.configure do |c|
106
+ c.readme_enhance! do |r|
107
+ r.readme(empty_group: :empty)
108
+ end
109
+ end
110
+ ```
111
+
112
+ ```ruby
113
+ # Setting enums will override the default readme groups
114
+ # Only these values will be allowed as readme values
115
+ # The order of the arry determines the order the group will be ouputted in the Readme
116
+ ThorEnhance.configure do |c|
117
+ c.readme_enhance! do |r|
118
+ r.readme(enums: ["cool", :features, "live", "here".to_sym])
119
+ end
120
+ end
121
+ ```
122
+
123
+ ### Custom Header Command Option
124
+
125
+ Custom Headers must get manually added to the configuration.
126
+
127
+ ```ruby
128
+ ThorEnhance.configure do |c|
129
+ c.readme_enhance! do |r|
130
+ # how_does_this_help will be required on every command
131
+ r.custom_header("how_does_this_help", required: true)
132
+ # when_should_i_use_this is a repeatable command
133
+ r.custom_header("when_should_i_use_this", repeatable: true)
134
+ # is_this_important is now a question. On the readme, `?` gets appened to the header
135
+ r.custom_header("is_this_important", question: true)
136
+ end
137
+ end
138
+ ```
139
+
140
+ The configuration enumerated above allows the ability to do the following:
141
+
142
+
143
+ ```ruby
144
+ desc "sample", "This Sample command"
145
+ how_does_this_help "This command will help enable additional configuration for each command. This will get appended to the Readme in the order it appears as originally configured", tag: "h4"
146
+ when_should_i_use_this "Use this whenever you like"
147
+ when_should_i_use_this "Or you dont ever have to use this", tag: "h2"
148
+ is_this_important "Yes this is imporant. It even has a custom tag", tag: 1
149
+ def sample;end;
150
+ ```
151
+
152
+ Take special not of the `tag` option on the custom header. By default, header will output as an <h2> header tag. To change, this you can use numeric `1|2|3|4` or you can use string `h[1234]`
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/inflections"
4
+ require "thor_enhance/autogenerate/option"
5
+ require "erb"
6
+
7
+ module ThorEnhance
8
+ module Autogenerate
9
+ class Command
10
+ COMMAND_ERB = "#{File.dirname(__FILE__)}/templates/command.rb.erb"
11
+ COMMAND_TEMPLATE = ERB.new(File.read(COMMAND_ERB))
12
+
13
+ AGGREGATE_OPTIONS_ERB = "#{File.dirname(__FILE__)}/templates/aggregate_options.rb.erb"
14
+ AGGREGATE_OPTIONS_TEMPLATE = ERB.new(File.read(AGGREGATE_OPTIONS_ERB))
15
+
16
+ FOOTER_ERB = "#{File.dirname(__FILE__)}/templates/footer.rb.erb"
17
+ FOOTER_TEMPLATE = ERB.new(File.read(FOOTER_ERB))
18
+
19
+ attr_reader :leaf, :name, :basename, :child_commands, :parent
20
+
21
+ def initialize(leaf:, name:, basename:, parent: nil)
22
+ @leaf = leaf
23
+ @name = name
24
+ @basename = basename
25
+ @child_commands = []
26
+ @parent = parent
27
+ initialize_children!
28
+ end
29
+
30
+ def initialize_children!
31
+ return unless children?
32
+
33
+ @child_commands = leaf.children.map do |name, child_leaf|
34
+ self.class.new(leaf: child_leaf, name: name, basename: basename, parent: self)
35
+ end
36
+ end
37
+
38
+ def method_options
39
+ @method_options ||= begin
40
+ _options = options.map { |name, option| Option.new(name: name, option: option) }
41
+
42
+ _options.group_by { _1.readme_type }
43
+ end
44
+ end
45
+
46
+ def command_erb
47
+ @command_erb ||= begin
48
+ params = {
49
+ basename_string: basename_string,
50
+ children_descriptors: children_descriptors,
51
+ command: command,
52
+ custom_headers: custom_headers,
53
+ description: description,
54
+ drawn_out_examples: drawn_out_examples,
55
+ footer_erb: footer_erb,
56
+ headers: headers,
57
+ method_options_erb: method_options_erb,
58
+ parent_basename_string: parent_basename_string,
59
+ title: title,
60
+ }
61
+ COMMAND_TEMPLATE.result_with_hash(params)
62
+ end
63
+ end
64
+
65
+ def footer_erb
66
+ @footer_erb ||= begin
67
+ regenerate_single_command = "#{parent_basename_string} thor_enhance_autogenerate --command #{command.usage} --apply"
68
+ regenerate_thor_command = "#{basename} thor_enhance_autogenerate --apply"
69
+ FOOTER_TEMPLATE.result_with_hash({ regenerate_single_command: regenerate_single_command, regenerate_thor_command: regenerate_thor_command })
70
+ end
71
+ end
72
+
73
+ def drawn_out_examples(with_desc: true)
74
+ case command.example
75
+ when nil
76
+ when Array
77
+ command.example.map do |example|
78
+ value = []
79
+ value << "# #{example[:arguments][:kwargs][:desc]}" if with_desc
80
+ value << "#{parent_basename_string} #{example[:input]}"
81
+ value.join("\n")
82
+ end
83
+ else
84
+ value = []
85
+ value << "# #{example[:arguments][:kwargs][:desc]}" if with_desc
86
+ value << "#{parent_basename_string} #{example[:input]}"
87
+ [value.join("\n")]
88
+ end
89
+ end
90
+
91
+ def method_options_erb
92
+ @method_options_erb ||= AGGREGATE_OPTIONS_TEMPLATE.result_with_hash({ method_options: method_options })
93
+ end
94
+
95
+ def basename_string
96
+ "#{parent_basename_string} #{command.usage}"
97
+ end
98
+
99
+ def parent_basename_string
100
+ parent_names = [basename]
101
+ temp_leaf = leaf
102
+ while parent = temp_leaf.parent
103
+ temp_leaf = parent
104
+ parent_names << parent.command.usage
105
+ end
106
+ parent_names.join(" ")
107
+ end
108
+
109
+ def parent_root
110
+ if parent
111
+ # Remove the last index of parent because that will be the Readme.md file
112
+ # We just want the directory of the parent file
113
+ parent.relative_readme_path[0..-2]
114
+ else
115
+ []
116
+ end
117
+ end
118
+
119
+ def relative_readme_path
120
+ if children?
121
+ # If children exist, this is a subcommand and needs to be a root ReadMe
122
+ [*parent_root, name, "Readme.md"]
123
+ else
124
+ [*parent_root, "#{name}.md"]
125
+ end
126
+ end
127
+
128
+ # this only returns children and its children
129
+ # Call this on top most parent to retreive family tree for subcommands
130
+ def flatten_children
131
+ return [] if child_commands.empty?
132
+
133
+ child_commands.map do |child|
134
+ [child, child.flatten_children]
135
+ end.flatten
136
+ end
137
+
138
+ def save_self!(root:, apply:)
139
+ absolute_path = "#{root}/#{relative_readme_path.join("/")}"
140
+ pathname = Pathname.new(absolute_path)
141
+ FileUtils.mkdir_p(pathname.dirname)
142
+ if File.exist?(absolute_path)
143
+ content = File.read(absolute_path)
144
+ diff = command_erb == content ? :same : :overwite
145
+ else
146
+ diff = :new
147
+ end
148
+
149
+ if apply
150
+ File.write(absolute_path, command_erb)
151
+ end
152
+
153
+ { path: absolute_path, diff: diff, apply: apply, self_for_root: self_for_root }
154
+ end
155
+
156
+ def description
157
+ command.long_description || command.description
158
+ end
159
+
160
+ def title
161
+ command.title || command.usage
162
+ end
163
+
164
+ def self_for_root
165
+ params_for_child(self)
166
+ end
167
+
168
+ private
169
+
170
+ def custom_headers
171
+ ThorEnhance.configuration.autogenerated_config.custom_headers.map do |header|
172
+ next unless command.respond_to?(header.to_sym)
173
+
174
+ header_value = command.public_send(header.to_sym)
175
+ next if header_value.nil?
176
+ case header_value
177
+ when Array
178
+ header_value.map { header_string(_1, header) }
179
+ else
180
+ header_string(header_value, header)
181
+ end
182
+
183
+ end.compact.flatten.join("\n\n")
184
+ end
185
+
186
+ def header_string(header_value, header)
187
+ header_name = header.to_s.gsub(/[_-]/, " ").titlecase
188
+ header_name += "?" if ThorEnhance.configuration.autogenerated_config.question_headers.include?(header)
189
+ header_tag = decipher_header_tag(header_value[:arguments][:kwargs][:tag])
190
+ header_input = header_value[:input]
191
+ "#{header_tag} #{header_name}\n\n#{header_input}"
192
+ end
193
+
194
+ def decipher_header_tag(input)
195
+ case input
196
+ when String
197
+ begin
198
+ input[0..1][-1].to_i.to_s == input[0..1][-1] ? "#" * input[0..1][-1].to_i : "##"
199
+ rescue
200
+ "##"
201
+ end
202
+ when Integer
203
+ "#" * input
204
+ else
205
+ "##"
206
+ end
207
+ end
208
+
209
+ def children_descriptors
210
+ child_commands.map { params_for_child(_1) }
211
+ end
212
+
213
+ def params_for_child(child)
214
+ {
215
+ title: child.title,
216
+ link: child.relative_readme_path[-1],
217
+ description: child.description,
218
+ basename_string: child.basename_string,
219
+ examples: child.drawn_out_examples(with_desc: false) || [],
220
+ }
221
+ end
222
+
223
+ def headers
224
+ (command.header || []).map { _1[:arguments][:kwargs] }
225
+ end
226
+
227
+ def children?
228
+ leaf.children?
229
+ end
230
+
231
+ def command
232
+ leaf.command
233
+ end
234
+
235
+ def options
236
+ command.options
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThorEnhance
4
+ module Autogenerate
5
+ class Configuration
6
+ attr_reader :question_headers, :custom_headers, :configuration, :readme_empty_group, :readme_skip_key, :readme_enums
7
+
8
+ DEFAULT_SKIP_KEY = :skip
9
+
10
+ def initialize(required: false)
11
+ @required = required
12
+ @configuration = { add_option_enhance: {}, add_command_method_enhance: {} }
13
+ @readme_enums = []
14
+ @custom_headers = []
15
+ @question_headers = []
16
+ end
17
+
18
+ def set_default_required(value)
19
+ @required = value
20
+ end
21
+
22
+ def default
23
+ ThorEnhance::Configuration.allow_changes?
24
+
25
+ example
26
+ header
27
+ title
28
+ readme
29
+ end
30
+
31
+ def title(required: false)
32
+ ThorEnhance::Configuration.allow_changes?
33
+
34
+ required = required.nil? ? @required : required
35
+ configuration[:add_command_method_enhance][:title] = { repeatable: false, required: required }
36
+ end
37
+
38
+ def example(required: nil, repeatable: true)
39
+ ThorEnhance::Configuration.allow_changes?
40
+
41
+ required = required.nil? ? @required : required
42
+ configuration[:add_command_method_enhance][:example] = { repeatable: repeatable, required: required, required_kwargs: [:desc] }
43
+ end
44
+
45
+ def header
46
+ ThorEnhance::Configuration.allow_changes?
47
+
48
+ configuration[:add_command_method_enhance][:header] = { repeatable: true, required: false, required_kwargs: [:name, :desc], optional_kwargs: [:tag] }
49
+ end
50
+
51
+ def custom_header(name, question: false, repeatable: false, required: false)
52
+ ThorEnhance::Configuration.allow_changes?
53
+
54
+ raise ArgumentError, "Custom Header name must be unique. #{name} is already defined as a custom header. " if custom_headers.include?(name.to_sym)
55
+
56
+ custom_headers << name.to_sym
57
+ question_headers << name.to_sym if question
58
+ configuration[:add_command_method_enhance][name.to_sym] = { repeatable: repeatable, required: required, optional_kwargs: [:tag] }
59
+ end
60
+
61
+ def readme(required: nil, empty_group: :unassigned, skip_key: DEFAULT_SKIP_KEY, enums: [:important, :advanced, skip_key.to_sym].compact)
62
+ ThorEnhance::Configuration.allow_changes?
63
+
64
+ @readme_empty_group = empty_group
65
+ @readme_skip_key = skip_key
66
+ @readme_enums = enums.map(&:to_sym)
67
+ required = required.nil? ? @required : required
68
+ configuration[:add_option_enhance][:readme] = { enums: enums, required: required }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ThorEnhance
6
+ module Autogenerate
7
+ class Option
8
+ TEMPLATE_ERB = "#{File.dirname(__FILE__)}/templates/option.rb.erb"
9
+ OPTION_TEMPLATE = ERB.new(File.read(TEMPLATE_ERB))
10
+
11
+ attr_reader :name, :option
12
+
13
+ def initialize(name:, option:)
14
+ @name = name
15
+ @option = option
16
+ end
17
+
18
+ def template_text
19
+ text = []
20
+ text << "# What: #{option.description}"
21
+ text << "# Type: #{option.type}"
22
+ text << "# Required: #{option.required}"
23
+ text << "# Allowed Inputs: #{option.enum}" if option.enum
24
+ text << invocations.map { "#{_1}"}.join(" | ")
25
+
26
+ text.join("\n")
27
+ end
28
+
29
+ def invocations
30
+ base = [option.switch_name] + option.aliases
31
+ if option.type == :boolean
32
+ counter = option.switch_name.sub("--", "--no-")
33
+ base << counter
34
+ end
35
+
36
+ base
37
+ end
38
+
39
+ def readme_type
40
+ option.readme || ThorEnhance.configuration.autogenerated_config.readme_empty_group
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ <% ThorEnhance.configuration.autogenerated_config.readme_enums.each_with_index do |group_name, index| %>
2
+ <% next if method_options[group_name].nil? %>
3
+ <details <%= "open" if index == 0 %>>
4
+ <summary> <h3> <%= group_name.to_s.titlecase %> options </h3> </summary>
5
+
6
+ ```bash
7
+ <%= method_options[group_name].map { _1.template_text }.join("\n") %>
8
+ ```
9
+
10
+ </details>
11
+
12
+ <% end %>