tocer 12.2.0 → 13.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.