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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5217817da2d19efe00f716f4a7a19f00f2070162727b55ce3597be283cb9c6ea
4
- data.tar.gz: 2f8ab15ebf72c9cd6ddc46535c82a8c429d6f17ef4d8ba22fae07323726a5e6d
3
+ metadata.gz: cfdcb94bdbe258d69714fca1ce8770bb28e24f4fcbb2c4395b864a41166b1071
4
+ data.tar.gz: 48df1c1cc65f575539f297698824f31b4893847b5d6f122f30f230f7a8852863
5
5
  SHA512:
6
- metadata.gz: dbf00ea393ade085fa7287b84bc506f510219ba530b57f63fffefb8430ead5a25936b5e5b44180fa7845191296d5bbe0f019b6cef0525686977096dd93bf6306
7
- data.tar.gz: 35a5bd9b3aa5d1bceabf6628d635277d4cd0a2e94136f1fee12e3b5a9d9305c9a6e7dc44ee7f906399de1eef012304d50c4a8be84cafde6c05dcd6518f188708
6
+ metadata.gz: f7adb266c2f825864de5ed254abda266fe0e7ea9f1b91c14b2c4252dd2f895a5cb22cb47f0d3b82cf0af701b87fabf0cac6dae0c74de977adee16336c6cabe0a
7
+ data.tar.gz: 336df48f731a136a255b20f79933748cf90db5b52bf5ccbbf2c54e01a9cfab46a407ee7ac23b00ad26f6d72b9d1923521ad858ad8b3844284b8d7bb248fbbc94
data/Rakefile CHANGED
@@ -1,8 +1,5 @@
1
1
  require "bundler/setup"
2
2
 
3
- APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
- load "rails/tasks/engine.rake"
5
-
6
- load "rails/tasks/statistics.rake"
3
+ # load "rails/tasks/statistics.rake"
7
4
 
8
5
  require "bundler/gem_tasks"
@@ -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
- <title>Remise</title>
5
- <%= csrf_meta_tags %>
6
- <%= csp_meta_tag %>
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
- <%= yield :head %>
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
- <%= stylesheet_link_tag "remise/application", media: "all" %>
11
- </head>
12
- <body>
24
+ .container {
25
+ max-width: 1200px;
26
+ margin: 0 auto;
27
+ }
13
28
 
14
- <%= yield %>
29
+ header {
30
+ text-align: center;
31
+ margin-bottom: 40px;
32
+ }
15
33
 
16
- </body>
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
@@ -1,2 +1,3 @@
1
1
  Remise::Engine.routes.draw do
2
+ root to: "application#index"
2
3
  end
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
@@ -1,3 +1,3 @@
1
1
  module Remise
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/remise.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "remise/version"
2
2
  require "remise/engine"
3
+ require "nero"
3
4
 
4
5
  module Remise
5
6
  # Your code goes here...
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.1.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
@@ -1,6 +0,0 @@
1
- module Remise
2
- class ApplicationMailer < ActionMailer::Base
3
- default from: "from@example.com"
4
- layout "mailer"
5
- end
6
- end
@@ -1,5 +0,0 @@
1
- module Remise
2
- class ApplicationRecord < ActiveRecord::Base
3
- self.abstract_class = true
4
- end
5
- end