debtective 0.2.1 → 0.2.3

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: 3a9c75632f4f3a8785af1c96209c22b68772f8bde253cbd76e2bdb774a3812a8
4
- data.tar.gz: d4b50399a0806ad8e4a6fb20305429882847ef79dd42bd176d17be3f5f1df786
3
+ metadata.gz: 4c9cd2c9eaaa35023797d69a854fd1b749269c03e029a1fd9447b993d653b917
4
+ data.tar.gz: 7b3cff2afb3474d1ec0ba8449f88e4795f91d35c4b49af4337e9eb64693443e2
5
5
  SHA512:
6
- metadata.gz: 59b68258f51e5f7a83992c8d24d0bc028fcb7dcdea2f379d0af011a8ec9c5dba645d6ffef4f94652d3ce7f0662185597934b96a6c8d2fa74ccc392b027b207fe
7
- data.tar.gz: a5556a654c194fc442727c67f6c06686c6df851fe4008540670603d5aac8203e67e96a62d498b0eb57b91bdae76a2c967d789b29709659b84d3178632e7b893f
6
+ metadata.gz: 3ad250e7e42470f6404de2eaff106319c5c7f6f543276e02ef28afba2a6fc301f3bb35ecea069737e21f5ceeb70126fc968e70118f3f4196f75eeebd0ad1c724
7
+ data.tar.gz: d648747191b567134e238e4692014ef211c5715bd5524245e524626de5b8ee8fd87979641a6a3238755a2512171266081072c9ca3a5fc47f3b16d7ce7084f752
data/bin/debtective ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "debtective"
5
+ require "debtective/output_todos"
6
+
7
+ Debtective::OutputTodos.new.call
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/todo"
4
+ require "debtective/find_end_of_statement"
5
+
6
+ module Debtective
7
+ # Find the todos comments and their boundaries
8
+ class BuildTodo
9
+ BEFORE_LINE_TODO_REGEX = /^\s*#\sTODO:\s/
10
+ INLINE_TODO_REGEX = /\s*#\sTODO:\s/
11
+ COMMENT_REGEX = /\s*#/
12
+
13
+ # @param pathname [Pathname]
14
+ # @param index [Integer]
15
+ def initialize(pathname, index)
16
+ @pathname = pathname
17
+ @index = index
18
+ end
19
+
20
+ # @return [Debtective::Todo]
21
+ def call
22
+ Todo.new(
23
+ @pathname,
24
+ lines,
25
+ todo_boundaries,
26
+ statement_boundaries
27
+ )
28
+ end
29
+
30
+ private
31
+
32
+ # @return [Array<String>]
33
+ def lines
34
+ @lines ||= @pathname.readlines
35
+ end
36
+
37
+ # @return [Range]
38
+ def todo_boundaries
39
+ @index..([@index, statement_boundaries.min - 1].max)
40
+ end
41
+
42
+ # range of the concerned code
43
+ # @return [Range]
44
+ def statement_boundaries
45
+ @statement_boundaries ||=
46
+ case lines[@index]
47
+ when BEFORE_LINE_TODO_REGEX
48
+ first_line_index = statement_start
49
+ last_line_index = FindEndOfStatement.new(lines, first_line_index).call
50
+ first_line_index..last_line_index
51
+ when INLINE_TODO_REGEX
52
+ @index..@index
53
+ end
54
+ end
55
+
56
+ # start index of the concerned code (i.e. below the TODO comment)
57
+ # @return [Integer]
58
+ def statement_start
59
+ lines.index.with_index do |line, i|
60
+ i > @index &&
61
+ !line.strip.empty? &&
62
+ !line.match?(COMMENT_REGEX)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,23 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parser/current"
3
+ require "debtective/stderr_helper"
4
4
 
5
5
  module Debtective
6
6
  # Find the index of the line ending a statement
7
7
  #
8
- # EndOfStatement.new(
9
- # [
10
- # "class User",
11
- # " def example",
12
- # " x + y",
13
- # " end"
14
- # "end"
15
- # ],
16
- # 1
17
- # ).call
18
- # => 3
8
+ # @example
9
+ # FindEndOfStatement.new(
10
+ # [
11
+ # "class User",
12
+ # " def example",
13
+ # " x + y",
14
+ # " end"
15
+ # "end"
16
+ # ],
17
+ # 1
18
+ # ).call
19
+ # => 3
19
20
  #
