yalphabetize 0.1.0 → 0.4.1

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: b507c005e0407945299b4d297a443fe5f019f876e90bba19e930630c11be8881
4
- data.tar.gz: 624a56393c2cf67a529ed4542f66a8308c409739c216052d62b4e75997b8b5eb
3
+ metadata.gz: 86d76847f2e23ff6fe3cd5a5e58304236f308cd0541a19bd3ab96f7db10bb601
4
+ data.tar.gz: 8c00fcc4c33db1396aeae8384d1fc62d12bb27dd5db2ef0e7115c6a1f4ccce09
5
5
  SHA512:
6
- metadata.gz: b77dd2c3d7e92e97e7c7d16cdd9359c6bd5b7084fe7006651a03d3416de73429403f17fcbd0b45f2d09225b842cb4bfb83adb9f48519b3ceb32853339e356e48
7
- data.tar.gz: 27495a87a0594f9c33751a7f48e67234b1d7e8067b2e095d5dd90d162237fb309243d0c3c0617795638f1f4b837cf478be1d3969b3ff245668bf45645bca5724
6
+ metadata.gz: 6e83b56bf3dafb17675575d038cb07fd69c66d7732846eb5ade8379a84d6ac2994471784b2ba6bbdfa306bcbecddcc5d7f175f31c95fd7eaf1b5b509a4d0ba91
7
+ data.tar.gz: 37c7d42a08c8a1ac39bfc7bdfb4ce65ec9a658884bbc433df5504f636a9797ad1695bffca1847d7a651331b9929f72bf3a2253f258be03383319e67857af0039
data/bin/yalphabetize CHANGED
@@ -1,27 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'yalphabetize/reader'
5
- require 'yalphabetize/alphabetizer'
6
- require 'yalphabetize/writer'
7
- require 'yalphabetize/offence_detector'
8
- require 'yalphabetize/yaml_finder'
9
- require 'yalphabetize/logger'
10
- require 'yalphabetizer'
4
+ require_relative '../lib/yalphabetize'
11
5
 
12
- reader = Yalphabetize::Reader
13
- finder = Yalphabetize::YamlFinder.new
14
- alphabetizer = Yalphabetize::Alphabetizer
15
- writer = Yalphabetize::Writer
16
- offence_detector = Yalphabetize::OffenceDetector
17
- logger = Yalphabetize::Logger.new
18
-
19
- exit Yalphabetizer.call(
20
- ARGV,
21
- reader: reader,
22
- finder: finder,
23
- alphabetizer: alphabetizer,
24
- writer: writer,
25
- offence_detector: offence_detector,
26
- logger: logger
27
- )
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
@@ -2,18 +2,20 @@
2
2
 
3
3
  module Yalphabetize
4
4
  class Alphabetizer
5
- def initialize(stream_node)
5
+ def initialize(stream_node, order_checker_class:)
6
6
  @stream_node = stream_node
7
+ @order_checker_class = order_checker_class
7
8
  end
8
9
 
9
10
  def call
11
+ transform(stream_node)
10
12
  alphabetize(stream_node)
11
13
  stream_node
12
14
  end
13
15
 
14
16
  private
15
17
 
16
- attr_reader :stream_node
18
+ attr_reader :stream_node, :order_checker_class
17
19
 
18
20
  def alphabetize(node)
19
21
  if node.mapping?
@@ -25,7 +27,9 @@ module Yalphabetize
25
27
  end
26
28
 
27
29
  def alphabetize_children(node)
28
- node.children.sort_by! { |key, _value| key.value }
30
+ node.children.sort! do |a, b|
31
+ order_checker_class.compare(a.first.value, b.first.value)
32
+ end
29
33
  end
30
34
 
31
35
  def unpair_children(node)
@@ -36,5 +40,23 @@ module Yalphabetize
36
40
  node.children.shift
37
41
  end
38
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
39
61
  end
40
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
@@ -1,40 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yalphabetize
2
4
  class Logger
3
- def initialize
5
+ DEFAULT_OUTPUT = $stdout
6
+
7
+ def initialize(output = DEFAULT_OUTPUT)
8
+ @output = output
4
9
  @inspected_count = 0
5
10
  @offences = {}
6
11
  end
7
12
 
8
- def task_summary(file_paths)
9
- puts "Inspecting #{file_paths.size} YAML files"
13
+ def initial_summary(file_paths)
14
+ output.puts "Inspecting #{file_paths.size} YAML files"
10
15
  end
11
16
 
12
17
  def log_offence(file_path)
13
18
  @inspected_count += 1
14
19
  offences[file_path] = :detected
15
- print red 'O'
20
+ output.print red 'O'
16
21
  end
17
22
 
18
23
  def log_no_offence
19
24
  @inspected_count += 1
20
- print green '.'
21
- end
22
-
23
- def log_inspected_count
24
- puts "\nInspected: #{inspected_count}"
25
+ output.print green '.'
25
26
  end
