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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e77cd0148a5d6e1d6c31b90a12d9d7cf85fbccea7e5efa6f4240c6f60759e69e
4
- data.tar.gz: d1c1b0c394f0c35a23b41fb05d1ebd39cfcf53f1bbc8c316a7dac4ee3d6fc046
3
+ metadata.gz: 7233124faca7d704c5fc49708d03df7bf821863a7ef1470d2525b00dadc13c80
4
+ data.tar.gz: 904ee4fb5f329b2b9be7fb33a1e7d4bebb3e15dddfee3b6928756e47aab4cf61
5
5
  SHA512:
6
- metadata.gz: 3478dcee26fe4f22d25bc81cc6f9bc874c8af9599c76971ae6c51b619c5ee6215600c161d34b36714f27e5a1b579c643f8679a770ce5e2470bf89d17203be085
7
- data.tar.gz: a1bdafe93d32883ebbd8ed601a83f5521ec7519597a02e8f0f6f325c17bb502b291350529c94dfd5b709e6eb83d8ace98a48c12a88a3c2e327e92b5b62341078
6
+ metadata.gz: 1021062eedb1e2f0a7ceefd6fa7783f55079d415097476b94cea6c399223a8af5a0e8311d560e423744ad35292707280641c81c3c8ebc0ff97baad2b1ec3f9fd
7
+ data.tar.gz: e633c877d5350bc007c0beb931c5eb7a322603b7cd0e4742c48ddc964b44d8da185cb444294c7d7e27e1ece707854948d94e9a534d03c5579eab770a1a6f97ef
data/bin/yalphabetize ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/yalphabetize'
5
+
6
+ exit Yalphabetize::CLI.call(ARGV)
@@ -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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yalphabetize
4
+ module OrderCheckers
5
+ class CapitalizedFirstThenAlphabetical < Base
6
+ def self.compare(string, other_string)
7
+ (string <=> other_string)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yalphabetize
4
+ module OrderCheckers
5
+ class CapitalizedLastThenAlphabetical < Base
6
+ def self.compare(string, other_string)
7
+ (string.swapcase <=> other_string.swapcase)
8
+ end
9
+ end
10
+ end
11
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'psych'
4
+
5
+ module Yalphabetize
6
+ class ParsingError < Psych::SyntaxError; end
7
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yalphabetize
4
+ class Version
5
+ STRING = '0.4.0'
6
+ end
7
+ 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
- class Yalphabetize
2
- def self.call
3
- {
4
- a: :b,
5
- c: :d
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.0.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: 2021-04-21 00:00:00.000000000 Z
12
- dependencies: []
13
- description:
14
- email:
15
- executables: []
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
- homepage:
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
- post_install_message:
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: []