debtective 0.2.2 → 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: fa54bf140a87eeb794cba1f8823d00a9ab5407b77f7d8b18c39414857e301edc
4
- data.tar.gz: 2169b279c88a1527c3e1107921aaacd7b81905442361804ac4eaf3731856357e
3
+ metadata.gz: 4c9cd2c9eaaa35023797d69a854fd1b749269c03e029a1fd9447b993d653b917
4
+ data.tar.gz: 7b3cff2afb3474d1ec0ba8449f88e4795f91d35c4b49af4337e9eb64693443e2
5
5
  SHA512:
6
- metadata.gz: e3c5d0c77005f8b6d410339b9e649ba774d4a677ccc60cb7ee96c4761b24bc740880d648871cc65309b542935008a13df8fcd30cd89b7c968243231046fdd728
7
- data.tar.gz: 8a9e4f628f36eb6d21eef51464d068e24fd696c8e8335127a2d95cc683bbca0c59bc91272e0450c8daac662f8f7f5014ff060bc692e7699925778c2f64725d9c
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
@@ -5,19 +5,20 @@ require "debtective/stderr_helper"
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
21
22
  include StderrHelper
22
23
 
23
24
  # @param lines [Array<String>] lines of code
@@ -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
@@ -1,37 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debtective/end_of_statement"
3
+ require "debtective/git_commit"
4
4
 
5
5
  module Debtective
6
6
  # Hold todo information
7
7
  class Todo
8
- attr_accessor :pathname, :todo_index, :boundaries
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
9
16
 
10
17
  # @param pathname [Pathname]
11
- # @param todo_index [Integer] first line of todo comment
12
- # @param boundaries [Range] first and last index of the concerned code
13
- def initialize(pathname, todo_index, boundaries)
18
+ # @param lines [Array<String>]
19
+ # @param todo_boundaries [Range]
20
+ # @param statement_boundaries [Range]
21
+ def initialize(pathname, lines, todo_boundaries, statement_boundaries)
14
22
  @pathname = pathname
15
- @todo_index = todo_index
16
- @boundaries = boundaries
23
+ @lines = lines
24
+ @todo_boundaries = todo_boundaries
25
+ @statement_boundaries = statement_boundaries
17
26
  end
18
27
 
19
28
  # location in the codebase
20
29
  # @return [String]
21
30
  def location
22
- "#{pathname}:#{todo_index + 1}"
31
+ "#{@pathname}:#{@todo_boundaries.min + 1}"
23
32
  end
24
33
 
25
34
  # size of the todo code
26
35
  # @return [Integer]
27
36
  def size
28
- boundaries.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
29
51
  end
30
52
 
31
- # line numbers (like displayed in an IDE)
32
- # @return [Range]
33
- def line_numbers
34
- (boundaries.min + 1)..(boundaries.max + 1)
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
+ }
35
67
  end
36
68
  end
37
69
  end
@@ -1,37 +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
- # @return [Array<FileTodos::Result>]
11
- def call
12
- ruby_pathnames
13
- .flat_map { FileTodos.new(_1).call }
10
+ # @param paths [Array<String>]
11
+ def initialize(paths)
12
+ @paths = paths
14
13
  end
15
14
 
16
- private
17
-
18
- # @return [Array<String>]
19
- def paths
20
- Debtective.configuration&.paths || DEFAULT_PATHS
15
+ # @return [Array<Debtective::Todo>]
16
+ def todos
17
+ @todos ||= Debtective::FindTodos.new(@paths).call
21
18
  end
22
19
 
23
- # @return [Array<Pathname>] only pathes to ruby files
24
- def ruby_pathnames
25
- paths
26
- .flat_map { File.extname(_1) == "" ? Dir[_1] : [_1] }
27
- .map { Pathname(_1) }
28
- .select { _1.file? && _1.extname == ".rb" }
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) }
29
26
  end
30
27
 
31
28
  # @return [Integer]
32
29
  def extended_count
33
- todos
34
- .sum { _1.boundaries.size }
30
+ todos.sum(&:size)
35
31
  end
36
32
 
37
33
  # @return [Integer]
@@ -39,12 +35,23 @@ module Debtective
39
35
  todos
40
36
  .group_by(&:pathname)
41
37
  .values
42
- .sum do |file_todos|
43
- file_todos
44
- .map { _1.boundaries.to_a }
38
+ .sum do |pathname_todos|
39
+ pathname_todos
40
+ .map { _1.statement_boundaries.to_a }
45
41
  .reduce(:|)
46
42
  .length
47
43
  end
48
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
49
56
  end
50
57
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Debtective
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.3"
5
5
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debtective/todo_list"
4
- require "debtective/todo_list_counts"
3
+ require "debtective/output_todos"
5
4
 
