tocer 12.2.0 → 13.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.
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module Tocer
6
+ module CLI
7
+ # Assembles and parses all Command Line Interface (CLI) options.
8
+ class Parser
9
+ CLIENT = OptionParser.new nil, 40, " "
10
+ SECTIONS = [Parsers::Core, Parsers::Flag].freeze # Order is important.
11
+
12
+ def initialize sections: SECTIONS, client: CLIENT, container: Container
13
+ @sections = sections
14
+ @client = client
15
+ @configuration = container[:configuration].dup
16
+ end
17
+
18
+ def call arguments = []
19
+ sections.each { |parser| parser.call configuration, client: }
20
+ client.parse arguments
21
+ configuration.freeze
22
+ end
23
+
24
+ def to_s = client.to_s
25
+
26
+ private
27
+
28
+ attr_reader :sections, :client, :configuration
29
+ end
30
+ end
31
+ end
@@ -1,29 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "tocer/identity"
3
+ require "refinements/structs"
4
4
 
5
5
  module Tocer
6
6
  module CLI
7
7
  module Parsers
8
8
  # Handles parsing of Command Line Interface (CLI) core options.
9
9
  class Core
10
- def self.call(options: {}, client: CLIENT) = new(options: options, client: client).call
10
+ using Refinements::Structs
11
11
 
12
- def initialize options: {}, client: CLIENT
13
- @options = options
12
+ def self.call(...) = new(...).call
13
+
14
+ def initialize configuration = Configuration::Loader.call,
15
+ client: Parser::CLIENT,
16
+ container: Container
17
+ @configuration = configuration
14
18
  @client = client
19
+ @container = container
15
20
  end
16
21
 
17
22
  def call arguments = []
18
- client.banner = "#{Identity::LABEL} - #{Identity::SUMMARY}"
23
+ client.banner = "Tocer - #{specification.summary}"
19
24
  client.separator "\nUSAGE:\n"
20
25
  collate
21
- arguments.empty? ? arguments : client.parse!(arguments)
26
+ client.parse arguments
27
+ configuration
22
28
  end
23
29
 
24
30
  private
25
31
 
26
- attr_reader :options, :client
32
+ attr_reader :configuration, :client, :container
27
33
 
28
34
  def collate = private_methods.sort.grep(/add_/).each { |method| __send__ method }
29
35
 
@@ -34,27 +40,35 @@ module Tocer
34
40
  %i[edit view],
35
41
  "Manage gem configuration: edit or view."
36
42
  ) do |action|
37
- options[:config] = action
43
+ configuration.merge! action_config: action
38
44
  end
39
45
  end
40
46
 
41
- def add_build
42
- client.on "-b", "--build [PATH]", %(Build table of contents. Default path: ".") do |value|
43
- options[:build] = value || "."
47
+ def add_insert
48
+ root_dir = configuration.root_dir
49
+
50
+ client.on(
51
+ "-i",
52
+ "--insert [PATH]",
53
+ %(Insert/update table of contents. Default: "#{root_dir}".)
54
+ ) do |path|
55
+ configuration.merge! action_insert: true, root_dir: path || root_dir
44
56
  end
45
57
  end
46
58
 
47
59
  def add_version
48
60
  client.on "-v", "--version", "Show gem version." do
49
- options[:version] = Identity::VERSION_LABEL
61
+ configuration.merge! action_version: true
50
62
  end
51
63
  end
52
64
 
53
65
  def add_help
54
66
  client.on "-h", "--help", "Show this message." do
55
- options[:help] = true
67
+ configuration.merge! action_help: true
56
68
  end
57
69
  end
70
+
71
+ def specification = container[__method__]
58
72
  end
59
73
  end
