yalphabetize 0.0.0 → 0.4.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.
- checksums.yaml +4 -4
- data/bin/yalphabetize +6 -0
- data/lib/yalphabetize/aliaser.rb +55 -0
- data/lib/yalphabetize/alphabetizer.rb +62 -0
- data/lib/yalphabetize/cli.rb +25 -0
- data/lib/yalphabetize/erb_compiler.rb +82 -0
- data/lib/yalphabetize/file_yalphabetizer.rb +69 -0
- data/lib/yalphabetize/logger.rb +80 -0
- data/lib/yalphabetize/offence_detector.rb +38 -0
- data/lib/yalphabetize/option_parser.rb +21 -0
- data/lib/yalphabetize/order_checkers/alphabetical_then_capitalized_first.rb +12 -0
- data/lib/yalphabetize/order_checkers/alphabetical_then_capitalized_last.rb +12 -0
- data/lib/yalphabetize/order_checkers/base.rb +15 -0
- data/lib/yalphabetize/order_checkers/capitalized_first_then_alphabetical.rb +11 -0
- data/lib/yalphabetize/order_checkers/capitalized_last_then_alphabetical.rb +11 -0
- data/lib/yalphabetize/order_checkers.rb +14 -0
- data/lib/yalphabetize/parsing_error.rb +7 -0
- data/lib/yalphabetize/reader.rb +52 -0
- data/lib/yalphabetize/sequence_indenter.rb +59 -0
- data/lib/yalphabetize/version.rb +7 -0
- data/lib/yalphabetize/writer.rb +40 -0
- data/lib/yalphabetize/yalphabetizer.rb +94 -0
- data/lib/yalphabetize/yaml_finder.rb +78 -0
- data/lib/yalphabetize.rb +44 -6
- metadata +120 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7233124faca7d704c5fc49708d03df7bf821863a7ef1470d2525b00dadc13c80
|
4
|
+
data.tar.gz: 904ee4fb5f329b2b9be7fb33a1e7d4bebb3e15dddfee3b6928756e47aab4cf61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1021062eedb1e2f0a7ceefd6fa7783f55079d415097476b94cea6c399223a8af5a0e8311d560e423744ad35292707280641c81c3c8ebc0ff97baad2b1ec3f9fd
|
7
|
+
data.tar.gz: e633c877d5350bc007c0beb931c5eb7a322603b7cd0e4742c48ddc964b44d8da185cb444294c7d7e27e1ece707854948d94e9a534d03c5579eab770a1a6f97ef
|
data/bin/yalphabetize
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class Aliaser
|
5
|
+
def initialize(stream_node)
|
6
|
+
@stream_node = stream_node
|
7
|
+
@aliases = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
nodes.each do |node|
|
12
|
+
next unless node.respond_to?(:anchor) && node.anchor
|
13
|
+
|
14
|
+
ensure_anchor_then_alias(node)
|
15
|
+
end
|
16
|
+
stream_node
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :stream_node, :aliases
|
22
|
+
|
23
|
+
def ensure_anchor_then_alias(node)
|
24
|
+
if node.alias?
|
25
|
+
aliases[node.anchor] ||= node
|
26
|
+
elsif aliases[node.anchor]
|
27
|
+
swap(node, aliases[node.anchor])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def swap(anchor_node, alias_node)
|
32
|
+
stream_node.each do |node|
|
33
|
+
node.children&.map! do |child_node|
|
34
|
+
convert_node(child_node, anchor_node, alias_node)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def nodes
|
40
|
+
stream_node.select do |node|
|
41
|
+
node.respond_to?(:anchor) && node.anchor
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def convert_node(child_node, anchor_node, alias_node)
|
46
|
+
if child_node == anchor_node
|
47
|
+
alias_node
|
48
|
+
elsif child_node == alias_node
|
49
|
+
anchor_node
|
50
|
+
else
|
51
|
+
child_node
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class Alphabetizer
|
5
|
+
def initialize(stream_node, order_checker_class:)
|
6
|
+
@stream_node = stream_node
|
7
|
+
@order_checker_class = order_checker_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
transform(stream_node)
|
12
|
+
alphabetize(stream_node)
|
13
|
+
stream_node
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :stream_node, :order_checker_class
|
19
|
+
|
20
|
+
def alphabetize(node)
|
21
|
+
if node.mapping?
|
22
|
+
alphabetize_children(node)
|
23
|
+
unpair_children(node)
|
24
|
+
end
|
25
|
+
|
26
|
+
node.children&.each { |child_node| alphabetize(child_node) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def alphabetize_children(node)
|
30
|
+
node.children.sort! do |a, b|
|
31
|
+
order_checker_class.compare(a.first.value, b.first.value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def unpair_children(node)
|
36
|
+
children_clone = node.children.dup
|
37
|
+
|
38
|
+
children_clone.each do |slice|
|
39
|
+
node.children.push(*slice)
|
40
|
+
node.children.shift
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def transform(node)
|
45
|
+
node.children&.each(&method(:transform))
|
46
|
+
|
47
|
+
return unless node.mapping?
|
48
|
+
|
49
|
+
pair_up_children(node)
|
50
|
+
end
|
51
|
+
|
52
|
+
def pair_up_children(node)
|
53
|
+
children_clone = node.children.dup
|
54
|
+
|
55
|
+
children_clone.each_slice(2) do |slice|
|
56
|
+
node.children.push(slice)
|
57
|
+
node.children.shift
|
58
|
+
node.children.shift
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class CLI
|
5
|
+
def self.call(argv)
|
6
|
+
new(argv).call
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(argv)
|
10
|
+
@argv = argv
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
Yalphabetizer.call argv, options
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :argv
|
20
|
+
|
21
|
+
def options
|
22
|
+
OptionParser.parse!(argv)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Inspired by ERB::Compiler (https://github.com/ruby/erb/blob/b58b188028fbb403f75d48d62717373fc0908f7a/lib/erb.rb)
|
4
|
+
|
5
|
+
module Yalphabetize
|
6
|
+
class ErbCompiler
|
7
|
+
DEFAULT_STAGS = ['<%%', '<%=', '<%#', '<%', '%{', '{{'].freeze
|
8
|
+
DEFAULT_ETAGS = ['%%>', '%>', '}}', '}'].freeze
|
9
|
+
|
10
|
+
class Scanner
|
11
|
+
STAG_REG = /(.*?)(#{DEFAULT_STAGS.join('|')}|\z)/m.freeze
|
12
|
+
ETAG_REG = /(.*?)(#{DEFAULT_ETAGS.join('|')}|\z)/m.freeze
|
13
|
+
|
14
|
+
def initialize(src)
|
15
|
+
@src = src
|
16
|
+
@stag = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def scan
|
20
|
+
until scanner.eos?
|
21
|
+
scanner.scan(stag ? ETAG_REG : STAG_REG)
|
22
|
+
yield(scanner[1])
|
23
|
+
yield(scanner[2])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :stag
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :src
|
32
|
+
|
33
|
+
def scanner
|
34
|
+
@_scanner ||= StringScanner.new(src)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@buffer = []
|
40
|
+
@content = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def compile(string)
|
44
|
+
scanner = Scanner.new(string)
|
45
|
+
scanner.scan do |token|
|
46
|
+
next if token.nil?
|
47
|
+
next if token == ''
|
48
|
+
|
49
|
+
if scanner.stag.nil?
|
50
|
+
compile_stag(token, scanner)
|
51
|
+
else
|
52
|
+
compile_etag(token, scanner)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def compile_stag(stag, scanner)
|
58
|
+
case stag
|
59
|
+
when *DEFAULT_STAGS
|
60
|
+
scanner.stag = stag
|
61
|
+
buffer << stag
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def compile_etag(etag, scanner)
|
66
|
+
buffer << etag
|
67
|
+
|
68
|
+
case etag
|
69
|
+
when *DEFAULT_ETAGS
|
70
|
+
content << buffer.join
|
71
|
+
self.buffer = []
|
72
|
+
scanner.stag = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :content
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_accessor :buffer
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class FileYalphabetizer
|
5
|
+
def initialize(file_path, **options)
|
6
|
+
@file_path = file_path
|
7
|
+
@reader_class = options[:reader_class]
|
8
|
+
@offence_detector_class = options[:offence_detector_class]
|
9
|
+
@logger = options[:logger]
|
10
|
+
@autocorrect = options[:autocorrect]
|
11
|
+
@alphabetizer_class = options[:alphabetizer_class]
|
12
|
+
@writer_class = options[:writer_class]
|
13
|
+
@order_checker_class = options[:order_checker_class]
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
if offences?
|
18
|
+
logger.log_offence(file_path)
|
19
|
+
autocorrect_file if autocorrect
|
20
|
+
else
|
21
|
+
logger.log_no_offence
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :file_path, :reader_class, :offence_detector_class, :logger, :autocorrect,
|
28
|
+
:alphabetizer_class, :writer_class, :order_checker_class
|
29
|
+
|
30
|
+
def autocorrect_file
|
31
|
+
alphabetize
|
32
|
+
handle_aliases
|
33
|
+
rewrite_yml_file
|
34
|
+
print_to_log
|
35
|
+
end
|
36
|
+
|
37
|
+
def offences?
|
38
|
+
offence_detector_class.new(stream_node, order_checker_class: order_checker_class).offences?
|
39
|
+
end
|
40
|
+
|
41
|
+
def alphabetize
|
42
|
+
alphabetizer_class.new(stream_node, order_checker_class: order_checker_class).call
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_aliases
|
46
|
+
Aliaser.new(stream_node).call
|
47
|
+
end
|
48
|
+
|
49
|
+
def rewrite_yml_file
|
50
|
+
writer_class.new(stream_node, file_path, interpolations_mapping).call
|
51
|
+
end
|
52
|
+
|
53
|
+
def print_to_log
|
54
|
+
logger.log_correction(file_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def reader
|
58
|
+
@_reader ||= reader_class.new(file_path)
|
59
|
+
end
|
60
|
+
|
61
|
+
def stream_node
|
62
|
+
@_stream_node ||= reader.to_ast
|
63
|
+
end
|
64
|
+
|
65
|
+
def interpolations_mapping
|
66
|
+
reader.interpolations_mapping
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class Logger
|
5
|
+
DEFAULT_OUTPUT = $stdout
|
6
|
+
|
7
|
+
def initialize(output = DEFAULT_OUTPUT)
|
8
|
+
@output = output
|
9
|
+
@inspected_count = 0
|
10
|
+
@offences = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initial_summary(file_paths)
|
14
|
+
output.puts "Inspecting #{file_paths.size} YAML files"
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_offence(file_path)
|
18
|
+
@inspected_count += 1
|
19
|
+
offences[file_path] = :detected
|
20
|
+
output.print red 'O'
|
21
|
+
end
|
22
|
+
|
23
|
+
def log_no_offence
|
24
|
+
@inspected_count += 1
|
25
|
+
output.print green '.'
|
26
|
+
end
|
27
|
+
|
28
|
+
def final_summary
|
29
|
+
output.puts "\nInspected: #{inspected_count}"
|
30
|
+
output.puts send(list_offences_color, "Offences: #{offences.size}")
|
31
|
+
|
32
|
+
offences.each { |file_path, status| puts_offence(file_path, status) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def offences?
|
36
|
+
@offences.any?
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_correction(file_path)
|
40
|
+
offences[file_path] = :corrected
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :offences, :inspected_count, :output
|
46
|
+
|
47
|
+
def puts_offence(file_path, status)
|
48
|
+
case status
|
49
|
+
when :detected
|
50
|
+
output.puts yellow file_path
|
51
|
+
when :corrected
|
52
|
+
output.puts("#{yellow(file_path)} #{green('CORRECTED')}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def list_offences_color
|
57
|
+
if offences?
|
58
|
+
:red
|
59
|
+
else
|
60
|
+
:green
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def red(string)
|
65
|
+
with_color(31, string)
|
66
|
+
end
|
67
|
+
|
68
|
+
def yellow(string)
|
69
|
+
with_color(33, string)
|
70
|
+
end
|
71
|
+
|
72
|
+
def green(string)
|
73
|
+
with_color(32, string)
|
74
|
+
end
|
75
|
+
|
76
|
+
def with_color(color, string)
|
77
|
+
"\e[#{color}m#{string}\e[0m"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class OffenceDetector
|
5
|
+
def initialize(stream_node, order_checker_class:)
|
6
|
+
@stream_node = stream_node
|
7
|
+
@order_checker_class = order_checker_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def offences?
|
11
|
+
!alphabetized?(stream_node)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :stream_node, :order_checker_class
|
17
|
+
|
18
|
+
def alphabetized?(node)
|
19
|
+
return false if node.mapping? && !alphabetized_children?(node)
|
20
|
+
|
21
|
+
if node.children
|
22
|
+
each_child_also_alphabetized?(node.children)
|
23
|
+
else
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def alphabetized_children?(node)
|
29
|
+
node.children.each_slice(2).each_cons(2).all? do |a, b|
|
30
|
+
order_checker_class.ordered?(a.first.value, b.first.value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def each_child_also_alphabetized?(children)
|
35
|
+
children.flatten&.inject(true) { |bool, child_node| bool && alphabetized?(child_node) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Yalphabetize
|
6
|
+
class OptionParser
|
7
|
+
PARSER = ::OptionParser.new do |o|
|
8
|
+
o.banner = 'Usage: yalphabetize [options] [file1, file2, ...]'
|
9
|
+
|
10
|
+
o.on('-a', '--autocorrect', 'Automatically alphabetize inspected yaml files')
|
11
|
+
o.on('-h', '--help', 'Prints this help') do
|
12
|
+
puts o
|
13
|
+
Kernel.exit
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse!(argv)
|
18
|
+
{}.tap { |options| PARSER.parse!(argv, into: options) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# AaBb
|
4
|
+
module Yalphabetize
|
5
|
+
module OrderCheckers
|
6
|
+
class AlphabeticalThenCapitalizedFirst < Base
|
7
|
+
def self.compare(string, other_string)
|
8
|
+
alphabetical_comparison(string, other_string).nonzero? || string <=> other_string
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# aAbB
|
4
|
+
module Yalphabetize
|
5
|
+
module OrderCheckers
|
6
|
+
class AlphabeticalThenCapitalizedLast < Base
|
7
|
+
def self.compare(string, other_string)
|
8
|
+
alphabetical_comparison(string, other_string).nonzero? || string.swapcase <=> other_string.swapcase
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
module OrderCheckers
|
5
|
+
class Base
|
6
|
+
def self.ordered?(string, other_string)
|
7
|
+
!compare(string, other_string).positive?
|
8
|
+
end
|
9
|
+
|
10
|
+
private_class_method def self.alphabetical_comparison(string, other_string)
|
11
|
+
string.downcase <=> other_string.downcase
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
module OrderCheckers
|
5
|
+
DEFAULT = CapitalizedFirstThenAlphabetical
|
6
|
+
|
7
|
+
TOKEN_MAPPING = {
|
8
|
+
'abAB' => CapitalizedLastThenAlphabetical,
|
9
|
+
'aAbB' => AlphabeticalThenCapitalizedLast,
|
10
|
+
'AaBb' => AlphabeticalThenCapitalizedFirst,
|
11
|
+
'ABab' => CapitalizedFirstThenAlphabetical
|
12
|
+
}.freeze
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'psych'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module Yalphabetize
|
7
|
+
class Reader
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
@interpolations_mapping = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ast
|
14
|
+
substitute_interpolations
|
15
|
+
stream_node
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :interpolations_mapping
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :path
|
23
|
+
|
24
|
+
def substitute_interpolations
|
25
|
+
erb_compiler = Yalphabetize::ErbCompiler.new
|
26
|
+
erb_compiler.compile(file)
|
27
|
+
|
28
|
+
erb_compiler.content.each do |interpolation|
|
29
|
+
uuid = SecureRandom.uuid
|
30
|
+
file.sub!(interpolation, uuid)
|
31
|
+
interpolations_mapping[uuid] = interpolation
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def file
|
36
|
+
@_file ||= File.read(path)
|
37
|
+
end
|
38
|
+
|
39
|
+
def stream_node
|
40
|
+
@_stream_node ||= Psych.parse_stream file
|
41
|
+
rescue Psych::SyntaxError => e
|
42
|
+
raise Yalphabetize::ParsingError.new(
|
43
|
+
path,
|
44
|
+
e.line,
|
45
|
+
e.column,
|
46
|
+
e.offset,
|
47
|
+
e.problem,
|
48
|
+
e.context
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'psych'
|
4
|
+
|
5
|
+
module Yalphabetize
|
6
|
+
class SequenceIndenter
|
7
|
+
def self.call(content)
|
8
|
+
new(content).call
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(content)
|
12
|
+
@content = content
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
return unless Yalphabetize.config['indent_sequences']
|
17
|
+
|
18
|
+
find_lines_to_indent
|
19
|
+
indent!
|
20
|
+
file_lines.join
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :content
|
26
|
+
|
27
|
+
def find_lines_to_indent
|
28
|
+
@nodes_to_indent = []
|
29
|
+
|
30
|
+
stream_node.each do |node|
|
31
|
+
next if node.document?
|
32
|
+
|
33
|
+
block_sequence_nodes(node)&.each do |sequence_node|
|
34
|
+
@nodes_to_indent.push(*sequence_node.children)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def block_sequence_nodes(node)
|
40
|
+
node.children&.select { |child_node| child_node.sequence? && child_node.style == 1 }
|
41
|
+
end
|
42
|
+
|
43
|
+
def stream_node
|
44
|
+
Psych.parse_stream content
|
45
|
+
end
|
46
|
+
|
47
|
+
def indent!
|
48
|
+
@nodes_to_indent.each do |node|
|
49
|
+
file_lines[node.start_line..node.end_line].each do |line|
|
50
|
+
line.gsub!(line, " #{line}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def file_lines
|
56
|
+
@_file_lines ||= content.each_line.to_a
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class Writer
|
5
|
+
MAX_LINE_WIDTH = -1
|
6
|
+
|
7
|
+
def initialize(stream_node, path, interpolations_mapping)
|
8
|
+
@stream_node = stream_node
|
9
|
+
@path = path
|
10
|
+
@interpolations_mapping = interpolations_mapping
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
indent_sequences
|
15
|
+
replace_interpolations
|
16
|
+
|
17
|
+
File.open(path, 'w') do |file|
|
18
|
+
file.write new_file_content
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :stream_node, :path, :interpolations_mapping
|
25
|
+
|
26
|
+
def new_file_content
|
27
|
+
@_new_file_content ||= stream_node.to_yaml(nil, line_width: MAX_LINE_WIDTH)
|
28
|
+
end
|
29
|
+
|
30
|
+
def replace_interpolations
|
31
|
+
interpolations_mapping.each do |uuid, interpolation|
|
32
|
+
new_file_content.sub!(uuid, interpolation)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def indent_sequences
|
37
|
+
@_new_file_content = SequenceIndenter.call(new_file_content)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Yalphabetize
|
4
|
+
class Yalphabetizer
|
5
|
+
def self.call(args = [], options = {})
|
6
|
+
new(args, options).call
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(args, options)
|
10
|
+
@args = args
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
initial_log
|
16
|
+
process_files
|
17
|
+
final_log
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :args, :options
|
23
|
+
|
24
|
+
def initial_log
|
25
|
+
logger.initial_summary(file_paths)
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_files
|
29
|
+
file_paths.each(&method(:process_file))
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_file(file_path)
|
33
|
+
file_yalphabetizer_class.new(
|
34
|
+
file_path,
|
35
|
+
reader_class: reader_class,
|
36
|
+
offence_detector_class: offence_detector_class,
|
37
|
+
logger: logger,
|
38
|
+
autocorrect: options[:autocorrect],
|
39
|
+
alphabetizer_class: alphabetizer_class,
|
40
|
+
writer_class: writer_class,
|
41
|
+
order_checker_class: order_checker_class
|
42
|
+
).call
|
43
|
+
end
|
44
|
+
|
45
|
+
def file_paths
|
46
|
+
@_file_paths ||= finder.paths(only: only_paths, exclude: Yalphabetize.config['exclude'])
|
47
|
+
end
|
48
|
+
|
49
|
+
def only_paths
|
50
|
+
if args.any?
|
51
|
+
args
|
52
|
+
else
|
53
|
+
Yalphabetize.config['only']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def final_log
|
58
|
+
logger.final_summary
|
59
|
+
logger.offences? ? 1 : 0
|
60
|
+
end
|
61
|
+
|
62
|
+
def reader_class
|
63
|
+
Yalphabetize::Reader
|
64
|
+
end
|
65
|
+
|
66
|
+
def finder
|
67
|
+
Yalphabetize::YamlFinder
|
68
|
+
end
|
69
|
+
|
70
|
+
def alphabetizer_class
|
71
|
+
Yalphabetize::Alphabetizer
|
72
|
+
end
|
73
|
+
|
74
|
+
def writer_class
|
75
|
+
Yalphabetize::Writer
|
76
|
+
end
|
77
|
+
|
78
|
+
def offence_detector_class
|
79
|
+
Yalphabetize::OffenceDetector
|
80
|
+
end
|
81
|
+
|
82
|
+
def logger
|
83
|
+
@_logger ||= Yalphabetize::Logger.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def file_yalphabetizer_class
|
87
|
+
Yalphabetize::FileYalphabetizer
|
88
|
+
end
|
89
|
+
|
90
|
+
def order_checker_class
|
91
|
+
OrderCheckers::TOKEN_MAPPING[Yalphabetize.config['sort_by']]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module Yalphabetize
|
6
|
+
class YamlFinder
|
7
|
+
YAML_EXTENSIONS = %w[.yml .yaml].freeze
|
8
|
+
|
9
|
+
def self.paths(only:, exclude:)
|
10
|
+
if exclude.any?
|
11
|
+
new.paths(only) - new.paths(exclude)
|
12
|
+
else
|
13
|
+
new.paths(only)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@files = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def paths(paths)
|
22
|
+
if paths.empty?
|
23
|
+
files << files_in_dir
|
24
|
+
else
|
25
|
+
process_paths(paths)
|
26
|
+
end
|
27
|
+
|
28
|
+
files.flatten.select(&method(:valid?)).map { |f| File.expand_path(f) }.uniq
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :files
|
34
|
+
|
35
|
+
def process_paths(paths)
|
36
|
+
paths.uniq.each do |path|
|
37
|
+
files << if glob?(path)
|
38
|
+
files_in_glob(path)
|
39
|
+
elsif File.directory?(path)
|
40
|
+
files_in_dir(path)
|
41
|
+
else
|
42
|
+
path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def files_in_glob(glob)
|
48
|
+
files_in_dir([glob, glob.gsub('**/', '').gsub('**', '')].uniq)
|
49
|
+
end
|
50
|
+
|
51
|
+
def files_in_dir(dir = Dir.pwd)
|
52
|
+
return if `sh -c 'command -v git'`.empty?
|
53
|
+
|
54
|
+
output, _error, status = Open3.capture3(
|
55
|
+
'git', 'ls-files', '-z', *Array(dir),
|
56
|
+
'--exclude-standard', '--others', '--cached', '--modified'
|
57
|
+
)
|
58
|
+
|
59
|
+
output.split("\0").uniq.map { |git_file| "#{Dir.pwd}/#{git_file}" } if status.success?
|
60
|
+
end
|
61
|
+
|
62
|
+
def valid?(path)
|
63
|
+
exists?(path) && yml?(path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def exists?(path)
|
67
|
+
File.exist?(path)
|
68
|
+
end
|
69
|
+
|
70
|
+
def yml?(path)
|
71
|
+
YAML_EXTENSIONS.include? File.extname(path)
|
72
|
+
end
|
73
|
+
|
74
|
+
def glob?(path)
|
75
|
+
path.include?('*')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/yalphabetize.rb
CHANGED
@@ -1,8 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'yalphabetize/aliaser'
|
4
|
+
require_relative 'yalphabetize/alphabetizer'
|
5
|
+
require_relative 'yalphabetize/cli'
|
6
|
+
require_relative 'yalphabetize/erb_compiler'
|
7
|
+
require_relative 'yalphabetize/file_yalphabetizer'
|
8
|
+
require_relative 'yalphabetize/logger'
|
9
|
+
require_relative 'yalphabetize/offence_detector'
|
10
|
+
require_relative 'yalphabetize/option_parser'
|
11
|
+
require_relative 'yalphabetize/order_checkers/base'
|
12
|
+
require_relative 'yalphabetize/order_checkers/alphabetical_then_capitalized_first'
|
13
|
+
require_relative 'yalphabetize/order_checkers/alphabetical_then_capitalized_last'
|
14
|
+
require_relative 'yalphabetize/order_checkers/capitalized_last_then_alphabetical'
|
15
|
+
require_relative 'yalphabetize/order_checkers/capitalized_first_then_alphabetical'
|
16
|
+
require_relative 'yalphabetize/order_checkers'
|
17
|
+
require_relative 'yalphabetize/parsing_error'
|
18
|
+
require_relative 'yalphabetize/reader'
|
19
|
+
require_relative 'yalphabetize/sequence_indenter'
|
20
|
+
require_relative 'yalphabetize/version'
|
21
|
+
require_relative 'yalphabetize/writer'
|
22
|
+
require_relative 'yalphabetize/yalphabetizer'
|
23
|
+
require_relative 'yalphabetize/yaml_finder'
|
24
|
+
|
25
|
+
module Yalphabetize
|
26
|
+
class << self
|
27
|
+
DEFAULT_CONFIG = {
|
28
|
+
'indent_sequences' => true,
|
29
|
+
'exclude' => [],
|
30
|
+
'only' => [],
|
31
|
+
'sort_by' => 'ABab'
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
def config
|
35
|
+
@_config ||= begin
|
36
|
+
specified = if File.exist?('.yalphabetize.yml')
|
37
|
+
Psych.load_file('.yalphabetize.yml') || {}
|
38
|
+
else
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
|
42
|
+
DEFAULT_CONFIG.merge(specified)
|
43
|
+
end
|
44
|
+
end
|
7
45
|
end
|
8
46
|
end
|
metadata
CHANGED
@@ -1,26 +1,134 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yalphabetize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Jenkins
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
date: 2022-01-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: psych
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: factory_bot
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
executables:
|
100
|
+
- yalphabetize
|
16
101
|
extensions: []
|
17
102
|
extra_rdoc_files: []
|
18
103
|
files:
|
104
|
+
- bin/yalphabetize
|
19
105
|
- lib/yalphabetize.rb
|
20
|
-
|
106
|
+
- lib/yalphabetize/aliaser.rb
|
107
|
+
- lib/yalphabetize/alphabetizer.rb
|
108
|
+
- lib/yalphabetize/cli.rb
|
109
|
+
- lib/yalphabetize/erb_compiler.rb
|
110
|
+
- lib/yalphabetize/file_yalphabetizer.rb
|
111
|
+
- lib/yalphabetize/logger.rb
|
112
|
+
- lib/yalphabetize/offence_detector.rb
|
113
|
+
- lib/yalphabetize/option_parser.rb
|
114
|
+
- lib/yalphabetize/order_checkers.rb
|
115
|
+
- lib/yalphabetize/order_checkers/alphabetical_then_capitalized_first.rb
|
116
|
+
- lib/yalphabetize/order_checkers/alphabetical_then_capitalized_last.rb
|
117
|
+
- lib/yalphabetize/order_checkers/base.rb
|
118
|
+
- lib/yalphabetize/order_checkers/capitalized_first_then_alphabetical.rb
|
119
|
+
- lib/yalphabetize/order_checkers/capitalized_last_then_alphabetical.rb
|
120
|
+
- lib/yalphabetize/parsing_error.rb
|
121
|
+
- lib/yalphabetize/reader.rb
|
122
|
+
- lib/yalphabetize/sequence_indenter.rb
|
123
|
+
- lib/yalphabetize/version.rb
|
124
|
+
- lib/yalphabetize/writer.rb
|
125
|
+
- lib/yalphabetize/yalphabetizer.rb
|
126
|
+
- lib/yalphabetize/yaml_finder.rb
|
127
|
+
homepage:
|
21
128
|
licenses: []
|
22
|
-
metadata:
|
23
|
-
|
129
|
+
metadata:
|
130
|
+
rubygems_mfa_required: 'true'
|
131
|
+
post_install_message:
|
24
132
|
rdoc_options: []
|
25
133
|
require_paths:
|
26
134
|
- lib
|
@@ -35,8 +143,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
35
143
|
- !ruby/object:Gem::Version
|
36
144
|
version: '0'
|
37
145
|
requirements: []
|
38
|
-
rubygems_version: 3.0.3
|
39
|
-
signing_key:
|
146
|
+
rubygems_version: 3.0.3.1
|
147
|
+
signing_key:
|
40
148
|
specification_version: 4
|
41
149
|
summary: Alphabetize your YAML files
|
42
150
|
test_files: []
|