debtective 0.2.3.2 → 0.2.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dbc3a3e7ee53fc04a2759fcf0e538773d2e03633e8d8770b536c6ce49d81466
4
- data.tar.gz: 3ae9df343e6cee84e979ccc66fe35fda106df0a69bf2c6c570cdfb76f92859f3
3
+ metadata.gz: '09e2ba02faff3be09a87e2babe120bb59e28f0ea509ad8cc8ef1f9a12cf55ae6'
4
+ data.tar.gz: e1c5d6197706783907a1756671928eac854d1d771eb541d58b18dba8053da003
5
5
  SHA512:
6
- metadata.gz: 0c1e6dacb2bbf1b23102add21f05f1872700ac71975b20e7fc70f1078e2a8faef5a3c4ac8daad30047e1e46ae287fac178c6916e7e4a54439ea1085f589a7155
7
- data.tar.gz: ed2f39253ec62220a150e7639628f2e8420375b7fef444a0c2d4f8bdd1f4a65f62449d9f7ab968001cada37fc57aaec80097931aebb259f61d09fa3943cbf3fb
6
+ metadata.gz: f1641ef5d5031d12d7c85665a252ab168cf456297621b81c400f22c8a8017868eb3c7873eade9ae2c8b0cb21c93a85d702ff16ffd4a45b822edd0d450ca42835
7
+ data.tar.gz: f371a5a826c7daff0c6cc2a31e7e5686731f29d74b568e96174de0000bdec70c62318481ff2a21ef98ff84d279053fbbb747511f75b0e0c79c8adb160f540bcb
data/bin/debtective CHANGED
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "debtective"
5
- require "debtective/output_todos"
6
5
 
7
6
  user_name =
8
7
  if ARGV.include?("--me")
@@ -11,11 +10,17 @@ user_name =
11
10
  ARGV[ARGV.index("--user") + 1]
12
11
  end
13
12
 
14
- if user_name
15
- puts "Searching TODOs from #{user_name}..."
13
+ quiet = ARGV.include?("--quiet")
14
+
15
+ case ARGV[0]
16
+ when "--todos"
17
+ require "debtective/todos/export"
18
+ Debtective::Todos::Export.new(user_name: user_name, quiet: quiet).call
19
+ when "--offenses"
20
+ require "debtective/offenses/export"
21
+ Debtective::Offenses::Export.new(user_name: user_name, quiet: quiet).call
22
+ when "--gems"
23
+ puts "Upcoming feature"
16
24
  else
17
- puts "Searching TODOs..."
25
+ puts "Please pass one of this options: [--todos, --offenses, --gems]"
18
26
  end