20
- class EndOfStatement
21
+ class FindEndOfStatement
22
+ include StderrHelper
23
+
21
24
  # @param lines [Array<String>] lines of code
22
25
  # @param index [Integer] index of the statement first line
23
26
  def initialize(lines, first_line_index)
@@ -27,24 +30,24 @@ module Debtective
27
30
 
28
31
  # index of the line ending the statement
29
32
  # @return [Integer]
33
+ # @note use suppress_stderr to prevent error outputs
34
+ # from RubyVM::InstructionSequence.compile
30
35
  def call
31
- suppress_stderr do
32
- last_line_index || @first_line_index
33
- end
36
+ suppress_stderr { last_line_index }
34
37
  end
35
38
 
36
39
  private
37
40
 
38
- # index of the line ending the statement
41
+ # recursively find index of the line ending the statement
42
+ # @param index [Integer] used for recursion
39
43
  # @return[Integer, void]
40
44
  # @note it is possible that no line ends the statement
41
45
  # 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
46
+ def last_line_index(index = @first_line_index)
47
+ return @first_line_index if index >= @lines.size
48
+ return index if !chained?(index) && statement?(index)
49
+
50
+ last_line_index(index + 1)
48
51
  end
49
52
 
50
53
  # check if the code from first index to given index is a statement
@@ -53,9 +56,8 @@ module Debtective
53
56
  # @return boolean
54
57
  def statement?(index)
55
58
  code = @lines[@first_line_index..index].join("\n")
56
- ::Parser::CurrentRuby.parse(code)
57
- true
58
- rescue Parser::SyntaxError
59
+ RubyVM::InstructionSequence.compile(code)
60
+ rescue SyntaxError
59
61
  false
60
62
  end
61
63
 
@@ -66,16 +68,5 @@ module Debtective
66
68
  def chained?(index)
67
69
  @lines[index + 1]&.match?(/^(\s*)\./)
68
70
  end
69
-
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)
79
- end
80
71
  end
