yalphabetize 0.1.0 → 0.2.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: 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: []