solidstats 0.0.4 → 1.1.0

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.
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solidstats
4
+ # Service to collect and process TODO items from codebase
5
+ class TodoService < DataCollectorService
6
+ # Initialize with default cache settings for TODO items
7
+ def initialize
8
+ super(Rails.root.join("tmp", "solidstats_todos.json"))
9
+ end
10
+
11
+ # Generate a summary for the dashboard display
12
+ # @return [Hash] Summary information with counts and status
13
+ def summary
14
+ todos = fetch
15
+ return { count: 0, status: "success", message: "No TODO items found" } if todos.nil? || todos.empty?
16
+
17
+ stats = calculate_stats(todos)
18
+
19
+ {
20
+ count: todos.count,
21
+ status: determine_status(todos),
22
+ message: generate_message(todos.count),
23
+ by_type: stats[:by_type],
24
+ hotspots: stats[:hotspots]
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ # Calculate statistics for the TODO items
31
+ # @param todos [Array] List of TODO items
32
+ # @return [Hash] Statistics about the TODO items
33
+ def calculate_stats(todos)
34
+ stats = {
35
+ by_type: {
36
+ "TODO" => todos.count { |t| (t[:type] || t["type"]) == "TODO" },
37
+ "FIXME" => todos.count { |t| (t[:type] || t["type"]) == "FIXME" },
38
+ "HACK" => todos.count { |t| (t[:type] || t["type"]) == "HACK" }
39
+ },
40
+ by_file: {}
41
+ }
42
+
43
+ # Group by file path
44
+ todos.each do |todo|
45
+ file_path = todo[:file] || todo["file"]
46
+ stats[:by_file][file_path] ||= 0
47
+ stats[:by_file][file_path] += 1
48
+ end
49
+
50
+ # Find files with most TODOs (top 5)
51
+ stats[:hotspots] = stats[:by_file].sort_by { |_, count| -count }
52
+ .first(5)
53
+ .to_h
54
+
55
+ stats
56
+ end
57
+
58
+ # Determine the status indicator based on TODO items
59
+ # @param todos [Array] List of TODO items
60
+ # @return [String] Status indicator (success, warning, or danger)
61
+ def determine_status(todos)
62
+ fixme_count = todos.count { |t| t[:type] == "FIXME" }
63
+ hack_count = todos.count { |t| t[:type] == "HACK" }
64
+
65
+ if hack_count > 0
66
+ "danger"
67
+ elsif fixme_count > 0
68
+ "warning"
69
+ elsif todos.count > 10
70
+ "warning"
71
+ else
72
+ "success"
73
+ end
74
+ end
75
+
76
+ # Generate a status message based on TODO count
77
+ # @param count [Integer] Number of TODOs
78
+ # @return [String] Human-readable status message
79
+ def generate_message(count)
80
+ "#{count} #{count == 1 ? 'item' : 'items'} found"
81
+ end
82
+
83
+ # Collect fresh TODO data by scanning the codebase
84
+ # @return [Array] List of TODO items found
85
+ def collect_data
86
+ todos = []
87
+ raw_output = `grep -r -n -E "(TODO|FIXME|HACK|todo|fixme|hack)" app lib`.split("\n")
88
+
89
+ raw_output.each do |line|
90
+ if line =~ /^(.+):(\d+):(.+)$/
91
+ file = $1
92
+ line_num = $2
93
+ content = $3
94
+
95
+ # Match only exact lowercase or uppercase variants
96
+ type_match = content.match(/(TODO|FIXME|HACK|todo|fixme|hack)/)
97
+ if type_match
98
+ # Convert to uppercase for consistency
99
+ type = type_match.to_s.upcase
100
+
101
+ todos << {
102
+ file: file,
103
+ line: line_num.to_i,
104
+ content: content.strip,
105
+ type: type
106
+ }
107
+ end
108
+ end
109
+ end
110
+
111
+ todos
112
+ end
113
+ end
114
+ end