81
72
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/build_todo"
4
+
5
+ module Debtective
6
+ # Find and investigate todo comments and return a list of todos
7
+ class FindTodos
8
+ TODO_REGEX = /#\sTODO:/
9
+
10
+ # @param paths [Array<String>]
11
+ def initialize(paths)
12
+ @paths = paths
13
+ end
14
+
15
+ # @return [Array<Debtective::Todo>]
16
+ def call
17
+ ruby_pathnames.flat_map { pathname_todos(_1) }
18
+ end
19
+
20
+ private
21
+
22
+ # @return [Array<Pathname>] only pathes to ruby files
23
+ def ruby_pathnames
24
+ @paths
25
+ .flat_map { Dir[_1] }
26
+ .map { Pathname(_1) }
27
+ .select { _1.file? && _1.extname == ".rb" }
28
+ end
29
+
30
+ # return todos in the pathname
31
+ # @return [Array<Debtective::Todo>]
32
+ def pathname_todos(pathname)
33
+ pathname.readlines.filter_map.with_index do |line, index|
34
+ next unless line.match?(TODO_REGEX)
35
+
36
+ Todo.build(pathname, index)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "git"
4
+ require "open3"
5
+
6
+ module Debtective
7
+ # find the commit that introduced a line of code
8
+ class GitCommit
9
+ Author = Struct.new(:email, :name)
10
+ Commit = Struct.new(:sha, :author, :time)
11
+
12
+ SPECIAL_CHARACTER_REGEX = /(?!\w|\s|#|:).+/
13
+
14
+ # @param pathname [Pathname] file path
15
+ # @param code [String] line of code
16
+ def initialize(pathname, code)
17
+ @pathname = pathname
18
+ @code = code
19
+ end
20
+
21
+ # @return [Debtective::GitCommit::Commit]
22
+ def call
23
+ Commit.new(sha, author, time)
24
+ rescue Git::GitExecuteError
25
+ author = Author.new(nil, nil)
26
+ Commit.new(nil, author, nil)
27
+ end
28
+
29
+ # @return [Debtective::GitCommit::Author]
30
+ def author
31
+ Author.new(commit.author.email, commit.author.name)
32
+ end
33
+
34
+ # @return [Time]
35
+ def time
36
+ commit.date
37
+ end
38
+
39
+ # @return [Git::Base]
40
+ def git
41
+ Git.open(".")
42
+ end
43
+
44
+ # @return [Git::Object::Commit]
45
+ def commit
46
+ git.gcommit(sha)
47
+ end
48
+
49
+ # commit sha
50
+ # @return [String]
51
+ def sha
52
+ @sha ||=
53
+ begin
54
+ cmd = "git log -S \"#{safe_code}\" #{@pathname}"
55
+ stdout, _stderr, _status = ::Open3.capture3(cmd)
56
+ stdout[/commit (\w{40})\n/, 1]
57
+ end
58
+ end
59
+
60
+ # characters " and ` can break the git command
61
+ # @return [String]
62
+ def safe_code
63
+ @code.gsub(/"/, "\\\"").gsub("`", "\\\\`")
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "debtective/todo_list"
5
+
6
+ module Debtective
7
+ # Generate todolist
8
+ class OutputTodos
9
+ FILE_PATH = "todos.json"
10
+
11
+ # @return [void]
12
+ def call
13
+ log_table_headers
14
+ log_table_rows
15
+ log_counts
16
+ update_json_file
17
+ end
18
+
19
+ private
20
+
21
+ # @return [void]
22
+ def update_json_file
23
+ File.open(FILE_PATH, "w") do |file|
24
+ file.puts(
25
+ JSON.pretty_generate(
26
+ todo_list.todos.map(&:to_h)
27
+ )
28
+ )
29
+ end
30
+ puts FILE_PATH
31
+ puts separator
32
+ end
33
+
34
+ # @return [void]
35
+ def create_directory
36
+ return if File.directory?(DIRECTORY_PATH)
37
+
38
+ FileUtils.mkdir_p(DIRECTORY_PATH)
39
+ end
40
+
41
+ # @return [void]
42
+ def log_table_headers
43
+ puts separator
44
+ puts table_row("location", "author", "days", "size")
45
+ puts separator
46
+ end
47
+
48
+ # @return [void]
49
+ def log_table_rows
50
+ with_trace_logs(
51
+ lambda do |todo|
52
+ puts(
53
+ table_row(
54
+ todo.location,
55
+ todo.commit.author.name || "?",
56
+ todo.days || "?",
57
+ todo.size
58
+ )
59
+ )
60
+ end
61
+ ) do
62
+ todo_list.todos
63
+ end
64
+ puts separator
65
+ end
66
+
67
+ # @param lambda [Lambda]
68
+ # @yield
69
+ def with_trace_logs(lambda)
70
+ trace =
71
+ TracePoint.new(:return) do |trace_point|
72
+ next unless trace_point.defined_class == Debtective::BuildTodo && trace_point.method_id == :call
73
+
74
+ todo = trace_point.return_value
75
+ lambda.call(todo)
76
+ end
77
+ trace.enable
78
+ yield
79
+ trace.disable
80
+ end
81
+
82
+ # @return [Debtective::Todo]
83
+ def todo_list
84
+ @todo_list ||= Debtective::TodoList.new(
85
+ Debtective.configuration&.paths || ["./**/*"]
86
+ )
87
+ end
88
+
89
+ # @return [void]
90
+ def log_counts
91
+ puts "count: #{todo_list.todos.count}"
92
+ puts "combined lines count: #{todo_list.combined_count}"
93
+ puts "extended lines count: #{todo_list.extended_count}"
94
+ puts separator
95
+ end
96
+
97
+ # @return [String]
98
+ def separator
99
+ @separator ||= Array.new(table_row(nil, nil, nil, nil).size) { "-" }.join
100
+ end
101
+
102
+ # @return [String]
103
+ def table_row(col1, col2, col3, col4)
104
+ [
105
+ format("%-80.80s", col1.to_s),
106
+ format("%-20.20s", col2.to_s),
107
+ format("%-12.12s", col3.to_s),
108
+ format("%-12.12s", col4.to_s)
109
+ ].join(" | ")
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debtective
4
+ # Silence the $stderr
5
+ module StderrHelper
6
+ # @return void
7
+ def suppress_stderr
8
+ original_stderr = $stderr.clone
9
+ $stderr.reopen(File.new("/dev/null", "w"))
10
+ yield
11
+ ensure
12
+ $stderr.reopen(original_stderr)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/git_commit"
4
+
5
+ module Debtective
6
+ # Hold todo information
7
+ class Todo
8
+ class << self
9
+ # @return [Debtective::Todo]
10
+ def build(pathname, index)
11
+ BuildTodo.new(pathname, index).call
12
+ end
13
+ end
14
+
15
+ attr_accessor :pathname, :todo_boundaries, :statement_boundaries
16
+
17
+ # @param pathname [Pathname]
18
+ # @param lines [Array<String>]
19
+ # @param todo_boundaries [Range]
20
+ # @param statement_boundaries [Range]
21
+ def initialize(pathname, lines, todo_boundaries, statement_boundaries)
22
+ @pathname = pathname
23
+ @lines = lines
24
+ @todo_boundaries = todo_boundaries
25
+ @statement_boundaries = statement_boundaries
26
+ end
27
+
28
+ # location in the codebase
29
+ # @return [String]
30
+ def location
31
+ "#{@pathname}:#{@todo_boundaries.min + 1}"
32
+ end
33
+
34
+ # size of the todo code
35
+ # @return [Integer]
36
+ def size
37
+ @statement_boundaries.size
38
+ end
39
+
40
+ # return commit that introduced the todo
41
+ # @return [Git::Object::Commit]
42
+ def commit
43
+ @commit ||= Debtective::GitCommit.new(@pathname, @lines[@todo_boundaries.min]).call
44
+ end
45
+
46
+ # @return [Integer]
47
+ def days
48
+ return if commit.time.nil?
49
+
50
+ ((Time.now - commit.time) / (24 * 60 * 60)).round
51
+ end
52
+
53
+ # @return [Hash]
54
+ def to_h
55
+ {
56
+ pathname: pathname,
57
+ location: location,
58
+ todo_boundaries: todo_boundaries.minmax,
59
+ statement_boundaries: statement_boundaries.minmax,
60
+ size: size,
61
+ commit: {
62
+ sha: commit.sha,
63
+ author: commit.author.to_h,
64
+ time: commit.time
65
+ }
66
+ }
67
+ end
68
+ end
69
+ end
@@ -1,52 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debtective/file_todos"
3
+ require "debtective/find_todos"
4
4
 
5
5
  module Debtective
6
- # Find and investigate todo comments
6
+ # Information about the todos in the codebase
7
7
  class TodoList
8
- DEFAULT_PATHS = ["./**/*"].freeze
8
+ Author = Struct.new(:email, :name, :todos)
9
9
 
10
- REPORTS = %i[
11
- combined_count
12
- extended_count
13
- todos
14
- ].freeze
15
-
16
- Result = Struct.new(*REPORTS)
17
-
18
- # @return [TodoList::Result]
19
- def call
20
- Result.new(*REPORTS.map { __send__(_1) })
21
- end
22
-
23
- private
24
-
25
- # @return [Array<String>]
26
- def paths
27
- Debtective.configuration&.paths || DEFAULT_PATHS
10
+ # @param paths [Array<String>]
11
+ def initialize(paths)
12
+ @paths = paths
28
13
  end
29
14
 
30
- # @return [Array<Pathname>] only pathes to ruby files
31
- def ruby_pathnames
32
- paths
33
- .flat_map { File.extname(_1) == "" ? Dir[_1] : [_1] }
34
- .map { Pathname(_1) }
35
- .select { _1.file? && _1.extname == ".rb" }
15
+ # @return [Array<Debtective::Todo>]
16
+ def todos
17
+ @todos ||= Debtective::FindTodos.new(@paths).call
36
18
  end
37
19
 
38
- # @return [Array<FileTodos::Result>]
39
- def todos
40
- @todos ||=
41
- ruby_pathnames
42
- .flat_map { FileTodos.new(_1).call }
43
- .select(&:any?)
20
+ # @return [Array<TodoList::Author>]
21
+ def authors
22
+ todos
23
+ .map { [_1.commit.author.email, _1.commit.author.name] }
24
+ .uniq
25
+ .map { author(_1) }
44
26
  end
45
27
 
46
28
  # @return [Integer]
47
29
  def extended_count
48
- todos
49
- .sum { _1.boundaries.size }
30
+ todos.sum(&:size)
50
31
  end
51
32
 
52
33
  # @return [Integer]
@@ -54,12 +35,23 @@ module Debtective
54
35
  todos
55
36
  .group_by(&:pathname)
56
37
  .values
57
- .sum do |file_todos|
58
- file_todos
59
- .map { _1.boundaries.to_a }
38
+ .sum do |pathname_todos|
39
+ pathname_todos
40
+ .map { _1.statement_boundaries.to_a }
60
41
  .reduce(:|)
61
42
  .length
62
43
  end
63
44
  end
45
+
46
+ private
47
+
48
+ # @param email_and_name [Array<String>]
49
+ # @return [TodoList::Author]
50
+ def author(email_and_name)
51
+ Author.new(
52
+ *email_and_name,
53
+ todos.select { email_and_name == [_1.commit.author.email, _1.commit.author.name] }
54
+ )
55
+ end
64
56
  end
65
57
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Debtective
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debtective/todo_list"
3
+ require "debtective/output_todos"
4
4
 
5
5
  begin
6
6
  require "#{Rails.root}/config/initializers/debtective" if defined?(Rails)
@@ -11,52 +11,6 @@ end
11
11
  namespace :debtective do
12
12
  desc "Todo List"
13
13
  task :todo_list do
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
14
+ Debtective::OutputTodos.new.call
61
15
  end
62
16
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debtective
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
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-09 00:00:00.000000000 Z
11
+ date: 2023-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: parser
14
+ name: git
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -115,14 +115,17 @@ executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
- - MIT-LICENSE
119
- - README.md
120
- - Rakefile
118
+ - bin/debtective
121
119
  - lib/debtective.rb
120
+ - lib/debtective/build_todo.rb
122
121
  - lib/debtective/configuration.rb
123
- - lib/debtective/end_of_statement.rb
124
- - lib/debtective/file_todos.rb
122
+ - lib/debtective/find_end_of_statement.rb
123
+ - lib/debtective/find_todos.rb
124
+ - lib/debtective/git_commit.rb
125
+ - lib/debtective/output_todos.rb
125
126
  - lib/debtective/railtie.rb
127
+ - lib/debtective/stderr_helper.rb
128
+ - lib/debtective/todo.rb
126
129
  - lib/debtective/todo_list.rb
127
130
  - lib/debtective/version.rb
128
131
  - lib/tasks/debtective/todo_list.rake
data/MIT-LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright 2021 Edouard Piron
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md DELETED
@@ -1,53 +0,0 @@
1
- # Debtective
2
-
3
- Find todos in your codebase so you don't forget to pay off your debts! 💰
4
-
5
- ## Usage
6
-
7
- Run the task with:
8
-
9
- ```bash
10
- bundle exec rake debtective:todo_list
11
- ```
12
-
13
- It outputs the todos positions with the concerned lines of code and some counts.
14
-
15
- ## Installation
16
-
17
- Add this line to your application's Gemfile:
18
-
19
- ```ruby
20
- group :development do
21
- gem "debtective"
22
- end
23
- ```
24
-
25
- And then execute:
26
-
27
- ```bash
28
- $ bundle
29
- ```
30
-
31
- Or install it yourself as:
32
-
33
- ```bash
34
- $ gem install debtective
35
- ```
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
-
47
- ## Contributing
48
-
49
- This gem is still a work in progress. You can use GitHub issue to start a discussion.
50
-
51
- ## License
52
-
53
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "debtective/end_of_statement"
4
-
5
- module Debtective
6
- # Find the todos comments and their boundaries
7
- class FileTodos
8
- Result = Struct.new(:pathname, :todo_index, :boundaries)
9
-
10
- BEFORE_LINE_TODO_REGEX = /^\s*#\sTODO:\s/
11
- INLINE_TODO_REGEX = /\s*#\sTODO:\s/
12
- COMMENT_REGEX = /\s*#/
13
-
14
- # @param pathname [Pathname]
15
- def initialize(pathname)
16
- @pathname = pathname
17
- end
18
-
19
- # @return [Array<FileTodos::Result>]
20
- def call
21
- lines.filter_map.with_index do |line, index|
22
- boundaries = boundaries(line, index)
23
- next if boundaries.nil?
24
-
25
- Result.new(@pathname, index, boundaries)
26
- end
27
- end
28
-
29
- private
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
-
46
- # @return [Array<String>]
47
- def lines
48
- @lines ||= @pathname.readlines
49
- end
50
-
51
- # @param todo_index [Integer]
52
- # @return [Integer]
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
59
- end
60
- end
61
- end