map_dev_tools 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.
@@ -0,0 +1,329 @@
1
+ doctype html
2
+ html
3
+ head
4
+ meta[charset="utf-8"]
5
+ title Map Preview
6
+ meta[name="viewport" content="width=device-width, initial-scale=1.0"]
7
+ link[rel="icon" type="image/png" href="/icons/favicon.png"]
8
+ link[href="https://unpkg.com/maplibre-gl@#{MapDevTools::MAPLIBRE_VERSION}/dist/maplibre-gl.css" rel='stylesheet']
9
+ script[src="https://unpkg.com/maplibre-gl@#{MapDevTools::MAPLIBRE_VERSION}/dist/maplibre-gl.js"]
10
+ script[src="https://unpkg.com/maplibre-contour@#{MapDevTools::CONTOUR_VERSION}/dist/index.min.js"]
11
+ script[src="https://d3js.org/d3.v#{MapDevTools::D3_VERSION}.min.js"]
12
+ script[src='/js/filters.js']
13
+ script[src='/js/contour.js']
14
+ style
15
+ | * { margin: 0; padding: 0; box-sizing: border-box; }
16
+ | html { height: 100%; }
17
+ | body {
18
+ | font-family: 'JetBrains Mono', 'Consolas', 'Monaco', monospace;
19
+ | background: #2b2b2b; color: #a9b7c6; line-height: 1.4; font-size: 14px;
20
+ | margin: 0; padding: 0; overflow: hidden;
21
+ | }
22
+ | .container { width: 100%; height: 100vh; margin: 0; padding: 0; }
23
+ | .header {
24
+ | background: #3c3f41; border: 1px solid #555555; padding: 20px;
25
+ | border-radius: 4px; margin-bottom: 20px;
26
+ | }
27
+ | .header h1 { color: #ffc66d; font-size: 20px; margin-bottom: 8px; }
28
+ | .header p { color: #808080; font-size: 13px; }
29
+ | .section {
30
+ | background: #3c3f41; border: 1px solid #555555; padding: 16px;
31
+ | border-radius: 4px; margin-bottom: 16px;
32
+ | }
33
+ | .section-title {
34
+ | color: #6a9955; font-weight: bold; margin-bottom: 12px; font-size: 16px;
35
+ | }
36
+ | .status-ok { color: #6a9955; }
37
+ | .status-error { color: #f44747; }
38
+ | .status-warning { color: #ffc66d; }
39
+ | .status-disabled { color: #808080; }
40
+ | .style-card {
41
+ | background: #313335; border: 1px solid #464749; padding: 12px;
42
+ | border-radius: 3px; margin-bottom: 16px;
43
+ | }
44
+ | .style-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
45
+ | .style-name { color: #ffc66d; font-weight: bold; }
46
+ | .style-id { color: #808080; font-size: 12px; }
47
+ | .style-description { color: #a9b7c6; font-size: 12px; margin-bottom: 8px; }
48
+ | .style-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px; }
49
+ | .stat-item { font-size: 12px; }
50
+ | .stat-label { color: #808080; }
51
+ | .stat-value { color: #6897bb; }
52
+ | .endpoint-container {
53
+ | display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
54
+ | padding: 2px 4px; border-radius: 2px; transition: all 0.2s ease; margin-left: 4px;
55
+ | }
56
+ | .endpoint-container:hover { background: #4b4d4f; }
57
+ | .endpoint-container.copy-success {
58
+ | background: #2d4a2d !important; border: 1px solid #6a9955;
59
+ | }
60
+ | .endpoint-url {
61
+ | color: #6897bb; font-family: 'Courier New', monospace; font-size: 11px;
62
+ | text-decoration: underline;
63
+ | }
64
+ | .copy-icon {
65
+ | color: #555555; font-size: 9px; flex-shrink: 0; transition: color 0.2s; opacity: 0.5;
66
+ | }
67
+ | .endpoint-container:hover .copy-icon { color: #808080; opacity: 0.8; }
68
+ | .copy-success .copy-icon { color: #6a9955 !important; opacity: 1 !important; }
69
+ | .style-actions { margin-top: 12px; }
70
+ | .btn {
71
+ | display: inline-block; padding: 6px 12px; background: #4b4d4f; color: #a9b7c6;
72
+ | text-decoration: none; border-radius: 3px; font-size: 12px;
73
+ | border: 1px solid #555555; margin-right: 8px;
74
+ | }
75
+ | .btn:hover { background: #5a5d5f; border-color: #666666; }
76
+ | .btn-primary {
77
+ | background: #6a9955; color: #ffffff; border-color: #7bb366;
78
+ | }
79
+ | .btn-primary:hover { background: #7bb366; border-color: #8cc477; }
80
+ | .btn-small { padding: 4px 8px; font-size: 10px; margin-right: 4px; }
81
+ | .refresh-btn {
82
+ | background: #ffc66d; color: #2b2b2b; border-color: #ffd87d; font-weight: bold;
83
+ | }
84
+ | .refresh-btn:hover { background: #ffd87d; border-color: #ffe88d; }
85
+ | .header-buttons { display: flex; gap: 8px; float: right; }
86
+ | .map-btn { background: #6897bb; color: #ffffff; border-color: #7aa8cc; }
87
+ | .map-btn:hover { background: #7aa8cc; border-color: #8bb9dd; }
88
+ | .sprite-section {
89
+ | margin-top: 12px; padding-top: 12px; border-top: 1px solid #464749;
90
+ | }
91
+ | .sprite-title { color: #6a9955; font-weight: bold; font-size: 12px; margin-bottom: 8px; }
92
+ | .sprite-endpoints { display: flex; flex-direction: column; gap: 6px; }
93
+ | .sprite-item { display: flex; align-items: center; gap: 8px; font-size: 11px; }
94
+ | .sprite-label { color: #808080; min-width: 40px; }
95
+ | .stats-grid {
96
+ | display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
97
+ | gap: 16px; margin-bottom: 20px;
98
+ | }
99
+ | .stat-box {
100
+ | background: #313335; border: 1px solid #464749; padding: 12px;
101
+ | border-radius: 3px; text-align: center;
102
+ | }
103
+ | .stat-number { font-size: 24px; font-weight: bold; color: #6897bb; margin-bottom: 4px; }
104
+ | .stat-desc { font-size: 12px; color: #808080; }
105
+ | .error-message { color: #f44747; font-size: 12px; margin-top: 4px; }
106
+ | .success-message { color: #6a9955; font-size: 12px; margin-top: 4px; }
107
+ | .performance-overlay {
108
+ | position: fixed; top: 0; left: 50%; transform: translateX(-50%); z-index: 1000;
109
+ | background: rgba(43, 43, 43, 0.95); border: 1px solid #555555;
110
+ | border-radius: 0 0 6px 6px; padding: 0; min-width: 400px;
111
+ | backdrop-filter: blur(10px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
112
+ | transition: all 0.3s ease;
113
+ | }
114
+ | .performance-toggle {
115
+ | position: absolute; top: 4px; right: 4px; z-index: 1001;
116
+ | background: none; border: none; color: #808080; cursor: pointer;
117
+ | font-size: 14px; padding: 0; width: 16px; height: 16px;
118
+ | display: flex; align-items: center; justify-content: center;
119
+ | border-radius: 2px; transition: all 0.2s ease;
120
+ | }
121
+ | .performance-toggle:hover {
122
+ | background: #4b4d4f; color: #a9b7c6;
123
+ | }
124
+ | .performance-content {
125
+ | padding: 8px 12px;
126
+ | }
127
+ | .metric-row {
128
+ | display: flex; justify-content: space-between; align-items: center;
129
+ | margin-bottom: 6px;
130
+ | }
131
+ | .metric-row:last-child { margin-bottom: 0; }
132
+ | .metric {
133
+ | display: flex; align-items: center; gap: 4px;
134
+ | font-size: 11px; flex: 1; justify-content: center;
135
+ | }
136
+ | .metric-label {
137
+ | color: #808080; font-weight: 500;
138
+ | }
139
+ | .metric-value {
140
+ | color: #6897bb; font-weight: bold; font-family: 'Courier New', monospace;
141
+ | min-width: 40px; text-align: center;
142
+ | }
143
+ | .metric-value.warning { color: #ffc66d; }
144
+ | .metric-value.error { color: #f44747; }
145
+ | .metric-value.success { color: #6a9955; }
146
+ | #map-container {
147
+ | position: relative; width: 100%; height: 100vh; background: #2b2b2b;
148
+ | }
149
+ | .map-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
150
+ | .controls {
151
+ | position: absolute; top: 10px; left: 10px; z-index: 1000;
152
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
153
+ | padding: 10px; border-radius: 4px; color: #a9b7c6;
154
+ | }
155
+ | .controls a { color: #6897bb; text-decoration: none; margin-right: 15px; }
156
+ | .controls a:hover { text-decoration: underline; }
157
+ | .layer-controls {
158
+ | position: absolute; top: 50%; left: 10px; transform: translateY(-50%); z-index: 1000;
159
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555; padding: 15px;
160
+ | border-radius: 4px; color: #a9b7c6; display: flex; flex-direction: column; gap: 6px;
161
+ | max-width: 300px; max-height: 80vh;
162
+ | }
163
+ | .mode-switcher {
164
+ | display: flex; gap: 2px; margin-bottom: 10px; background: #3c3f41;
165
+ | border-radius: 3px; padding: 2px; flex-shrink: 0;
166
+ | }
167
+ | .mode-button {
168
+ | background: #3c3f41; color: #808080; border: none; padding: 6px 12px;
169
+ | border-radius: 2px; cursor: pointer; font-size: 11px; transition: all 0.2s ease; flex: 1;
170
+ | }
171
+ | .mode-button:hover { background: #4b4d4f; color: #a9b7c6; }
172
+ | .mode-button.active { background: #6897bb; color: #ffffff; }
173
+ | .control-panel {
174
+ | display: none; flex-direction: column; gap: 6px;
175
+ | max-height: calc(80vh - 200px); overflow-y: auto;
176
+ | }
177
+ | .control-panel::-webkit-scrollbar { width: 8px; }
178
+ | .control-panel::-webkit-scrollbar-track { background: #3c3f41; border-radius: 4px; }
179
+ | .control-panel::-webkit-scrollbar-thumb {
180
+ | background: #555555; border-radius: 4px; border: 1px solid #3c3f41;
181
+ | }
182
+ | .control-panel::-webkit-scrollbar-thumb:hover { background: #666666; }
183
+ | .control-panel { scrollbar-width: thin; scrollbar-color: #555555 #3c3f41; }
184
+ | .control-panel.active { display: flex; }
185
+ | .control-panel-header {
186
+ | flex-shrink: 0; border-bottom: 1px solid #555555; padding-bottom: 8px; margin-bottom: 8px;
187
+ | display: flex; flex-direction: column; gap: 6px;
188
+ | }
189
+ | .control-panel-content {
190
+ | flex: 1; overflow-y: auto;
191
+ | }
192
+ | .control-button {
193
+ | background: #4b4d4f; color: #a9b7c6; border: 1px solid #555555;
194
+ | padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: 11px;
195
+ | transition: all 0.2s ease; width: 100%; box-sizing: border-box;
196
+ | }
197
+ | .control-button:hover { background: #5a5d5f; border-color: #666666; }
198
+ | .control-button.active { background: #6a9955; color: #ffffff; border-color: #7bb366; }
199
+ | .control-button.inactive { background: #3c3f41; color: #808080; border-color: #464749; }
200
+ | .filter-group {
201
+ | margin-bottom: 8px;
202
+ | border: 1px solid #555555;
203
+ | border-radius: 3px;
204
+ | overflow: hidden;
205
+ | }
206
+ | .filter-group-button {
207
+ | width: 100%;
208
+ | font-weight: bold;
209
+ | background: #4b4d4f;
210
+ | border: none;
211
+ | border-bottom: 1px solid #555555;
212
+ | }
213
+ | .filter-sub-buttons {
214
+ | display: flex;
215
+ | flex-direction: column;
216
+ | gap: 2px;
217
+ | padding: 3px;
218
+ | background: #3c3f41;
219
+ | border: 1px solid #6a9955;
220
+ | border-radius: 2px;
221
+ | box-sizing: border-box;
222
+ | }
223
+ | .filter-sub-button {
224
+ | font-size: 10px;
225
+ | padding: 4px 8px;
226
+ | margin: 0;
227
+ | background: #ffc66d;
228
+ | color: #2b2b2b;
229
+ | border: 1px solid #e6b85a;
230
+ | border-radius: 2px;
231
+ | cursor: pointer;
232
+ | transition: all 0.2s ease;
233
+ | }
234
+ | .filter-sub-button:hover {
235
+ | background: #ffd87d;
236
+ | border-color: #f0c050;
237
+ | }
238
+ | .filter-sub-button.active {
239
+ | background: #ffd87d;
240
+ | color: #2b2b2b;
241
+ | border-color: #f0c050;
242
+ | font-weight: bold;
243
+ | }
244
+ | .filter-sub-button.inactive {
245
+ | background: #3c3f41;
246
+ | color: #808080;
247
+ | border-color: #464749;
248
+ | }
249
+
250
+ | .toggle-basemap {
251
+ | position: absolute; top: 10px; right: 10px; z-index: 1000;
252
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555; padding: 10px;
253
+ | border-radius: 4px; color: #a9b7c6; border: none; cursor: pointer;
254
+ | }
255
+ | .toggle-basemap:hover { background: rgba(75, 77, 79, 0.95); }
256
+ | #loading-indicator {
257
+ | position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
258
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
259
+ | border-radius: 6px; padding: 12px 20px; z-index: 1000;
260
+ | backdrop-filter: blur(10px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
261
+ | transition: all 0.3s ease; display: none;
262
+ | }
263
+ | .loading-progress { text-align: center; color: #a9b7c6; }
264
+ | .loading-text { font-size: 14px; color: #a9b7c6; margin-bottom: 8px; }
265
+ | .progress-bar {
266
+ | width: 200px; height: 4px; background: #3c3f41; border-radius: 2px;
267
+ | overflow: hidden; margin: 0 auto;
268
+ | }
269
+ | .progress-fill {
270
+ | height: 100%; background: #6897bb; border-radius: 2px;
271
+ | transition: width 0.3s ease; width: 0%;
272
+ | }
273
+ | /* MapLibre Controls */
274
+ | .maplibregl-ctrl-scale, .maplibregl-ctrl-group, .maplibregl-ctrl-terrain, .maplibregl-ctrl-globe {
275
+ | background: rgba(60, 63, 65, 0.95) !important; border: 1px solid #555555 !important;
276
+ | border-radius: 4px !important; backdrop-filter: blur(10px) !important;
277
+ | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
278
+ | }
279
+ | .maplibregl-ctrl-scale { color: rgb(100, 185, 61) !important; font-size: 11px !important; padding: 4px 8px !important; }
280
+ | .maplibregl-ctrl-scale-line { background: #6897bb !important; border: 1px solid #7aa8cc !important; }
281
+ | .maplibregl-ctrl-group button { background: rgba(75, 105, 61, 0.8) !important; border: 1px solid rgba(72, 139, 92, 0.4) !important; color: #ffffff !important; font-size: 14px !important; font-weight: bold !important; transition: all 0.2s ease !important; }
282
+ | .maplibregl-ctrl-group button:hover { background: rgba(255, 198, 109, 0.9) !important; border-color: #ffd87d !important; color: #2b2b2b !important; transform: scale(1.05) !important; }
283
+ | .maplibregl-ctrl-group button:active { background: rgba(106, 153, 85, 1) !important; transform: scale(0.95) !important; }
284
+ | .maplibregl-ctrl-terrain button { background: rgba(104, 151, 187, 0.8) !important; border: 1px solid #7aa8cc !important; color: #ffffff !important; font-size: 16px !important; font-weight: bold !important; transition: all 0.2s ease !important; padding: 8px !important; }
285
+ | .maplibregl-ctrl-terrain button:hover { background: rgba(104, 151, 187, 1) !important; border-color: #8bb9dd !important; transform: scale(1.1) !important; box-shadow: 0 2px 8px rgba(104, 151, 187, 0.4) !important; }
286
+ | .maplibregl-ctrl-terrain button:active { transform: scale(0.95) !important; }
287
+ | .maplibregl-ctrl-globe button { background: rgba(255, 198, 109, 0.8) !important; border: 1px solid #ffd87d !important; color: #2b2b2b !important; font-size: 16px !important; font-weight: bold !important; transition: all 0.2s ease !important; padding: 8px !important; }
288
+ | .maplibregl-ctrl-globe button:hover { background: rgba(255, 198, 109, 1) !important; border-color: #ffe88d !important; transform: scale(1.1) !important; box-shadow: 0 2px 8px rgba(255, 198, 109, 0.4) !important; }
289
+ | .maplibregl-ctrl-globe button:active { transform: scale(0.95) !important; }
290
+ | .profile-overlay {
291
+ | position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 1000;
292
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
293
+ | border-radius: 6px; padding: 12px; width: 70vw; max-width: 800px;
294
+ | backdrop-filter: blur(10px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
295
+ | }
296
+ | .profile-header {
297
+ | display: flex; justify-content: space-between; align-items: center;
298
+ | margin-bottom: 8px; border-bottom: 1px solid #555555; padding-bottom: 8px;
299
+ | }
300
+ | .profile-title { color: #ffc66d; font-weight: bold; font-size: 12px; }
301
+ | .profile-close {
302
+ | background: none; border: none; color: #808080; cursor: pointer;
303
+ | font-size: 14px; padding: 0; width: 16px; height: 16px;
304
+ | display: flex; align-items: center; justify-content: center;
305
+ | border-radius: 2px; transition: all 0.2s ease;
306
+ | }
307
+ | .profile-close:hover { background: #4b4d4f; color: #a9b7c6; }
308
+ | .profile-stats {
309
+ | display: flex; justify-content: space-between; margin-bottom: 8px;
310
+ | font-size: 10px; color: #808080;
311
+ | }
312
+ | .profile-chart { margin-top: 8px; }
313
+ | .elevation-tooltip {
314
+ | position: fixed; z-index: 1001; pointer-events: none;
315
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
316
+ | border-radius: 4px; padding: 8px 12px; font-size: 16px;
317
+ | color: #6a9955; backdrop-filter: blur(10px);
318
+ | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
319
+ | transform: translate(10px, -50%); margin-left: 10px;
320
+ | font-weight: bold;
321
+ | }
322
+ | .profile-area { fill: #4CAF50; opacity: 0.3; }
323
+ | .profile-line { fill: none; stroke: #2196F3; stroke-width: 2; }
324
+ | .profile-tooltip-line { stroke: #ff0000; stroke-width: 1; stroke-dasharray: 3,3; }
325
+ | .profile-tooltip-circle { fill: #ff0000; r: 4; }
326
+ | .profile-tooltip-text { text-anchor: middle; fill: #ff0000; font-size: 14px; }
327
+ body
328
+ .container
329
+ == yield
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra/base'
4
+ require 'slim'
5
+ require 'rack'
6
+
7
+ module MapDevTools
8
+ # Fixed versions for guaranteed compatibility
9
+ MAPLIBRE_VERSION = '5.7.3'
10
+ CONTOUR_VERSION = '0.1.0'
11
+ D3_VERSION = '7'
12
+
13
+ # Rack middleware for serving static JS files from gem
14
+ class StaticMiddleware
15
+ def initialize(app)
16
+ @app = app
17
+ @gem_public_path = File.expand_path('map_dev_tools/public', __dir__)
18
+ end
19
+
20
+ def call(env)
21
+ request = Rack::Request.new(env)
22
+
23
+ if request.path.match?(%r{^/js/})
24
+ serve_js_file(request.path)
25
+ else
26
+ @app.call(env)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def serve_js_file(path)
33
+ file_path = File.join(@gem_public_path, path)
34
+
35
+ if File.exist?(file_path) && File.file?(file_path)
36
+ [200, { 'Content-Type' => 'application/javascript' }, [File.read(file_path)]]
37
+ else
38
+ [404, { 'Content-Type' => 'text/plain' }, ['File not found']]
39
+ end
40
+ end
41
+ end
42
+
43
+ # Sinatra extension for map development tools
44
+ module Extension
45
+ def self.registered(app)
46
+ app.helpers Helpers
47
+ app.use StaticMiddleware
48
+ end
49
+ end
50
+
51
+ # Helper methods for map development tools
52
+ module Helpers
53
+ def render_map_dev_tools
54
+ gem_views_path = File.expand_path('map_dev_tools/views', __dir__)
55
+ original_views = settings.views
56
+ settings.set :views, gem_views_path
57
+ slim(:map, layout: :map_layout)
58
+ ensure
59
+ settings.set :views, original_views
60
+ end
61
+
62
+ def render_map_layout
63
+ gem_views_path = File.expand_path('map_dev_tools/views', __dir__)
64
+ original_views = settings.views
65
+
66
+ settings.set :views, gem_views_path
67
+ slim(:map_layout)
68
+ ensure
69
+ settings.set :views, original_views
70
+ end
71
+
72
+ def style_url
73
+ params[:style_url]
74
+ end
75
+
76
+ def should_show_map?
77
+ !!(params[:style] || params[:style_url] || params[:source])
78
+ end
79
+ end
80
+
81
+ # Standalone Sinatra application for map development
82
+ class App < Sinatra::Base
83
+ register Extension
84
+
85
+ configure do
86
+ set :views, File.expand_path('map_dev_tools/views', __dir__)
87
+ set :public_folder, File.expand_path('map_dev_tools/public', __dir__)
88
+ end
89
+
90
+ get '/js/:file' do
91
+ serve_js_file(params[:file])
92
+ end
93
+
94
+ get '/map' do
95
+ slim :map, layout: :map_layout
96
+ end
97
+
98
+ private
99
+
100
+ def serve_js_file(filename)
101
+ gem_js_path = File.expand_path("map_dev_tools/public/js/#{filename}", __dir__)
102
+ if File.exist?(gem_js_path)
103
+ content_type 'application/javascript'
104
+ File.read(gem_js_path)
105
+ else
106
+ status 404
107
+ 'File not found'
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ Sinatra.register MapDevTools::Extension
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe MapDevTools do
6
+ describe 'App functionality' do
7
+ let(:app) { Class.new(MapDevTools::App) { set :environment, :test } }
8
+
9
+ it 'provides complete map development interface' do
10
+ get '/map'
11
+ expect(last_response).to be_ok
12
+
13
+ expect(last_response.body).to include('map-container')
14
+ expect(last_response.body).to include('maplibre-gl')
15
+ expect(last_response.body).to include('maplibre-contour')
16
+ expect(last_response.body).to include('d3')
17
+ end
18
+
19
+ it 'serves all required JavaScript modules' do
20
+ %w[filters.js contour.js].each do |js_file|
21
+ get "/js/#{js_file}"
22
+ expect(last_response).to be_ok
23
+ expect(last_response.content_type).to include('javascript')
24
+ expect(last_response.body).not_to be_empty
25
+ end
26
+ end
27
+ end
28
+
29
+ describe 'External dependencies' do
30
+ let(:app) { Class.new(MapDevTools::App) { set :environment, :test } }
31
+
32
+ it 'includes proper external dependencies' do
33
+ get '/map'
34
+ body = last_response.body
35
+
36
+ expect(body).to include('unpkg.com/maplibre-gl')
37
+ expect(body).to include('unpkg.com/maplibre-contour')
38
+ expect(body).to include('d3js.org/d3')
39
+ end
40
+ end
41
+
42
+ describe 'Extension integration' do
43
+ it 'provides extension for Sinatra integration' do
44
+ test_app = Class.new(Sinatra::Base) do
45
+ register MapDevTools::Extension
46
+ end
47
+
48
+ expect(test_app.ancestors).to include(MapDevTools::Extension)
49
+ expect(test_app.settings.map_dev_tools_options).to be_a(Hash)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'map_dev_tools'
5
+ require 'rspec'
6
+ require 'rack/test'
7
+
8
+ RSpec.configure do |config|
9
+ config.include Rack::Test::Methods
10
+ config.before(:each) { ENV['RACK_ENV'] = 'test' }
11
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: map_dev_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Ludov
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rack
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '4.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '2.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '4.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: sinatra
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '2.1'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '5.0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '2.1'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '5.0'
52
+ - !ruby/object:Gem::Dependency
53
+ name: slim
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '4.1'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '4.1'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '6.0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: bundler
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '2.0'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '2.0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: rack-test
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '2.0'
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '2.0'
100
+ - !ruby/object:Gem::Dependency
101
+ name: rake
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '13.0'
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '13.0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: rspec
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '3.10'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '3.10'
128
+ - !ruby/object:Gem::Dependency
129
+ name: rubocop
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '1.0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '1.0'
142
+ description: A Ruby gem providing development tools for MapLibre GL JS styles, including
143
+ advanced layer filtering, terrain visualization, elevation profiles, and performance
144
+ monitoring.
145
+ email:
146
+ - efraam@gmail.com
147
+ executables: []
148
+ extensions: []
149
+ extra_rdoc_files: []
150
+ files:
151
+ - ".github/workflows/gem-push.yml"
152
+ - ".rubocop.yml"
153
+ - CHANGELOG.md
154
+ - LICENSE
155
+ - README.md
156
+ - Rakefile
157
+ - docs/README_RU.md
158
+ - lib/map_dev_tools.rb
159
+ - lib/map_dev_tools/public/js/contour.js
160
+ - lib/map_dev_tools/public/js/filters.js
161
+ - lib/map_dev_tools/version.rb
162
+ - lib/map_dev_tools/views/map.slim
163
+ - lib/map_dev_tools/views/map_layout.slim
164
+ - spec/map_dev_tools_spec.rb
165
+ - spec/spec_helper.rb
166
+ homepage: https://github.com/Efreh/map_dev_tools
167
+ licenses:
168
+ - MIT
169
+ metadata:
170
+ homepage_uri: https://github.com/Efreh/map_dev_tools
171
+ source_code_uri: https://github.com/Efreh/map_dev_tools
172
+ changelog_uri: https://github.com/Efreh/map_dev_tools/blob/main/CHANGELOG.md
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: 2.7.0
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubygems_version: 3.6.9
188
+ specification_version: 4
189
+ summary: Development tools for MapLibre GL JS styles with advanced filtering and terrain
190
+ features
191
+ test_files: []