better_page 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +62 -0
- data/MIT-LICENSE +20 -0
- data/README.md +357 -0
- data/Rakefile +3 -0
- data/docs/00-README.md +17 -0
- data/docs/01-getting-started.md +137 -0
- data/docs/02-component-registry.md +192 -0
- data/docs/03-base-pages.md +238 -0
- data/docs/04-schema-validation.md +180 -0
- data/docs/05-turbo-support.md +220 -0
- data/docs/06-compliance-analyzer.md +147 -0
- data/docs/07-configuration.md +157 -0
- data/guide/00-README.md +32 -0
- data/guide/01-quick-start.md +148 -0
- data/guide/02-building-index-page.md +258 -0
- data/guide/03-building-show-page.md +266 -0
- data/guide/04-building-form-page.md +309 -0
- data/guide/05-custom-pages.md +325 -0
- data/guide/06-best-practices.md +311 -0
- data/lib/better_page/base_page.rb +161 -0
- data/lib/better_page/compliance/analyzer.rb +409 -0
- data/lib/better_page/component_registry.rb +393 -0
- data/lib/better_page/config.rb +165 -0
- data/lib/better_page/configuration.rb +153 -0
- data/lib/better_page/custom_base_page.rb +85 -0
- data/lib/better_page/default_components.rb +200 -0
- data/lib/better_page/form_base_page.rb +170 -0
- data/lib/better_page/index_base_page.rb +69 -0
- data/lib/better_page/railtie.rb +34 -0
- data/lib/better_page/show_base_page.rb +120 -0
- data/lib/better_page/validation_error.rb +7 -0
- data/lib/better_page/version.rb +3 -0
- data/lib/better_page.rb +80 -0
- data/lib/generators/better_page/component_generator.rb +131 -0
- data/lib/generators/better_page/install_generator.rb +160 -0
- data/lib/generators/better_page/page_generator.rb +101 -0
- data/lib/generators/better_page/sync_generator.rb +109 -0
- data/lib/generators/better_page/templates/application_page.rb.tt +12 -0
- data/lib/generators/better_page/templates/better_page_initializer.rb.tt +53 -0
- data/lib/generators/better_page/templates/custom_base_page.rb.tt +83 -0
- data/lib/generators/better_page/templates/custom_page.rb.tt +31 -0
- data/lib/generators/better_page/templates/edit_page.rb.tt +46 -0
- data/lib/generators/better_page/templates/form_base_page.rb.tt +126 -0
- data/lib/generators/better_page/templates/index_base_page.rb.tt +65 -0
- data/lib/generators/better_page/templates/index_page.rb.tt +56 -0
- data/lib/generators/better_page/templates/javascript/controllers/app_nav_controller.js +57 -0
- data/lib/generators/better_page/templates/javascript/controllers/drawer_controller.js +99 -0
- data/lib/generators/better_page/templates/javascript/controllers/dropdown_controller.js +60 -0
- data/lib/generators/better_page/templates/javascript/controllers/index.js +36 -0
- data/lib/generators/better_page/templates/javascript/controllers/modal_controller.js +70 -0
- data/lib/generators/better_page/templates/javascript/controllers/sidebar_controller.js +152 -0
- data/lib/generators/better_page/templates/javascript/controllers/table_controller.js +60 -0
- data/lib/generators/better_page/templates/javascript/controllers/tabs_controller.js +89 -0
- data/lib/generators/better_page/templates/new_page.rb.tt +46 -0
- data/lib/generators/better_page/templates/show_base_page.rb.tt +117 -0
- data/lib/generators/better_page/templates/show_page.rb.tt +45 -0
- data/lib/generators/better_page/templates/view_components/application_view_component.rb.tt +7 -0
- data/lib/generators/better_page/templates/view_components/custom_view_component.html.erb.tt +21 -0
- data/lib/generators/better_page/templates/view_components/custom_view_component.rb.tt +21 -0
- data/lib/generators/better_page/templates/view_components/form_view_component.html.erb.tt +25 -0
- data/lib/generators/better_page/templates/view_components/form_view_component.rb.tt +23 -0
- data/lib/generators/better_page/templates/view_components/index_view_component.html.erb.tt +33 -0
- data/lib/generators/better_page/templates/view_components/index_view_component.rb.tt +29 -0
- data/lib/generators/better_page/templates/view_components/show_view_component.html.erb.tt +29 -0
- data/lib/generators/better_page/templates/view_components/show_view_component.rb.tt +25 -0
- data/lib/generators/better_page/templates/view_components/ui/alerts_component.html.erb.tt +47 -0
- data/lib/generators/better_page/templates/view_components/ui/alerts_component.rb.tt +47 -0
- data/lib/generators/better_page/templates/view_components/ui/content_section_component.html.erb.tt +42 -0
- data/lib/generators/better_page/templates/view_components/ui/content_section_component.rb.tt +34 -0
- data/lib/generators/better_page/templates/view_components/ui/drawer_component.html.erb.tt +73 -0
- data/lib/generators/better_page/templates/view_components/ui/drawer_component.rb.tt +78 -0
- data/lib/generators/better_page/templates/view_components/ui/errors_component.html.erb.tt +23 -0
- data/lib/generators/better_page/templates/view_components/ui/errors_component.rb.tt +18 -0
- data/lib/generators/better_page/templates/view_components/ui/field_component.html.erb.tt +65 -0
- data/lib/generators/better_page/templates/view_components/ui/field_component.rb.tt +91 -0
- data/lib/generators/better_page/templates/view_components/ui/footer_component.html.erb.tt +33 -0
- data/lib/generators/better_page/templates/view_components/ui/footer_component.rb.tt +32 -0
- data/lib/generators/better_page/templates/view_components/ui/header_component.html.erb.tt +55 -0
- data/lib/generators/better_page/templates/view_components/ui/header_component.rb.tt +39 -0
- data/lib/generators/better_page/templates/view_components/ui/modal_component.html.erb.tt +70 -0
- data/lib/generators/better_page/templates/view_components/ui/modal_component.rb.tt +54 -0
- data/lib/generators/better_page/templates/view_components/ui/overview_component.html.erb.tt +22 -0
- data/lib/generators/better_page/templates/view_components/ui/overview_component.rb.tt +71 -0
- data/lib/generators/better_page/templates/view_components/ui/pagination_component.html.erb.tt +63 -0
- data/lib/generators/better_page/templates/view_components/ui/pagination_component.rb.tt +69 -0
- data/lib/generators/better_page/templates/view_components/ui/panel_component.html.erb.tt +31 -0
- data/lib/generators/better_page/templates/view_components/ui/panel_component.rb.tt +23 -0
- data/lib/generators/better_page/templates/view_components/ui/statistics_component.html.erb.tt +33 -0
- data/lib/generators/better_page/templates/view_components/ui/statistics_component.rb.tt +51 -0
- data/lib/generators/better_page/templates/view_components/ui/table_component.html.erb.tt +112 -0
- data/lib/generators/better_page/templates/view_components/ui/table_component.rb.tt +88 -0
- data/lib/generators/better_page/templates/view_components/ui/tabs_component.html.erb.tt +52 -0
- data/lib/generators/better_page/templates/view_components/ui/tabs_component.rb.tt +76 -0
- data/lib/generators/better_page/templates/view_components/ui/widget_component.html.erb.tt +72 -0
- data/lib/generators/better_page/templates/view_components/ui/widget_component.rb.tt +34 -0
- data/lib/tasks/better_page.rake +70 -0
- data/lib/tasks/better_page_tasks.rake +4 -0
- metadata +188 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterPage
|
|
4
|
+
module Compliance
|
|
5
|
+
# Analyzer for page compliance with architecture conventions.
|
|
6
|
+
# Validates that pages follow the presentation-layer pattern:
|
|
7
|
+
# - No database queries
|
|
8
|
+
# - No business logic
|
|
9
|
+
# - No service layer access
|
|
10
|
+
# - Required component methods implemented
|
|
11
|
+
# - Plain Hash objects (no OpenStruct)
|
|
12
|
+
#
|
|
13
|
+
class Analyzer
|
|
14
|
+
attr_reader :results, :total_pages, :compliant_count, :warning_count, :error_count
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@results = []
|
|
18
|
+
@total_pages = 0
|
|
19
|
+
@compliant_count = 0
|
|
20
|
+
@warning_count = 0
|
|
21
|
+
@error_count = 0
|
|
22
|
+
@verbose = ENV["VERBOSE"] == "true"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Analyze all pages in the app/pages directory
|
|
26
|
+
# @return [void]
|
|
27
|
+
def analyze_all
|
|
28
|
+
page_files = find_all_pages
|
|
29
|
+
@total_pages = page_files.count
|
|
30
|
+
|
|
31
|
+
puts "Found #{@total_pages} page files to analyze..."
|
|
32
|
+
puts
|
|
33
|
+
|
|
34
|
+
page_files.each_with_index do |file_path, index|
|
|
35
|
+
print "\rAnalyzing... #{index + 1}/#{@total_pages}" unless @verbose
|
|
36
|
+
|
|
37
|
+
result = analyze_page(file_path)
|
|
38
|
+
@results << result
|
|
39
|
+
|
|
40
|
+
update_counters(result)
|
|
41
|
+
|
|
42
|
+
if @verbose
|
|
43
|
+
puts format_single_page_report(result)
|
|
44
|
+
puts
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
puts "\r" + (" " * 50) + "\r" unless @verbose
|
|
49
|
+
generate_report
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Analyze a single page file
|
|
53
|
+
# @param file_path [String] path to the page file
|
|
54
|
+
# @return [Hash] analysis result
|
|
55
|
+
def analyze_page(file_path)
|
|
56
|
+
content = File.read(file_path)
|
|
57
|
+
|
|
58
|
+
result = {
|
|
59
|
+
file_path: file_path,
|
|
60
|
+
class_name: extract_class_name(content),
|
|
61
|
+
page_type: categorize_page_type(file_path),
|
|
62
|
+
namespace: extract_namespace(file_path),
|
|
63
|
+
issues: [],
|
|
64
|
+
warnings: [],
|
|
65
|
+
compliant: true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Run all compliance checks
|
|
69
|
+
check_page_structure(content, result)
|
|
70
|
+
check_ui_configuration_only(content, result)
|
|
71
|
+
check_template_system_compliance(content, result)
|
|
72
|
+
check_architecture_compliance(content, result)
|
|
73
|
+
check_hash_usage_patterns(content, result)
|
|
74
|
+
|
|
75
|
+
# Determine overall compliance
|
|
76
|
+
result[:compliant] = result[:issues].empty?
|
|
77
|
+
result[:status] = if result[:issues].any?
|
|
78
|
+
:error
|
|
79
|
+
elsif result[:warnings].any?
|
|
80
|
+
:warning
|
|
81
|
+
else
|
|
82
|
+
:compliant
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
result
|
|
86
|
+
rescue StandardError => e
|
|
87
|
+
{
|
|
88
|
+
file_path: file_path,
|
|
89
|
+
class_name: "PARSE_ERROR",
|
|
90
|
+
page_type: :unknown,
|
|
91
|
+
namespace: "UNKNOWN",
|
|
92
|
+
issues: [ "Parse error: #{e.message}" ],
|
|
93
|
+
warnings: [],
|
|
94
|
+
compliant: false,
|
|
95
|
+
status: :error
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Format a single page analysis result for display
|
|
100
|
+
# @param result [Hash] analysis result
|
|
101
|
+
# @return [String] formatted report
|
|
102
|
+
def format_single_page_report(result)
|
|
103
|
+
output = []
|
|
104
|
+
status_icon = case result[:status]
|
|
105
|
+
when :compliant then "[OK]"
|
|
106
|
+
when :warning then "[WARN]"
|
|
107
|
+
when :error then "[ERROR]"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
output << "#{status_icon} #{result[:file_path]}"
|
|
111
|
+
output << " Class: #{result[:class_name]}" if result[:class_name] != "UNKNOWN"
|
|
112
|
+
output << " Type: #{result[:page_type]} | Namespace: #{result[:namespace]}" if result[:page_type] != :unknown
|
|
113
|
+
|
|
114
|
+
if result[:issues].any?
|
|
115
|
+
output << " Issues:"
|
|
116
|
+
result[:issues].each { |issue| output << " - #{issue}" }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if result[:warnings].any?
|
|
120
|
+
output << " Warnings:"
|
|
121
|
+
result[:warnings].each { |warning| output << " - #{warning}" }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
output.join("\n")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Generate and print the final analysis report
|
|
128
|
+
# @return [void]
|
|
129
|
+
def generate_report
|
|
130
|
+
puts "SUMMARY"
|
|
131
|
+
puts "======="
|
|
132
|
+
puts "Total pages analyzed: #{@total_pages}"
|
|
133
|
+
puts "[OK] Fully compliant: #{@compliant_count} (#{percentage(@compliant_count)}%)"
|
|
134
|
+
puts "[WARN] With warnings: #{@warning_count} (#{percentage(@warning_count)}%)"
|
|
135
|
+
puts "[ERROR] With errors: #{@error_count} (#{percentage(@error_count)}%)"
|
|
136
|
+
puts
|
|
137
|
+
|
|
138
|
+
# Show critical issues
|
|
139
|
+
error_results = @results.select { |r| r[:status] == :error }
|
|
140
|
+
if error_results.any?
|
|
141
|
+
puts "CRITICAL ISSUES"
|
|
142
|
+
puts "==============="
|
|
143
|
+
error_results.each do |result|
|
|
144
|
+
puts "- #{result[:file_path]}"
|
|
145
|
+
result[:issues].each { |issue| puts " - #{issue}" }
|
|
146
|
+
end
|
|
147
|
+
puts
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Show warnings
|
|
151
|
+
warning_results = @results.select { |r| r[:status] == :warning }
|
|
152
|
+
if warning_results.any?
|
|
153
|
+
puts "WARNINGS"
|
|
154
|
+
puts "========"
|
|
155
|
+
warning_results.each do |result|
|
|
156
|
+
puts "- #{result[:file_path]}"
|
|
157
|
+
result[:warnings].each { |warning| puts " - #{warning}" }
|
|
158
|
+
end
|
|
159
|
+
puts
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
generate_recommendations
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def find_all_pages
|
|
168
|
+
Dir.glob("app/pages/**/*_page.rb").sort
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def extract_class_name(content)
|
|
172
|
+
match = content.match(/class\s+([A-Za-z:]+Page)\s*/)
|
|
173
|
+
match ? match[1] : "UNKNOWN"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def extract_namespace(file_path)
|
|
177
|
+
parts = file_path.split("/")
|
|
178
|
+
return "UNKNOWN" unless parts.include?("pages")
|
|
179
|
+
|
|
180
|
+
pages_index = parts.index("pages")
|
|
181
|
+
return "Root" if parts.length <= pages_index + 2
|
|
182
|
+
|
|
183
|
+
parts[pages_index + 1].capitalize
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def categorize_page_type(file_path)
|
|
187
|
+
case file_path
|
|
188
|
+
when %r{/base_page\.rb$}, %r{/application_page\.rb$}
|
|
189
|
+
:base
|
|
190
|
+
when %r{pages/[^/]+_page\.rb$}
|
|
191
|
+
:root_page
|
|
192
|
+
when %r{pages/\w+/\w+_page\.rb$}
|
|
193
|
+
:namespaced_page
|
|
194
|
+
when %r{/index_page\.rb$}
|
|
195
|
+
:index_page
|
|
196
|
+
when %r{/show_page\.rb$}
|
|
197
|
+
:show_page
|
|
198
|
+
when %r{/new_page\.rb$}
|
|
199
|
+
:form_page
|
|
200
|
+
when %r{/edit_page\.rb$}
|
|
201
|
+
:form_page
|
|
202
|
+
when %r{/custom_page\.rb$}
|
|
203
|
+
:custom_page
|
|
204
|
+
else
|
|
205
|
+
:unknown
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def check_page_structure(content, result)
|
|
210
|
+
# Check proper class naming
|
|
211
|
+
class_name = result[:class_name]
|
|
212
|
+
result[:issues] << 'Page class must end with "Page"' unless class_name.end_with?("Page")
|
|
213
|
+
|
|
214
|
+
# Check namespace structure for namespaced pages
|
|
215
|
+
if result[:page_type] == :namespaced_page
|
|
216
|
+
expected_module = result[:namespace]
|
|
217
|
+
unless content.match?(/module\s+#{expected_module}/i)
|
|
218
|
+
result[:warnings] << "Expected module #{expected_module} for namespaced page"
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Check for initialize method (required for non-base pages)
|
|
223
|
+
return if content.include?("def initialize") || result[:page_type] == :base
|
|
224
|
+
|
|
225
|
+
result[:issues] << "Page must have initialize method"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def check_ui_configuration_only(content, result)
|
|
229
|
+
# Check for FORBIDDEN database access
|
|
230
|
+
database_patterns = [
|
|
231
|
+
{ pattern: /\.find\(/, message: "Database queries forbidden in Page" },
|
|
232
|
+
{ pattern: /\.where\(/, message: "Database queries forbidden in Page" },
|
|
233
|
+
{ pattern: /\.all\b/, message: "Database queries forbidden in Page" },
|
|
234
|
+
{ pattern: /\.count\b/, message: "Database queries forbidden in Page" },
|
|
235
|
+
{ pattern: /\.joins\(/, message: "Database queries forbidden in Page" },
|
|
236
|
+
{ pattern: /\.includes\(/, message: "Database queries forbidden in Page" }
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
database_patterns.each do |check|
|
|
240
|
+
result[:issues] << check[:message] if content.match?(check[:pattern])
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Check for business logic (FORBIDDEN)
|
|
244
|
+
business_logic_patterns = [
|
|
245
|
+
{ pattern: /def\s+calculate_/, message: "Business calculations forbidden in Page" },
|
|
246
|
+
{ pattern: /def\s+process_/, message: "Business processing forbidden in Page" },
|
|
247
|
+
{ pattern: /def\s+validate_(?!form_panels)/, message: "Validation logic forbidden in Page" },
|
|
248
|
+
{ pattern: /def\s+save_/, message: "Persistence operations forbidden in Page" }
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
business_logic_patterns.each do |check|
|
|
252
|
+
result[:issues] << check[:message] if content.match?(check[:pattern])
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Check for Service layer access (FORBIDDEN)
|
|
256
|
+
if content.match?(/Service\.new|service\s*=.*Service/)
|
|
257
|
+
result[:issues] << "Service layer access forbidden in Page"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Check for external dependencies (FORBIDDEN)
|
|
261
|
+
external_patterns = [
|
|
262
|
+
/Net::HTTP/,
|
|
263
|
+
/HTTParty/,
|
|
264
|
+
/Faraday/,
|
|
265
|
+
/Redis/
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
external_patterns.each do |pattern|
|
|
269
|
+
if content.match?(pattern)
|
|
270
|
+
result[:issues] << "External dependencies forbidden in Page"
|
|
271
|
+
break
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def check_template_system_compliance(content, result)
|
|
277
|
+
page_type = result[:page_type]
|
|
278
|
+
|
|
279
|
+
# Check for required component methods based on page type
|
|
280
|
+
# New pattern: simple method names matching registered components
|
|
281
|
+
case page_type
|
|
282
|
+
when :index_page
|
|
283
|
+
required_methods = %w[header table]
|
|
284
|
+
check_required_component_methods(content, result, required_methods)
|
|
285
|
+
|
|
286
|
+
when :show_page
|
|
287
|
+
required_methods = %w[header]
|
|
288
|
+
check_required_component_methods(content, result, required_methods)
|
|
289
|
+
|
|
290
|
+
when :form_page
|
|
291
|
+
required_methods = %w[header panels]
|
|
292
|
+
check_required_component_methods(content, result, required_methods)
|
|
293
|
+
|
|
294
|
+
when :custom_page
|
|
295
|
+
required_methods = %w[content]
|
|
296
|
+
check_required_component_methods(content, result, required_methods)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Check for main page method (index, show, new, edit, form, custom)
|
|
300
|
+
page_methods = %w[index show new edit form custom]
|
|
301
|
+
found_main_method = page_methods.any? { |method| content.include?("def #{method}") }
|
|
302
|
+
|
|
303
|
+
return if found_main_method || result[:page_type] == :base
|
|
304
|
+
|
|
305
|
+
result[:warnings] << "Page should have main method (index, show, form, or custom)"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def check_architecture_compliance(content, result)
|
|
309
|
+
# Check for hardcoded paths (DISCOURAGED)
|
|
310
|
+
if content.match?(%r{"/\w+})
|
|
311
|
+
result[:warnings] << "Hardcoded paths detected - prefer Rails path helpers"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Check for HTML generation (should be in components)
|
|
315
|
+
html_patterns = [
|
|
316
|
+
/<\w+/,
|
|
317
|
+
/html_safe/,
|
|
318
|
+
/raw\(/,
|
|
319
|
+
/content_tag/
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
html_patterns.each do |pattern|
|
|
323
|
+
if content.match?(pattern)
|
|
324
|
+
result[:warnings] << "HTML generation found - should be handled by template system"
|
|
325
|
+
break
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def check_hash_usage_patterns(content, result)
|
|
331
|
+
# Check for OpenStruct usage (FORBIDDEN)
|
|
332
|
+
if content.include?("OpenStruct") || content.include?("ostruct")
|
|
333
|
+
result[:issues] << "OpenStruct usage forbidden - use plain Hash objects"
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Check for Struct usage (DISCOURAGED)
|
|
337
|
+
if content.match?(/Struct\.new/)
|
|
338
|
+
result[:warnings] << "Struct usage discouraged - prefer plain Hash for consistency"
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def check_required_component_methods(content, result, required_methods)
|
|
343
|
+
required_methods.each do |method|
|
|
344
|
+
unless content.include?("def #{method}")
|
|
345
|
+
result[:issues] << "Missing required component method: #{method}"
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def update_counters(result)
|
|
351
|
+
case result[:status]
|
|
352
|
+
when :compliant
|
|
353
|
+
@compliant_count += 1
|
|
354
|
+
when :warning
|
|
355
|
+
@warning_count += 1
|
|
356
|
+
when :error
|
|
357
|
+
@error_count += 1
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def percentage(count)
|
|
362
|
+
return 0 if @total_pages.zero?
|
|
363
|
+
|
|
364
|
+
((count.to_f / @total_pages) * 100).round(1)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def generate_recommendations
|
|
368
|
+
puts "RECOMMENDATIONS"
|
|
369
|
+
puts "==============="
|
|
370
|
+
|
|
371
|
+
issues_by_type = {}
|
|
372
|
+
@results.each do |result|
|
|
373
|
+
(result[:issues] + result[:warnings]).each do |issue|
|
|
374
|
+
issues_by_type[issue] ||= 0
|
|
375
|
+
issues_by_type[issue] += 1
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
if issues_by_type.any?
|
|
380
|
+
puts "Top issues to address:"
|
|
381
|
+
issues_by_type.sort_by { |_, count| -count }.first(5).each_with_index do |(issue, count), index|
|
|
382
|
+
puts "#{index + 1}. #{issue} (#{count} pages affected)"
|
|
383
|
+
end
|
|
384
|
+
else
|
|
385
|
+
puts "All pages are compliant!"
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
puts
|
|
389
|
+
puts "NEXT STEPS:"
|
|
390
|
+
|
|
391
|
+
if @error_count > 0
|
|
392
|
+
puts "1. Remove database queries from Pages"
|
|
393
|
+
puts "2. Remove business logic - keep UI configuration only"
|
|
394
|
+
puts "3. Implement required component methods for template system"
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
if @warning_count > 0
|
|
398
|
+
step = @error_count > 0 ? 4 : 1
|
|
399
|
+
puts "#{step}. Replace OpenStruct with plain Hash objects"
|
|
400
|
+
puts "#{step + 1}. Use Rails path helpers instead of hardcoded paths"
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
if @error_count.zero? && @warning_count.zero?
|
|
404
|
+
puts "1. Consider implementing optional component methods for better UI"
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|