debtective 0.2.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 +4 -4
- data/README.md +17 -5
- data/lib/debtective/end_of_statement.rb +57 -45
- data/lib/debtective/file_todos.rb +30 -20
- data/lib/debtective/todo_list.rb +1 -1
- data/lib/debtective/version.rb +1 -1
- data/lib/tasks/debtective/todo_list.rake +48 -2
- metadata +16 -3
- data/lib/debtective/report.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a9c75632f4f3a8785af1c96209c22b68772f8bde253cbd76e2bdb774a3812a8
|
4
|
+
data.tar.gz: d4b50399a0806ad8e4a6fb20305429882847ef79dd42bd176d17be3f5f1df786
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59b68258f51e5f7a83992c8d24d0bc028fcb7dcdea2f379d0af011a8ec9c5dba645d6ffef4f94652d3ce7f0662185597934b96a6c8d2fa74ccc392b027b207fe
|
7
|
+
data.tar.gz: a5556a654c194fc442727c67f6c06686c6df851fe4008540670603d5aac8203e67e96a62d498b0eb57b91bdae76a2c967d789b29709659b84d3178632e7b893f
|
data/README.md
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
# Debtective
|
2
2
|
|
3
|
-
|
3
|
+
Find todos in your codebase so you don't forget to pay off your debts! 💰
|
4
4
|
|
5
5
|
## Usage
|
6
6
|
|
7
|
-
Run the task:
|
7
|
+
Run the task with:
|
8
8
|
|
9
|
-
|
9
|
+
```bash
|
10
|
+
bundle exec rake debtective:todo_list
|
11
|
+
```
|
10
12
|
|
11
|
-
|
13
|
+
It outputs the todos positions with the concerned lines of code and some counts.
|
12
14
|
|
13
15
|
## Installation
|
14
16
|
|
@@ -16,7 +18,7 @@ Add this line to your application's Gemfile:
|
|
16
18
|
|
17
19
|
```ruby
|
18
20
|
group :development do
|
19
|
-
gem
|
21
|
+
gem "debtective"
|
20
22
|
end
|
21
23
|
```
|
22
24
|
|
@@ -32,6 +34,16 @@ Or install it yourself as:
|
|
32
34
|
$ gem install debtective
|
33
35
|
```
|
34
36
|
|
37
|
+
Configure the paths that should be analyzed (all by default):
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# config/initializers/debtective.rb
|
41
|
+
|
42
|
+
Debtective.configure do |config|
|
43
|
+
config.paths = ["app/**/*", "lib/**/*"]
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
35
47
|
## Contributing
|
36
48
|
|
37
49
|
This gem is still a work in progress. You can use GitHub issue to start a discussion.
|
@@ -1,69 +1,81 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "parser/current"
|
4
|
+
|
3
5
|
module Debtective
|
4
|
-
# Find
|
5
|
-
#
|
6
|
-
# With a class, a def, an if statement... or any "block statement"
|
7
|
-
#
|
8
|
-
# 0 def foo
|
9
|
-
# 1 do_this <--
|
10
|
-
# 2 end
|
11
|
-
# 3 end
|
12
|
-
#
|
13
|
-
# The end of the block is the last line before the `end` keyword (index 1)
|
14
|
-
#
|
15
|
-
# With a carriage return
|
16
|
-
#
|
17
|
-
# 0 User
|
18
|
-
# 1 .where(draft: true)
|
19
|
-
# 2 .preload(:tasks) <--
|
20
|
-
# 3 end
|
21
|
-
#
|
22
|
-
# The end of the block is the last line of the statement (index 2)
|
6
|
+
# Find the index of the line ending a statement
|
23
7
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
8
|
+
# EndOfStatement.new(
|
9
|
+
# [
|
10
|
+
# "class User",
|
11
|
+
# " def example",
|
12
|
+
# " x + y",
|
13
|
+
# " end"
|
14
|
+
# "end"
|
15
|
+
# ],
|
16
|
+
# 1
|
17
|
+
# ).call
|
18
|
+
# => 3
|
31
19
|
#
|
32
20
|
class EndOfStatement
|
33
|
-
NON_ENDING_KEYWORDS_REGEX = /^(\s*)(else$|elsif\s|(rescue(\s|$))|ensure)/
|
34
|
-
|
35
21
|
# @param lines [Array<String>] lines of code
|
36
|
-
|
22
|
+
# @param index [Integer] index of the statement first line
|
23
|
+
def initialize(lines, first_line_index)
|
37
24
|
@lines = lines
|
25
|
+
@first_line_index = first_line_index
|
38
26
|
end
|
39
27
|
|
40
|
-
#
|
28
|
+
# index of the line ending the statement
|
41
29
|
# @return [Integer]
|
42
30
|
def call
|
43
|
-
|
31
|
+
suppress_stderr do
|
32
|
+
last_line_index || @first_line_index
|
33
|
+
end
|
44
34
|
end
|
45
35
|
|
46
36
|
private
|
47
37
|
|
48
|
-
#
|
49
|
-
# @return
|
50
|
-
|
51
|
-
|
38
|
+
# index of the line ending the statement
|
39
|
+
# @return[Integer, void]
|
40
|
+
# @note it is possible that no line ends the statement
|
41
|
+
# especially if first line is not the start of a statement
|
42
|
+
def last_line_index
|
43
|
+
@lines.index.with_index do |_line, index|
|
44
|
+
index >= @first_line_index &&
|
45
|
+
statement?(index) &&
|
46
|
+
!chained?(index)
|
47
|
+
end
|
52
48
|
end
|
53
49
|
|
54
|
-
#
|
55
|
-
#
|
56
|
-
|
57
|
-
|
50
|
+
# check if the code from first index to given index is a statement
|
51
|
+
# e.i. can be parsed by a ruby parser (using whitequark/parser)
|
52
|
+
# @param index [Integer]
|
53
|
+
# @return boolean
|
54
|
+
def statement?(index)
|
55
|
+
code = @lines[@first_line_index..index].join("\n")
|
56
|
+
::Parser::CurrentRuby.parse(code)
|
57
|
+
true
|
58
|
+
rescue Parser::SyntaxError
|
59
|
+
false
|
60
|
+
end
|
58
61
|
|
59
|
-
|
62
|
+
# check if current line is chained
|
63
|
+
# e.i. next line start with a .
|
64
|
+
# @param index [Integer]
|
65
|
+
# @return boolean
|
66
|
+
def chained?(index)
|
67
|
+
@lines[index + 1]&.match?(/^(\s*)\./)
|
60
68
|
end
|
61
69
|
|
62
|
-
#
|
63
|
-
#
|
64
|
-
# @return
|
65
|
-
def
|
66
|
-
|
70
|
+
# silence the $stderr
|
71
|
+
# to avoid logs from the parser
|
72
|
+
# @return void
|
73
|
+
def suppress_stderr
|
74
|
+
original_stderr = $stderr.clone
|
75
|
+
$stderr.reopen(File.new("/dev/null", "w"))
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
$stderr.reopen(original_stderr)
|
67
79
|
end
|
68
80
|
end
|
69
81
|
end
|
@@ -5,10 +5,11 @@ require "debtective/end_of_statement"
|
|
5
5
|
module Debtective
|
6
6
|
# Find the todos comments and their boundaries
|
7
7
|
class FileTodos
|
8
|
-
Result = Struct.new(:pathname, :boundaries)
|
8
|
+
Result = Struct.new(:pathname, :todo_index, :boundaries)
|
9
9
|
|
10
|
-
BEFORE_LINE_TODO_REGEX = /^\s*#\
|
11
|
-
INLINE_TODO_REGEX = /\s*#\
|
10
|
+
BEFORE_LINE_TODO_REGEX = /^\s*#\sTODO:\s/
|
11
|
+
INLINE_TODO_REGEX = /\s*#\sTODO:\s/
|
12
|
+
COMMENT_REGEX = /\s*#/
|
12
13
|
|
13
14
|
# @param pathname [Pathname]
|
14
15
|
def initialize(pathname)
|
@@ -18,34 +19,43 @@ module Debtective
|
|
18
19
|
# @return [Array<FileTodos::Result>]
|
19
20
|
def call
|
20
21
|
lines.filter_map.with_index do |line, index|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
Result.new(@pathname, index..index)
|
26
|
-
end
|
22
|
+
boundaries = boundaries(line, index)
|
23
|
+
next if boundaries.nil?
|
24
|
+
|
25
|
+
Result.new(@pathname, index, boundaries)
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
30
29
|
private
|
31
30
|
|
31
|
+
# return todo boundaries if there is a todo
|
32
|
+
# @param line [String]
|
33
|
+
# @param index [Integer]
|
34
|
+
# @return [Range, nil]
|
35
|
+
def boundaries(line, index)
|
36
|
+
case line
|
37
|
+
when BEFORE_LINE_TODO_REGEX
|
38
|
+
first_line_index = statement_first_line_index(index)
|
39
|
+
last_line_index = EndOfStatement.new(@lines, first_line_index).call
|
40
|
+
first_line_index..last_line_index
|
41
|
+
when INLINE_TODO_REGEX
|
42
|
+
index..index
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
32
46
|
# @return [Array<String>]
|
33
47
|
def lines
|
34
48
|
@lines ||= @pathname.readlines
|
35
49
|
end
|
36
50
|
|
37
|
-
# @param
|
38
|
-
# @return [Boundaries]
|
39
|
-
def boundaries(index)
|
40
|
-
offset = index + 2
|
41
|
-
offset..end_line(index) + offset
|
42
|
-
end
|
43
|
-
|
44
|
-
# @param index [Integer]
|
51
|
+
# @param todo_index [Integer]
|
45
52
|
# @return [Integer]
|
46
|
-
def
|
47
|
-
|
48
|
-
|
53
|
+
def statement_first_line_index(todo_index)
|
54
|
+
@lines.index.with_index do |line, i|
|
55
|
+
i > todo_index &&
|
56
|
+
!line.strip.empty? &&
|
57
|
+
!line.match?(COMMENT_REGEX)
|
58
|
+
end
|
49
59
|
end
|
50
60
|
end
|
51
61
|
end
|
data/lib/debtective/todo_list.rb
CHANGED
@@ -27,7 +27,7 @@ module Debtective
|
|
27
27
|
Debtective.configuration&.paths || DEFAULT_PATHS
|
28
28
|
end
|
29
29
|
|
30
|
-
# @return [Array<Pathname>] only
|
30
|
+
# @return [Array<Pathname>] only pathes to ruby files
|
31
31
|
def ruby_pathnames
|
32
32
|
paths
|
33
33
|
.flat_map { File.extname(_1) == "" ? Dir[_1] : [_1] }
|
data/lib/debtective/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "debtective/
|
3
|
+
require "debtective/todo_list"
|
4
4
|
|
5
5
|
begin
|
6
6
|
require "#{Rails.root}/config/initializers/debtective" if defined?(Rails)
|
@@ -11,6 +11,52 @@ end
|
|
11
11
|
namespace :debtective do
|
12
12
|
desc "Todo List"
|
13
13
|
task :todo_list do
|
14
|
-
Debtective::
|
14
|
+
todo_list = Debtective::TodoList.new.call
|
15
|
+
todos = todo_list.todos.sort_by { _1.boundaries.size }.reverse
|
16
|
+
|
17
|
+
separator = Array.new(120 + 3 + 12 + 3 + 12) { "-" }.join
|
18
|
+
|
19
|
+
puts separator
|
20
|
+
|
21
|
+
puts "todo list"
|
22
|
+
|
23
|
+
puts separator
|
24
|
+
|
25
|
+
puts(
|
26
|
+
[
|
27
|
+
"pathname".ljust(120),
|
28
|
+
"lines".rjust(12)
|
29
|
+
].join(" | ")
|
30
|
+
)
|
31
|
+
|
32
|
+
puts separator
|
33
|
+
|
34
|
+
todos.each do |todo|
|
35
|
+
location = "#{todo.pathname}:#{todo.todo_index + 1}".ljust(120)
|
36
|
+
puts(
|
37
|
+
[
|
38
|
+
location,
|
39
|
+
[todo.boundaries.first + 1, todo.boundaries.last + 1].join(":").rjust(12),
|
40
|
+
todo.boundaries.size.to_s.rjust(12)
|
41
|
+
].join(" | ")
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
puts separator
|
46
|
+
|
47
|
+
puts "todos count"
|
48
|
+
puts todos.count
|
49
|
+
|
50
|
+
puts separator
|
51
|
+
|
52
|
+
puts "combined lines count"
|
53
|
+
puts todo_list.combined_count
|
54
|
+
|
55
|
+
puts separator
|
56
|
+
|
57
|
+
puts "extended lines count"
|
58
|
+
puts todo_list.extended_count
|
59
|
+
|
60
|
+
puts separator
|
15
61
|
end
|
16
62
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debtective
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edouard Piron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parser
|
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'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rake
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,7 +123,6 @@ files:
|
|
109
123
|
- lib/debtective/end_of_statement.rb
|
110
124
|
- lib/debtective/file_todos.rb
|
111
125
|
- lib/debtective/railtie.rb
|
112
|
-
- lib/debtective/report.rb
|
113
126
|
- lib/debtective/todo_list.rb
|
114
127
|
- lib/debtective/version.rb
|
115
128
|
- lib/tasks/debtective/todo_list.rake
|
data/lib/debtective/report.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "debtective/todo_list"
|
4
|
-
|
5
|
-
module Debtective
|
6
|
-
# Output information about the debt
|
7
|
-
class Report
|
8
|
-
# output todo list
|
9
|
-
def call
|
10
|
-
puts_separator
|
11
|
-
puts "todo list"
|
12
|
-
puts_separator
|
13
|
-
puts table_header
|
14
|
-
puts_separator
|
15
|
-
puts_table_content
|
16
|
-
puts_separator
|
17
|
-
puts_todos_count
|
18
|
-
puts_separator
|
19
|
-
puts_combines_lines_count
|
20
|
-
puts_separator
|
21
|
-
puts_extended_lines_count
|
22
|
-
puts_separator
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# @return [TodoList::Result]
|
28
|
-
def todo_list
|
29
|
-
@todo_list ||= Debtective::TodoList.new.call
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Array<FileTodos::Result>]
|
33
|
-
def todos
|
34
|
-
@todos ||= todo_list.todos.sort_by { _1.boundaries.size }.reverse
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [String]
|
38
|
-
def puts_separator
|
39
|
-
puts Array.new(table_header.length) { "-" }.join
|
40
|
-
end
|
41
|
-
|
42
|
-
# @return [void]
|
43
|
-
def table_header
|
44
|
-
@table_header ||= [
|
45
|
-
"pathname".ljust(120),
|
46
|
-
"boundaries".rjust(12),
|
47
|
-
"lines".rjust(12)
|
48
|
-
].join(" | ")
|
49
|
-
end
|
50
|
-
|
51
|
-
# @return [void]
|
52
|
-
def puts_table_content
|
53
|
-
todos.each do |todo|
|
54
|
-
puts(
|
55
|
-
[
|
56
|
-
todo.pathname.to_s.ljust(120),
|
57
|
-
todo.boundaries.to_s.rjust(12),
|
58
|
-
todo.boundaries.size.to_s.rjust(12)
|
59
|
-
].join(" | ")
|
60
|
-
)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# @return [void]
|
65
|
-
def puts_todos_count
|
66
|
-
puts "todos count"
|
67
|
-
puts todos.count
|
68
|
-
end
|
69
|
-
|
70
|
-
# @return [void]
|
71
|
-
def puts_combines_lines_count
|
72
|
-
puts "combined lines count"
|
73
|
-
puts todo_list.combined_count
|
74
|
-
end
|
75
|
-
|
76
|
-
# @return [void]
|
77
|
-
def puts_extended_lines_count
|
78
|
-
puts "extended lines count"
|
79
|
-
puts todo_list.extended_count
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|