mnogootex 0.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.github/actions/setup-ruby/action.yml +34 -0
  3. data/.github/workflows/main.yml +44 -0
  4. data/.gitignore +3 -0
  5. data/.rspec +0 -2
  6. data/.rubocop.yml +15 -0
  7. data/CHANGELOG.md +55 -0
  8. data/CODE_OF_CONDUCT.md +1 -1
  9. data/Gemfile +4 -3
  10. data/Guardfile +56 -0
  11. data/README.md +260 -20
  12. data/Rakefile +25 -4
  13. data/demo/.mnogootexrc +4 -0
  14. data/demo/demo.asciicast +114 -0
  15. data/demo/demo.gif +0 -0
  16. data/demo/main.tex +5 -0
  17. data/exe/mnogootex +2 -92
  18. data/lib/mnogootex/cfg.rb +72 -0
  19. data/lib/mnogootex/cli.rb +63 -0
  20. data/lib/mnogootex/job/logger.rb +53 -0
  21. data/lib/mnogootex/job/porter.rb +63 -0
  22. data/lib/mnogootex/job/runner.rb +60 -0
  23. data/lib/mnogootex/job/warden.rb +104 -0
  24. data/lib/mnogootex/log/level.rb +17 -0
  25. data/lib/mnogootex/log/levels.yml +29 -0
  26. data/lib/mnogootex/log/line.rb +14 -0
  27. data/lib/mnogootex/log/matcher.rb +17 -0
  28. data/lib/mnogootex/log/matchers.yml +205 -0
  29. data/lib/mnogootex/log/processor.rb +115 -0
  30. data/lib/mnogootex/log.rb +23 -0
  31. data/lib/mnogootex/utils.rb +27 -0
  32. data/lib/mnogootex/version.rb +3 -1
  33. data/lib/mnogootex.rb +4 -4
  34. data/mnogootex.gemspec +43 -18
  35. data/spec/mnogootex/cfg_spec.rb +54 -0
  36. data/spec/mnogootex/job/porter_spec.rb +140 -0
  37. data/spec/mnogootex/job/runner_spec.rb +74 -0
  38. data/spec/mnogootex/log/processor_spec.rb +203 -0
  39. data/spec/mnogootex/utils_spec.rb +52 -0
  40. data/spec/spec_helper.rb +124 -0
  41. metadata +150 -29
  42. data/.gitmodules +0 -3
  43. data/.travis.yml +0 -5
  44. data/bin/console +0 -14
  45. data/bin/setup +0 -8
  46. data/lib/mnogootex/configuration.rb +0 -46
  47. data/lib/mnogootex/job.rb +0 -75
@@ -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) # rubocop:disable Lint/StructNewOverride
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| line.text =~ m.regexp }
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,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Mnogootex
6
+ module Utils
7
+ def self.short_md5(input)
8
+ [Digest::MD5.digest(input)]. # get 16 bytes of MD5
9
+ pack('m0'). # pack them into 22+2 base64 bytes (w/o trailing newline)
10
+ tr('+/', '-_'). # make then url/path-safe
11
+ chomp('==') # drop last 2 padding bytes
12
+ end
13
+
14
+ def self.humanize_bytes(size)
15
+ %w[b Kb Mb Gb Tb Pb Eb Zb Yb].reduce(size) do |magnitude, unit|
16
+ break "#{magnitude}#{unit}" if magnitude < 1024
17
+
18
+ magnitude / 1024
19
+ end
20
+ end
21
+
22
+ def self.dir_size(mask)
23
+ Dir.glob(Pathname.new(mask).join('**', '*')).
24
+ map! { |f| Pathname.new(f).size }.inject(:+) || 0
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mnogootex
2
- VERSION = "0.2.1"
4
+ VERSION = '2.0.0'
3
5
  end
data/lib/mnogootex.rb CHANGED
@@ -1,7 +1,7 @@
1
- require "mnogootex/version"
2
- require "mnogootex/configuration"
3
- require "mnogootex/job"
1
+ # frozen_string_literal: true
2
+
3
+ require 'mnogootex/version'
4
+ require 'mnogootex/cli'
4
5
 
5
6
  module Mnogootex
6
- # Your code goes here...
7
7
  end
data/mnogootex.gemspec CHANGED
@@ -1,30 +1,55 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "mnogootex/version"
5
+ require 'mnogootex/version'
6
6
 
7
- Gem::Specification.new do |spec|
8
- spec.name = "mnogootex"
7
+ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
8
+ spec.name = 'mnogootex'
9
9
  spec.version = Mnogootex::VERSION
10
- spec.authors = ["Paolo Brasolin"]
11
- spec.email = ["paolo.brasolin@gmail.com"]
10
+ spec.authors = ['Paolo Brasolin']
11
+ spec.email = ['paolo.brasolin@gmail.com']
12
12
 
