yalphabetize 0.1.0 → 0.2.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: 1f090c28c32a6a7248ee2c256ca9d2c166947c113025cf40701471df67de36b0
4
+ data.tar.gz: 42c03996392e0d810bbda8e2c2c49df5915d45ddf749d199722edc9d99105cde
5
5
  SHA512:
6
- metadata.gz: b77dd2c3d7e92e97e7c7d16cdd9359c6bd5b7084fe7006651a03d3416de73429403f17fcbd0b45f2d09225b842cb4bfb83adb9f48519b3ceb32853339e356e48
7
- data.tar.gz: 27495a87a0594f9c33751a7f48e67234b1d7e8067b2e095d5dd90d162237fb309243d0c3c0617795638f1f4b837cf478be1d3969b3ff245668bf45645bca5724
6
+ metadata.gz: bd41579935c1d015927274f68bdb525b0b4c9c713fcdc0aa68fa5b23c6c48c26cbf09f7d94d7343969324e78dedc0c2bd3cf8f9a0769a426ec871001f1478fe7
7
+ data.tar.gz: ed42eecc90264a3d79ac52e08e15b4f177bda3f1f765bff77a94e965f734c937608bc404f5e9123369ec02335d19cba100d6f109c0661cf79294dfb3e2a2b94e
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 'yalphabetize/cli'
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,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yalphabetize/alphabetizer'
4
+ require 'yalphabetize/file_yalphabetizer'
5
+ require 'yalphabetize/logger'
6
+ require 'yalphabetize/offence_detector'
7
+ require 'yalphabetize/reader'
8
+ require 'yalphabetize/writer'
9
+ require 'yalphabetize/yaml_finder'
10
+ require 'yalphabetizer'
11
+
12
+ module Yalphabetize
13
+ class CLI
14
+ def self.call(argv)
15
+ new(argv).call
16
+ end
17
+
18
+ def initialize(argv)
19
+ @argv = argv
20
+ end
21
+
22
+ def call
23
+ Yalphabetizer.call(
24
+ argv,
25
+ reader_class: Yalphabetize::Reader,
26
+ finder: Yalphabetize::YamlFinder.new,
27
+ alphabetizer_class: Yalphabetize::Alphabetizer,
28
+ writer_class: Yalphabetize::Writer,
29
+ offence_detector_class: Yalphabetize::OffenceDetector,
30
+ logger: Yalphabetize::Logger.new($stdout),
31
+ file_yalphabetizer_class: Yalphabetize::FileYalphabetizer
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :argv
38
+ end
39
+ 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.b)
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,63 @@
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
+ end
14
+
15
+ def call
16
+ if offences?
17
+ logger.log_offence(file_path)
18
+ autocorrect_file if autocorrect
19
+ else
20
+ logger.log_no_offence
21
+ end
22
+
23
+ replace_interpolations
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :file_path, :reader_class, :offence_detector_class,
29
+ :logger, :autocorrect, :alphabetizer_class, :writer_class
30
+
31
+ def autocorrect_file
32
+ sorted_stream_node = alphabetizer_class.new(unsorted_stream_node).call
33
+ writer_class.new(sorted_stream_node, file_path).call
34
+ logger.log_correction(file_path)
35
+ end
36
+
37
+ def offences?
38
+ offence_detector_class.new(unsorted_stream_node).offences?
39
+ end
40
+
41
+ def replace_interpolations
42
+ interpolations_mapping.each do |uuid, interpolation|
43
+ file_content = File.read(file_path)
44
+ file_content.sub!(uuid, interpolation)
45
+ File.open(file_path, 'w') do |file|
46
+ file.write file_content
47
+ end
48
+ end
49
+ end
50
+
51
+ def reader
52
+ @_reader ||= reader_class.new(file_path)
53
+ end
54
+
55
+ def unsorted_stream_node
56
+ @_unsorted_stream_node ||= reader.to_ast
57
+ end
58
+
59
+ def interpolations_mapping
60
+ reader.interpolations_mapping
61
+ end
62
+ end
63
+ end
@@ -1,40 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yalphabetize
2
4
  class Logger
3
- def initialize
5
+ def initialize(output)
6
+ @output = output
4
7
  @inspected_count = 0
5
8
  @offences = {}
6
9
  end
7
10
 
8
- def task_summary(file_paths)
9
- puts "Inspecting #{file_paths.size} YAML files"
11
+ def initial_summary(file_paths)
12
+ output.puts "Inspecting #{file_paths.size} YAML files"
10
13
  end
11
14
 
12
15
  def log_offence(file_path)
13
16
  @inspected_count += 1
14
17
  offences[file_path] = :detected
15
- print red 'O'
18
+ output.print red 'O'
16
19
  end
17
20
 
18
21
  def log_no_offence
19
22
  @inspected_count += 1
20
- print green '.'
21
- end
22
-
23
- def log_inspected_count
24
- puts "\nInspected: #{inspected_count}"
23
+ output.print green '.'
25
24
  end
26
25
 