19
-
20
- quiet = ARGV.include?("--quiet")
21
- Debtective::OutputTodos.new(user_name, quiet: quiet).call
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "pathname"
5
+ require "debtective/print"
6
+
7
+ module Debtective
8
+ # Export elements in a JSON file
9
+ class Export
10
+ # @param user_name [String] git user email to filter
11
+ # @param quiet [boolean]
12
+ def initialize(user_name: nil, quiet: false)
13
+ @user_name = user_name
14
+ @quiet = quiet
15
+ end
16
+
17
+ # @return [void]
18
+ def call
19
+ @elements = @quiet ? find_elements : log_table
20
+ filter_elements!
21
+ log_counts unless @quiet
22
+ update_json_file
23
+ puts(FILE_PATH) unless @quiet
24
+ end
25
+
26
+ private
27
+
28
+ FILE_PATH = ""
29
+
30
+ # list of paths to search in
31
+ # @return [Array<String>]
32
+ def pathnames
33
+ (Debtective.configuration&.paths || ["./**/*"])
34
+ .flat_map { Dir[_1] }
35
+ .map { Pathname(_1) }
36
+ .select { _1.file? && _1.extname == ".rb" }
37
+ end
38
+
39
+ # select only elements commited by the given user
40
+ # @return [void]
41
+ def filter_elements!
42
+ return if @user_name.nil?
43
+
44
+ @elements.select! { _1.commit.author.name == @user_name }
45
+ end
46
+
47
+ # @return [void]
48
+ def log_counts
49
+ puts "total: #{@elements.count}"
50
+ end
51
+
52
+ # write elements into the JSON file
53
+ # @return [void]
54
+ def update_json_file
55
+ File.open(self.class::FILE_PATH, "w") do |file|
56
+ file.puts(
57
+ JSON.pretty_generate(
58
+ @elements.map(&:to_h)
59
+ )
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
@@ -4,21 +4,21 @@ require "git"
4
4
  require "open3"
5
5
 
6
6
  module Debtective
7
- # find the commit that introduced a line of code
8
- class GitCommit
7
+ # Find the commit that introduced the given line of code
8
+ class FindCommit
9
9
  Author = Struct.new(:email, :name)
10
10
  Commit = Struct.new(:sha, :author, :time)
11
11
 
12
12
  SPECIAL_CHARACTER_REGEX = /(?!\w|\s|#|:).+/
13
13
 
14
14
  # @param pathname [Pathname] file path
15
- # @param code [String] line of code
16
- def initialize(pathname, code)
15
+ # @param code_line [String] line of code_line
16
+ def initialize(pathname:, code_line:)
17
17
  @pathname = pathname
18
- @code = code
18
+ @code_line = code_line
19
19
  end
20
20
 
21
- # @return [Debtective::GitCommit::Commit]
21
+ # @return [Debtective::FindCommit::Commit]
22
22
  def call
23
23
  Commit.new(sha, author, time)
24
24
  rescue Git::GitExecuteError
@@ -26,7 +26,7 @@ module Debtective
26
26
  Commit.new(nil, author, nil)
27
27
  end
28
28
 
29
- # @return [Debtective::GitCommit::Author]
29
+ # @return [Debtective::FindCommit::Author]
30
30
  def author
31
31
  Author.new(commit.author.email, commit.author.name)
32
32
  end
@@ -36,17 +36,6 @@ module Debtective
36
36
  commit.date
37
37
  end
38
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
39
  # @return [String]
51
40
  def sha
52
41
  @sha ||=
@@ -57,10 +46,22 @@ module Debtective
57
46
  end
58
47
  end
59
48
 
49
+ private
50
+
51
+ # @return [Git::Base]
52
+ def git
53
+ Git.open(".")
54
+ end
55
+
56
+ # @return [Git::Object::Commit]
57
+ def commit
58
+ git.gcommit(sha)
59
+ end
60
+
60
61
  # characters " and ` can break the git command
61
62
  # @return [String]
62
63
  def safe_code
63
- @code.gsub(/"/, "\\\"").gsub("`", "\\\\`")
64
+ @code_line.gsub(/"/, "\\\"").gsub("`", "\\\\`")
64
65
  end
65
66
  end
66
67
  end
@@ -23,7 +23,7 @@ module Debtective
23
23
 
24
24
  # @param lines [Array<String>] lines of code
25
25
  # @param index [Integer] index of the statement first line
26
- def initialize(lines, first_line_index)
26
+ def initialize(lines:, first_line_index:)
27
27
  @lines = lines
28
28
  @first_line_index = first_line_index
29
29
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/export"
4
+ require "debtective/print"
5
+ require "debtective/offenses/offense"
6
+
7
+ module Debtective
8
+ module Offenses
9
+ # Export offenses in a JSON file
10
+ class Export < Debtective::Export
11
+ private
12
+
13
+ FILE_PATH = "offenses.json"
14
+
15
+ TABLE_COLUMNS = [
16
+ Debtective::Print::Column.new(
17
+ "location",
18
+ "%-80.80s",
19
+ -> { _1.location }
20
+ ),
21
+ Debtective::Print::Column.new(
22
+ "author",
23
+ "%-20.20s",
24
+ -> { _1.commit.author.name || "?" }
25
+ ),
26
+ Debtective::Print::Column.new(
27
+ "cop",
28
+ "%-27.27s",
29
+ -> { _1.cop || "?" }
30
+ )
31
+ ].freeze
32
+
33
+ # @return [Array<Debtective::Offenses::Offense>]
34
+ def log_table
35
+ Debtective::Print.new(
36
+ columns: TABLE_COLUMNS,
37
+ track: Debtective::Offenses::Offense,
38
+ user_name: @user_name
39
+ ).call { find_elements }
40
+ end
41
+
42
+ # @return [Array<Debtective::Offenses::Offense>]
43
+ def find_elements
44
+ pathnames.flat_map do |pathname|
45
+ pathname.readlines.filter_map.with_index do |line, index|
46
+ next unless line =~ /\s# rubocop:disable (.*)/
47
+
48
+ Debtective::Offenses::Offense.new(
49
+ pathname: pathname,
50
+ index: index,
51
+ cop: Regexp.last_match[1]
52
+ )
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/find_commit"
4
+
5
+ module Debtective
6
+ module Offenses
7
+ # Hold offense information
8
+ class Offense
9
+ attr_accessor :pathname, :cop
10
+
11
+ # @param pathname [Pathname]
12
+ # @param cop [Array<String>]
13
+ def initialize(pathname:, index:, cop:)
14
+ @pathname = pathname
15
+ @index = index
16
+ @cop = cop
17
+ end
18
+
19
+ # location in the codebase
20
+ # @return [String]
21
+ def location
22
+ "#{@pathname}:#{@index + 1}"
23
+ end
24
+
25
+ # return commit that introduced the offense
26
+ # @return [Debtective::FindCommit::Commit]
27
+ def commit
28
+ @commit ||=
29
+ Debtective::FindCommit.new(
30
+ pathname: @pathname,
31
+ code_line: @pathname.readlines[@index]
32
+ ).call
33
+ end
34
+
35
+ # @return [Integer]
36
+ def days
37
+ return if commit.time.nil?
38
+
39
+ ((Time.now - commit.time) / (24 * 60 * 60)).round
40
+ end
41
+
42
+ # @return [Hash]
43
+ def to_h
44
+ {
45
+ pathname: @pathname,
46
+ location: location,
47
+ index: @index,
48
+ commit: {
49
+ sha: commit.sha,
50
+ author: commit.author.to_h,
51
+ time: commit.time
52
+ }
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debtective
4
+ # Print elements as a table in the stdout
5
+ class Print
6
+ Column = Struct.new(:name, :format, :lambda)
7
+
8
+ # @param columns [Array<Debtective::Print::Column>]
9
+ # @param track [Class]
10
+ # @param user_name [String] git user name to filter
11
+ def initialize(columns:, track:, user_name: nil)
12
+ @columns = columns
13
+ @track = track
14
+ @user_name = user_name
15
+ end
16
+
17
+ # @return [Array]
18
+ def call
19
+ puts separator
20
+ puts headers_row
21
+ puts separator
22
+ trace.enable
23
+ result = yield
24
+ trace.disable
25
+ puts separator
26
+ result
27
+ end
28
+
29
+ private
30
+
31
+ # use a trace to log each element as soon as it is found
32
+ # @return [Tracepoint]
33
+ def trace
34
+ TracePoint.new(:return) do |trace_point|
35
+ next unless trace_point.defined_class == @track && trace_point.method_id == :initialize
36
+
37
+ object = trace_point.self
38
+ log_object(object)
39
+ end
40
+ end
41
+
42
+ # @return [String]
43
+ def headers_row
44
+ @columns.map do |column|
45
+ format(column.format, column.name)
46
+ end.join(" | ")
47
+ end
48
+
49
+ # @return [String]
50
+ def object_row(object)
51
+ @columns.map do |column|
52
+ format(column.format, column.lambda.call(object))
53
+ end.join(" | ")
54
+ end
55
+
56
+ # if a user_name is given and is not the commit author
57
+ # the line is temporary and will be replaced by the next one
58
+ # @return [void]
59
+ def log_object(object)
60
+ if @user_name.nil? || @user_name == object.commit.author.name
61
+ puts object_row(object)
62
+ else
63
+ print "#{object_row(object)}\r"
64
+ end
65
+ end
66
+
67
+ # @return [String]
68
+ def separator
69
+ @separator ||=
70
+ begin
71
+ size = @columns.map { format(_1.format, "") }.join("---").size
72
+ "-" * size
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/todos/todo"
4
+ require "debtective/find_end_of_statement"
5
+
6
+ module Debtective
7
+ module Todos
8
+ # Build a Todo object given the pathname and the comment index
9
+ class Build
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
+ # @param index [Integer]
16
+ def initialize(pathname:, index:)
17
+ @pathname = pathname
18
+ @index = index
19
+ end
20
+
21
+ # @return [Debtective::Todos::Todo]
22
+ def call
23
+ Debtective::Todos::Todo.new(
24
+ pathname: @pathname,
25
+ lines: lines,
26
+ todo_boundaries: todo_boundaries,
27
+ statement_boundaries: statement_boundaries
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ # @return [Array<String>]
34
+ def lines
35
+ @lines ||= @pathname.readlines
36
+ end
37
+
38
+ # @return [Range]
39
+ def todo_boundaries
40
+ @index..([@index, statement_boundaries.min - 1].max)
41
+ end
42
+
43
+ # range of the concerned code
44
+ # @return [Range]
45
+ def statement_boundaries
46
+ @statement_boundaries ||=
47
+ case lines[@index]
48
+ when BEFORE_LINE_TODO_REGEX
49
+ first_line_index = statement_start
50
+ last_line_index = Debtective::FindEndOfStatement.new(lines: lines, first_line_index: first_line_index).call
51
+ first_line_index..last_line_index
52
+ when INLINE_TODO_REGEX
53
+ @index..@index
54
+ end
55
+ end
56
+
57
+ # start index of the concerned code (i.e. below the TODO comment)
58
+ # @return [Integer]
59
+ def statement_start
60
+ lines.index.with_index do |line, i|
61
+ i > @index &&
62
+ !line.strip.empty? &&
63
+ !line.match?(COMMENT_REGEX)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/export"
4
+ require "debtective/print"
5
+ require "debtective/todos/build"
6
+ require "debtective/todos/todo"
7
+
8
+ module Debtective
9
+ module Todos
10
+ # Export todos in a JSON file
11
+ class Export < Debtective::Export
12
+ private
13
+
14
+ FILE_PATH = "todos.json"
15
+
16
+ TABLE_COLUMNS = [
17
+ Debtective::Print::Column.new(
18
+ "location",
19
+ "%-80.80s",
20
+ -> { _1.location }
21
+ ),
22
+ Debtective::Print::Column.new(
23
+ "author",
24
+ "%-20.20s",
25
+ -> { _1.commit.author.name || "?" }
26
+ ),
27
+ Debtective::Print::Column.new(
28
+ "days",
29
+ "%-12.12s",
30
+ -> { _1.days || "?" }
31
+ ),
32
+ Debtective::Print::Column.new(
33
+ "size",
34
+ "%-12.12s",
35
+ -> { _1.size }
36
+ )
37
+ ].freeze
38
+
39
+ # @return [Array<Debtective::Todos::Todo>]
40
+ def log_table
41
+ Debtective::Print.new(
42
+ columns: TABLE_COLUMNS,
43
+ track: Debtective::Todos::Todo,
44
+ user_name: @user_name
45
+ ).call { find_elements }
46
+ end
47
+
48
+ # @return [Array<Debtective::Todos::Todo>]
49
+ def find_elements
50
+ pathnames.flat_map do |pathname|
51
+ pathname.readlines.filter_map.with_index do |line, index|
52
+ next unless line =~ /#\sTODO:/
53
+
54
+ Debtective::Todos::Build.new(
55
+ pathname: pathname,
56
+ index: index
57
+ ).call
58
+ end
59
+ end
60
+ end
61
+
62
+ # @return [void]
63
+ def log_counts
64
+ puts "total: #{@elements.count}"
65
+ puts "extended lines count: #{extended_count}"
66
+ puts "combined lines count: #{combined_count}"
67
+ end
68
+
69
+ # @return [Integer]
70
+ def extended_count
71
+ @elements.sum(&:size)
72
+ end
73
+
74
+ # @return [Integer]
75
+ def combined_count
76
+ @elements
77
+ .group_by(&:pathname)
78
+ .values
79
+ .sum do |pathname_todos|
80
+ pathname_todos
81
+ .map { _1.statement_boundaries.to_a }
82
+ .reduce(:|)
83
+ .length
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/find_commit"
4
+
5
+ module Debtective
6
+ module Todos
7
+ # Hold todo information
8
+ class Todo
9
+ attr_accessor :pathname, :todo_boundaries, :statement_boundaries
10
+
11
+ # @param pathname [Pathname]
12
+ # @param lines [Array<String>]
13
+ # @param todo_boundaries [Range]
14
+ # @param statement_boundaries [Range]
15
+ def initialize(pathname:, lines:, todo_boundaries:, statement_boundaries:)
16
+ @pathname = pathname
17
+ @lines = lines
18
+ @todo_boundaries = todo_boundaries
19
+ @statement_boundaries = statement_boundaries
20
+ end
21
+
22
+ # location in the codebase
23
+ # @return [String]
24
+ def location
25
+ "#{@pathname}:#{@todo_boundaries.min + 1}"
26
+ end
27
+
28
+ # size of the todo code
29
+ # @return [Integer]
30
+ def size
31
+ @statement_boundaries.size
32
+ end
33
+
34
+ # return commit that introduced the todo
35
+ # @return [Debtective::FindCommit::Commit]
36
+ def commit
37
+ @commit ||=
38
+ Debtective::FindCommit.new(
39
+ pathname: @pathname,
40
+ code_line: @lines[@todo_boundaries.min]
41
+ ).call
42
+ end
43
+
44
+ # @return [Integer]
45
+ def days
46
+ return if commit.time.nil?
47
+
48
+ ((Time.now - commit.time) / (24 * 60 * 60)).round
49
+ end
50
+
51
+ # @return [Hash]
52
+ def to_h
53
+ {
54
+ pathname: @pathname,
55
+ location: location,
56
+ todo_boundaries: todo_boundaries.minmax,
57
+ statement_boundaries: statement_boundaries.minmax,
58
+ size: size,
59
+ commit: {
60
+ sha: commit.sha,
61
+ author: commit.author.to_h,
62
+ time: commit.time
63
+ }
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Debtective
4
- VERSION = "0.2.3.2"
4
+ VERSION = "0.2.3.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debtective
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3.2
4
+ version: 0.2.3.4
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-16 00:00:00.000000000 Z
11
+ date: 2023-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: git
@@ -118,19 +118,19 @@ extra_rdoc_files: []
118
118
  files:
119
119
  - bin/debtective
120
120
  - lib/debtective.rb
121
- - lib/debtective/build_todo.rb
122
121
  - lib/debtective/configuration.rb
122
+ - lib/debtective/export.rb
123
+ - lib/debtective/find_commit.rb
123
124
  - lib/debtective/find_end_of_statement.rb
124
- - lib/debtective/find_todos.rb
125
- - lib/debtective/git_commit.rb
126
- - lib/debtective/output_todos.rb
127
- - lib/debtective/print_table.rb
125
+ - lib/debtective/offenses/export.rb
126
+ - lib/debtective/offenses/offense.rb
127
+ - lib/debtective/print.rb
128
128
  - lib/debtective/railtie.rb
129
129
  - lib/debtective/stderr_helper.rb
130
- - lib/debtective/todo.rb
131
- - lib/debtective/todo_list.rb
130
+ - lib/debtective/todos/build.rb
131
+ - lib/debtective/todos/export.rb
132
+ - lib/debtective/todos/todo.rb
132
133
  - lib/debtective/version.rb
133
- - lib/tasks/debtective/todo_list.rake
134
134
  homepage: https://github.com/BigBigDoudou/debtective
135
135
  licenses:
136
136
  - MIT
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pathname"
4
- require "debtective/todo"
5
- require "debtective/find_end_of_statement"
6
-
7
- module Debtective
8
- # Find the todos comments and their boundaries
9
- class BuildTodo
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
- # @param index [Integer]
16
- def initialize(pathname, index)
17
- @pathname = pathname
18
- @index = index
19
- end
20
-
21
- # @return [Debtective::Todo]
22
- def call
23
- Todo.new(
24
- @pathname,
25
- lines,
26
- todo_boundaries,
27
- statement_boundaries
28
- )
29
- end
30
-
31
- private
32
-
33
- # @return [Array<String>]
34
- def lines
35
- @lines ||= @pathname.readlines
36
- end
37
-
38
- # @return [Range]
39
- def todo_boundaries
40
- @index..([@index, statement_boundaries.min - 1].max)
41
- end
42
-
43
- # range of the concerned code
44
- # @return [Range]
45
- def statement_boundaries
46
- @statement_boundaries ||=
47
- case lines[@index]
48
- when BEFORE_LINE_TODO_REGEX
49
- first_line_index = statement_start
50
- last_line_index = FindEndOfStatement.new(lines, first_line_index).call
51
- first_line_index..last_line_index
52
- when INLINE_TODO_REGEX
53
- @index..@index
54
- end
55
- end
56
-
57
- # start index of the concerned code (i.e. below the TODO comment)
58
- # @return [Integer]
59
- def statement_start
60
- lines.index.with_index do |line, i|
61
- i > @index &&
62
- !line.strip.empty? &&
63
- !line.match?(COMMENT_REGEX)
64
- end
65
- end
66
- end
67
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pathname"
4
- require "debtective/build_todo"
5
-
6
- module Debtective
7
- # Find and investigate todo comments and return a list of todos
8
- class FindTodos
9
- TODO_REGEX = /#\sTODO:/
10
-
11
- # @param paths [Array<String>]
12
- def initialize(paths)
13
- @paths = paths
14
- end
15
-
16
- # @return [Array<Debtective::Todo>]
17
- def call
18
- ruby_pathnames.flat_map { pathname_todos(_1) }
19
- end
20
-
21
- private
22
-
23
- # @return [Array<Pathname>] only pathes to ruby files
24
- def ruby_pathnames
25
- @paths
26
- .flat_map { Dir[_1] }
27
- .map { Pathname(_1) }
28
- .select { _1.file? && _1.extname == ".rb" }
29
- end
30
-
31
- # return todos in the pathname
32
- # @return [Array<Debtective::Todo>]
33
- def pathname_todos(pathname)
34
- pathname.readlines.filter_map.with_index do |line, index|
35
- next unless line.match?(TODO_REGEX)
36
-
37
- Todo.build(pathname, index)
38
- end
39
- end
40
- end
41
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "debtective/print_table"
5
- require "pry"
6
-
7
- module Debtective
8
- # Generate todolist
9
- class OutputTodos
10
- FILE_PATH = "todos.json"
11
-
12
- # @param user_name [String] git user email to filter
13
- def initialize(user_name = nil, quiet: false)
14
- @user_name = user_name
15
- @quiet = quiet
16
- end
17
-
18
- # @return [void]
19
- def call
20
- @todo_list = log_table
21
- @todo_list ||= Debtective::TodoList.new(Debtective.configuration&.paths || ["./**/*"])
22
- filter_todo_list!
23
- log_counts
24
- update_json_file
25
- puts FILE_PATH
26
- end
27
-
28
- private
29
-
30
- # @return [void]
31
- def log_table
32
- return if @quiet
33
-
34
- Debtective::PrintTable.new(@user_name).call
35
- end
36
-
37
- # @return [void]
38
- def filter_todo_list!
39
- !@user_name.nil? && @todo_list.todos.select! { _1.commit.author.name == @user_name }
40
- end
41
-
42
- # @return [void]
43
- def log_counts
44
- @return if @quiet
45
-
46
- puts "total: #{@todo_list.todos.count}"
47
- puts "combined lines count: #{@todo_list.combined_count}"
48
- puts "extended lines count: #{@todo_list.extended_count}"
49
- end
50
-
51
- # @return [void]
52
- def update_json_file
53
- File.open(FILE_PATH, "w") do |file|
54
- file.puts(
55
- JSON.pretty_generate(
56
- @todo_list.todos.map(&:to_h)
57
- )
58
- )
59
- end
60
- end
61
- end
62
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "debtective/todo_list"
4
-
5
- module Debtective
6
- # Print todos as a table in the stdout
7
- class PrintTable
8
- # @param user_name [String] git user email to filter
9
- def initialize(user_name = nil)
10
- @user_name = user_name
11
- end
12
-
13
- # @return [Debtective::TodoList]
14
- def call
15
- log_table_headers
16
- log_table_rows
17
- todo_list
18
- end
19
-
20
- private
21
-
22
- # @return [void]
23
- def log_table_headers
24
- puts separator
25
- puts table_row("location", "author", "days", "size")
26
- puts separator
27
- end
28
-
29
- # @return [void]
30
- def log_table_rows
31
- trace.enable
32
- todo_list.todos
33
- trace.disable
34
- puts separator
35
- end
36
-
37
- # use a trace to log each todo as soon as it is found
38
- # @return [Tracepoint]
39
- def trace
40
- TracePoint.new(:return) do |trace_point|
41
- next unless trace_point.defined_class == Debtective::BuildTodo && trace_point.method_id == :call
42
-
43
- todo = trace_point.return_value
44
- log_todo(todo)
45
- end
46
- end
47
-
48
- # if a user_name is given and is not the commit author
49
- # the line is temporary and will be replaced by the next one
50
- # @return [void]
51
- def log_todo(todo)
52
- row = table_row(
53
- todo.location,
54
- todo.commit.author.name || "?",
55
- todo.days || "?",
56
- todo.size
57
- )
58
- if @user_name.nil? || @user_name == todo.commit.author.name
59
- puts row
60
- else
61
- print "#{row}\r"
62
- end
63
- end
64
-
65
- # @return [Debtective::Todo]
66
- def todo_list
67
- @todo_list ||= Debtective::TodoList.new(
68
- Debtective.configuration&.paths || ["./**/*"]
69
- )
70
- end
71
-
72
- # @return [String]
73
- def table_row(location, author, days, size)
74
- [
75
- format("%-80.80s", location),
76
- format("%-20.20s", author),
77
- format("%-12.12s", days),
78
- format("%-12.12s", size)
79
- ].join(" | ")
80
- end
81
-
82
- # @return [String]
83
- def separator
84
- @separator ||= Array.new(table_row(nil, nil, nil, nil).size) { "-" }.join
85
- end
86
- end
87
- end
@@ -1,69 +0,0 @@
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,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "debtective/find_todos"
4
-
5
- module Debtective
6
- # Information about the todos in the codebase
7
- class TodoList
8
- Author = Struct.new(:email, :name, :todos)
9
-
10
- # @param paths [Array<String>]
11
- def initialize(paths)
12
- @paths = paths
13
- end
14
-
15
- # @return [Array<Debtective::Todo>]
16
- def todos
17
- @todos ||= Debtective::FindTodos.new(@paths).call
18
- end
19
-
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) }
26
- end
27
-
28
- # @return [Integer]
29
- def extended_count
30
- todos.sum(&:size)
31
- end
32
-
33
- # @return [Integer]
34
- def combined_count
35
- todos
36
- .group_by(&:pathname)
37
- .values
38
- .sum do |pathname_todos|
39
- pathname_todos
40
- .map { _1.statement_boundaries.to_a }
41
- .reduce(:|)
42
- .length
43
- end
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
56
- end
57
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "debtective/output_todos"
4
-
5
- begin
6
- require "#{Rails.root}/config/initializers/debtective" if defined?(Rails)
7
- rescue LoadError
8
- nil
9
- end
10
-
11
- namespace :debtective do
12
- desc "Todo List"
13
- task :todo_list do
14
- user_name =
15
- if ARGV.include?("--me")
16
- `git config user.name`.strip
17
- elsif ARGV.include?("--user")
18
- ARGV[ARGV.index("--user") + 1]
19
- end
20
-
21
- if user_name
22
- puts "Searching TODOs from #{user_name}..."
23
- else
24
- puts "Searching TODOs..."
25
- end
26
-
27
- Debtective::OutputTodos.new(user_name, quiet: ARGV.include?("--quiet")).call
28
- end
29
- end