rails_map 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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.idea/.gitignore +8 -0
  3. data/.idea/material_theme_project_new.xml +12 -0
  4. data/AUTHENTICATION.md +221 -0
  5. data/CHANGELOG.md +75 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE.txt +21 -0
  8. data/QUICKSTART.md +156 -0
  9. data/README.md +178 -0
  10. data/Rakefile +8 -0
  11. data/app/controllers/rails_map/docs_controller.rb +73 -0
  12. data/app/models/rails_map/user.rb +12 -0
  13. data/app/views/rails_map/docs/_styles.html.erb +489 -0
  14. data/app/views/rails_map/docs/controller.html.erb +137 -0
  15. data/app/views/rails_map/docs/index.html.erb +212 -0
  16. data/app/views/rails_map/docs/model.html.erb +214 -0
  17. data/app/views/rails_map/docs/routes.html.erb +139 -0
  18. data/config/initializers/rails_map.example.rb +44 -0
  19. data/config/routes.rb +9 -0
  20. data/docs/index.html +1354 -0
  21. data/lib/generators/rails_map/install_generator.rb +49 -0
  22. data/lib/generators/rails_map/templates/README +47 -0
  23. data/lib/generators/rails_map/templates/initializer.rb +38 -0
  24. data/lib/generators/rails_map/templates/migration.rb +14 -0
  25. data/lib/rails_map/auth.rb +15 -0
  26. data/lib/rails_map/configuration.rb +35 -0
  27. data/lib/rails_map/engine.rb +11 -0
  28. data/lib/rails_map/generators/html_generator.rb +120 -0
  29. data/lib/rails_map/parsers/model_parser.rb +257 -0
  30. data/lib/rails_map/parsers/route_parser.rb +356 -0
  31. data/lib/rails_map/railtie.rb +46 -0
  32. data/lib/rails_map/version.rb +5 -0
  33. data/lib/rails_map.rb +66 -0
  34. data/templates/controller.html.erb +74 -0
  35. data/templates/index.html.erb +64 -0
  36. data/templates/layout.html.erb +289 -0
  37. data/templates/model.html.erb +219 -0
  38. data/templates/routes.html.erb +86 -0
  39. metadata +144 -0