27
- def list_offences
28
- puts send(list_offences_color, "Offences: #{offences.size}")
26
+ def final_summary
27
+ output.puts "\nInspected: #{inspected_count}"
28
+ output.puts send(list_offences_color, "Offences: #{offences.size}")
29
29
 
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
30
+ offences.each { |file_path, status| puts_offence(file_path, status) }
38
31
  end
39
32
 
40
33
  def offences?
@@ -47,7 +40,16 @@ module Yalphabetize
47
40
 
48
41
  private
49
42
 
50
- attr_reader :offences, :inspected_count
43
+ attr_reader :output, :offences, :inspected_count
44
+
45
+ def puts_offence(file_path, status)
46
+ case status
47
+ when :detected
48
+ output.puts yellow file_path
49
+ when :corrected
50
+ output.puts("#{yellow(file_path)} #{green('CORRECTED')}")
51
+ end
52
+ end
51
53
 
52
54
  def list_offences_color
53
55
  if offences?
@@ -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,24 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'psych'
4
+ require 'securerandom'
5
+ require 'yalphabetize/erb_compiler'
6
+ require 'yalphabetize/parsing_error'
4
7
 
5
8
  module Yalphabetize
6
9
  class Reader
7
10
  def initialize(path)
8
11
  @path = path
12
+ @interpolations_mapping = {}
9
13
  end
10
14
 
11
15
  def to_ast
16
+ substitute_interpolations
12
17
  transform(stream_node)
13
18
  stream_node
14
19
  end
15
20
 
21
+ attr_reader :interpolations_mapping
22
+
16
23
  private
17
24
 
18
25
  attr_reader :path
19
26
 
27
+ def substitute_interpolations
28
+ erb_compiler = Yalphabetize::ErbCompiler.new
29
+ erb_compiler.compile(file)
30
+
31
+ erb_compiler.content.each do |interpolation|
32
+ uuid = SecureRandom.uuid
33
+ file.sub!(interpolation, uuid)
34
+ interpolations_mapping[uuid] = interpolation
35
+ end
36
+ end
37
+
38
+ def file
39
+ @_file ||= File.read(path)
40
+ end
41
+
20
42
  def stream_node
21
- @stream_node ||= Psych.parse_stream File.read(path)
43
+ @_stream_node ||= Psych.parse_stream file
44
+ rescue Psych::SyntaxError => e
45
+ raise Yalphabetize::ParsingError.new(
46
+ path,
47
+ e.line,
48
+ e.column,
49
+ e.offset,
50
+ e.problem,
51
+ e.context
52
+ )
22
53
  end
23
54
 
24
55
  def transform(node)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yalphabetize
4
+ class Version
5
+ STRING = '0.2.1'
6
+ end
7
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Yalphabetize
4
4
  class Writer
5
+ MAX_LINE_WIDTH = -1
6
+
5
7
  def initialize(stream_node, path)
6
8
  @stream_node = stream_node
7
9
  @path = path
@@ -9,12 +11,16 @@ module Yalphabetize
9
11
 
10
12
  def call
11
13
  File.open(path, 'w') do |file|
12
- file.write(stream_node.to_yaml)
14
+ file.write new_file_content
13
15
  end
14
16
  end
15
17
 
16
18
  private
17
19
 
18
20
  attr_reader :stream_node, :path
21
+
22
+ def new_file_content
23
+ stream_node.to_yaml nil, line_width: MAX_LINE_WIDTH
24
+ end
19
25
  end
20
26
  end
@@ -1,20 +1,70 @@
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 initialize
10
+ @files = []
11
+ end
8
12
 
9
- output, _error, status = Open3.capture3(
10
- 'git', 'ls-files', '-z', './**/*.yml',
11
- '--exclude-standard', '--others', '--cached', '--modified'
12
- )
13
+ def paths(paths)
14
+ if paths.empty?
15
+ files << files_in_dir
16
+ else
17
+ process_paths(paths)
18
+ end
19
+
20
+ files.flatten.select(&method(:valid?)).map { |f| File.expand_path(f) }.uniq
21
+ end
13
22
 
14
- return unless status.success?
23
+ private
15
24
 
16
- output.split("\0").uniq.map { |git_file| "#{Dir.pwd}/#{git_file}" }
25
+ attr_reader :files
26
+
27
+ def process_paths(paths)
28
+ paths.uniq.each do |path|
29
+ files << if glob?(path)
30
+ files_in_glob(path)
31
+ elsif File.directory?(path)
32
+ files_in_dir(path)
33
+ else
34
+ path
35
+ end
17
36
  end
18
37
  end