26
27
 
27
- def list_offences
28
- puts send(list_offences_color, "Offences: #{offences.size}")
28
+ def final_summary
29
+ output.puts "\nInspected: #{inspected_count}"
30
+ output.puts send(list_offences_color, "Offences: #{offences.size}")
29
31
 
30
- @offences.each do |file_path, status|
31
- case status
32
- when :detected
33
- puts yellow file_path
34
- when :corrected
35
- puts("#{yellow(file_path)} #{green("CORRECTED")}")
36
- end
37
- end
32
+ offences.each { |file_path, status| puts_offence(file_path, status) }
38
33
  end
39
34
 
40
35
  def offences?
@@ -47,7 +42,16 @@ module Yalphabetize
47
42
 
48
43
  private
49
44
 
50
- attr_reader :offences, :inspected_count
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
51
55
 
52
56
  def list_offences_color
53
57
  if offences?
@@ -2,8 +2,9 @@
2
2
 
3
3
  module Yalphabetize
4
4
  class OffenceDetector
5
- def initialize(stream_node)
5
+ def initialize(stream_node, order_checker_class:)
6
6
  @stream_node = stream_node
7
+ @order_checker_class = order_checker_class
7
8
  end
8
9
 
9
10
  def offences?
@@ -12,7 +13,7 @@ module Yalphabetize
12
13
 
13
14
  private
14
15
 
15
- attr_reader :stream_node
16
+ attr_reader :stream_node, :order_checker_class
16
17
 
17
18
  def alphabetized?(node)
18
19
  return false if node.mapping? && !alphabetized_children?(node)
@@ -25,7 +26,9 @@ module Yalphabetize
25
26
  end
26
27
 
27
28
  def alphabetized_children?(node)
28
- node.children.each_cons(2).all? { |a, b| (a.first.value <=> b.first.value) <= 0 }
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
29
32
  end
30
33
 
31
34
  def each_child_also_alphabetized?(children)
@@ -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
@@ -1,42 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'psych'
4
+ require 'securerandom'
4
5
 
5
6
  module Yalphabetize
6
7
  class Reader
7
8
  def initialize(path)
8
9
  @path = path
10
+ @interpolations_mapping = {}
9
11
  end
10
12
 
11
13
  def to_ast
12
- transform(stream_node)
14
+ substitute_interpolations
13
15
  stream_node
14
16
  end
15
17
 
18
+ attr_reader :interpolations_mapping
19
+
16
20
  private
17
21
 
18
22
  attr_reader :path
19
23
 
20
- def stream_node
21
- @stream_node ||= Psych.parse_stream File.read(path)
22
- end
23
-
24
- def transform(node)
25
- node.children&.each(&method(:transform))
26
-
27
- return unless node.mapping?
24
+ def substitute_interpolations
25
+ erb_compiler = Yalphabetize::ErbCompiler.new
26
+ erb_compiler.compile(file)
28
27
 
29
- pair_up_children(node)
28
+ erb_compiler.content.each do |interpolation|
29
+ uuid = SecureRandom.uuid
30
+ file.sub!(interpolation, uuid)
31
+ interpolations_mapping[uuid] = interpolation
32
+ end
30
33
  end
31
34
 
32
- def pair_up_children(node)
33
- children_clone = node.children.dup
35
+ def file
36
+ @_file ||= File.read(path)
37
+ end
34
38
 
35
- children_clone.each_slice(2) do |slice|
36
- node.children.push(slice)
37
- node.children.shift
38
- node.children.shift
39
- end
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
+ )
40
50
  end
41
51
  end
42
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.select(&:mapping?).each do |mapping_node|
31
+ block_sequence_nodes(mapping_node)&.each do |sequence_node|
32
+ @nodes_to_indent.push(*sequence_node.children)
33
+ end
34
+ end
35
+ end
36
+
37
+ def block_sequence_nodes(node)
38
+ node.children&.select { |child_node| child_node.sequence? && child_node.style == 1 }
39
+ end
40
+
41
+ def stream_node
42
+ Psych.parse_stream content
43
+ end
44
+
45
+ def indent!
46
+ @nodes_to_indent.each do |node|
47
+ node_end_line = node.start_line == node.end_line ? node.end_line : node.end_line - 1
48
+
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.1'
6
+ end
7
+ end
@@ -2,19 +2,39 @@
2
2
 
3
3
  module Yalphabetize
4
4
  class Writer
5
- def initialize(stream_node, path)
5
+ MAX_LINE_WIDTH = -1
6
+
7
+ def initialize(stream_node, path, interpolations_mapping)
6
8
  @stream_node = stream_node
7
9
  @path = path
10
+ @interpolations_mapping = interpolations_mapping
8
11
  end
9
12
 
10
13
  def call
