mnogootex 0.2.1 → 1.0.0

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