@@ -0,0 +1,289 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%= title %> - <%= app_name %> Documentation</title>
7
+ <style>
8
+ :root {
9
+ --primary-color: <%= theme_color %>;
10
+ --primary-dark: color-mix(in srgb, <%= theme_color %> 80%, black);
11
+ --bg-color: #f8fafc;
12
+ --card-bg: #ffffff;
13
+ --text-color: #1e293b;
14
+ --text-muted: #64748b;
15
+ --border-color: #e2e8f0;
16
+ --success-color: #22c55e;
17
+ --warning-color: #f59e0b;
18
+ --danger-color: #ef4444;
19
+ --info-color: #3b82f6;
20
+ }
21
+
22
+ * {
23
+ box-sizing: border-box;
24
+ margin: 0;
25
+ padding: 0;
26
+ }
27
+
28
+ body {
29
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
30
+ background-color: var(--bg-color);
31
+ color: var(--text-color);
32
+ line-height: 1.6;
33
+ }
34
+
35
+ .container {
36
+ max-width: 1400px;
37
+ margin: 0 auto;
38
+ padding: 2rem;
39
+ }
40
+
41
+ /* Header */
42
+ .header {
43
+ background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
44
+ color: white;
45
+ padding: 2rem 0;
46
+ margin-bottom: 2rem;
47
+ }
48
+
49
+ .header h1 {
50
+ font-size: 2rem;
51
+ font-weight: 700;
52
+ }
53
+
54
+ .header .subtitle {
55
+ opacity: 0.9;
56
+ margin-top: 0.5rem;
57
+ }
58
+
59
+ /* Breadcrumb */
60
+ .breadcrumb {
61
+ display: flex;
62
+ gap: 0.5rem;
63
+ margin-bottom: 1.5rem;
64
+ font-size: 0.875rem;
65
+ }
66
+
67
+ .breadcrumb a {
68
+ color: var(--primary-color);
69
+ text-decoration: none;
70
+ }
71
+
72
+ .breadcrumb a:hover {
73
+ text-decoration: underline;
74
+ }
75
+
76
+ .breadcrumb span {
77
+ color: var(--text-muted);
78
+ }
79
+
80
+ /* Navigation */
81
+ .nav {
82
+ display: flex;
83
+ gap: 1rem;
84
+ margin-bottom: 2rem;
85
+ }
86
+
87
+ .nav a {
88
+ padding: 0.75rem 1.5rem;
89
+ background: var(--card-bg);
90
+ border: 1px solid var(--border-color);
91
+ border-radius: 0.5rem;
92
+ color: var(--text-color);
93
+ text-decoration: none;
94
+ font-weight: 500;
95
+ transition: all 0.2s;
96
+ }
97
+
98
+ .nav a:hover, .nav a.active {
99
+ background: var(--primary-color);
100
+ color: white;
101
+ border-color: var(--primary-color);
102
+ }
103
+
104
+ /* Cards */
105
+ .card {
106
+ background: var(--card-bg);
107
+ border: 1px solid var(--border-color);
108
+ border-radius: 0.75rem;
109
+ padding: 1.5rem;
110
+ margin-bottom: 1.5rem;
111
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
112
+ }
113
+
114
+ .card-header {
115
+ display: flex;
116
+ justify-content: space-between;
117
+ align-items: center;
118
+ margin-bottom: 1rem;
119
+ padding-bottom: 1rem;
120
+ border-bottom: 1px solid var(--border-color);
121
+ }
122
+
123
+ .card-title {
124
+ font-size: 1.25rem;
125
+ font-weight: 600;
126
+ }
127
+
128
+ /* Grid */
129
+ .grid {
130
+ display: grid;
131
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
132
+ gap: 1.5rem;
133
+ }
134
+
135
+ /* Tables */
136
+ .table-container {
137
+ overflow-x: auto;
138
+ }
139
+
140
+ table {
141
+ width: 100%;
142
+ border-collapse: collapse;
143
+ }
144
+
145
+ th, td {
146
+ padding: 0.75rem 1rem;
147
+ text-align: left;
148
+ border-bottom: 1px solid var(--border-color);
149
+ }
150
+
151
+ th {
152
+ background: var(--bg-color);
153
+ font-weight: 600;
154
+ font-size: 0.875rem;
155
+ text-transform: uppercase;
156
+ letter-spacing: 0.05em;
157
+ color: var(--text-muted);
158
+ }
159
+
160
+ tr:hover {
161
+ background: var(--bg-color);
162
+ }
163
+
164
+ /* Badges */
165
+ .badge {
166
+ display: inline-block;
167
+ padding: 0.25rem 0.75rem;
168
+ border-radius: 9999px;
169
+ font-size: 0.75rem;
170
+ font-weight: 600;
171
+ text-transform: uppercase;
172
+ }
173
+
174
+ .badge-get { background: #dcfce7; color: #166534; }
175
+ .badge-post { background: #dbeafe; color: #1e40af; }
176
+ .badge-put, .badge-patch { background: #fef3c7; color: #92400e; }
177
+ .badge-delete { background: #fee2e2; color: #991b1b; }
178
+ .badge-any { background: #e2e8f0; color: #475569; }
179
+
180
+ .badge-belongs-to { background: #ede9fe; color: #5b21b6; }
181
+ .badge-has-many { background: #cffafe; color: #0e7490; }
182
+ .badge-has-one { background: #fce7f3; color: #9d174d; }
183
+ .badge-has-and-belongs-to-many { background: #fff7ed; color: #9a3412; }
184
+
185
+ /* Type badges for columns */
186
+ .type-badge {
187
+ font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
188
+ font-size: 0.75rem;
189
+ padding: 0.125rem 0.5rem;
190
+ background: var(--bg-color);
191
+ border-radius: 0.25rem;
192
+ color: var(--text-muted);
193
+ }
194
+
195
+ /* Code */
196
+ code {
197
+ font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
198
+ font-size: 0.875rem;
199
+ background: var(--bg-color);
200
+ padding: 0.125rem 0.375rem;
201
+ border-radius: 0.25rem;
202
+ }
203
+
204
+ /* Links */
205
+ a.link {
206
+ color: var(--primary-color);
207
+ text-decoration: none;
208
+ }
209
+
210
+ a.link:hover {
211
+ text-decoration: underline;
212
+ }
213
+
214
+ /* Stats */
215
+ .stats {
216
+ display: flex;
217
+ gap: 2rem;
218
+ margin-bottom: 2rem;
219
+ }
220
+
221
+ .stat {
222
+ text-align: center;
223
+ }
224
+
225
+ .stat-value {
226
+ font-size: 2rem;
227
+ font-weight: 700;
228
+ color: var(--primary-color);
229
+ }
230
+
231
+ .stat-label {
232
+ font-size: 0.875rem;
233
+ color: var(--text-muted);
234
+ }
235
+
236
+ /* Section headers */
237
+ .section-header {
238
+ font-size: 1.5rem;
239
+ font-weight: 600;
240
+ margin: 2rem 0 1rem;
241
+ padding-bottom: 0.5rem;
242
+ border-bottom: 2px solid var(--primary-color);
243
+ }
244
+
245
+ /* Empty state */
246
+ .empty-state {
247
+ text-align: center;
248
+ padding: 3rem;
249
+ color: var(--text-muted);
250
+ }
251
+
252
+ /* Responsive */
253
+ @media (max-width: 768px) {
254
+ .container {
255
+ padding: 1rem;
256
+ }
257
+
258
+ .grid {
259
+ grid-template-columns: 1fr;
260
+ }
261
+
262
+ .stats {
263
+ flex-direction: column;
264
+ gap: 1rem;
265
+ }
266
+
267
+ .nav {
268
+ flex-wrap: wrap;
269
+ }
270
+ }
271
+ </style>
272
+ </head>
273
+ <body>
274
+ <header class="header">
275
+ <div class="container">
276
+ <h1><%= app_name %> Documentation</h1>
277
+ <p class="subtitle">Auto-generated API documentation</p>
278
+ </div>
279
+ </header>
280
+
281
+ <main class="container">
282
+ <%= content %>
283
+ </main>
284
+
285
+ <footer style="text-align: center; padding: 2rem; color: var(--text-muted); font-size: 0.875rem;">
286
+ Generated by RailsDocGenerator on <%= Time.now.strftime('%Y-%m-%d %H:%M:%S') %>
287
+ </footer>
288
+ </body>
289
+ </html>
@@ -0,0 +1,219 @@
1
+ <div class="breadcrumb">
2
+ <a href="../index.html">Home</a>
3
+ <span>/</span>
4
+ <span><%= model.name %></span>
5
+ </div>
6
+
7
+ <div class="card">
8
+ <div class="card-header">
9
+ <h2 class="card-title"><%= model.name %></h2>
10
+ </div>
11
+
12
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1rem;">
13
+ <div>
14
+ <strong>Table Name:</strong><br>
15
+ <code><%= model.table_name || 'N/A' %></code>
16
+ </div>
17
+ <div>
18
+ <strong>Primary Key:</strong><br>
19
+ <code><%= model.primary_key || 'id' %></code>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Columns Section -->
25
+ <div class="card">
26
+ <div class="card-header">
27
+ <h3 class="card-title">Columns</h3>
28
+ <span class="badge badge-any"><%= model.columns.size %></span>
29
+ </div>
30
+
31
+ <% if model.columns.any? %>
32
+ <div class="table-container">
33
+ <table>
34
+ <thead>
35
+ <tr>
36
+ <th>Name</th>
37
+ <th>Type</th>
38
+ <th>Nullable</th>
39
+ <th>Default</th>
40
+ <th>Details</th>
41
+ </tr>
42
+ </thead>
43
+ <tbody>
44
+ <% model.columns.each do |column| %>
45
+ <tr>
46
+ <td>
47
+ <code><%= column.name %></code>
48
+ <% if column.name == model.primary_key %>
49
+ <span class="badge badge-post" style="margin-left: 0.5rem;">PK</span>
50
+ <% end %>
51
+ </td>
52
+ <td><span class="type-badge"><%= column.type %></span></td>
53
+ <td>
54
+ <% if column.null %>
55
+ <span style="color: var(--success-color);">Yes</span>
56
+ <% else %>
57
+ <span style="color: var(--danger-color);">No</span>
58
+ <% end %>
59
+ </td>
60
+ <td>
61
+ <% if column.default.nil? %>
62
+ <span style="color: var(--text-muted);">—</span>
63
+ <% else %>
64
+ <code><%= column.default %></code>
65
+ <% end %>
66
+ </td>
67
+ <td>
68
+ <% details = [] %>
69
+ <% details << "limit: #{column.limit}" if column.limit %>
70
+ <% details << "precision: #{column.precision}" if column.precision %>
71
+ <% details << "scale: #{column.scale}" if column.scale %>
72
+ <% if details.any? %>
73
+ <span style="color: var(--text-muted); font-size: 0.875rem;"><%= details.join(', ') %></span>
74
+ <% else %>
75
+ <span style="color: var(--text-muted);">—</span>
76
+ <% end %>
77
+ </td>
78
+ </tr>
79
+ <% end %>
80
+ </tbody>
81
+ </table>
82
+ </div>
83
+ <% else %>
84
+ <div class="empty-state">No columns found</div>
85
+ <% end %>
86
+ </div>
87
+
88
+ <!-- Associations Section -->
89
+ <div class="card">
90
+ <div class="card-header">
91
+ <h3 class="card-title">Associations</h3>
92
+ <span class="badge badge-any"><%= model.associations.size %></span>
93
+ </div>
94
+
95
+ <% if model.associations.any? %>
96
+ <div class="table-container">
97
+ <table>
98
+ <thead>
99
+ <tr>
100
+ <th>Type</th>
101
+ <th>Name</th>
102
+ <th>Related Model</th>
103
+ <th>Foreign Key</th>
104
+ <th>Options</th>
105
+ </tr>
106
+ </thead>
107
+ <tbody>
108
+ <% model.associations.each do |assoc| %>
109
+ <tr>
110
+ <td>
111
+ <span class="badge badge-<%= assoc.type.gsub('_', '-') %>">
112
+ <%= assoc.type.gsub('_', ' ') %>
113
+ </span>
114
+ </td>
115
+ <td><code><%= assoc.name %></code></td>
116
+ <td>
117
+ <% related_file = "#{assoc.class_name.underscore.gsub('/', '_')}.html" %>
118
+ <a href="<%= related_file %>" class="link"><%= assoc.class_name %></a>
119
+ </td>
120
+ <td>
121
+ <% if assoc.foreign_key %>
122
+ <code><%= assoc.foreign_key %></code>
123
+ <% else %>
124
+ <span style="color: var(--text-muted);">—</span>
125
+ <% end %>
126
+ </td>
127
+ <td>
128
+ <% if assoc.options.any? %>
129
+ <% assoc.options.each do |key, value| %>
130
+ <code><%= key %>: <%= value %></code><br>
131
+ <% end %>
132
+ <% else %>
133
+ <span style="color: var(--text-muted);">—</span>
134
+ <% end %>
135
+ </td>
136
+ </tr>
137
+ <% end %>
138
+ </tbody>
139
+ </table>
140
+ </div>
141
+ <% else %>
142
+ <div class="empty-state">No associations found</div>
143
+ <% end %>
144
+ </div>
145
+
146
+ <!-- Validations Section -->
147
+ <% if model.validations.any? %>
148
+ <div class="card">
149
+ <div class="card-header">
150
+ <h3 class="card-title">Validations</h3>
151
+ <span class="badge badge-any"><%= model.validations.size %></span>
152
+ </div>
153
+
154
+ <div class="table-container">
155
+ <table>
156
+ <thead>
157
+ <tr>
158
+ <th>Attribute</th>
159
+ <th>Validation</th>
160
+ <th>Options</th>
161
+ </tr>
162
+ </thead>
163
+ <tbody>
164
+ <% model.validations.each do |validation| %>
165
+ <tr>
166
+ <td><code><%= validation.attribute %></code></td>
167
+ <td><code><%= validation.kind %></code></td>
168
+ <td>
169
+ <% if validation.options.any? %>
170
+ <% validation.options.each do |key, value| %>
171
+ <code><%= key %>: <%= value %></code><br>
172
+ <% end %>
173
+ <% else %>
174
+ <span style="color: var(--text-muted);">—</span>
175
+ <% end %>
176
+ </td>
177
+ </tr>
178
+ <% end %>
179
+ </tbody>
180
+ </table>
181
+ </div>
182
+ </div>
183
+ <% end %>
184
+
185
+ <!-- Scopes Section -->
186
+ <% if model.scopes.any? %>
187
+ <div class="card">
188
+ <div class="card-header">
189
+ <h3 class="card-title">Scopes</h3>
190
+ <span class="badge badge-any"><%= model.scopes.size %></span>
191
+ </div>
192
+
193
+ <div class="table-container">
194
+ <table>
195
+ <thead>
196
+ <tr>
197
+ <th>Name</th>
198
+ <th>Arguments</th>
199
+ </tr>
200
+ </thead>
201
+ <tbody>
202
+ <% model.scopes.each do |scope| %>
203
+ <tr>
204
+ <td><code><%= scope.name %></code></td>
205
+ <td>
206
+ <% if scope.arity && scope.arity != 0 %>
207
+ <%= scope.arity.abs %> argument<%= scope.arity.abs != 1 ? 's' : '' %>
208
+ <%= scope.arity < 0 ? '(variable)' : '' %>
209
+ <% else %>
210
+ <span style="color: var(--text-muted);">None</span>
211
+ <% end %>
212
+ </td>
213
+ </tr>
214
+ <% end %>
215
+ </tbody>
216
+ </table>
217
+ </div>
218
+ </div>
219
+ <% end %>
@@ -0,0 +1,86 @@
1
+ <div class="breadcrumb">
2
+ <a href="index.html">Home</a>
3
+ <span>/</span>
4
+ <span>All Routes</span>
5
+ </div>
6
+
7
+ <div class="card">
8
+ <div class="card-header">
9
+ <h2 class="card-title">All Routes</h2>
10
+ <span class="badge badge-any"><%= routes_count %> total</span>
11
+ </div>
12
+
13
+ <% if routes.any? %>
14
+ <div class="table-container">
15
+ <table>
16
+ <thead>
17
+ <tr>
18
+ <th>Method</th>
19
+ <th>Path</th>
20
+ <th>Controller#Action</th>
21
+ <th>Parameters</th>
22
+ </tr>
23
+ </thead>
24
+ <tbody>
25
+ <% routes.each do |controller, data| %>
26
+ <% data[:routes].each do |route| %>
27
+ <tr>
28
+ <td>
29
+ <span class="badge badge-<%= route.verb.downcase.split('|').first %>">
30
+ <%= route.verb %>
31
+ </span>
32
+ </td>
33
+ <td><code><%= route.path %></code></td>
34
+ <td>
35
+ <a href="controllers/<%= controller.gsub('/', '_') %>.html" class="link">
36
+ <%= controller %>#<%= route.action %>
37
+ </a>
38
+ </td>
39
+ <td>
40
+ <% all_params = [] %>
41
+ <% all_params += route.path_params if route.path_params&.any? %>
42
+ <% verb = route.verb.to_s.upcase %>
43
+ <% if %w[GET DELETE].include?(verb) %>
44
+ <% all_params += route.query_params if route.query_params&.any? %>
45
+ <% elsif %w[POST PUT PATCH].include?(verb) %>
46
+ <% all_params += route.request_body_params if route.request_body_params&.any? %>
47
+ <% end %>
48
+
49
+ <% if all_params.any? %>
50
+ <details style="cursor: pointer;">
51
+ <summary style="color: var(--primary); font-weight: 500;">
52
+ <%= all_params.size %> parameter<%= all_params.size > 1 ? 's' : '' %>
53
+ </summary>
54
+ <div style="margin-top: 0.5rem; padding: 0.75rem; background: var(--bg-secondary); border-radius: 6px; font-size: 0.875rem;">
55
+ <% all_params.each do |param| %>
56
+ <div style="margin-bottom: 0.5rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border);">
57
+ <div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.25rem;">
58
+ <code style="font-weight: 600; color: var(--primary);"><%= param[:name] %></code>
59
+ <span class="badge" style="font-size: 0.75rem; padding: 0.125rem 0.375rem;"><%= param[:type] %></span>
60
+ <% if param[:required] %>
61
+ <span style="color: #dc2626; font-size: 0.75rem; font-weight: 600;">required</span>
62
+ <% else %>
63
+ <span style="color: #6b7280; font-size: 0.75rem;">optional</span>
64
+ <% end %>
65
+ </div>
66
+ <div style="color: #6b7280; font-size: 0.75rem;">
67
+ Location: <%= param[:location] %>
68
+ </div>
69
+ </div>
70
+ <% end %>
71
+ </div>
72
+ </details>
73
+ <% else %>
74
+ <span style="color: #6b7280;">No parameters</span>
75
+ <% end %>
76
+ </td>
77
+ </tr>
78
+ <% end %>
79
+ <% end %>
80
+ </tbody>
81
+ </table>
82
+ </div>
83
+ <% else %>
84
+ <div class="empty-state">No routes found</div>
85
+ <% end %>
86
+ </div>