remise 0.1.0 → 0.3.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 +4 -4
- data/Rakefile +1 -4
- data/app/controllers/remise/application_controller.rb +12 -0
- data/app/helpers/remise/application_helper.rb +28 -0
- data/app/views/layouts/remise/application.html.erb +254 -11
- data/app/views/remise/application/index.html.erb +63 -0
- data/config/routes.rb +1 -0
- data/lib/remise/engine.rb +18 -0
- data/lib/remise/version.rb +1 -1
- data/lib/remise.rb +1 -0
- metadata +16 -3
- data/app/mailers/remise/application_mailer.rb +0 -6
- data/app/models/remise/application_record.rb +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cfdcb94bdbe258d69714fca1ce8770bb28e24f4fcbb2c4395b864a41166b1071
|
|
4
|
+
data.tar.gz: 48df1c1cc65f575539f297698824f31b4893847b5d6f122f30f230f7a8852863
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7adb266c2f825864de5ed254abda266fe0e7ea9f1b91c14b2c4252dd2f895a5cb22cb47f0d3b82cf0af701b87fabf0cac6dae0c74de977adee16336c6cabe0a
|
|
7
|
+
data.tar.gz: 336df48f731a136a255b20f79933748cf90db5b52bf5ccbbf2c54e01a9cfab46a407ee7ac23b00ad26f6d72b9d1923521ad858ad8b3844284b8d7bb248fbbc94
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
module Remise
|
|
2
2
|
class ApplicationController < ActionController::Base
|
|
3
|
+
def index
|
|
4
|
+
@config = read_config
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
protected
|
|
8
|
+
|
|
9
|
+
def read_config
|
|
10
|
+
config_path = Rails.root.join("config", "remise.yml")
|
|
11
|
+
return {} unless File.exist?(config_path)
|
|
12
|
+
|
|
13
|
+
Nero.load_file(config_path, symbolize_names: true)
|
|
14
|
+
end
|
|
3
15
|
end
|
|
4
16
|
end
|
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
module Remise
|
|
2
2
|
module ApplicationHelper
|
|
3
|
+
def leading_emoji(label)
|
|
4
|
+
label[/^\p{Emoji}/u]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def strip_leading_emoji(label)
|
|
8
|
+
label.sub(/^\p{Emoji}/u, "")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Given a config like
|
|
12
|
+
# `url: { value: "https://...", formatted: "short.form/path" }`
|
|
13
|
+
# This method gets the formatted-value:
|
|
14
|
+
# prefer_formatted(config[:url]) #=> "short.form/path"
|
|
15
|
+
# Also works for `url: "one value"`.
|
|
16
|
+
def prefer_formatted(h)
|
|
17
|
+
return h[:formatted] if h.is_a?(Hash) && h.has_key?(:formatted)
|
|
18
|
+
return h[:value] if h.is_a?(Hash)
|
|
19
|
+
h
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Given a config like
|
|
23
|
+
# `url: { value: "https://...", formatted: "short.form/path" }`
|
|
24
|
+
# This method gets the value:
|
|
25
|
+
# prefer_value(config[:url]) #=> "https://..."
|
|
26
|
+
# Also works for `url: "one value"`.
|
|
27
|
+
def prefer_value(h)
|
|
28
|
+
return h[:value] if h.is_a?(Hash) && h.has_key?(:value)
|
|
29
|
+
h
|
|
30
|
+
end
|
|
3
31
|
end
|
|
4
32
|
end
|
|
@@ -1,17 +1,260 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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>🚂 Remise - Development Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
7
13
|
|
|
8
|
-
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
16
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
17
|
+
<%# TODO customize %>
|
|
18
|
+
<%# background: #667EEA; %>
|
|
19
|
+
<%# background: linear-gradient(90deg, rgba(102, 126, 234, 1) 0%, rgba(87, 199, 133, 1) 50%, rgba(237, 221, 83, 1) 100%); %>
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
padding: 40px 20px;
|
|
22
|
+
}
|
|
9
23
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
24
|
+
.container {
|
|
25
|
+
max-width: 1200px;
|
|
26
|
+
margin: 0 auto;
|
|
27
|
+
}
|
|
13
28
|
|
|
14
|
-
|
|
29
|
+
header {
|
|
30
|
+
text-align: center;
|
|
31
|
+
margin-bottom: 40px;
|
|
32
|
+
}
|
|
15
33
|
|
|
16
|
-
|
|
34
|
+
h1 {
|
|
35
|
+
color: white;
|
|
36
|
+
font-size: 3em;
|
|
37
|
+
margin-bottom: 10px;
|
|
38
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.subtitle {
|
|
42
|
+
color: rgba(255,255,255,0.9);
|
|
43
|
+
font-size: 1.2em;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sections-grid {
|
|
47
|
+
display: grid;
|
|
48
|
+
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
|
49
|
+
gap: 24px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.section {
|
|
53
|
+
background: white;
|
|
54
|
+
border-radius: 12px;
|
|
55
|
+
padding: 24px;
|
|
56
|
+
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
|
57
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.section:hover {
|
|
61
|
+
transform: translateY(-4px);
|
|
62
|
+
box-shadow: 0 12px 24px rgba(0,0,0,0.15);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.section-header {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 12px;
|
|
69
|
+
margin-bottom: 20px;
|
|
70
|
+
padding-bottom: 12px;
|
|
71
|
+
border-bottom: 2px solid #f0f0f0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.section-emoji {
|
|
75
|
+
font-size: 2em;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.section-title {
|
|
79
|
+
font-size: 1.4em;
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
color: #333;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.links-list {
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
gap: 12px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.link-item {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: flex-start;
|
|
93
|
+
gap: 12px;
|
|
94
|
+
padding: 12px;
|
|
95
|
+
background: #f8f9fa;
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
text-decoration: none;
|
|
98
|
+
color: #333;
|
|
99
|
+
transition: all 0.2s;
|
|
100
|
+
border-left: 3px solid transparent;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.link-item:hover {
|
|
104
|
+
background: #e9ecef;
|
|
105
|
+
border-left-color: #667eea;
|
|
106
|
+
transform: translateX(4px);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.link-emoji {
|
|
110
|
+
font-size: 1.5em;
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.link-content {
|
|
115
|
+
flex: 1;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.link-title {
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
color: #333;
|
|
122
|
+
margin-bottom: 4px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.link-url {
|
|
126
|
+
font-size: 0.9em;
|
|
127
|
+
color: #666;
|
|
128
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.link-meta {
|
|
132
|
+
font-size: 0.85em;
|
|
133
|
+
color: #28a745;
|
|
134
|
+
margin-top: 4px;
|
|
135
|
+
font-style: italic;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Expandable sub-links using details/summary */
|
|
139
|
+
.link-item-expandable {
|
|
140
|
+
display: block;
|
|
141
|
+
padding: 12px;
|
|
142
|
+
background: #f8f9fa;
|
|
143
|
+
border-radius: 8px;
|
|
144
|
+
border-left: 3px solid transparent;
|
|
145
|
+
transition: all 0.2s;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.link-item-expandable:hover {
|
|
149
|
+
background: #e9ecef;
|
|
150
|
+
border-left-color: #667eea;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.link-item-expandable summary {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: flex-start;
|
|
156
|
+
gap: 12px;
|
|
157
|
+
cursor: pointer;
|
|
158
|
+
list-style: none;
|
|
159
|
+
user-select: none;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.link-item-expandable summary::-webkit-details-marker {
|
|
163
|
+
display: none;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.link-item-expandable summary::before {
|
|
167
|
+
content: '▶';
|
|
168
|
+
font-size: 0.8em;
|
|
169
|
+
color: #666;
|
|
170
|
+
transition: transform 0.2s;
|
|
171
|
+
margin-top: 4px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.link-item-expandable[open] summary::before {
|
|
175
|
+
transform: rotate(90deg);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.sub-links {
|
|
179
|
+
margin-top: 12px;
|
|
180
|
+
padding-top: 12px;
|
|
181
|
+
border-top: 1px solid #dee2e6;
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
gap: 8px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.sub-link {
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
gap: 8px;
|
|
191
|
+
padding: 8px 12px;
|
|
192
|
+
margin-left: 24px;
|
|
193
|
+
background: white;
|
|
194
|
+
border-radius: 6px;
|
|
195
|
+
text-decoration: none;
|
|
196
|
+
color: #555;
|
|
197
|
+
font-size: 0.9em;
|
|
198
|
+
transition: all 0.2s;
|
|
199
|
+
border-left: 2px solid transparent;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.sub-link:hover {
|
|
203
|
+
background: #f8f9fa;
|
|
204
|
+
border-left-color: #667eea;
|
|
205
|
+
transform: translateX(4px);
|
|
206
|
+
color: #333;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.sub-link-icon {
|
|
210
|
+
font-size: 1.2em;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.sub-link-title {
|
|
214
|
+
font-weight: 500;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.sub-link-path {
|
|
218
|
+
color: #888;
|
|
219
|
+
font-size: 0.85em;
|
|
220
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
221
|
+
margin-left: auto;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.status-indicator {
|
|
225
|
+
display: inline-block;
|
|
226
|
+
width: 10px;
|
|
227
|
+
height: 10px;
|
|
228
|
+
border-radius: 50%;
|
|
229
|
+
margin-right: 6px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.status-production { background: #28a745; }
|
|
233
|
+
.status-staging { background: #ffc107; }
|
|
234
|
+
.status-development { background: #007bff; }
|
|
235
|
+
|
|
236
|
+
.last-updated {
|
|
237
|
+
text-align: center;
|
|
238
|
+
color: #ffffff;
|
|
239
|
+
font-size: 0.9em;
|
|
240
|
+
margin-top: 30px;
|
|
241
|
+
font-style: italic;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
footer {
|
|
245
|
+
text-align: center;
|
|
246
|
+
margin-top: 40px;
|
|
247
|
+
color: rgba(255,255,255,0.8);
|
|
248
|
+
font-size: 0.9em;
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
251
|
+
</head>
|
|
252
|
+
<body>
|
|
253
|
+
<%= yield %>
|
|
254
|
+
|
|
255
|
+
<div class="last-updated">
|
|
256
|
+
Last updated:
|
|
257
|
+
<span id="lastUpdated"><%= Time.current %></span>
|
|
258
|
+
</div>
|
|
259
|
+
</body>
|
|
17
260
|
</html>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<h1>🚂 Remise</h1>
|
|
3
|
+
<p class="subtitle">Your Rails Development Hub</p>
|
|
4
|
+
</header>
|
|
5
|
+
|
|
6
|
+
<div class="sections-grid">
|
|
7
|
+
<%- @config[:sections].each do |section_key, section| %>
|
|
8
|
+
<div class="section">
|
|
9
|
+
<div class="section-header">
|
|
10
|
+
<span class="section-emoji"><%= leading_emoji(section[:label]) %></span>
|
|
11
|
+
<h2 class="section-title"><%= strip_leading_emoji(section[:label]) %></h2>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="links-list">
|
|
14
|
+
<% section[:items].each do |section_item_key, section_item| %>
|
|
15
|
+
<% if section_item[:paths].present? %>
|
|
16
|
+
<details class="link-item-expandable">
|
|
17
|
+
<summary>
|
|
18
|
+
<span class="link-emoji"><%= leading_emoji(section_item[:label]) %></span>
|
|
19
|
+
<div class="link-content">
|
|
20
|
+
<div class="link-title"><%= strip_leading_emoji(section_item[:label]) %></div>
|
|
21
|
+
<div class="link-url"><a
|
|
22
|
+
href="<%= prefer_value(section_item[:url]) %>"
|
|
23
|
+
target="_blank"
|
|
24
|
+
onclick="event.stopPropagation()"
|
|
25
|
+
title="<%= prefer_value(section_item[:url]) %>"
|
|
26
|
+
><%= prefer_formatted(section_item[:url]) %></a></div>
|
|
27
|
+
<%# TODO dynamic stuff
|
|
28
|
+
<div class="link-meta">v4.0.21 • 2025-07-01 by eval</div> %>
|
|
29
|
+
</div>
|
|
30
|
+
</summary>
|
|
31
|
+
<div class="sub-links">
|
|
32
|
+
<% section_item[:paths].each do |section_item_path_key, section_item_path| %>
|
|
33
|
+
<a
|
|
34
|
+
href="<%= prefer_value(section_item_path[:url]).start_with?("http") ? prefer_value(section_item_path[:url]) : prefer_value(section_item[:url]) + prefer_value(section_item_path[:url]) %>"
|
|
35
|
+
title="<%= prefer_value(section_item_path[:url]) %>"
|
|
36
|
+
target="_blank"
|
|
37
|
+
class="sub-link"
|
|
38
|
+
>
|
|
39
|
+
<span class="sub-link-icon"><%= leading_emoji(section_item_path[:title]) %></span>
|
|
40
|
+
<span class="sub-link-title"><%= strip_leading_emoji(section_item_path[:title]) %></span>
|
|
41
|
+
<span class="sub-link-path"><%= prefer_formatted(section_item_path[:url]) %></span>
|
|
42
|
+
</a>
|
|
43
|
+
<% end %>
|
|
44
|
+
</div>
|
|
45
|
+
</details>
|
|
46
|
+
<% else %>
|
|
47
|
+
<a href="<%= prefer_value(section_item[:url]) %>"
|
|
48
|
+
class="link-item"
|
|
49
|
+
target="_blank"
|
|
50
|
+
title="<%= prefer_value(section_item[:url]) %>"
|
|
51
|
+
>
|
|
52
|
+
<span class="link-emoji"><%= leading_emoji(section_item[:label]) %></span>
|
|
53
|
+
<div class="link-content">
|
|
54
|
+
<div class="link-title"><%= strip_leading_emoji(section_item[:label]) %></div>
|
|
55
|
+
<div class="link-url"><%= prefer_formatted(section_item[:url]) %></div>
|
|
56
|
+
</div>
|
|
57
|
+
</a>
|
|
58
|
+
<% end %>
|
|
59
|
+
<% end %>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<%- end %>
|
|
63
|
+
</div>
|
data/config/routes.rb
CHANGED
data/lib/remise/engine.rb
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
module Remise
|
|
2
2
|
class Engine < ::Rails::Engine
|
|
3
3
|
isolate_namespace Remise
|
|
4
|
+
|
|
5
|
+
config.before_initialize do
|
|
6
|
+
Nero.configure do |nero|
|
|
7
|
+
nero.add_tag("uri.fmt/strip-protocol") do |tag|
|
|
8
|
+
{
|
|
9
|
+
value: tag.args.join,
|
|
10
|
+
formatted: tag.args.join[/https?:\/\/(.+)/, 1]
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
nero.add_tag("uri.fmt/path-only") do |tag|
|
|
15
|
+
{
|
|
16
|
+
value: tag.args.join,
|
|
17
|
+
formatted: URI(tag.args.join).path
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
4
22
|
end
|
|
5
23
|
end
|
data/lib/remise/version.rb
CHANGED
data/lib/remise.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: remise
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gert Goet
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 7.2.2.1
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: nero
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
description: |-
|
|
27
41
|
A remise (from French) is a railway roundhouse - the building where locomotives
|
|
28
42
|
are stored, maintained, and prepared for their daily runs. Just like a remise
|
|
@@ -42,9 +56,8 @@ files:
|
|
|
42
56
|
- app/controllers/remise/application_controller.rb
|
|
43
57
|
- app/helpers/remise/application_helper.rb
|
|
44
58
|
- app/jobs/remise/application_job.rb
|
|
45
|
-
- app/mailers/remise/application_mailer.rb
|
|
46
|
-
- app/models/remise/application_record.rb
|
|
47
59
|
- app/views/layouts/remise/application.html.erb
|
|
60
|
+
- app/views/remise/application/index.html.erb
|
|
48
61
|
- config/routes.rb
|
|
49
62
|
- lib/remise.rb
|
|
50
63
|
- lib/remise/engine.rb
|