debtective 0.2.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: 609f9971e421988a317405b19b0cb8aacc172ee246e29d2484d290f6fb6649ab
4
- data.tar.gz: b084f3772dfb519af3a3e254fa04f576d0edcbe59961841192cd5370c2daf7ac
3
+ metadata.gz: 3a9c75632f4f3a8785af1c96209c22b68772f8bde253cbd76e2bdb774a3812a8
4
+ data.tar.gz: d4b50399a0806ad8e4a6fb20305429882847ef79dd42bd176d17be3f5f1df786
5
5
  SHA512:
6
- metadata.gz: 84a8a437407fba597289be78946a41094d34cca1eeb27fed496c010e0d062adba149f47832f2e9c558d92d9b0b23e13bd2fbd551168761eafeb02757c7816861
7
- data.tar.gz: 0fb7f4326d4e3e9ee29c54117287f4eb3f9fd74083e0706b99bf2b30e68e31ef4b4f865511a80857f3b5aea49a2000f14d1f214427cdf3078115fb08b2be470a
6
+ metadata.gz: 59b68258f51e5f7a83992c8d24d0bc028fcb7dcdea2f379d0af011a8ec9c5dba645d6ffef4f94652d3ce7f0662185597934b96a6c8d2fa74ccc392b027b207fe
7
+ data.tar.gz: a5556a654c194fc442727c67f6c06686c6df851fe4008540670603d5aac8203e67e96a62d498b0eb57b91bdae76a2c967d789b29709659b84d3178632e7b893f
data/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # Debtective
2
2
 
3
- Help find out todos in your application.
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
- - `bundle exec rake debtective:todo_list`
9
+ ```bash
10
+ bundle exec rake debtective:todo_list
11
+ ```
10
12
 
11
- More tasks to come!
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 'debtective'
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 end of a block given its first line and the next lines
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
- # With a single line (no block)
25
- #
26
- # 0 do_this <--
27
- # 1 do_that
28
- # 2 end
29
- #
30
- # Then the end is the first line (index 0)
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
- def initialize(lines)
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
- # ends of the statement
28
+ # index of the line ending the statement
41
29
  # @return [Integer]
42
30
  def call
43
- @lines[1..]&.index { end_of_statement?(_1) } || 0
31
+ suppress_stderr do
32
+ last_line_index || @first_line_index
33
+ end
44
34
  end
45
35
 
46
36
  private
47
37
 
48
- # indent (number of spaces) of the first line | for memoization
49
- # @return [Integer]
50
- def first_line_indent
51
- @first_line_indent ||= indent(@lines[0])
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
- # return true if the line is the end of the statement
55
- # @return [Boolean]
56
- def end_of_statement?(line)
57
- return false if line.match?(NON_ENDING_KEYWORDS_REGEX)
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
- indent(line) <= first_line_indent
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
- # returns the indent (number of spaces) of the line
63
- # @param line [String]
64
- # @return [Integer]
65
- def indent(line)
66
- line.match(/^(\s*).*$/)[1].length
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*#\stodo:\s/i
11
- INLINE_TODO_REGEX = /\s*#\stodo:\s/i
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
- case line
22
- when BEFORE_LINE_TODO_REGEX
23
- Result.new(@pathname, boundaries(index))
24
- when INLINE_TODO_REGEX
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 index [Integer]
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 end_line(index)
47
- next_lines = lines[(index + 1)..]
48
- EndOfStatement.new(next_lines).call
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
@@ -27,7 +27,7 @@ module Debtective
27
27
  Debtective.configuration&.paths || DEFAULT_PATHS
28
28
  end
29
29
 
30
- # @return [Array<Pathname>] only path to ruby files
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] }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Debtective
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debtective/report"
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::Report.new.call
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.0
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-07 00:00:00.000000000 Z
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
@@ -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