solidstats 1.1.0 → 2.0.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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +33 -0
  4. data/app/assets/javascripts/solidstats/application.js +257 -0
  5. data/app/assets/javascripts/solidstats/dashboard.js +225 -0
  6. data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
  7. data/app/assets/stylesheets/solidstats/application.css +6 -1
  8. data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
  9. data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
  10. data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
  11. data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
  12. data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
  13. data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
  14. data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
  15. data/app/assets/stylesheets/solidstats/components/security.css +332 -0
  16. data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
  17. data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
  18. data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
  19. data/app/components/solidstats/base_component.rb +88 -0
  20. data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
  21. data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
  22. data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
  23. data/app/components/solidstats/code_quality/section_component.rb +34 -0
  24. data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
  25. data/app/components/solidstats/dashboard_header_component.rb +33 -0
  26. data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
  27. data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
  28. data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
  29. data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
  30. data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
  31. data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
  32. data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
  33. data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
  34. data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
  35. data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
  36. data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
  37. data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
  38. data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
  39. data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
  40. data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
  41. data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
  42. data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
  43. data/app/components/solidstats/quick_navigation_component.rb +21 -0
  44. data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
  45. data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
  46. data/app/components/solidstats/security/overview_component.html.erb +21 -0
  47. data/app/components/solidstats/security/overview_component.rb +104 -0
  48. data/app/components/solidstats/security/section_component.html.erb +26 -0
  49. data/app/components/solidstats/security/section_component.rb +52 -0
  50. data/app/components/solidstats/security/timeline_component.html.erb +39 -0
  51. data/app/components/solidstats/security/timeline_component.rb +43 -0
  52. data/app/components/solidstats/tasks_section_component.html.erb +17 -0
  53. data/app/components/solidstats/tasks_section_component.rb +22 -0
  54. data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
  55. data/app/components/solidstats/ui/action_button_component.rb +71 -0
  56. data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
  57. data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
  58. data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
  59. data/app/components/solidstats/ui/navigation_component.rb +72 -0
  60. data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
  61. data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
  62. data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
  63. data/app/components/solidstats/ui/status_badge_component.rb +42 -0
  64. data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
  65. data/app/components/solidstats/ui/summary_card_component.rb +63 -0
  66. data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
  67. data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
  68. data/app/controllers/solidstats/dashboard_controller.rb +8 -5
  69. data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
  70. data/app/helpers/solidstats/application_helper.rb +42 -0
  71. data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
  72. data/app/services/solidstats/log_size_monitor_service.rb +10 -10
  73. data/app/views/layouts/solidstats/application.html.erb +2 -1
  74. data/app/views/solidstats/dashboard/index.html.erb +67 -1337
  75. data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
  76. data/config/routes.rb +5 -2
  77. data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
  78. data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
  79. data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
  80. data/lib/generators/solidstats/feature/templates/component.scss +243 -0
  81. data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
  82. data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
  83. data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
  84. data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
  85. data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
  86. data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
  87. data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
  88. data/lib/generators/solidstats/install_generator.rb +109 -0
  89. data/lib/generators/solidstats/templates/initializer.rb +112 -0
  90. data/lib/solidstats/asset_compatibility.rb +238 -0
  91. data/lib/solidstats/asset_manifest.rb +205 -0
  92. data/lib/solidstats/engine.rb +114 -9
  93. data/lib/solidstats/version.rb +1 -1
  94. data/lib/solidstats.rb +299 -2
  95. data/lib/tasks/solidstats_install.rake +122 -2
  96. metadata +97 -2
