mnogootex 0.2.1 → 2.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.
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