13
- spec.summary = %q{Mnogootex (многоꙮтех) is a device to handle the compilation of TeX sources with different preambles at one time.}
14
- spec.description = %q{Mnogootex (многоꙮтех) is a device to handle the compilation of TeX sources with different preambles at one time. This avoids wasting time when you have to submit a paper to journals using outdated or crummy document classes.}
15
- spec.homepage = "https://github.com/tetrapharmakon/mnogootex"
16
- spec.license = "MIT"
13
+ spec.summary = <<~SUMMARY.tr("\n", ' ').squeeze(' ').strip
14
+ Многоꙮтех (mnogootex) is a utility that parallelizes compilation
15
+ of a LaTeX document using different classes and offers a
16
+ meaningfully filtered output.
17
+ SUMMARY
18
+
19
+ spec.description = <<~DESCRIPTION.tr("\n", ' ').squeeze(' ').strip
20
+ Многоꙮтех (mnogootex) is a utility that parallelizes compilation
21
+ of a LaTeX document using different classes and offers a
22
+ meaningfully filtered output.
23
+ The motivating use case is maintaining a single preamble while
24
+ submitting a paper to many journals using their outdated or crummy
25
+ document classes.
26
+ DESCRIPTION
27
+
28
+ spec.homepage = 'https://github.com/tetrapharmakon/mnogootex'
29
+ spec.license = 'MIT'
17
30
 
18
31
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(test|spec|features|mwe|mnogootex_classes)/})
32
+ f.match(%r{^(test|mwe)/})
20
33
  end
21
- spec.bindir = "exe"
34
+ spec.bindir = 'exe'
22
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ["lib"]
36
+ spec.require_paths = ['lib']
37
+
38
+ spec.required_ruby_version = '>= 2.6'
24
39
 
25
- spec.add_development_dependency "bundler", "~> 1.16"
26
- spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "rspec", "~> 3.0"
40
+ spec.add_development_dependency 'bundler', '~> 2.2.30'
41
+ spec.add_development_dependency 'byebug', '~> 11.1.3'
42
+ # spec.add_development_dependency 'dry-inflector', '~> 0.1.1'
43
+ spec.add_development_dependency 'guard', '~> 2.18.0'
44
+ spec.add_development_dependency 'guard-rspec', '~> 4.7.3'
45
+ # spec.add_development_dependency 'mutant', '~> 0.8.14'
46
+ # spec.add_development_dependency 'mutant-rspec', '~> 0.8.14'
47
+ spec.add_development_dependency 'rake', '~> 13.0.6'
48
+ spec.add_development_dependency 'rspec', '~> 3.10.0'
49
+ spec.add_development_dependency 'rubocop', '~> 1.22.3'
50
+ spec.add_development_dependency 'simplecov', '~> 0.21.2'
51
+ spec.add_development_dependency 'yard', '~> 0.9.26'
28
52
 
29
- spec.add_dependency "colorize"
53
+ spec.add_dependency 'colorize', '~> 0.8.1'
54
+ spec.add_dependency 'thor', '~> 0.20.0'
30
55
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'yaml'
6
+ require 'tmpdir'
7
+
8
+ require 'mnogootex/cfg'
9
+
10
+ describe Mnogootex::Cfg do
11
+ describe 'load_descending' do
12
+ context 'fake file structure' do
13
+ subject { described_class.load_descending(pathname: Pathname.new('foobar'), basename: 'cfg.yml') }
14
+
15
+ it 'raises an error on loading a fake path' do
16
+ expect { subject }.to raise_exception Errno::ENOENT
17
+ end
18
+ end
19
+
20
+ context 'real file structure' do
21
+ subject { described_class.load_descending(pathname: tmp_dir.join('A', 'B'), basename: 'cfg.yml') }
22
+ let(:defaults) { { default: true, winner: 'default' } }
23
+ let(:tmp_dir) { Pathname.new(Dir.mktmpdir).join('mnogootex-test') }
24
+
25
+ before do
26
+ tmp_dir.join('A', 'B').mkpath
27
+ tmp_dir.join('cfg.yml').write('') # NOTE: empty file
28
+ tmp_dir.join('A', 'cfg.yml').write({ parent: true, winner: 'parent', merged: { deep: true } }.to_yaml)
29
+ tmp_dir.join('A', 'B', 'cfg.yml').write({ child: true, winner: 'child', merged: {} }.to_yaml)
30
+ end
31
+
32
+ after do
33
+ tmp_dir.rmtree
34
+ end
35
+
36
+ it 'merges paths' do
37
+ expect(subject).to include(:parent)
38
+ expect(subject).to include(:child)
39
+ end
40
+
41
+ it 'merges shallowly' do
42
+ expect(subject[:merged]).to_not include(:deep)
43
+ end
44
+
45
+ it 'privileges deeper paths' do
46
+ expect(subject[:winner]).to eq('child')
47
+ end
48
+
49
+ it 'privileges non defaults' do
50
+ expect(subject[:winner]).to_not eq('default')
51
+ end
52
+ end
53
+ end
54
+ end