@@ -0,0 +1,136 @@
1
+ require "net/http"
2
+
3
+ module Solidstats
4
+ module GemMetadata
5
+ class FetcherService
6
+ GemMeta = Struct.new(:name, :version, :current_version, :released, :info, :runtime, keyword_init: true)
7
+
8
+ def self.call(gem_names = nil, force_refresh = false)
9
+ new.call(gem_names, force_refresh)
10
+ end
11
+
12
+ def call(gem_names = nil, force_refresh = false)
13
+ gem_names = get_gem_names(gem_names)
14
+ fetch_metadata(gem_names, force_refresh)
15
+ end
16
+
17
+ private
18
+
19
+ def get_gem_names(gem_names)
20
+ return gem_names if gem_names.present?
21
+
22
+ gemfile_lock = Rails.root.join("Gemfile.lock")
23
+ return [] unless File.exist?(gemfile_lock)
24
+
25
+ lockfile = File.read(gemfile_lock)
26
+ parser = Bundler::LockfileParser.new(lockfile)
27
+ parser.specs.map(&:name)
28
+ end
29
+
30
+ def get_current_versions
31
+ gemfile_lock = Rails.root.join("Gemfile.lock")
32
+ return {} unless File.exist?(gemfile_lock)
33
+
34
+ lockfile = File.read(gemfile_lock)
35
+ parser = Bundler::LockfileParser.new(lockfile)
36
+
37
+ parser.specs.each_with_object({}) do |spec, versions|
38
+ versions[spec.name] = spec.version.to_s
39
+ end
40
+ end
41
+
42
+ def fetch_metadata(gem_names, force_refresh)
43
+ current_versions = get_current_versions
44
+
45
+ gem_names.map do |name|
46
+ fetch_gem_data(name, current_versions[name], force_refresh)
47
+ end
48
+ end
49
+
50
+ def fetch_gem_data(name, current_version, force_refresh)
51
+ cached_data = fetch_from_cache(name) unless force_refresh
52
+
53
+ if cached_data
54
+ # Update current version if it changed since caching
55
+ if current_version && cached_data.current_version != current_version
56
+ cached_data.current_version = current_version
57
+ cache_data(name, cached_data)
58
+ end
59
+ return cached_data
60
+ end
61
+
62
+ begin
63
+ data = fetch_from_api(name)
64
+ meta = GemMeta.new(
65
+ name: data["name"],
66
+ version: data["version"],
67
+ current_version: current_version,
68
+ released: data["version_created_at"] ? Time.parse(data["version_created_at"]) : nil,
69
+ info: data["info"],
70
+ runtime: data["dependencies"] && data["dependencies"]["runtime"] || []
71
+ )
72
+ cache_data(name, meta)
73
+ meta
74
+ rescue => e
75
+ Rails.logger.error("Failed to fetch gem metadata for #{name}: #{e.message}")
76
+ GemMeta.new(name: name, current_version: current_version, info: "(API unavailable)")
77
+ end
78
+ end
79
+
80
+ def fetch_from_api(name)
81
+ uri = URI.parse("https://rubygems.org/api/v1/gems/#{name}.json")
82
+ http = Net::HTTP.new(uri.host, uri.port)
83
+ http.use_ssl = true
84
+ http.open_timeout = 5
85
+ http.read_timeout = 5
86
+
87
+ response = http.get(uri.request_uri)
88
+
89
+ if response.code == "200"
90
+ JSON.parse(response.body)
91
+ else
92
+ raise "API request failed with status #{response.code}"
93
+ end
94
+ end
95
+
96
+ def fetch_from_cache(name)
97
+ cache_file = cache_path(name)
98
+ return nil unless File.exist?(cache_file)
99
+
100
+ data = JSON.parse(File.read(cache_file))
101
+ cache_time = File.mtime(cache_file)
102
+
103
+ # Return nil if cache is older than 24 hours
104
+ return nil if Time.now - cache_time > 24.hours
105
+
106
+ GemMeta.new(
107
+ name: data["name"],
108
+ version: data["version"],
109
+ current_version: data["current_version"],
110
+ released: data["released"] ? Time.parse(data["released"]) : nil,
111
+ info: data["info"],
112
+ runtime: data["runtime"] || []
113
+ )
114
+ end
115
+
116
+ def cache_data(name, meta)
117
+ FileUtils.mkdir_p(File.dirname(cache_path(name)))
118
+
119
+ data = {
120
+ "name" => meta.name,
121
+ "version" => meta.version,
122
+ "current_version" => meta.current_version,
123
+ "released" => meta.released&.iso8601,
124
+ "info" => meta.info,
125
+ "runtime" => meta.runtime
126
+ }
127
+
128
+ File.write(cache_path(name), data.to_json)
129
+ end
130
+
131
+ def cache_path(name)
132
+ Rails.root.join("tmp", "cache", "gem_metadata", "#{name}.json")
133
+ end
134
+ end
135
+ end
136
+ end
@@ -6,7 +6,7 @@ module Solidstats
6
6
  def collect_data
7
7
  log_files = scan_log_directory
8
8
  total_size_bytes = log_files.sum { |file| file[:size_bytes] }
9
-
9
+
10
10
  # Create aggregated data
11
11
  {
12
12
  log_dir_path: log_directory,
@@ -24,11 +24,11 @@ module Solidstats
24
24
  if filename.present?
25
25
  # Truncate specific log file
26
26
  # Ensure filename has .log extension
27
- filename = "#{filename}.log" unless filename.end_with?('.log')
28
-
27
+ filename = "#{filename}.log" unless filename.end_with?(".log")
28
+
29
29
  file_path = File.join(log_directory, filename)
30
30
  if File.exist?(file_path)
31
- File.open(file_path, 'w') { |f| f.truncate(0) }
31
+ File.open(file_path, "w") { |f| f.truncate(0) }
32
32
  { success: true, message: "Log file '#{filename}' truncated successfully" }
33
33
  else
34
34
  { success: false, message: "Log file '#{filename}' not found" }
@@ -36,7 +36,7 @@ module Solidstats
36
36
  else
37
37
  # Truncate all log files
38
38
  scan_log_directory.each do |log_file|
39
- File.open(log_file[:path], 'w') { |f| f.truncate(0) }
39
+ File.open(log_file[:path], "w") { |f| f.truncate(0) }
40
40
  end
41
41
  { success: true, message: "All log files truncated successfully" }
42
42
  end
@@ -50,16 +50,16 @@ module Solidstats
50
50
  def log_directory
51
51
  Rails.root.join("log").to_s
52
52
  end
53
-
53
+
54
54
  def scan_log_directory
55
55
  log_files = []
56
-
56
+
57
57
  # Get all files in the log directory
58
58
  Dir.glob(File.join(log_directory, "*.log")).each do |file_path|
59
59
  if File.file?(file_path)
60
60
  size_bytes = File.size(file_path)
61
61
  filename = File.basename(file_path)
62
-
62
+
63
63
  log_files << {
64
64
  filename: filename,
65
65
  path: file_path,
@@ -70,7 +70,7 @@ module Solidstats
70
70
  }
71
71
  end
72
72
  end
73
-
73
+
74
74
  # Sort by size (largest first)
75
75
  log_files.sort_by { |file| -file[:size_bytes] }
76
76
  end
@@ -81,7 +81,7 @@ module Solidstats
81
81
 
82
82
  def calculate_status(size_bytes)
83
83
  size_mb = bytes_to_mb(size_bytes)
84
-
84
+
85
85
  if size_mb >= DANGER_THRESHOLD
86
86
  :danger
87
87
  elsif size_mb >= WARNING_THRESHOLD
@@ -7,7 +7,8 @@
7
7
 
8
8
  <%= yield :head %>
9
9
 
10
- <%= stylesheet_link_tag "solidstats/application", media: "all" %>
10
+ <%= solidstats_styles %>
11
+ <%= solidstats_scripts %>
11
12
  </head>
12
13
  <body>
13
14