yalphabetize 0.1.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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