railbow 0.6.0 → 0.7.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/CHANGELOG.md +78 -0
- data/README.md +40 -16
- data/lib/railbow/about_formatter.rb +57 -21
- data/lib/railbow/calendar.rb +2 -4
- data/lib/railbow/clock.rb +72 -0
- data/lib/railbow/color_assigner.rb +6 -12
- data/lib/railbow/config.rb +1 -1
- data/lib/railbow/demo/fixtures.rb +4 -4
- data/lib/railbow/demo/status_demo.rb +5 -11
- data/lib/railbow/formatters/base.rb +34 -26
- data/lib/railbow/init.rb +6 -2
- data/lib/railbow/migration_formatter.rb +41 -25
- data/lib/railbow/migration_parser.rb +12 -19
- data/lib/railbow/multi_db.rb +15 -1
- data/lib/railbow/name_collision_resolver.rb +14 -27
- data/lib/railbow/name_formatter.rb +15 -16
- data/lib/railbow/notes_formatter.rb +42 -19
- data/lib/railbow/params.rb +33 -27
- data/lib/railbow/railtie.rb +8 -6
- data/lib/railbow/routes_formatter.rb +18 -6
- data/lib/railbow/stats_formatter.rb +139 -69
- data/lib/railbow/status/ghosts.rb +5 -6
- data/lib/railbow/status/git_data.rb +11 -18
- data/lib/railbow/status/help.rb +8 -2
- data/lib/railbow/status/printer.rb +20 -34
- data/lib/railbow/status/section.rb +29 -44
- data/lib/railbow/table/renderer.rb +32 -36
- data/lib/railbow/tasks/migrate_status.rake +33 -17
- data/lib/railbow/tasks/stats.rake +11 -75
- data/lib/railbow/version.rb +1 -1
- data/lib/railbow.rb +28 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdfe578f284175c30e3b9f4c77a83f62b8e63b8019e575590f8ad471e5639efb
|
|
4
|
+
data.tar.gz: 5874946340d18626a566b0190f734f6a94a55a3d40dfb0777760ab377b9d295d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3fc62787e1bfd0913be00ca297c5c9210a312b067c68960c13699298a3a064e396a20bc952e65544d19059a03c02454c99413b8f59fd84e6890d9145e8ba03c1
|
|
7
|
+
data.tar.gz: e2253cf2117f5ed4a6702f9500774789d05007876f09ad043b014f9e7b0d53f9616ea21fc32247463081e131aa533b2bf149cb65be99f8f08b37478a281e22ee
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,84 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-10-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `RBW_TZ` (or `tz:` in `.railbow.yml`) picks the timezone railbow shows
|
|
13
|
+
dates in: `local` (the default), `utc`, or any zone Rails knows, such as
|
|
14
|
+
`America/New_York`. It covers the `db:migrate:status` date column, calendar
|
|
15
|
+
separators and landed badges, `rails notes` blame dates, the `rails about`
|
|
16
|
+
schema version, and where `RBW_SINCE` windows start.
|
|
17
|
+
- A railbow bug no longer breaks the command it decorates. If rendering
|
|
18
|
+
raises, railbow prints a one-line `railbow:` warning to stderr and falls back
|
|
19
|
+
to the stock Rails output for `rails about`, `rails stats`, `rails notes`,
|
|
20
|
+
`rails routes`, migrations and `db:migrate:status` (including multi-database
|
|
21
|
+
runs), then stays plain for the rest of the process. A migration body is
|
|
22
|
+
never re-run by the fallback.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Dates in `db:migrate:status` (and calendar separators) are now shown in
|
|
27
|
+
local time. They used to repeat the UTC digits of the Migration ID, so on a
|
|
28
|
+
machine outside UTC the column shifts by the UTC offset. Set `tz: utc` (or
|
|
29
|
+
`RBW_TZ=utc`) for the previous display.
|
|
30
|
+
- `rails about` now says something the stock output does not. The environment
|
|
31
|
+
is colored (production in bold red, development green, anything else
|
|
32
|
+
yellow), the database schema version shows its time and age, version
|
|
33
|
+
numbers share one accent color, the application root steps back, and list
|
|
34
|
+
values such as Middleware get a line per item. The Rails title line is kept,
|
|
35
|
+
and the old "Property / Value" header row is gone.
|
|
36
|
+
- `rails stats` is redrawn in the `db:migrate:status` table style: code and
|
|
37
|
+
test directories under their own headings, thousands grouped, zeros dimmed,
|
|
38
|
+
M/C and LOC/M dimmed for tests, a bar for each directory's share of all LOC,
|
|
39
|
+
and a bold Total row. Directories registered by other gems (such as
|
|
40
|
+
rspec-rails) are included.
|
|
41
|
+
- The default `since` window is now 90 days (was 70), close to three months.
|
|
42
|
+
Set `since: 70d` (or `RBW_SINCE=70d`) to keep the old window.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- `rails routes` no longer leaves a blank line before every piece of a wrapped
|
|
47
|
+
route name when the other columns nearly fill the terminal. A group too wide
|
|
48
|
+
for the terminal drops its Prefix column, and a table never wraps its last
|
|
49
|
+
column into lines that would run past the edge: the value stays on its row.
|
|
50
|
+
- `RBW_DATE=rel` ages are no longer off by the machine's UTC offset.
|
|
51
|
+
Migration IDs are UTC timestamps but were read as local time.
|
|
52
|
+
- `rails about` no longer crashes with `uninitialized class variable
|
|
53
|
+
@@properties in Railbow::AboutFormatter`.
|
|
54
|
+
- `rails stats` is formatted again. Since Rails 8.0 it is a Rails command
|
|
55
|
+
that never loads rake tasks, so railbow's hook never ran, and on every Rails
|
|
56
|
+
version the formatter read the wrong data and printed nothing of its own.
|
|
57
|
+
On Rails 7.2 and 8.0, `rake stats` no longer risks running railbow's
|
|
58
|
+
Rails 8.1 replacement task next to the stock one.
|
|
59
|
+
- `rails notes` no longer prints `fatal: no such path ... in HEAD` for notes
|
|
60
|
+
in untracked or gitignored files (such as `app/assets/builds`), and no
|
|
61
|
+
longer hides those notes as older than the `since` window. They are dated
|
|
62
|
+
as uncommitted, the way `git blame` dates uncommitted lines, and with
|
|
63
|
+
`RBW_GIT=author` they show dim dashes in the author column instead of git's
|
|
64
|
+
"Not Committed Yet" squeezed into a name like "No Y".
|
|
65
|
+
|
|
66
|
+
## [0.6.2] - 2026-09-21
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- Demo fixtures, README examples and spec data now use the PawShop sample
|
|
71
|
+
project throughout.
|
|
72
|
+
|
|
73
|
+
## [0.6.1] - 2026-08-10
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- On a narrow terminal, a migration's branch and landing badges no longer
|
|
78
|
+
vanish from the `db:migrate:status` name column, leaving a stray `...`
|
|
79
|
+
hanging in the padding where they used to sit. The badges are padded flush
|
|
80
|
+
against the right edge of the column, which is exactly where the width
|
|
81
|
+
budget was cutting; the name now gives up the space instead and the badges
|
|
82
|
+
stay put. When a badge is so wide that keeping it would leave under 12
|
|
83
|
+
columns for the name, it is dropped outright rather than squeezing the name
|
|
84
|
+
down to nothing.
|
|
85
|
+
|
|
8
86
|
## [0.6.0] - 2026-08-03
|
|
9
87
|
|
|
10
88
|
### Fixed
|
data/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
- **Routes** - color-coded HTTP verbs and highlighted parameters in `rails routes`
|
|
20
20
|
- **Stats** - beautiful `rails stats` tables
|
|
21
21
|
- **Notes** - `rails notes` with git blame, author colors, date filtering, and sorting
|
|
22
|
-
- **About** -
|
|
22
|
+
- **About** - `rails about` with the environment color-coded (production in red) and the schema version shown as a date and age
|
|
23
23
|
- **Git Integration** - authors, diffs, branch origin, landing dates, uncommitted file indicators
|
|
24
24
|
- **Calendar View** - month separators plus week ticks or full week rows, with optional per-section counts
|
|
25
25
|
- **Multiple Databases** - read one table by default, the rest summarized in a line each (and expanded when they have migrations pending); sharded databases merged with a status per shard
|
|
@@ -104,7 +104,7 @@ Out of the box you get:
|
|
|
104
104
|
- **Landing dates** - `⤻ Mar 04` badge when a migration was merged to main after its creation
|
|
105
105
|
- **Branch badges** - `⎇ PS-142` showing the source branch/ticket
|
|
106
106
|
- **Affected tables** - color-coded table names extracted from migration files
|
|
107
|
-
- **Time filtering** - only the last
|
|
107
|
+
- **Time filtering** - only the last 90 days shown by default (`since: 90d`), as a soft limit: never fewer than 10 migrations, however old (`since_min: 10`)
|
|
108
108
|
- **Your migrations highlighted** - rows authored by you are visually distinct
|
|
109
109
|
- **Pending migrations greyed out** - a `down` row keeps its status glyph but loses its colors
|
|
110
110
|
|
|
@@ -158,13 +158,13 @@ By default you read one table: the first database in `database.yml` is rendered
|
|
|
158
158
|
in full and every other one is summarized in a line each (`db: focus`).
|
|
159
159
|
|
|
160
160
|
```
|
|
161
|
-
📊 2 databases · primary,
|
|
161
|
+
📊 2 databases · primary, log
|
|
162
162
|
|
|
163
|
-
──── primary · myapp_development ────
|
|
164
|
-
↑↑ │ 20260721130000 │ 2026-07-21 13:00:00 │ Partition
|
|
165
|
-
↓↓ │ 20260727170000 │ 2026-07-27 17:00:00 │ Add
|
|
163
|
+
──── primary · myapp_development ──── 12 of 240 ────────────────────────
|
|
164
|
+
↑↑ │ 20260721130000 │ 2026-07-21 13:00:00 │ Partition visit reminders ● visit_reminders
|
|
165
|
+
↓↓ │ 20260727170000 │ 2026-07-27 17:00:00 │ Add microchip ids to animals ● animals
|
|
166
166
|
|
|
167
|
-
⋯
|
|
167
|
+
⋯ log · 5 migrations, all applied · latest Jul 22 2026
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
**A database with migrations pending is always expanded**, however focused the
|
|
@@ -179,14 +179,14 @@ db: "" # or RBW_DB= on the command line
|
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
```
|
|
182
|
-
──── primary · myapp_development ────
|
|
183
|
-
↑↑ │ 20260721130000 │ 2026-07-21 13:00:00 │ Partition
|
|
182
|
+
──── primary · myapp_development ──── 12 of 240 ────────────────────────
|
|
183
|
+
↑↑ │ 20260721130000 │ 2026-07-21 13:00:00 │ Partition visit reminders ● visit_reminders
|
|
184
184
|
|
|
185
|
-
────
|
|
186
|
-
↑↑ │ 20260722120000 │ 2026-07-22 12:00:00 │ Drop archived
|
|
185
|
+
──── log · log_development ─────────────────────────────────────────────
|
|
186
|
+
↑↑ │ 20260722120000 │ 2026-07-22 12:00:00 │ Drop archived visit logs ● archived_visit_logs
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
A section header counts what it is showing: `
|
|
189
|
+
A section header counts what it is showing: `12 of 240` means 12 fell inside
|
|
190
190
|
the time window. When the window found too few and the `since_min` floor topped
|
|
191
191
|
the section back up, it reads `last 10 of 50` instead, because the rows are
|
|
192
192
|
then the newest ten rather than a window's worth.
|
|
@@ -197,7 +197,7 @@ rather than an empty table, which is what keeps the `cache`, `queue` and
|
|
|
197
197
|
|
|
198
198
|
```
|
|
199
199
|
⋯ cache · 5 migrations, all applied · latest Feb 12 2024
|
|
200
|
-
⋯ queue · 12 migrations, 3 pending outside the
|
|
200
|
+
⋯ queue · 12 migrations, 3 pending outside the 90d window - RBW_SINCE=all
|
|
201
201
|
```
|
|
202
202
|
|
|
203
203
|
Databases that share a `migrations_paths` (horizontal sharding) run the same
|
|
@@ -236,7 +236,21 @@ HTTP verbs are color-coded (GET=green, POST=yellow, PATCH/PUT=cyan, DELETE=red),
|
|
|
236
236
|
|
|
237
237
|
### `rails stats`
|
|
238
238
|
|
|
239
|
-
Code statistics
|
|
239
|
+
Code statistics in the same table style as `db:migrate:status`, code and tests grouped, with each directory's share of all LOC:
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
Name Lines LOC Classes Methods M/C LOC/M Share of LOC
|
|
243
|
+
Code │ │ │ │ │ │ │
|
|
244
|
+
Controllers │ 57,143 │ 42,693 │ 212 │ 3,472 │ 16 │ 10 │ █████████▊ 22.0%
|
|
245
|
+
Models │ 76,529 │ 48,353 │ 302 │ 4,819 │ 15 │ 8 │ ███████████ 24.9%
|
|
246
|
+
Views │ 98,412 │ 70,120 │ 0 │ 0 │ 0 │ 0 │ ████████████████ 36.1%
|
|
247
|
+
Tests │ │ │ │ │ │ │
|
|
248
|
+
Model specs │ 40,210 │ 33,000 │ 0 │ 120 │ 0 │ 273 │ ███████▌ 17.0%
|
|
249
|
+
│ │ │ │ │ │ │
|
|
250
|
+
Total │ 272,294 │ 194,166 │ 514 │ 8,411 │ 16 │ 21 │
|
|
251
|
+
|
|
252
|
+
Code 161,166 LOC · Tests 33,000 LOC · Code to Test Ratio 1:0.2
|
|
253
|
+
```
|
|
240
254
|
|
|
241
255
|
### `rails notes`
|
|
242
256
|
|
|
@@ -266,7 +280,7 @@ rake railbow:init
|
|
|
266
280
|
### Example `.railbow.yml`
|
|
267
281
|
|
|
268
282
|
```yaml
|
|
269
|
-
since:
|
|
283
|
+
since: 90d
|
|
270
284
|
since_min: 10
|
|
271
285
|
date: rel
|
|
272
286
|
git: "author:me,diff,mask:auto"
|
|
@@ -293,9 +307,10 @@ Every option can also be set via `RBW_*` environment variables, which override c
|
|
|
293
307
|
|---|---|---|
|
|
294
308
|
| `RBW_PLAIN` | `1` | Disable all formatting |
|
|
295
309
|
| `RBW_FORCE` | `1` | Force formatting even when piped, in CI, or run by an LLM agent (`RBW_PLAIN=1` still wins) |
|
|
296
|
-
| `RBW_SINCE` | `2mo`, `
|
|
310
|
+
| `RBW_SINCE` | `2mo`, `90d`, `1y`, `all` | Filter migrations by time period |
|
|
297
311
|
| `RBW_SINCE_MIN` | `10`, `0` | Never show fewer than this many migrations, however old (default 10; `0` disables) |
|
|
298
312
|
| `RBW_DATE` | `full`, `rel`, `short`, `custom(%b %d)` | Date display format |
|
|
313
|
+
| `RBW_TZ` | `local` (default), `utc`, `America/New_York` | Timezone dates are shown in. Migration IDs are UTC; this picks the clock they are displayed on |
|
|
299
314
|
| `RBW_GIT` | `author:me,diff,mask:auto` | Git integration options |
|
|
300
315
|
| `RBW_VIEW` | `calendar,tables` | Enable calendar view and table detection |
|
|
301
316
|
| `RBW_CALENDAR` | `wticks`, `wdividers,counts`, `` (empty) | Week markers: tick marks, full week separator rows, per-section counts. Empty means month separators only |
|
|
@@ -344,6 +359,11 @@ Formatting auto-disables when:
|
|
|
344
359
|
formatted output to a file, or letting an agent inspect the real rendering).
|
|
345
360
|
An explicit `RBW_PLAIN=1` always wins over `RBW_FORCE`.
|
|
346
361
|
|
|
362
|
+
If railbow itself hits an error while rendering, the command still works: it
|
|
363
|
+
prints a one-line `railbow:` warning to stderr naming the error, then shows the
|
|
364
|
+
stock Rails output and stays plain for the rest of the run. Migrations are never
|
|
365
|
+
re-run by this; only their output falls back.
|
|
366
|
+
|
|
347
367
|
## Requirements
|
|
348
368
|
|
|
349
369
|
- Ruby >= 3.1.0
|
|
@@ -357,6 +377,10 @@ bundle exec rake # Run tests + linting (RSpec + Standard)
|
|
|
357
377
|
bin/console # Interactive prompt
|
|
358
378
|
```
|
|
359
379
|
|
|
380
|
+
To see a change in a real app (one that loads this checkout), set `RBW_APP` to
|
|
381
|
+
its path, then `just shot "rails routes"` saves a PNG and
|
|
382
|
+
`just cast "rails routes" 100` a scrolling GIF at 100 columns, in `tmp/shots/`.
|
|
383
|
+
|
|
360
384
|
## Contributing
|
|
361
385
|
|
|
362
386
|
Bug reports and pull requests are welcome at [github.com/amberpixels/railbow](https://github.com/amberpixels/railbow).
|
|
@@ -1,37 +1,73 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "formatters/base"
|
|
4
|
-
require_relative "config"
|
|
5
|
-
require_relative "table"
|
|
6
4
|
|
|
7
5
|
module Railbow
|
|
8
6
|
module AboutFormatter
|
|
7
|
+
TITLE = "About your application's environment"
|
|
8
|
+
|
|
9
9
|
def to_s
|
|
10
10
|
return super if Railbow.plain?
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Railbow.guard(-> { super }) { railbow_to_s }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Same shape as Rails' own listing (title, then one aligned row per
|
|
18
|
+
# property), with the values styled by what they mean. Properties other
|
|
19
|
+
# gems register render as plain text.
|
|
20
|
+
def railbow_to_s
|
|
21
|
+
f = Formatters::Base.new
|
|
22
|
+
width = properties.map { |(name, _)| name.length }.max.to_i
|
|
23
|
+
lines = [f.bold(TITLE)]
|
|
13
24
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
properties.each do |(name, value)|
|
|
26
|
+
# Lists such as Middleware get a line per item instead of one long run.
|
|
27
|
+
values = Array(value).map { |v| railbow_style_value(f, name, v.to_s) }
|
|
28
|
+
lines << "#{f.bold(name.ljust(width))} #{values.first}"
|
|
29
|
+
values.drop(1).each { |v| lines << "#{" " * width} #{v}" }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
lines.join("\n")
|
|
33
|
+
end
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
def railbow_style_value(f, name, value)
|
|
36
|
+
case name
|
|
37
|
+
when "Environment" then railbow_style_environment(f, value)
|
|
38
|
+
when "Application root" then f.dim(value)
|
|
39
|
+
when "Database schema version" then railbow_style_schema_version(f, value)
|
|
40
|
+
when /version\z/i then railbow_style_version(f, value)
|
|
41
|
+
else value
|
|
22
42
|
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Production is the one environment worth a jolt.
|
|
46
|
+
def railbow_style_environment(f, value)
|
|
47
|
+
case value
|
|
48
|
+
when "production" then f.bold(f.red(value))
|
|
49
|
+
when "development" then f.green(value)
|
|
50
|
+
else f.yellow(value)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The version number takes the accent; anything trailing it, like Ruby's
|
|
55
|
+
# build date and platform, steps back.
|
|
56
|
+
def railbow_style_version(f, value)
|
|
57
|
+
match = value.match(/\d+(?:\.\w+)+/)
|
|
58
|
+
return value unless match
|
|
59
|
+
|
|
60
|
+
tail = match.post_match.empty? ? "" : f.dim(match.post_match)
|
|
61
|
+
"#{match.pre_match}#{f.cyan(match[0])}#{tail}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Shown on railbow's clock (RBW_TZ), with the zone named because the
|
|
65
|
+
# digits of the version itself are UTC.
|
|
66
|
+
def railbow_style_schema_version(f, value)
|
|
67
|
+
time = Railbow::Clock.from_version(value)
|
|
68
|
+
return value unless time
|
|
23
69
|
|
|
24
|
-
|
|
25
|
-
columns: columns,
|
|
26
|
-
theme: Table::Themes::PLAIN,
|
|
27
|
-
compact: Railbow::Params.compact_options,
|
|
28
|
-
aliases: Railbow::Config.table_aliases
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
output = +"\n"
|
|
32
|
-
output << renderer.render(rows)
|
|
33
|
-
output << "\n"
|
|
34
|
-
output
|
|
70
|
+
"#{f.cyan(value)} #{f.dim("(#{time.strftime("%Y-%m-%d %H:%M %Z")}, #{f.format_relative_time_from(time)})")}"
|
|
35
71
|
end
|
|
36
72
|
end
|
|
37
73
|
end
|
data/lib/railbow/calendar.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
|
+
require_relative "clock"
|
|
4
5
|
|
|
5
6
|
module Railbow
|
|
6
7
|
# Turns an ordered list of migration versions into the calendar furniture that
|
|
@@ -58,10 +59,7 @@ module Railbow
|
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def parse_date(version)
|
|
61
|
-
|
|
62
|
-
Date.new(v[0..3].to_i, v[4..5].to_i, v[6..7].to_i)
|
|
63
|
-
rescue Date::Error
|
|
64
|
-
nil
|
|
62
|
+
Railbow::Clock.from_version(version)&.to_date
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
def month_key(date)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
|
|
5
|
+
module Railbow
|
|
6
|
+
# The clock railbow shows times on. Migration IDs and git dates are fixed
|
|
7
|
+
# moments; RBW_TZ (or `tz:` in .railbow.yml) only picks the zone they are
|
|
8
|
+
# shown in: "local" (the default), "utc", or any zone name Rails accepts,
|
|
9
|
+
# e.g. "America/New_York" or "Eastern Time (US & Canada)".
|
|
10
|
+
module Clock
|
|
11
|
+
@zones = {}
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# A migration ID is the UTC time the migration was generated, which is
|
|
16
|
+
# how Rails names them. Returns nil for anything that is not one.
|
|
17
|
+
def from_version(version)
|
|
18
|
+
v = version.to_s
|
|
19
|
+
return nil unless v.match?(/\A\d{14}\z/)
|
|
20
|
+
|
|
21
|
+
utc = Time.utc(
|
|
22
|
+
v[0..3].to_i, v[4..5].to_i, v[6..7].to_i,
|
|
23
|
+
v[8..9].to_i, v[10..11].to_i, v[12..13].to_i
|
|
24
|
+
)
|
|
25
|
+
in_zone(utc)
|
|
26
|
+
rescue ArgumentError
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def in_zone(time)
|
|
31
|
+
case (z = zone)
|
|
32
|
+
when :local then time.getlocal
|
|
33
|
+
when :utc then time.getutc
|
|
34
|
+
else time.in_time_zone(z)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def today
|
|
39
|
+
in_zone(Time.now).to_date
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The migration ID of the midnight that starts date on this clock, so a
|
|
43
|
+
# day-based window can be compared against migration IDs directly.
|
|
44
|
+
def version_at(date)
|
|
45
|
+
start = case (z = zone)
|
|
46
|
+
when :local then Time.local(date.year, date.month, date.day)
|
|
47
|
+
when :utc then Time.utc(date.year, date.month, date.day)
|
|
48
|
+
else z.local(date.year, date.month, date.day)
|
|
49
|
+
end
|
|
50
|
+
start.utc.strftime("%Y%m%d%H%M%S")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def zone
|
|
54
|
+
name = Params.tz
|
|
55
|
+
@zones[name] ||= resolve(name)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def resolve(name)
|
|
59
|
+
case name.downcase
|
|
60
|
+
when "local" then :local
|
|
61
|
+
when "utc" then :utc
|
|
62
|
+
else
|
|
63
|
+
require "active_support"
|
|
64
|
+
require "active_support/time"
|
|
65
|
+
ActiveSupport::TimeZone[name] || begin
|
|
66
|
+
warn " Warning: unrecognized RBW_TZ=#{name}, showing local time"
|
|
67
|
+
:local
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Railbow
|
|
4
|
-
#
|
|
4
|
+
# Spreads a set of labels evenly around the color wheel by rank. Hashing
|
|
5
|
+
# into a palette could land two labels on neighboring colors; by rank, two
|
|
6
|
+
# labels sit opposite each other, three about 120 deg apart, and so on.
|
|
5
7
|
#
|
|
6
|
-
# Instead of hashing into a fixed palette (where two labels can collide
|
|
7
|
-
# onto adjacent colors), this assigns colors by rank — evenly spacing
|
|
8
|
-
# hues around the color wheel. Two labels always get maximally different
|
|
9
|
-
# colors, three labels get ~120 deg apart, etc.
|
|
10
|
-
#
|
|
11
|
-
# Usage:
|
|
12
8
|
# assigner = Railbow::ColorAssigner.new(["Alice", "Bob", "Carol"])
|
|
13
9
|
# assigner.color_for("Alice") # => "\e[38;5;174m"
|
|
14
10
|
# assigner.code_for("Bob") # => 116 (256-color code)
|
|
15
11
|
#
|
|
16
12
|
class ColorAssigner
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# so they look pleasant on dark terminals.
|
|
13
|
+
# Muted 256-color codes in hue order, at moderate saturation and
|
|
14
|
+
# lightness so they sit well on dark terminals.
|
|
20
15
|
WHEEL = [
|
|
21
16
|
174, # 0° coral / muted rose
|
|
22
17
|
216, # 30° peach
|
|
@@ -39,13 +34,12 @@ module Railbow
|
|
|
39
34
|
end
|
|
40
35
|
end
|
|
41
36
|
|
|
42
|
-
# Returns the 256-color ANSI escape for the given label.
|
|
43
37
|
def color_for(label)
|
|
44
38
|
code = code_for(label)
|
|
45
39
|
"\e[38;5;#{code}m"
|
|
46
40
|
end
|
|
47
41
|
|
|
48
|
-
#
|
|
42
|
+
# An unknown label gets the first color.
|
|
49
43
|
def code_for(label)
|
|
50
44
|
@mapping[label] || WHEEL[0]
|
|
51
45
|
end
|
data/lib/railbow/config.rb
CHANGED
|
@@ -5,15 +5,15 @@ require "date"
|
|
|
5
5
|
module Railbow
|
|
6
6
|
module Demo
|
|
7
7
|
module Fixtures
|
|
8
|
-
# Fictional
|
|
8
|
+
# Fictional pet shop SaaS — "PawShop"
|
|
9
9
|
# 4 authors: Alice Chen, Bob Wilson, Carol Davis, Me (current user)
|
|
10
10
|
DEMO_USER_NAME = "Me"
|
|
11
11
|
DEMO_USER_EMAIL = "me@example.com"
|
|
12
12
|
|
|
13
13
|
AUTHORS = {
|
|
14
|
-
alice: {name: "Alice Chen", email: "alice@
|
|
15
|
-
bob: {name: "Bob Wilson", email: "bob@
|
|
16
|
-
carol: {name: "Carol Davis", email: "carol@
|
|
14
|
+
alice: {name: "Alice Chen", email: "alice@pawshop.dev"},
|
|
15
|
+
bob: {name: "Bob Wilson", email: "bob@pawshop.dev"},
|
|
16
|
+
carol: {name: "Carol Davis", email: "carol@pawshop.dev"},
|
|
17
17
|
me: {name: DEMO_USER_NAME, email: DEMO_USER_EMAIL}
|
|
18
18
|
}.freeze
|
|
19
19
|
|
|
@@ -31,11 +31,10 @@ module Railbow
|
|
|
31
31
|
git_email = Fixtures::DEMO_USER_EMAIL
|
|
32
32
|
git_name = Fixtures::DEMO_USER_NAME
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# Decides which landed badges count as fresh.
|
|
35
35
|
latest_version = migrations.last[:version]
|
|
36
36
|
latest_mig_date = parse_version_date(latest_version)
|
|
37
37
|
|
|
38
|
-
# Check if any landed tags exist
|
|
39
38
|
has_landed_tags = migrations.any? { |m|
|
|
40
39
|
next false unless m[:landed_date]
|
|
41
40
|
mig_date = parse_version_date(m[:version])
|
|
@@ -52,15 +51,15 @@ module Railbow
|
|
|
52
51
|
Railbow::Table::Column.new(label: "Migration Name",
|
|
53
52
|
max_width: name_col_width,
|
|
54
53
|
truncate: needs_name_truncation,
|
|
54
|
+
truncate_fn: ->(cell_raw, max_w) { formatter.name_with_tags_fitted(cell_raw, max_w) },
|
|
55
55
|
shrinkable: true, shrink_floor: 24)
|
|
56
56
|
]
|
|
57
|
-
# No
|
|
57
|
+
# No Who column: author:me only highlights.
|
|
58
58
|
|
|
59
59
|
tables_truncate_fn = ->(cell_raw, max_w) { formatter.table_tags_fitted(cell_raw, max_w) }
|
|
60
60
|
table_columns << Railbow::Table::Column.new(label: "Tables", droppable: 1,
|
|
61
61
|
truncate: true, truncate_fn: tables_truncate_fn)
|
|
62
62
|
|
|
63
|
-
# Build rows
|
|
64
63
|
highlight_rows = Set.new
|
|
65
64
|
down_rows = Set.new
|
|
66
65
|
rows = migrations.each_with_index.map do |m, idx|
|
|
@@ -73,10 +72,8 @@ module Railbow
|
|
|
73
72
|
|
|
74
73
|
display_name = m[:name]
|
|
75
74
|
|
|
76
|
-
# Diff tag (branch badge)
|
|
77
75
|
diff_tag = m[:branch] ? formatter.diff_tag_branch(m[:branch]) : nil
|
|
78
76
|
|
|
79
|
-
# Landed badge
|
|
80
77
|
landed_tag = nil
|
|
81
78
|
if m[:landed_date]
|
|
82
79
|
mig_date = parse_version_date(m[:version])
|
|
@@ -86,7 +83,6 @@ module Railbow
|
|
|
86
83
|
end
|
|
87
84
|
end
|
|
88
85
|
|
|
89
|
-
# Append tags to display_name
|
|
90
86
|
tags = [landed_tag, diff_tag].compact.join(" ")
|
|
91
87
|
if !tags.empty? && name_col_width
|
|
92
88
|
tags_width = formatter.display_width(formatter.strip_ansi(tags))
|
|
@@ -101,20 +97,18 @@ module Railbow
|
|
|
101
97
|
|
|
102
98
|
created_at = formatter.format_date(m[:version], date_format)
|
|
103
99
|
|
|
104
|
-
#
|
|
100
|
+
# author:me highlighting.
|
|
105
101
|
if m[:author_email] == git_email ||
|
|
106
102
|
(git_name && m[:author_name] && m[:author_name].downcase == git_name.downcase)
|
|
107
103
|
highlight_rows << idx
|
|
108
104
|
end
|
|
109
105
|
|
|
110
|
-
# Table tags
|
|
111
106
|
table_tags = formatter.table_tags(m[:tables])
|
|
112
107
|
|
|
113
108
|
[colored_status, m[:version], created_at, display_name, table_tags]
|
|
114
109
|
end
|
|
115
110
|
|
|
116
|
-
#
|
|
117
|
-
# tried out here without a Rails project.
|
|
111
|
+
# Real params, so RBW_CALENDAR can be tried without a Rails project.
|
|
118
112
|
calendar = Railbow::Calendar.build(
|
|
119
113
|
migrations.map { |m| m[:version] },
|
|
120
114
|
weeks: Railbow::Params.calendar_wdividers?,
|