mnogootex 0.2.1 → 1.0.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,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+
5
+ module Mnogootex
6
+ module Job
7
+ class Logger < Thread
8
+ def initialize(spinner:, processor:, runners:, porters:)
9
+ super do
10
+ while runners.any?(&:alive?)
11
+ self.class.print_status(runners: runners, spinner: spinner)
12
+ sleep 0.02 # 50 fps
13
+ end
14
+ self.class.print_status(runners: runners, spinner: spinner)
15
+ puts
16
+ self.class.print_outcome(runners: runners, porters: porters, processor: processor)
17
+ end
18
+ end
19
+
20
+ class << self
21
+ def print_status(runners:, spinner:)
22
+ spinners_frames = []
23
+ runners.each do |runner|
24
+ spinner_frame = spinner[runner.count_lines % spinner.size]
25
+ spinners_frames << colour_by_state(spinner_frame, runner)
26
+ end
27
+ print "Runners: #{spinners_frames.join}\r"
28
+ end
29
+
30
+ def print_outcome(runners:, porters:, processor:)
31
+ puts 'Outcome:'
32
+ porters.zip(runners).each do |porter, runner|
33
+ outcome_icon = runner.successful? ? '✔'.green : '✘'.red
34
+ puts " #{outcome_icon} #{porter.hid}"
35
+ puts processor.call(runner.log_lines) unless runner.successful?
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def colour_by_state(string, runner)
42
+ if runner.alive?
43
+ string.yellow
44
+ elsif runner.successful?
45
+ string.green
46
+ else
47
+ string.red
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'tmpdir'
5
+
6
+ require 'mnogootex/utils'
7
+
8
+ module Mnogootex
9
+ module Job
10
+ class Porter
11
+ attr_reader :hid
12
+
13
+ def initialize(hid:, source_path:)
14
+ @source_path = Pathname.new(source_path).realpath
15
+ @hid = hid
16
+ end
17
+
18
+ def target_dir
19
+ @target_dir ||= Pathname.new(Dir.tmpdir).join('mnogootex', source_id, hid)
20
+ end
21
+
22
+ def target_path
23
+ @target_path ||= target_dir.join(@source_path.basename)
24
+ end
25
+
26
+ def clobber
27
+ target_dir.rmtree if target_dir.directory?
28
+ end
29
+
30
+ def provide
31
+ target_dir.mkpath
32
+ # NOTE: can't use Pathname.join here since it elides the dot:
33
+ FileUtils.cp_r File.join(@source_path.dirname, '.'), target_dir
34
+ target_dir.join('.mnogootex.yml').tap { |p| p.delete if p.file? }
35
+ target_dir.join('.mnogootex.src').make_symlink(@source_path)
36
+ end
37
+
38
+ private
39
+
40
+ def source_id
41
+ @source_id ||= Utils.short_md5(@source_path.to_s)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
5
+ module Mnogootex
6
+ module Job
7
+ class Runner
8
+ attr_reader :hid, :log_lines
9
+
10
+ def initialize(cl:, chdir:)
11
+ @log_lines = []
12
+ _, @stream, @thread = Open3.popen2e(*cl, chdir: chdir)
13
+ @poller = start_poller
14
+ end
15
+
16
+ def alive?
17
+ @poller.alive?
18
+ end
19
+
20
+ def successful?
21
+ @poller.value.exitstatus.zero?
22
+ end
23
+
24
+ def count_lines
25
+ return log_lines.size unless alive?
26
+ @ticks = [@ticks || -1, log_lines.size - 1].min + 1
27
+ end
28
+
29
+ private
30
+
31
+ def start_poller
32
+ Thread.new do
33
+ until (line = @stream.gets).nil?
34
+ log_lines << line
35
+ end
36
+ # NOTE: waits on @thread and returns its value
37
+ @thread.value
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+
5
+ require 'mnogootex/log'
6
+ require 'mnogootex/log/processor'
7
+ require 'mnogootex/job/porter'
8
+ require 'mnogootex/job/runner'
9
+ require 'mnogootex/job/logger'
10
+
11
+ module Mnogootex
12
+ module Job
13
+ class Warden
14
+ def initialize(source:, configuration:)
15
+ @source = source
16
+ @configuration = configuration
17
+
18
+ @processor = nil
19
+ @porters = []
20
+ @runners = []
21
+ @logger = nil
22
+ end
23
+
24
+ def start
25
+ init_processor
26
+ init_porters
27
+ exec_porters
28
+ init_and_exec_runners
29
+ init_and_exec_logger
30
+ @logger.join
31
+ end
32
+
33
+ private
34
+
35
+ def init_porters
36
+ @configuration['jobs'].each do |cls|
37
+ @porters << Mnogootex::Job::Porter.new(
38
+ hid: cls,
39
+ source_path: @source
40
+ )
41
+ end
42
+ end
43
+
44
+ def exec_porters
45
+ @porters.each do |porter|
46
+ porter.clobber
47
+ porter.provide
48
+ transformer(porter.hid, porter.target_path)
49
+ end
50
+ end
51
+
52
+ def init_and_exec_runners
53
+ @runners = @porters.map do |porter|
54
+ Mnogootex::Job::Runner.new(
55
+ cl: commandline(porter.target_path),
56
+ chdir: porter.target_dir
57
+ )
58
+ end
59
+ end
60
+
61
+ def init_processor
62
+ @processor = Log::Processor.new(
63
+ matchers: Mnogootex::Log::DEFAULT_MATCHERS,
64
+ levels: Mnogootex::Log::DEFAULT_LEVELS,
65
+ min_level: :info,
66
+ colorize: true,
67
+ indent_width: 4
68
+ )
69
+ end
70
+
71
+ def init_and_exec_logger
72
+ @logger = Mnogootex::Job::Logger.new(
73
+ spinner: @configuration['spinner'],
74
+ processor: @processor.method(:run),
75
+ runners: @runners,
76
+ porters: @porters
77
+ )
78
+ end
79
+
80
+ # TODO: generalize, integrate with Runner
81
+ def commandline(target_pathname)
82
+ [
83
+ *@configuration['commandline'],
84
+ target_pathname.basename.to_s
85
+ ]
86
+ end
87
+
88
+ # TODO: generalize, integrate with Porter
89
+ def transformer(new_class_name, target_pathname)
90
+ old_code = target_pathname.read
91
+ new_code = old_code.sub(
92
+ /\\documentclass(\[.*?\])?{.*?}/,
93
+ "\\documentclass{#{new_class_name}}"
94
+ )
95
+ target_pathname.write(new_code)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnogootex
4
+ module Log
5
+ # This data structure represents a log level usually referred to
6
+ # by its {name}. It has a numeric {priority} and a {color} used
7
+ # for rendering.
8
+ #
9
+ # @!attribute priority
10
+ # @return [Numeric] the numeric priority of the log level
11
+ # @!attribute name
12
+ # @return [Symbol] the human readable name of the log level
13
+ # @!attribute color
14
+ # @return [Symbol] the color visually representing the {priority}
15
+ Level = Struct.new(:priority, :name, :color)
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ - !ruby/struct:Mnogootex::Log::Level
2
+ priority: -.inf
3
+ name: !ruby/symbol all
4
+ # NOTE: merely symbolic, not to be used in matchers
5
+
6
+ - !ruby/struct:Mnogootex::Log::Level
7
+ priority: 0
8
+ name: !ruby/symbol trace
9
+ color: !ruby/symbol white
10
+
11
+ - !ruby/struct:Mnogootex::Log::Level
12
+ priority: 1
13
+ name: !ruby/symbol info
14
+ color: !ruby/symbol light_white
15
+
16
+ - !ruby/struct:Mnogootex::Log::Level
17
+ priority: 2
18
+ name: !ruby/symbol warning
19
+ color: !ruby/symbol light_yellow
20
+
21
+ - !ruby/struct:Mnogootex::Log::Level
22
+ priority: 3
23
+ name: !ruby/symbol error
24
+ color: !ruby/symbol light_red
25
+
26
+ - !ruby/struct:Mnogootex::Log::Level
27
+ priority: .inf
28
+ name: !ruby/symbol off
29
+ # NOTE: merely symbolic, not to be used in matchers
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnogootex
4
+ module Log
5
+ # This data structure represents a log line.
6
+ # It can have a log {level} along with its {text}.
7
+ #
8
+ # @!attribute text
9
+ # @return [String] the contents of the line
10
+ # @!attribute level
11
+ # @return [Symbol] the associated log level
12
+ Line = Struct.new(:text, :level)
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mnogootex
4
+ module Log
5
+ # This data structure represents a typology of log line chunks
6
+ # belonging to a given log {level}.
7
+ # They start with a line matching {regexp} and have a fixed {length}.
8
+ #
9
+ # @!attribute regexp
10
+ # @return [Regexp] the regexp to match the first line
11
+ # @!attribute level
12
+ # @return [Symbol] the associated log level
13
+ # @!attribute length
14
+ # @return [Integer] the number of matched lines
15
+ Matcher = Struct.new(:regexp, :level, :length)
16
+ end
17
+ end
@@ -0,0 +1,205 @@
1
+ - !ruby/struct:Mnogootex::Log::Matcher
2
+ regexp: !ruby/regexp '/LaTeX Warning: You have requested package/'
3
+ level: !ruby/symbol trace
4
+ length: 1
5
+
6
+ - !ruby/struct:Mnogootex::Log::Matcher
7
+ regexp: !ruby/regexp '/LaTeX Font Warning: Some font shapes/'
8
+ level: !ruby/symbol trace
9
+ length: 1
10
+
11
+ - !ruby/struct:Mnogootex::Log::Matcher
12
+ regexp: !ruby/regexp '/LaTeX Font Warning: Size substitutions/'
13
+ level: !ruby/symbol trace
14
+ length: 1
15
+
16
+ - !ruby/struct:Mnogootex::Log::Matcher
17
+ regexp: !ruby/regexp '/Package caption Warning: Unsupported document class/'
18
+ level: !ruby/symbol trace
19
+ length: 1
20
+
21
+ - !ruby/struct:Mnogootex::Log::Matcher
22
+ regexp: !ruby/regexp '/Package fixltx2e Warning: fixltx2e is not required/'
23
+ level: !ruby/symbol trace
24
+ length: 1
25
+
26
+ - !ruby/struct:Mnogootex::Log::Matcher
27
+ regexp: !ruby/regexp '/Package frenchb?\.ldf Warning: (Figures|The definition)/'
28
+ level: !ruby/symbol trace
29
+ length: 1
30
+
31
+ - !ruby/struct:Mnogootex::Log::Matcher
32
+ regexp: !ruby/regexp '/\*\*\* Reloading Xunicode for encoding/' # spurious ***
33
+ level: !ruby/symbol trace
34
+ length: 1
35
+
36
+ - !ruby/struct:Mnogootex::Log::Matcher
37
+ regexp: !ruby/regexp '/This is `?(epsf\.tex|.*\.sty|TAP)/' # so what
38
+ level: !ruby/symbol trace
39
+ length: 1
40
+
41
+ - !ruby/struct:Mnogootex::Log::Matcher
42
+ regexp: !ruby/regexp '/pdfTeX warning:.*inclusion: fou/'
43
+ level: !ruby/symbol trace
44
+ length: 1
45
+ sample: |
46
+ pdfTeX warning: pdflatex.exe (file ./fig.pdf): PDF inclusion: found PDF version <1.6>, but at most version <1.5> allowed
47
+
48
+ - !ruby/struct:Mnogootex::Log::Matcher
49
+ regexp: !ruby/regexp '/pdfTeX warning:.*inclusion: mul/'
50
+ level: !ruby/symbol trace
51
+ length: 1
52
+ sample: |
53
+ pdfTeX warning: pdflatex (file ./doc.pdf): PDF inclusion: multiple pdfs with page group included in a single page
54
+
55
+ - !ruby/struct:Mnogootex::Log::Matcher
56
+ regexp: !ruby/regexp '/libpng warning: iCCP: Not recognizing/'
57
+ level: !ruby/symbol trace
58
+ length: 1
59
+
60
+ - !ruby/struct:Mnogootex::Log::Matcher
61
+ regexp: !ruby/regexp '/! $/'
62
+ level: !ruby/symbol trace
63
+ length: 1
64
+
65
+ - !ruby/struct:Mnogootex::Log::Matcher
66
+ regexp: !ruby/regexp '/This is/'
67
+ level: !ruby/symbol info
68
+ length: 1
69
+
70
+ - !ruby/struct:Mnogootex::Log::Matcher
71
+ regexp: !ruby/regexp '/Output written/'
72
+ level: !ruby/symbol info
73
+ length: 1
74
+
75
+ - !ruby/struct:Mnogootex::Log::Matcher
76
+ regexp: !ruby/regexp '/No pages of output/'
77
+ level: !ruby/symbol info
78
+ length: 1
79
+
80
+ # TODO: better classification below this point
81
+
82
+ - !ruby/struct:Mnogootex::Log::Matcher
83
+ regexp: !ruby/regexp '/\(.*end occurred inside a group/'
84
+ level: !ruby/symbol warning
85
+ length: 1
86
+
87
+ - !ruby/struct:Mnogootex::Log::Matcher
88
+ regexp: !ruby/regexp '/\\endL.*problem/' # XeTeX?
89
+ level: !ruby/symbol warning
90
+ length: 1
91
+
92
+ - !ruby/struct:Mnogootex::Log::Matcher
93
+ regexp: !ruby/regexp '/\*\*\*\s/' # *** from some packages or subprograms
94
+ level: !ruby/symbol warning
95
+ length: 1
96
+
97
+ - !ruby/struct:Mnogootex::Log::Matcher
98
+ regexp: !ruby/regexp '/all text was ignored after line/'
99
+ level: !ruby/symbol warning
100
+ length: 1
101
+
102
+ - !ruby/struct:Mnogootex::Log::Matcher
103
+ regexp: !ruby/regexp '/.*for symbol.*on input line/'
104
+ level: !ruby/symbol warning
105
+ length: 1
106
+
107
+ - !ruby/struct:Mnogootex::Log::Matcher
108
+ regexp: !ruby/regexp '/^.*?:[0-9]+:/' # usual file:lineno: form
109
+ level: !ruby/symbol warning
110
+ length: 1
111
+
112
+ - !ruby/struct:Mnogootex::Log::Matcher
113
+ regexp: !ruby/regexp '/l\.[0-9]+/' # line number marking
114
+ level: !ruby/symbol warning
115
+ length: 1
116
+
117
+ - !ruby/struct:Mnogootex::Log::Matcher
118
+ regexp: !ruby/regexp '/(LaTeX|Package|Class).*Warning/'
119
+ level: !ruby/symbol warning
120
+ length: 1
121
+
122
+ - !ruby/struct:Mnogootex::Log::Matcher
123
+ regexp: !ruby/regexp '/(Und|Ov)erfull/'
124
+ level: !ruby/symbol warning
125
+ length: 1
126
+
127
+ - !ruby/struct:Mnogootex::Log::Matcher
128
+ regexp: !ruby/regexp '/.*Citation.*undefined/'
129
+ level: !ruby/symbol warning
130
+ length: 1
131
+
132
+ - !ruby/struct:Mnogootex::Log::Matcher
133
+ regexp: !ruby/regexp '/Missing character:/' # good to show (need \tracinglostchars=1)
134
+ level: !ruby/symbol warning
135
+ length: 1
136
+
137
+ - !ruby/struct:Mnogootex::Log::Matcher
138
+ regexp: !ruby/regexp '/.*Fatal error/'
139
+ level: !ruby/symbol error
140
+ length: 1
141
+
142
+ - !ruby/struct:Mnogootex::Log::Matcher
143
+ regexp: !ruby/regexp '/.* Error/' # as in \Url Error ->...
144
+ level: !ruby/symbol error
145
+ length: 1
146
+
147
+ - !ruby/struct:Mnogootex::Log::Matcher
148
+ regexp: !ruby/regexp '/(LaTeX|Package|Class).*Error/'
149
+ level: !ruby/symbol error
150
+ length: 1
151
+
152
+ - !ruby/struct:Mnogootex::Log::Matcher
153
+ regexp: !ruby/regexp '/^> [^<]/' # from \show..., but not "> <img.whatever"
154
+ level: !ruby/symbol error
155
+ length: 2
156
+
157
+ - !ruby/struct:Mnogootex::Log::Matcher
158
+ regexp: !ruby/regexp '/^.*pdfTeX warning/' # pdftex complaints often cross lines
159
+ level: !ruby/symbol error
160
+ length: 2
161
+
162
+ - !ruby/struct:Mnogootex::Log::Matcher
163
+ regexp: !ruby/regexp '/^LaTeX Font Warning: Font shape/'
164
+ level: !ruby/symbol error
165
+ length: 2
166
+
167
+ - !ruby/struct:Mnogootex::Log::Matcher
168
+ regexp: !ruby/regexp '/^Package hyperref Warning: Token not allowed/'
169
+ level: !ruby/symbol error
170
+ length: 2
171
+
172
+ - !ruby/struct:Mnogootex::Log::Matcher
173
+ regexp: !ruby/regexp '/^removed on input line/' # hyperref
174
+ level: !ruby/symbol error
175
+ length: 2
176
+
177
+ - !ruby/struct:Mnogootex::Log::Matcher
178
+ regexp: !ruby/regexp '/^Runaway argument/'
179
+ level: !ruby/symbol error
180
+ length: 2
181
+
182
+ - !ruby/struct:Mnogootex::Log::Matcher
183
+ regexp: !ruby/regexp "/^! Undefined control sequence./"
184
+ level: !ruby/symbol error
185
+ length: 3
186
+
187
+ - !ruby/struct:Mnogootex::Log::Matcher
188
+ regexp: !ruby/regexp "/^! Too many }'s./"
189
+ level: !ruby/symbol error
190
+ length: 3
191
+ sample: |
192
+ ! Too many }'s.
193
+ l.7 ...d foo bar baz qux zod foo bar baz qux zod }
194
+ foo
195
+
196
+ - !ruby/struct:Mnogootex::Log::Matcher
197
+ regexp: !ruby/regexp '/^!/' # usual ! form
198
+ level: !ruby/symbol error
199
+ length: 2
200
+
201
+ - !ruby/struct:Mnogootex::Log::Matcher
202
+ # NOTE: do not remove, this is a catch-all filter to mark untagged lines as :trace
203
+ regexp: !ruby/regexp '/.*/'
204
+ level: !ruby/symbol trace
205
+ length: 1
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mnogootex/log'
4
+ require 'mnogootex/log/line'
5
+
6
+ require 'colorize'
7
+
8
+ module Mnogootex
9
+ module Log
10
+ # This class exposes methods to
11
+ # {Processor.strings_to_lines! convert} strings into {Line}s that can be
12
+ # {Processor.tag_lines! tagged},
13
+ # {Processor.filter_lines! filtered},
14
+ # {Processor.colorize_lines! colored} (using {Level}s and {Matcher}s to define how)
15
+ # and finally
16
+ # {Processor.render_lines! rendered} into printable content.
17
+ #
18
+ # It can also be {Processor.initialize instantiated} with a specific configuration
19
+ # to {#run} the whole process repeatably on multiple inputs.
20
+ class Processor
21
+ # Converts strings into {Line}s.
22
+ #
23
+ # @param strings [Array<String>]
24
+ # @return [Array<Line>]
25
+ def self.strings_to_lines!(strings)
26
+ strings.map! do |line|
27
+ Line.new line.chomp
28
+ end
29
+ end
30
+
31
+ # Updates {Line#level}s of the given {Line}s using the {Matcher}s.
32
+ #
33
+ # @param lines [Array<Line>]
34
+ # @param matchers [Array<Matcher>]
35
+ # @return [Array<Line>]
36
+ def self.tag_lines!(lines, matchers:)
37
+ tail_length, matcher = 0 # , nil
38
+ lines.each do |line|
39
+ if tail_length.zero?
40
+ matcher = matchers.detect { |m| m.regexp === line.text }
41
+ tail_length = matcher&.length&.-(1) || 0
42
+ else # still on the tail of the previous match
43
+ tail_length -= 1
44
+ end
45
+ line.level = matcher&.level
46
+ end
47
+ end
48
+
49
+ # Discards {Line}s having {Line.level}s with {Level#priority}
50
+ # lower than the minimum, according the {Level}s hash.
51
+ #
52
+ # @param lines [Array<Line>]
53
+ # @param levels [Hash<Symbol, Level>]
54
+ # @param min_level [Symbol]
55
+ # @return [Array<Line>]
56
+ def self.filter_lines!(lines, levels:, min_level:)
57
+ lines.select! do |line|
58
+ levels.fetch(line.level).priority >= levels.fetch(min_level).priority
59
+ end
60
+ end
61
+
62
+ # Applies {Level#color}s to the {Line}s, according the {Level}s hash.
63
+ #
64
+ # @param lines [Array<Line>]
65
+ # @param levels [Array<Level>]
66
+ # @return [Array<Line>]
67
+ def self.colorize_lines!(lines, levels:)
68
+ lines.each do |line|
69
+ line.text = line.text.colorize(levels.fetch(line.level).color)
70
+ end
71
+ end
72
+
73
+ # Renders {Line}s to space-indented strings terminated by a newline.
74
+ #
75
+ # @param lines [Array<Line>]
76
+ # @param indent_width [Fixnum]
77
+ # @return [Array<String>]
78
+ def self.render_lines!(lines, indent_width:)
79
+ lines.map! { |line| "#{' ' * indent_width}#{line.text}\n" }
80
+ end
81
+
82
+ # @param matchers [Array<Matcher>]
83
+ # @param levels [Array<Level>]
84
+ # @param indent_width [Fixnum]
85
+ # @param min_level [Symbol]
86
+ def initialize(matchers:, levels:, min_level:, colorize:, indent_width:)
87
+ @matchers = matchers
88
+ @levels = levels
89
+ @min_level = min_level
90
+ @colorize = colorize
91
+ @indent_width = indent_width
92
+ end
93
+
94
+ # Runs the {Processor Processor} on the given strings to
95
+ # {Processor.strings_to_lines! convert},
96
+ # {Processor.tag_lines! tag},
97
+ # {Processor.filter_lines! filter},
98
+ # {Processor.colorize_lines! color} and
99
+ # {Processor.render_lines! render} them
100
+ # using its {Processor.initialize initialization} parameters.
101
+ #
102
+ # @param lines [Array<String>]
103
+ # @return [Array<String>]
104
+ def run(lines)
105
+ @lines = lines.dup
106
+ Processor.strings_to_lines! @lines
107
+ Processor.tag_lines! @lines, matchers: @matchers
108
+ Processor.filter_lines! @lines, levels: @levels, min_level: @min_level
109
+ Processor.colorize_lines! @lines, levels: @levels if @colorize
110
+ Processor.render_lines! @lines, indent_width: @indent_width
111
+ @lines
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mnogootex/log/level'
4
+ require 'mnogootex/log/matcher'
5
+
6
+ require 'pathname'
7
+ require 'yaml'
8
+
9
+ module Mnogootex
10
+ # {Log} implements means to reduce log floods into filtered, color coded and human friendly summaries.
11
+ #
12
+ # * {Line}s are log lines.
13
+ # * {Level}s define log levels, their priority and color coding.
14
+ # * {Matcher}s define patterns to determine the level of log lines.
15
+ # * {Processor}s implement all transformations.
16
+ #
17
+ module Log
18
+ DEFAULT_LEVELS_PATH = Pathname.new(__dir__).join('log', 'levels.yml')
19
+ DEFAULT_MATCHERS_PATH = Pathname.new(__dir__).join('log', 'matchers.yml')
20
+ DEFAULT_LEVELS = YAML.load_file(DEFAULT_LEVELS_PATH).map { |l| [l.name, l] }.to_h.freeze
21
+ DEFAULT_MATCHERS = YAML.load_file(DEFAULT_MATCHERS_PATH).freeze
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # Usage: source this in your bash profile as
2
+ # [ -s "$(mnogootex mnogoo)" ] && . "$(mnogootex mnogoo)"
3
+
4
+ mnogoo () {
5
+ if [ "$1" = cd ]; then
6
+ MN_PATH="$(IS_MNOGOO=true mnogootex dir "${@:2}")" || return
7
+ cd "$MN_PATH" || exit
8
+ elif [ "$1" = open ]; then
9
+ MN_PATH="$(IS_MNOGOO=true mnogootex pdf "${@:2}")" || return
10
+ if command -v open >/dev/null 2>&1; then
11
+ printf '%s\n' "$MN_PATH" | while read -r line; do open "$line"; done
12
+ elif command -v xdg-open >/dev/null 2>&1; then
13
+ printf '%s\n' "$MN_PATH" | while read -r line; do xdg-open "$line"; done
14
+ else
15
+ echo "No known file opener (open, xdg-open) found on your system."
16
+ echo "Please do chime in with suggestions: <paolo.brasolin@gmail.com>"
17
+ fi
18
+ else
19
+ IS_MNOGOO=true mnogootex "$@"
20
+ fi
21
+ }