60
74
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/structs"
4
+
5
+ module Tocer
6
+ module CLI
7
+ module Parsers
8
+ # Handles parsing of Command Line Interface (CLI) flags.
9
+ class Flag
10
+ using Refinements::Structs
11
+
12
+ def self.call(...) = new(...).call
13
+
14
+ def initialize configuration = Configuration::Loader.call, client: Parser::CLIENT
15
+ @configuration = configuration
16
+ @client = client
17
+ end
18
+
19
+ def call arguments = []
20
+ client.separator "\nOPTIONS:\n"
21
+ private_methods.sort.grep(/add_/).each { |method| __send__ method }
22
+ client.parse arguments
23
+ configuration
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :configuration, :client
29
+
30
+ def add_label
31
+ client.on(
32
+ "--label [LABEL]",
33
+ %(Add label. Default: "#{configuration.label}".)
34
+ ) do |value|
35
+ configuration.merge! label: value if value
36
+ end
37
+ end
38
+
39
+ def add_include
40
+ client.on(
41
+ "--includes [a,b,c]",
42
+ Array,
43
+ %(Add include patterns. Default: #{configuration.includes}.)
44
+ ) do |items|
45
+ configuration.merge! includes: items if items
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,41 +4,42 @@ module Tocer
4
4
  module CLI
5
5
  # The main Command Line Interface (CLI) object.
6
6
  class Shell
7
- PROCESSORS = {config: Processors::Config.new, build: Processors::Build.new}.freeze
7
+ ACTIONS = {config: Actions::Config.new, insert: Actions::Insert.new}.freeze
8
8
 
9
- def initialize parser: Parsers::Assembler.new, processors: PROCESSORS
9
+ def initialize parser: Parser.new, actions: ACTIONS, container: Container
10
10
  @parser = parser
11
- @processors = processors
11
+ @actions = actions
12
+ @container = container
12
13
  end
13
14
 
14
15
  def call arguments = []
15
- parse arguments
16
-
17
- case options
18
- in config: action then process_config action
19
- in build: path then process_build path
20
- in version: then puts version
21
- else usage
22
- end
16
+ perform parser.call(arguments)
17
+ rescue OptionParser::ParseError => error
18
+ puts error.message
23
19
  end
24
20
 
25
21
  private
26
22
 
27
- attr_reader :parser, :processors
23
+ attr_reader :parser, :actions, :container
28
24
 
29
- def parse arguments = []
30
- parser.call arguments
31
- rescue StandardError => error
32
- puts error.message
25
+ def perform configuration
26
+ case configuration
27
+ in action_config: Symbol => action then process_config action
28
+ in action_insert: true then process_insert configuration
29
+ in action_version: true then logger.info { "Tocer #{specification.version}" }
30
+ else usage
31
+ end
33
32
  end
34
33
 
35
- def process_config(action) = processors.fetch(:config).call(action)
36
-
37
- def process_build(path) = processors.fetch(:build).call(path, options)
34
+ def process_config(action) = actions.fetch(:config).call(action)
38
35
 
39
- def options = parser.to_h
36
+ def process_insert(configuration) = actions.fetch(:insert).call(configuration)
40
37
 
41
38
  def usage = puts(parser.to_s)
39
+
40
+ def specification = container[__method__]
41
+
42
+ def logger = container[__method__]
42
43
  end
43
44
  end
44
45
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tocer
4
+ module Configuration
5
+ # Defines the content of the configuration for use throughout the gem.
6
+ Content = Struct.new(
7
+ :action_config,
8
+ :action_help,
9
+ :action_insert,
10
+ :action_version,
11
+ :includes,
12
+ :label,
13
+ :root_dir,
14
+ keyword_init: true
15
+ ) do
16
+ def initialize *arguments
17
+ super
18
+ freeze
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,4 @@
1
1
  :label: "## Table of Contents"
2
2
  :includes:
3
3
  - "README.md"
4
+ :root_dir: "."
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "refinements/structs"
5
+ require "runcom"
6
+ require "yaml"
7
+
8
+ module Tocer
9
+ module Configuration
10
+ # Represents the fully assembled Command Line Interface (CLI) configuration.
11
+ class Loader
12
+ using Refinements::Structs
13
+
14
+ DEFAULTS = YAML.load_file(Pathname(__dir__).join("defaults.yml")).freeze
15
+ CLIENT = Runcom::Config.new "tocer/configuration.yml", defaults: DEFAULTS
16
+
17
+ def self.call = new.call
18
+
19
+ def self.with_defaults = new(client: DEFAULTS)
20
+
21
+ def initialize content: Content.new, client: CLIENT
22
+ @content = content
23
+ @client = client
24
+ end
25
+
26
+ def call = content.merge(**client.to_h)
27
+
28
+ private
29
+
30
+ attr_reader :content, :client
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-container"
4
+ require "logger"
5
+ require "pastel"
6
+
7
+ module Tocer
8
+ # Provides a global gem container for injection into other objects.
9
+ module Container
10
+ extend Dry::Container::Mixin
11
+
12
+ SPEC_PATH = "#{__dir__}/../../tocer.gemspec".freeze
13
+
14
+ register(:configuration) { Configuration::Loader.call }
15
+ register(:specification) { Gem::Specification.load SPEC_PATH }
16
+ register(:colorizer) { Pastel.new enabled: $stdout.tty? }
17
+ register(:kernel) { Kernel }
18
+
19
+ register :log_colors do
20
+ {
21
+ "DEBUG" => self[:colorizer].white.detach,
22
+ "INFO" => self[:colorizer].green.detach,
23
+ "WARN" => self[:colorizer].yellow.detach,
24
+ "ERROR" => self[:colorizer].red.detach,
25
+ "FATAL" => self[:colorizer].white.bold.on_red.detach,
26
+ "ANY" => self[:colorizer].white.bold.detach
27
+ }
28
+ end
29
+
30
+ register :logger do
31
+ Logger.new $stdout,
32
+ level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
33
+ formatter: (
34
+ lambda do |severity, _at, _name, message|
35
+ self[:log_colors][severity].call "#{message}\n"
36
+ end
37
+ )
38
+ end
39
+ end
40
+ end
@@ -17,6 +17,8 @@ module Tocer
17
17
  @message = message
18
18
  end
19
19
 
20
+ def comments = "#{start_tag}\n#{finish_tag}\n"
21
+
20
22
  def start_index(lines) = self.class.index(lines, start_id)
21
23
 
22
24
  def start_tag = comment(start_id, message)
@@ -25,6 +27,8 @@ module Tocer
25
27
 
26
28
  def finish_tag = comment(finish_id, message)
27
29
 
30
+ def empty?(lines) = (finish_index(lines) - start_index(lines)) == 1
31
+
28
32
  def prependable?(lines) = start_index(lines).zero? && finish_index(lines).zero?
29
33
 
30
34
  private
@@ -13,7 +13,7 @@ module Tocer
13
13
 
14
14
  def self.setup = new.install
15
15
 
16
- def initialize configuration: CLI::Configuration::Loader.new.call, runner: Runner.new
16
+ def initialize configuration = Configuration::Loader.call, runner: Runner.new
17
17
  @configuration = configuration
18
18
  @runner = runner
19
19
  end
@@ -21,7 +21,7 @@ module Tocer
21
21
  def install
22
22
  desc "Add/Update Table of Contents (README)"
23
23
  task :toc, %i[label includes] do |_task, arguments|
24
- runner.call(**configuration.merge(**arguments.to_h).to_h)
24
+ runner.call configuration.merge(**arguments.to_h)
25
25
  end
26
26
  end
27
27
 
data/lib/tocer/runner.rb CHANGED
@@ -7,21 +7,20 @@ module Tocer
7
7
  class Runner
8
8
  using Refinements::Pathnames
9
9
 
10
- def initialize configuration: CLI::Configuration::Loader.call, writer: Writer.new
11
- @configuration = configuration
10
+ def initialize writer: Writer.new
12
11
  @writer = writer
13
12
  end
14
13
 
15
- def call root_dir: ".", label: configuration.label, includes: configuration.includes
16
- Pathname(root_dir).files(%({#{includes.join ","}}))
17
- .each do |path|
18
- yield path if block_given?
19
- writer.call path, label: label
20
- end
14
+ def call configuration = Configuration::Loader.call
15
+ Pathname(configuration.root_dir).files(%({#{configuration.includes.join ","}}))
16
+ .each do |path|
17
+ yield path if block_given?
18
+ writer.call path, label: configuration.label
19
+ end
21
20
  end
22
21
 
23
22
  private
24
23
 
25
- attr_reader :configuration, :writer
24
+ attr_reader :writer
26
25
  end
27
26
  end
@@ -11,8 +11,9 @@ module Tocer
11
11
  end
12
12
 
13
13
  def call markdown
14
- transformers.find { |pattern, transformer| break transformer if pattern.match? markdown }
15
- .then { |transformer| transformer.new markdown }
14
+ transformers.find do |pattern, transformer|
15
+ break transformer.new markdown if pattern.match? markdown
16
+ end
16
17
  end
17
18
 
18
19
  private
data/lib/tocer/writer.rb CHANGED
@@ -24,7 +24,7 @@ module Tocer
24
24
  @builder = builder
25
25
  end
26
26
 
27
- def call path, label: CLI::Configuration::Loader.call.label
27
+ def call path, label: Configuration::Loader.call.label
28
28
  path.rewrite do |body|
29
29
  lines = body.each_line.to_a
30
30
  builder.prependable?(lines) ? prepend(lines, label) : replace(lines, label)
@@ -41,14 +41,22 @@ module Tocer
41
41
  klass = self.class
42
42
 
43
43
  klass.add(
44
- start_index: start_index,
44
+ start_index:,
45
45
  old_lines: klass.remove(start_index, finish_index, lines),
46
- new_lines: content(lines[finish_index, lines.length], label)
46
+ new_lines: new_lines(lines, label, finish_index)
47
47
  ).join
48
48
  end
49
49
 
50
+ def new_lines lines, label, finish_index
51
+ if builder.unbuildable? lines
52
+ builder.comments
53
+ else
54
+ content lines[finish_index, lines.length], label
55
+ end
56
+ end
57
+
50
58
  def prepend(lines, label) = [content(lines, label), lines.join].compress.join("\n")
51
59
 
52
- def content(lines, label) = builder.call(lines, label: label)
60
+ def content(lines, label) = builder.call(lines, label:)
53
61
  end
54
62
  end
data/lib/tocer.rb CHANGED
@@ -1,20 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "tocer/identity"
4
- require "tocer/elements/comment_block"
5
- require "tocer/parsers/header"
6
- require "tocer/transformers/link"
7
- require "tocer/transformers/text"
8
- require "tocer/transformers/finder"
9
- require "tocer/builder"
10
- require "tocer/writer"
11
- require "tocer/runner"
12
- require "tocer/cli/configuration/content"
13
- require "tocer/cli/configuration/loader"
14
- require "tocer/cli/parsers"
15
- require "tocer/cli/parsers/build"
16
- require "tocer/cli/parsers/core"
17
- require "tocer/cli/parsers/assembler"
18
- require "tocer/cli/processors/build"
19
- require "tocer/cli/processors/config"
20
- require "tocer/cli/shell"
3
+ require "zeitwerk"
4
+
5
+ Zeitwerk::Loader.for_gem.then do |loader|
6
+ loader.inflector.inflect "cli" => "CLI"
7
+ loader.ignore "#{__dir__}/tocer/rake"
8
+ loader.setup
9
+ end
10
+
11
+ # Main namespace.
12
+ module Tocer
13
+ end
data/tocer.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "tocer"
5
+ spec.version = "13.1.0"
6
+ spec.platform = Gem::Platform::RUBY
7
+ spec.authors = ["Brooke Kuhlmann"]
8
+ spec.email = ["brooke@alchemists.io"]
9
+ spec.homepage = "https://www.alchemists.io/projects/tocer"
10
+ spec.summary = "A command line interface for generating table of contents for Markdown files."
11
+ spec.license = "Hippocratic-3.0"
12
+
13
+ spec.metadata = {
14
+ "bug_tracker_uri" => "https://github.com/bkuhlmann/tocer/issues",
15
+ "changelog_uri" => "https://www.alchemists.io/projects/tocer/versions",
16
+ "documentation_uri" => "https://www.alchemists.io/projects/tocer",
17
+ "label" => "Tocer",
18
+ "rubygems_mfa_required" => "true",
19
+ "source_code_uri" => "https://github.com/bkuhlmann/tocer"
20
+ }
21
+
22
+ spec.signing_key = Gem.default_key_path
23
+ spec.cert_chain = [Gem.default_cert_path]
24
+
25
+ spec.required_ruby_version = "~> 3.1"
26
+ spec.add_dependency "dry-container", "~> 0.9"
27
+ spec.add_dependency "pastel", "~> 0.8"
28
+ spec.add_dependency "refinements", "~> 9.1"
29
+ spec.add_dependency "runcom", "~> 8.0"
30
+ spec.add_dependency "zeitwerk", "~> 2.5"
31
+
32
+ spec.bindir = "exe"
33
+ spec.executables << "tocer"
34
+ spec.extra_rdoc_files = Dir["README*", "LICENSE*"]
35
+ spec.files = Dir["*.gemspec", "lib/**/*"]
36
+ spec.require_paths = ["lib"]
37
+ end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tocer
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.2.0
4
+ version: 13.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -28,36 +28,78 @@ cert_chain:
28
28
  lkHilIrX69jq8wMPpBhlaw2mRmeSL50Wv5u6xVBvOHhXFSP1crXM95vfLhLyRYod
29
29
  W2A=
30
30
  -----END CERTIFICATE-----
31
- date: 2021-11-20 00:00:00.000000000 Z
31
+ date: 2022-01-23 00:00:00.000000000 Z
32
32
  dependencies:
33
+ - !ruby/object:Gem::Dependency
34
+ name: dry-container
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.9'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.9'
47
+ - !ruby/object:Gem::Dependency
48
+ name: pastel
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.8'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.8'
33
61
  - !ruby/object:Gem::Dependency
34
62
  name: refinements
35
63
  requirement: !ruby/object:Gem::Requirement
36
64
  requirements:
37
65
  - - "~>"
38
66
  - !ruby/object:Gem::Version
39
- version: '8.5'
67
+ version: '9.1'
40
68
  type: :runtime
41
69
  prerelease: false
42
70
  version_requirements: !ruby/object:Gem::Requirement
43
71
  requirements:
44
72
  - - "~>"
45
73
  - !ruby/object:Gem::Version
46
- version: '8.5'
74
+ version: '9.1'
47
75
  - !ruby/object:Gem::Dependency
48
76
  name: runcom
49
77
  requirement: !ruby/object:Gem::Requirement
50
78
  requirements:
51
79
  - - "~>"
52
80
  - !ruby/object:Gem::Version
53
- version: '7.0'
81
+ version: '8.0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '8.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: zeitwerk
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.5'
54
96
  type: :runtime
55
97
  prerelease: false
56
98
  version_requirements: !ruby/object:Gem::Requirement
57
99
  requirements:
58
100
  - - "~>"
59
101
  - !ruby/object:Gem::Version
60
- version: '7.0'
102
+ version: '2.5'
61
103
  description:
62
104
  email:
63
105
  - brooke@alchemists.io
@@ -73,18 +115,17 @@ files:
73
115
  - exe/tocer
74
116
  - lib/tocer.rb
75
117
  - lib/tocer/builder.rb
76
- - lib/tocer/cli/configuration/content.rb
77
- - lib/tocer/cli/configuration/defaults.yml
78
- - lib/tocer/cli/configuration/loader.rb
79
- - lib/tocer/cli/parsers.rb
80
- - lib/tocer/cli/parsers/assembler.rb
81
- - lib/tocer/cli/parsers/build.rb
118
+ - lib/tocer/cli/actions/config.rb
119
+ - lib/tocer/cli/actions/insert.rb
120
+ - lib/tocer/cli/parser.rb
82
121
  - lib/tocer/cli/parsers/core.rb
83
- - lib/tocer/cli/processors/build.rb
84
- - lib/tocer/cli/processors/config.rb
122
+ - lib/tocer/cli/parsers/flag.rb
85
123
  - lib/tocer/cli/shell.rb
124
+ - lib/tocer/configuration/content.rb
125
+ - lib/tocer/configuration/defaults.yml
126
+ - lib/tocer/configuration/loader.rb
127
+ - lib/tocer/container.rb
86
128
  - lib/tocer/elements/comment_block.rb
87
- - lib/tocer/identity.rb
88
129
  - lib/tocer/parsers/header.rb
89
130
  - lib/tocer/rake/setup.rb
90
131
  - lib/tocer/rake/tasks.rb
@@ -93,13 +134,15 @@ files:
93
134
  - lib/tocer/transformers/link.rb
94
135
  - lib/tocer/transformers/text.rb
95
136
  - lib/tocer/writer.rb
137
+ - tocer.gemspec
96
138
  homepage: https://www.alchemists.io/projects/tocer
97
139
  licenses:
98
- - Apache-2.0
140
+ - Hippocratic-3.0
99
141
  metadata:
100
142
  bug_tracker_uri: https://github.com/bkuhlmann/tocer/issues
101
- changelog_uri: https://www.alchemists.io/projects/tocer/changes.html
143
+ changelog_uri: https://www.alchemists.io/projects/tocer/versions
102
144
  documentation_uri: https://www.alchemists.io/projects/tocer
145
+ label: Tocer
103
146
  rubygems_mfa_required: 'true'
104
147
  source_code_uri: https://github.com/bkuhlmann/tocer
105
148
  post_install_message:
@@ -110,14 +153,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
153
  requirements:
111
154
  - - "~>"
112
155
  - !ruby/object:Gem::Version
113
- version: '3.0'
156
+ version: '3.1'
114
157
  required_rubygems_version: !ruby/object:Gem::Requirement
115
158
  requirements:
116
159
  - - ">="
117
160
  - !ruby/object:Gem::Version
118
161
  version: '0'
119
162
  requirements: []
120
- rubygems_version: 3.2.31
163
+ rubygems_version: 3.3.5
121
164
  signing_key:
122
165
  specification_version: 4
123
166
  summary: A command line interface for generating table of contents for Markdown files.