38
+
39
+ def files_in_glob(glob)
40
+ files_in_dir([glob, glob.gsub('**', '')].uniq)
41
+ end
42
+
43
+ def files_in_dir(dir = Dir.pwd)
44
+ return if `sh -c 'command -v git'`.empty?
45
+
46
+ output, _error, status = Open3.capture3(
47
+ 'git', 'ls-files', '-z', *Array(dir),
48
+ '--exclude-standard', '--others', '--cached', '--modified'
49
+ )
50
+
51
+ output.split("\0").uniq.map { |git_file| "#{Dir.pwd}/#{git_file}" } if status.success?
52
+ end
53
+
54
+ def valid?(path)
55
+ exists?(path) && yml?(path)
56
+ end
57
+
58
+ def exists?(path)
59
+ File.exist?(path)
60
+ end
61
+
62
+ def yml?(path)
63
+ YAML_EXTENSIONS.include? File.extname(path)
64
+ end
65
+
66
+ def glob?(path)
67
+ path.include?('*')
68
+ end
19
69
  end
20
70
  end
data/lib/yalphabetizer.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
- require 'pry'
5
4
 
6
5
  class Yalphabetizer
7
6
  def self.call(args = [], **options)
@@ -11,56 +10,60 @@ class Yalphabetizer
11
10
  def initialize(args, options)
12
11
  @args = args
13
12
 
14
- @reader = options[:reader]
13
+ @reader_class = options[:reader_class]
15
14
  @finder = options[:finder]
16
- @alphabetizer = options[:alphabetizer]
17
- @writer = options[:writer]
18
- @offence_detector = options[:offence_detector]
15
+ @alphabetizer_class = options[:alphabetizer_class]
16
+ @writer_class = options[:writer_class]
17
+ @offence_detector_class = options[:offence_detector_class]
19
18
  @logger = options[:logger]
19
+ @file_yalphabetizer_class = options[:file_yalphabetizer_class]
20
20
  end
21
21
 
22
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
23
+ initial_log
24
+ process_files
25
+ final_log
26
+ end
27
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
28
+ private
35
29
 
36
- logger.log_inspected_count
37
- logger.list_offences
30
+ attr_reader :args, :reader_class, :finder, :alphabetizer_class, :writer_class,
31
+ :offence_detector_class, :logger, :file_yalphabetizer_class
38
32
 
39
- logger.offences? ? 1 : 0
33
+ def initial_log
34
+ logger.initial_summary(file_paths)
40
35
  end
41
36
 
42
- private
37
+ def process_files
38
+ file_paths.each(&method(:process_file))
39
+ end
43
40
 
44
- attr_reader :args, :reader, :finder, :alphabetizer, :writer, :offence_detector, :logger
41
+ def process_file(file_path)
42
+ file_yalphabetizer_class.new(
43
+ file_path,
44
+ reader_class: reader_class,
45
+ offence_detector_class: offence_detector_class,
46
+ logger: logger,
47
+ autocorrect: autocorrect?,
48
+ alphabetizer_class: alphabetizer_class,
49
+ writer_class: writer_class
50
+ ).call
51
+ end
45
52
 
46
53
  def file_paths
47
- finder.paths
54
+ explicit_paths = args.reject { |arg| arg[0] == '-' }
55
+ finder.paths(explicit_paths)
48
56
  end
49
57
 
50
58
  def autocorrect?
51
59
  return true if args.include? '-a'
52
- return true if args.include? '-autocorrect'
60
+ return true if args.include? '--autocorrect'
53
61
 
54
62
  false
55
63
  end
56
64
 
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
+ def final_log
66
+ logger.final_summary
67
+ logger.offences? ? 1 : 0
65
68
  end
66
69
  end
metadata CHANGED
@@ -1,17 +1,73 @@
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.2.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: 2021-07-11 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: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
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: rspec
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: rubocop
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
+ description:
70
+ email:
15
71
  executables:
16
72
  - yalphabetize
17
73
  extensions: []
@@ -19,16 +75,21 @@ extra_rdoc_files: []
19
75
  files:
20
76
  - bin/yalphabetize
21
77
  - lib/yalphabetize/alphabetizer.rb
78
+ - lib/yalphabetize/cli.rb
79
+ - lib/yalphabetize/erb_compiler.rb
80
+ - lib/yalphabetize/file_yalphabetizer.rb
22
81
  - lib/yalphabetize/logger.rb
23
82
  - lib/yalphabetize/offence_detector.rb
83
+ - lib/yalphabetize/parsing_error.rb
24
84
  - lib/yalphabetize/reader.rb
85
+ - lib/yalphabetize/version.rb
25
86
  - lib/yalphabetize/writer.rb
26
87
  - lib/yalphabetize/yaml_finder.rb
27
88
  - lib/yalphabetizer.rb
28
- homepage:
89
+ homepage:
29
90
  licenses: []
30
91
  metadata: {}
31
- post_install_message:
92
+ post_install_message:
32
93
  rdoc_options: []
33
94
  require_paths:
34
95
  - lib
@@ -43,8 +104,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
104
  - !ruby/object:Gem::Version
44
105
  version: '0'
45
106
  requirements: []
46
- rubygems_version: 3.1.4
47
- signing_key:
107
+ rubygems_version: 3.0.3.1
108
+ signing_key:
48
109
  specification_version: 4
49
110
  summary: Alphabetize your YAML files
50
111
  test_files: []