14
+ indent_sequences
15
+ replace_interpolations
16
+
11
17
  File.open(path, 'w') do |file|
12
- file.write(stream_node.to_yaml)
18
+ file.write new_file_content
13
19
  end
14
20
  end
15
21
 
16
22
  private
17
23
 
18
- attr_reader :stream_node, :path
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
19
39
  end
20
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
@@ -1,20 +1,78 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open3'
4
+
3
5
  module Yalphabetize
4
6
  class YamlFinder
5
- def paths
6
- @_paths ||= begin
7
- return if `sh -c 'command -v git'`.empty?
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
8
20
 
9
- output, _error, status = Open3.capture3(
10
- 'git', 'ls-files', '-z', './**/*.yml',
11
- '--exclude-standard', '--others', '--cached', '--modified'
12
- )
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
13
30
 
14
- return unless status.success?
31
+ private
15
32
 
16
- output.split("\0").uniq.map { |git_file| "#{Dir.pwd}/#{git_file}" }
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
17
44
  end
18
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
19
77
  end
20
78
  end
@@ -0,0 +1,46 @@
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
45
+ end
46
+ end
metadata CHANGED
@@ -1,34 +1,134 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yalphabetize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.1
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-05-09 00:00:00.000000000 Z
12
- dependencies: []
13
- description:
14
- email:
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:
15
99
  executables:
16
100
  - yalphabetize
17
101
  extensions: []
18
102
  extra_rdoc_files: []
19
103
  files:
20
104
  - bin/yalphabetize
105
+ - lib/yalphabetize.rb
106
+ - lib/yalphabetize/aliaser.rb
21
107
  - lib/yalphabetize/alphabetizer.rb
108
+ - lib/yalphabetize/cli.rb
109
+ - lib/yalphabetize/erb_compiler.rb
110
+ - lib/yalphabetize/file_yalphabetizer.rb
22
111
  - lib/yalphabetize/logger.rb
23
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
24
121
  - lib/yalphabetize/reader.rb
122
+ - lib/yalphabetize/sequence_indenter.rb
123
+ - lib/yalphabetize/version.rb
25
124
  - lib/yalphabetize/writer.rb
125
+ - lib/yalphabetize/yalphabetizer.rb
26
126
  - lib/yalphabetize/yaml_finder.rb
27
- - lib/yalphabetizer.rb
28
- homepage:
127
+ homepage:
29
128
  licenses: []
30
- metadata: {}
31
- post_install_message:
129
+ metadata:
130
+ rubygems_mfa_required: 'true'
131
+ post_install_message:
32
132
  rdoc_options: []
33
133
  require_paths:
34
134
  - lib
@@ -43,8 +143,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
143
  - !ruby/object:Gem::Version
44
144
  version: '0'
45
145
  requirements: []
46
- rubygems_version: 3.1.4
47
- signing_key:
146
+ rubygems_version: 3.0.3.1
147
+ signing_key:
48
148
  specification_version: 4
49
149
  summary: Alphabetize your YAML files
50
150
  test_files: []
data/lib/yalphabetizer.rb DELETED
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'pry'
5
-
6
- class Yalphabetizer
7
- def self.call(args = [], **options)
8
- new(args, options).call
9
- end
10
-
11
- def initialize(args, options)
12
- @args = args
13
-
14
- @reader = options[:reader]
15
- @finder = options[:finder]
16
- @alphabetizer = options[:alphabetizer]
17
- @writer = options[:writer]
18
- @offence_detector = options[:offence_detector]
19
- @logger = options[:logger]
20
- end
21
-
22
- def call
23
- logger.task_summary(file_paths)
24
-
25
- file_paths.each do |file_path|
26
- unsorted_stream_node = reader.new(file_path).to_ast
27
-
28
- if offences?(unsorted_stream_node)
29
- logger.log_offence(file_path)
30
- autocorrect(unsorted_stream_node, file_path) if autocorrect?
31
- else
32
- logger.log_no_offence
33
- end
34
- end
35
-
36
- logger.log_inspected_count
37
- logger.list_offences
38
-
39
- logger.offences? ? 1 : 0
40
- end
41
-
42
- private
43
-
44
- attr_reader :args, :reader, :finder, :alphabetizer, :writer, :offence_detector, :logger
45
-
46
- def file_paths
47
- finder.paths
48
- end
49
-
50
- def autocorrect?
51
- return true if args.include? '-a'
52
- return true if args.include? '-autocorrect'
53
-
54
- false
55
- end
56
-
57
- def autocorrect(unsorted_stream_node, file_path)
58
- sorted_stream_node = alphabetizer.new(unsorted_stream_node).call
59
- writer.new(sorted_stream_node, file_path).call
60
- logger.log_correction(file_path)
61
- end
62
-
63
- def offences?(stream_node)
64
- offence_detector.new(stream_node).offences?
65
- end
66
- end