debtective 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 609f9971e421988a317405b19b0cb8aacc172ee246e29d2484d290f6fb6649ab
4
+ data.tar.gz: b084f3772dfb519af3a3e254fa04f576d0edcbe59961841192cd5370c2daf7ac
5
+ SHA512:
6
+ metadata.gz: 84a8a437407fba597289be78946a41094d34cca1eeb27fed496c010e0d062adba149f47832f2e9c558d92d9b0b23e13bd2fbd551168761eafeb02757c7816861
7
+ data.tar.gz: 0fb7f4326d4e3e9ee29c54117287f4eb3f9fd74083e0706b99bf2b30e68e31ef4b4f865511a80857f3b5aea49a2000f14d1f214427cdf3078115fb08b2be470a
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
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 ADDED
@@ -0,0 +1,41 @@
1
+ # Debtective
2
+
3
+ Help find out todos in your application.
4
+
5
+ ## Usage
6
+
7
+ Run the task:
8
+
9
+ - `bundle exec rake debtective:todo_list`
10
+
11
+ More tasks to come!
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ group :development do
19
+ gem 'debtective'
20
+ end
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```bash
26
+ $ bundle
27
+ ```
28
+
29
+ Or install it yourself as:
30
+
31
+ ```bash
32
+ $ gem install debtective
33
+ ```
34
+
35
+ ## Contributing
36
+
37
+ This gem is still a work in progress. You can use GitHub issue to start a discussion.
38
+
39
+ ## License
40
+
41
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
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]
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debtective
4
+ # Manage gem configuration
5
+ class Configuration
6
+ attr_accessor :paths
7
+ end
8
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ 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)
23
+ #
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)
31
+ #
32
+ class EndOfStatement
33
+ NON_ENDING_KEYWORDS_REGEX = /^(\s*)(else$|elsif\s|(rescue(\s|$))|ensure)/
34
+
35
+ # @param lines [Array<String>] lines of code
36
+ def initialize(lines)
37
+ @lines = lines
38
+ end
39
+
40
+ # ends of the statement
41
+ # @return [Integer]
42
+ def call
43
+ @lines[1..]&.index { end_of_statement?(_1) } || 0
44
+ end
45
+
46
+ private
47
+
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])
52
+ end
53
+
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)
58
+
59
+ indent(line) <= first_line_indent
60
+ end
61
+
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
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,51 @@
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, :boundaries)
9
+
10
+ BEFORE_LINE_TODO_REGEX = /^\s*#\stodo:\s/i
11
+ INLINE_TODO_REGEX = /\s*#\stodo:\s/i
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
+ 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
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ # @return [Array<String>]
33
+ def lines
34
+ @lines ||= @pathname.readlines
35
+ end
36
+
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]
45
+ # @return [Integer]
46
+ def end_line(index)
47
+ next_lines = lines[(index + 1)..]
48
+ EndOfStatement.new(next_lines).call
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debtective
4
+ # Makes Rails aware of the tasks
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load "tasks/debtective/todo_list.rake"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,82 @@
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
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/file_todos"
4
+
5
+ module Debtective
6
+ # Find and investigate todo comments
7
+ class TodoList
8
+ DEFAULT_PATHS = ["./**/*"].freeze
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
28
+ end
29
+
30
+ # @return [Array<Pathname>] only path 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" }
36
+ end
37
+
38
+ # @return [Array<FileTodos::Result>]
39
+ def todos
40
+ @todos ||=
41
+ ruby_pathnames
42
+ .flat_map { FileTodos.new(_1).call }
43
+ .select(&:any?)
44
+ end
45
+
46
+ # @return [Integer]
47
+ def extended_count
48
+ todos
49
+ .sum { _1.boundaries.size }
50
+ end
51
+
52
+ # @return [Integer]
53
+ def combined_count
54
+ todos
55
+ .group_by(&:pathname)
56
+ .values
57
+ .sum do |file_todos|
58
+ file_todos
59
+ .map { _1.boundaries.to_a }
60
+ .reduce(:|)
61
+ .length
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Debtective
4
+ VERSION = "0.2.0"
5
+ end
data/lib/debtective.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/version"
4
+ require "debtective/configuration"
5
+ require "debtective/railtie" if defined?(Rails)
6
+
7
+ # Entrypoint
8
+ module Debtective
9
+ class << self
10
+ attr_accessor :configuration
11
+
12
+ # Configures Debtective
13
+ # @return Debtective::Configuration
14
+ # @yieldparam [Proc] configuration to apply
15
+ def configure
16
+ self.configuration ||= Configuration.new
17
+ yield(configuration)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "debtective/report"
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
+ Debtective::Report.new.call
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: debtective
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Edouard Piron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-02-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Find TODOs and compute debt size
98
+ email:
99
+ - ed.piron@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - MIT-LICENSE
105
+ - README.md
106
+ - Rakefile
107
+ - lib/debtective.rb
108
+ - lib/debtective/configuration.rb
109
+ - lib/debtective/end_of_statement.rb
110
+ - lib/debtective/file_todos.rb
111
+ - lib/debtective/railtie.rb
112
+ - lib/debtective/report.rb
113
+ - lib/debtective/todo_list.rb
114
+ - lib/debtective/version.rb
115
+ - lib/tasks/debtective/todo_list.rake
116
+ homepage: https://github.com/BigBigDoudou/debtective
117
+ licenses:
118
+ - MIT
119
+ metadata:
120
+ homepage_uri: https://github.com/BigBigDoudou/debtective
121
+ source_code_uri: https://github.com/perangusta/debtective
122
+ changelog_uri: https://github.com/perangusta/debtective/CHANGELOG.md
123
+ rubygems_mfa_required: 'true'
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '3.0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubygems_version: 3.2.32
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Find TODOs and compute debt size
143
+ test_files: []