closing_comments 0.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 +7 -0
- data/bin/closing_comments +40 -0
- data/ensure_version.rb +52 -0
- data/lib/closing_comments.rb +3 -0
- data/lib/closing_comments/commentable.rb +97 -0
- data/lib/closing_comments/processor.rb +70 -0
- data/lib/closing_comments/source.rb +133 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3db284866963dbbf428bd2ba95eea36824e3b1b2
|
4
|
+
data.tar.gz: d194e7ac4fc744fda9df95cfb01ae078f8a187db
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3237f635443fdcd056454dd5295f1eeab4a1c54650050136657e7f0b02d51697919f32302c4d0ec629d01c73bf8fc7aece58729a13ca8938e9c5e797661a317c
|
7
|
+
data.tar.gz: 8a95866c78a9114ed6e69354e8400ce9b0ee28a7b2c321cfab30ffdbbb02a38cd6c58da3a4586bf4e5666411af01f5159f8f6dca2428cf1209e1dc2e4b54a026
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
5
|
+
require 'optparse'
|
6
|
+
require 'closing_comments'
|
7
|
+
|
8
|
+
module ClosingComments
|
9
|
+
class App
|
10
|
+
def initialize(args)
|
11
|
+
@args = args
|
12
|
+
@processor = options.fetch(:processor, Processor::Reporter.new)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
@args = ['.'] if @args.empty?
|
17
|
+
@args.each(&method(:process))
|
18
|
+
@processor.report
|
19
|
+
Kernel.exit(@processor.success? ? 0 : 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def process(path)
|
23
|
+
return @processor.process(path: path) if File.file?(path)
|
24
|
+
Dir.glob(File.join(path, '/**/*.rb')).each(&method(:process))
|
25
|
+
end
|
26
|
+
|
27
|
+
def options
|
28
|
+
{}.tap do |options|
|
29
|
+
OptionParser.new do |opts|
|
30
|
+
opts.banner = 'Usage: closing_comments [options]'
|
31
|
+
opts.on('-a', '--auto-fix', 'Automatically fix issues') do
|
32
|
+
options[:processor] = Processor::Fixer.new
|
33
|
+
end
|
34
|
+
end.parse!(@args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end # class App
|
38
|
+
end # module ClosingComments
|
39
|
+
|
40
|
+
ClosingComments::App.new(ARGV).run
|
data/ensure_version.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
require 'colorize'
|
6
|
+
|
7
|
+
class EnsureVersion
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def call
|
11
|
+
if compare
|
12
|
+
puts "#{'[OK]'.green} Local version (#{local_version}) higher than " \
|
13
|
+
"remote (#{remote_version})."
|
14
|
+
Kernel.exit(0)
|
15
|
+
end
|
16
|
+
puts "#{'[FAIL]'.red} Local version (#{local_version}) not higher than " \
|
17
|
+
"remote (#{remote_version}), please bump."
|
18
|
+
Kernel.exit(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def compare
|
24
|
+
local_version > remote_version
|
25
|
+
end
|
26
|
+
|
27
|
+
def local_version
|
28
|
+
Gem.loaded_specs[name].version
|
29
|
+
end
|
30
|
+
|
31
|
+
def remote_version
|
32
|
+
@remote_version ||= begin
|
33
|
+
version = JSON.parse(rubygems_response.body).fetch('version')
|
34
|
+
Gem::Version.new(version == 'unknown' ? 0 : version)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# This method reeks of :reek:FeatureEnvy (url).
|
39
|
+
def rubygems_response
|
40
|
+
url = URI.parse("https://rubygems.org/api/v1/versions/#{name}/latest.json")
|
41
|
+
req = Net::HTTP::Get.new(url.to_s)
|
42
|
+
Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
|
43
|
+
http.request(req)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def name
|
48
|
+
'closing_comments'
|
49
|
+
end
|
50
|
+
end # class EnsureVersion
|
51
|
+
|
52
|
+
EnsureVersion.instance.call if __FILE__ == $PROGRAM_NAME
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module ClosingComments
|
5
|
+
class Commentable
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@node, :children, :loc
|
9
|
+
def_delegators :loc, :first_line, :last_line
|
10
|
+
|
11
|
+
def initialize(node)
|
12
|
+
@node = node
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
@name ||= name!(children.first)
|
17
|
+
end
|
18
|
+
|
19
|
+
def single_line?
|
20
|
+
first_line == last_line
|
21
|
+
end
|
22
|
+
|
23
|
+
def ending
|
24
|
+
"end # #{entity} #{name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# This method reeks of :reek:FeatureEnvy and :reek:TooManyStatements.
|
30
|
+
def name!(node)
|
31
|
+
return node unless node.is_a?(Parser::AST::Node)
|
32
|
+
type = node.type
|
33
|
+
return '' if type == :cbase
|
34
|
+
first, second = node.children
|
35
|
+
if type == :str
|
36
|
+
loc = node.loc
|
37
|
+
# Preserve quote formatting, some folks may prefer double quotes.
|
38
|
+
return "#{loc.begin.source}#{first}#{loc.end.source}"
|
39
|
+
end
|
40
|
+
first ? [name!(first), second].join('::') : second
|
41
|
+
end
|
42
|
+
|
43
|
+
class Class < Commentable
|
44
|
+
def entity
|
45
|
+
'class'
|
46
|
+
end
|
47
|
+
end # class Class
|
48
|
+
|
49
|
+
class Module < Commentable
|
50
|
+
def entity
|
51
|
+
'module'
|
52
|
+
end
|
53
|
+
end # class Module
|
54
|
+
|
55
|
+
class Block < Commentable
|
56
|
+
RSPEC = %i[context describe shared_context shared_examples].freeze
|
57
|
+
private_constant :RSPEC
|
58
|
+
|
59
|
+
def commentable?
|
60
|
+
dispatch.type == :send && rspec?
|
61
|
+
end
|
62
|
+
|
63
|
+
def name
|
64
|
+
name!(dispatch.children[2])
|
65
|
+
end
|
66
|
+
|
67
|
+
def entity
|
68
|
+
return message if implicit_receiver?
|
69
|
+
[name!(receiver), message].join('.')
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def rspec?
|
75
|
+
return false unless implicit_receiver? || name!(receiver) == :RSpec
|
76
|
+
RSPEC.include?(message)
|
77
|
+
end
|
78
|
+
|
79
|
+
def dispatch
|
80
|
+
children.first
|
81
|
+
end
|
82
|
+
|
83
|
+
def receiver
|
84
|
+
dispatch.children.first
|
85
|
+
end
|
86
|
+
|
87
|
+
# This method reeks of :reek:NilCheck.
|
88
|
+
def implicit_receiver?
|
89
|
+
receiver.nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
def message
|
93
|
+
dispatch.children[1]
|
94
|
+
end
|
95
|
+
end # class Block
|
96
|
+
end # class Commentable
|
97
|
+
end # module ClosingComments
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
module ClosingComments
|
4
|
+
class Processor
|
5
|
+
def initialize
|
6
|
+
@reportables = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# TODO(marcinw): catch parsing errors;
|
10
|
+
def process(path:)
|
11
|
+
source = Source.from(path: path)
|
12
|
+
handle(source)
|
13
|
+
@reportables[path] = source if source.problematic?
|
14
|
+
end
|
15
|
+
|
16
|
+
def success?
|
17
|
+
@reportables.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :reportables
|
23
|
+
|
24
|
+
class Reporter < Processor
|
25
|
+
def handle(source)
|
26
|
+
print source.problematic? ? 'F'.red : '.'.green
|
27
|
+
end
|
28
|
+
|
29
|
+
def report
|
30
|
+
puts("\n\n")
|
31
|
+
return puts 'All good!'.green if reportables.empty?
|
32
|
+
puts "Problems #{action} in #{reportables.count} files:\n".red
|
33
|
+
reportables.each(&method(:report_file))
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def action
|
39
|
+
'found'
|
40
|
+
end
|
41
|
+
|
42
|
+
def report_file(path, source)
|
43
|
+
puts "#{path}:"
|
44
|
+
source.problems.sort_by(&:line).each do |problem|
|
45
|
+
puts " #{problem.line}:#{problem.column} #{problem.message}"
|
46
|
+
end
|
47
|
+
puts('')
|
48
|
+
end
|
49
|
+
end # class Reporter
|
50
|
+
|
51
|
+
class Fixer < Reporter
|
52
|
+
# This method reeks of :reek:FeatureEnvy.
|
53
|
+
def handle(source)
|
54
|
+
return super unless source.problematic?
|
55
|
+
File.write(source.path, source.fix)
|
56
|
+
print 'A'.yellow
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def action
|
62
|
+
'fixed'
|
63
|
+
end
|
64
|
+
end # class Fixer
|
65
|
+
|
66
|
+
class JSON < Processor
|
67
|
+
def handle(_); end # Don't stare, I'm just implementing an API, ok?
|
68
|
+
end # class JSON
|
69
|
+
end # class Processor
|
70
|
+
end # module ClosingComments
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'parser/ruby24'
|
3
|
+
|
4
|
+
module ClosingComments
|
5
|
+
class Source
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :path, :content
|
9
|
+
def_delegators :visitor, :entities
|
10
|
+
|
11
|
+
def self.from(path:)
|
12
|
+
new(path: path, content: File.read(path))
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(path:, content:)
|
16
|
+
@path = path
|
17
|
+
@content = content
|
18
|
+
end
|
19
|
+
|
20
|
+
def problematic?
|
21
|
+
problems.count.positive?
|
22
|
+
end
|
23
|
+
|
24
|
+
def problems
|
25
|
+
@problems ||=
|
26
|
+
entities
|
27
|
+
.reject(&:single_line?)
|
28
|
+
.reject(&method(:commented?))
|
29
|
+
.map { |ent| Problem.new(ent, line(number: ent.last_line)) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def fix
|
33
|
+
lines.map.with_index(1) { |default, no| fixes[no] || default }.join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def fixes
|
39
|
+
@fixes ||=
|
40
|
+
problems
|
41
|
+
.map { |problem| [problem.line, problem.fix] }
|
42
|
+
.to_h
|
43
|
+
end
|
44
|
+
|
45
|
+
def line(number:)
|
46
|
+
lines[number - 1]
|
47
|
+
end
|
48
|
+
|
49
|
+
def lines
|
50
|
+
@lines ||= content.split("\n").push('')
|
51
|
+
end
|
52
|
+
|
53
|
+
# This method reeks of :reek:FeatureEnvy (entity).
|
54
|
+
def commented?(entity)
|
55
|
+
line(number: entity.last_line).end_with?(entity.ending)
|
56
|
+
end
|
57
|
+
|
58
|
+
def visitor
|
59
|
+
@visitor ||= visitor_class.new(content)
|
60
|
+
end
|
61
|
+
|
62
|
+
def visitor_class
|
63
|
+
path.end_with?('_spec.rb') ? Visitor::Spec : Visitor
|
64
|
+
end
|
65
|
+
|
66
|
+
class Problem
|
67
|
+
def initialize(entity, line)
|
68
|
+
@entity = entity
|
69
|
+
@line = line
|
70
|
+
end
|
71
|
+
|
72
|
+
def line
|
73
|
+
@entity.last_line
|
74
|
+
end
|
75
|
+
|
76
|
+
def column
|
77
|
+
@line.index('end')
|
78
|
+
end
|
79
|
+
|
80
|
+
def fix
|
81
|
+
@line[0...column] + @entity.ending
|
82
|
+
end
|
83
|
+
|
84
|
+
def message
|
85
|
+
"missing #{@entity.entity} closing comment"
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_h
|
89
|
+
{
|
90
|
+
line: line,
|
91
|
+
column: column,
|
92
|
+
message: message,
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end # class Problem
|
96
|
+
|
97
|
+
class Visitor
|
98
|
+
attr_reader :entities
|
99
|
+
|
100
|
+
def initialize(content)
|
101
|
+
@entities = []
|
102
|
+
recursively_visit(Parser::Ruby24.parse(content))
|
103
|
+
entities.freeze
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def recursively_visit(node)
|
109
|
+
return unless node.is_a?(Parser::AST::Node)
|
110
|
+
visit_current(node)
|
111
|
+
node.children.each(&method(:recursively_visit))
|
112
|
+
end
|
113
|
+
|
114
|
+
def visit_current(node)
|
115
|
+
case node.type
|
116
|
+
when :class then entities << Commentable::Class.new(node)
|
117
|
+
when :module then entities << Commentable::Module.new(node)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Spec < Visitor
|
122
|
+
private
|
123
|
+
|
124
|
+
def visit_current(node)
|
125
|
+
return super unless node.type == :block
|
126
|
+
block = Commentable::Block.new(node)
|
127
|
+
entities << block if block.commentable?
|
128
|
+
end
|
129
|
+
end # class Spec
|
130
|
+
end # class Visitor
|
131
|
+
private_constant :Visitor
|
132
|
+
end # class Source
|
133
|
+
end # module ClosingComments
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: closing_comments
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marcin Wyszynski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: parser
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.14.6
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '1.14'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 1.14.6
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rake
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '12.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '12.0'
|
75
|
+
description: Helper to add closing comments to Ruby entities
|
76
|
+
email:
|
77
|
+
executables:
|
78
|
+
- closing_comments
|
79
|
+
extensions: []
|
80
|
+
extra_rdoc_files: []
|
81
|
+
files:
|
82
|
+
- bin/closing_comments
|
83
|
+
- ensure_version.rb
|
84
|
+
- lib/closing_comments.rb
|
85
|
+
- lib/closing_comments/commentable.rb
|
86
|
+
- lib/closing_comments/processor.rb
|
87
|
+
- lib/closing_comments/source.rb
|
88
|
+
homepage: https://github.com/marcinwyszynski/closing_comments
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.6.12
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Helper to add closing comments to Ruby entities
|
112
|
+
test_files: []
|