6
5
  begin
7
6
  require "#{Rails.root}/config/initializers/debtective" if defined?(Rails)
@@ -12,40 +11,6 @@ end
12
11
  namespace :debtective do
13
12
  desc "Todo List"
14
13
  task :todo_list do
15
- array_row =
16
- lambda do |value1, value2, value3|
17
- [
18
- value1.to_s.ljust(120),
19
- value2.to_s.rjust(12),
20
- value3.to_s.rjust(12)
21
- ].join(" | ").prepend("| ").concat(" |")
22
- end
23
-
24
- separator = Array.new(array_row.call(nil, nil, nil).size) { "-" }.join
25
-
26
- todo_list = Debtective::TodoList.new.call
27
- todos = todo_list.sort_by { _1.boundaries.size }.reverse
28
- todo_list_counts = Debtective::TodoListCounts.new(todos)
29
-
30
- puts separator
31
- puts "todo list"
32
- puts separator
33
- puts array_row.call("pathname", "line numbers", "lines count")
34
- puts separator
35
- todos.each { puts array_row.call(_1.location, _1.line_numbers, _1.size) }
36
-
37
- puts separator
38
- puts "todos count"
39
- puts todos.count
40
-
41
- puts separator
42
- puts "combined lines count"
43
- puts todo_list_counts.combined_count
44
-
45
- puts separator
46
- puts "extended lines count"
47
- puts todo_list_counts.extended_count
48
-
49
- puts separator
14
+ Debtective::OutputTodos.new.call
50
15
  end
51
16
  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.2
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-10 00:00:00.000000000 Z
11
+ date: 2023-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: git
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
@@ -101,18 +115,18 @@ executables: []
101
115
  extensions: []
102
116
  extra_rdoc_files: []
103
117
  files:
104
- - MIT-LICENSE
105
- - README.md
106
- - Rakefile
118
+ - bin/debtective
107
119
  - lib/debtective.rb
120
+ - lib/debtective/build_todo.rb
108
121
  - lib/debtective/configuration.rb
109
- - lib/debtective/end_of_statement.rb
110
- - 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
111
126
  - lib/debtective/railtie.rb
112
127
  - lib/debtective/stderr_helper.rb
113
128
  - lib/debtective/todo.rb
114
129
  - lib/debtective/todo_list.rb
115
- - lib/debtective/todo_list_counts.rb
116
130
  - lib/debtective/version.rb
117
131
  - lib/tasks/debtective/todo_list.rake
118
132
  homepage: https://github.com/BigBigDoudou/debtective
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,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "debtective/todo"
4
- require "debtective/end_of_statement"
5
-
6
- module Debtective
7
- # Find the todos comments and their boundaries
8
- class FileTodos
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
- def initialize(pathname)
15
- @pathname = pathname
16
- end
17
-
18
- # @return [Array<FileTodos::Result>]
19
- def call
20
- lines.filter_map.with_index do |line, index|
21
- boundaries = boundaries(line, index)
22
- next if boundaries.nil?
23
-
24
- Todo.new(@pathname, index, boundaries)
25
- end
26
- end
27
-
28
- private
29
-
30
- # return todo boundaries if there is a todo
31
- # @param line [String]
32
- # @param index [Integer]
33
- # @return [Range, nil]
34
- def boundaries(line, index)
35
- case line
36
- when BEFORE_LINE_TODO_REGEX
37
- first_line_index = statement_first_line_index(index)
38
- last_line_index = EndOfStatement.new(@lines, first_line_index).call
39
- first_line_index..last_line_index
40
- when INLINE_TODO_REGEX
41
- index..index
42
- end
43
- end
44
-
45
- # @return [Array<String>]
46
- def lines
47
- @lines ||= @pathname.readlines
48
- end
49
-
50
- # @param todo_index [Integer]
51
- # @return [Integer]
52
- def statement_first_line_index(todo_index)
53
- @lines.index.with_index do |line, i|
54
- i > todo_index &&
55
- !line.strip.empty? &&
56
- !line.match?(COMMENT_REGEX)
57
- end
58
- end
59
- end
60
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Debtective
4
- # Apply calculations on a todo list
5
- class TodoListCounts
6
- # @param todo_list [Array<Debtective::Todo>]
7
- def initialize(todos)
8
- @todos = todos
9
- end
10
-
11
- # @return [Integer]
12
- def extended_count
13
- @todos.sum(&:size)
14
- end
15
-
16
- # @return [Integer]
17
- def combined_count
18
- @todos
19
- .group_by(&:pathname)
20
- .values
21
- .sum do |pathname_todos|
22
- pathname_todos
23
- .map { _1.boundaries.to_a }
24
- .reduce(:|)
25
- .length
26
- end
27
- end
28
- end
29
- end