railbow 0.3.0 → 0.4.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 +41 -0
- data/README.md +42 -2
- data/lib/railbow/calendar.rb +89 -0
- data/lib/railbow/demo/status_demo.rb +22 -38
- data/lib/railbow/init.rb +7 -1
- data/lib/railbow/params.rb +20 -1
- data/lib/railbow/table/column.rb +5 -2
- data/lib/railbow/table/renderer.rb +60 -31
- data/lib/railbow/table/theme.rb +3 -2
- data/lib/railbow/tasks/migrate_status.rake +60 -69
- data/lib/railbow/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55d44fb326ce90c9bc4e843104639b49c2d96c866401bb53c962f226166bb22f
|
|
4
|
+
data.tar.gz: 808baadb49bf2e7d96586a1a809501bdbe87cfa454db019d184ca36804cc813a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c256343dee985473f88c27aa26e0bb827587d49001eaf82f7a8d85dbf3a56197d65ba12e3641d64c87cfbcac290a2b787020d1850a53ce0e8fbf746dc2d3ef9e
|
|
7
|
+
data.tar.gz: 4d5740b3a9a98aa692b7b031fa26a811709c4724c996ee9dfd00f0dfc3f339bf431499c27c3af46b354a7aeb7ae9bd95503f99402b1d5e44f3b1fc6314cd64a1
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,47 @@ 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.4.0] - 2026-07-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `RBW_CALENDAR=wdividers` gives every ISO week its own separator row, the
|
|
13
|
+
fuller counterpart to `wticks`. A week that opens a new month is left to the
|
|
14
|
+
month row rather than being announced twice. The default stays `wticks`, so
|
|
15
|
+
existing output is unchanged until you opt in.
|
|
16
|
+
- `RBW_CALENDAR=counts` appends `· N migrations` to every separator row: how
|
|
17
|
+
many migrations that section holds, counted up to the next separator.
|
|
18
|
+
- `RBW_CALENDAR=wlabel:<fmt>` sets the strftime pattern for week rows,
|
|
19
|
+
alongside the existing `label:` for month rows. It defaults to the month
|
|
20
|
+
pattern, so the week number sits at the same offset on every separator row
|
|
21
|
+
rather than shifting left when no new month is announced.
|
|
22
|
+
- `railbow demo status` now honors `RBW_CALENDAR`, so the calendar modes can be
|
|
23
|
+
tried without a Rails project.
|
|
24
|
+
- An empty `RBW_CALENDAR` (month separators, no week markers) is now documented
|
|
25
|
+
in the help output, the `railbow init` template, and the README. It always
|
|
26
|
+
worked; nobody could have guessed it.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Calendar separator rows are drawn in a muted purple (256-color 97) instead of
|
|
31
|
+
the brighter 141, so they frame the migrations without competing with them.
|
|
32
|
+
- Migrations that are not applied (`down`) now render as a greyed-out row in
|
|
33
|
+
`db:migrate:status`, instead of signalling their state through the status
|
|
34
|
+
column alone. The row keeps its layout and its status glyph keeps its color;
|
|
35
|
+
everything else drops its own colors, so pending migrations read as inactive
|
|
36
|
+
next to the applied ones.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- Rows whose last column wrapped were indented with too wide a blank prefix:
|
|
41
|
+
building the row mutated the very cell that the wrapped-line indent is
|
|
42
|
+
measured from. Reachable from `routes`, `about`, and `stats`.
|
|
43
|
+
- A separator label wider than its column no longer pushes the rest of the row
|
|
44
|
+
sideways. Separator rows are drawn as empty walls with the label laid over
|
|
45
|
+
them, so a long label spills into the blank space to its right.
|
|
46
|
+
- `RBW_HELP` output for `db:migrate:status` uses plain hyphens instead of
|
|
47
|
+
em-dashes, matching the rest of the user-facing text.
|
|
48
|
+
|
|
8
49
|
## [0.3.0] - 2026-07-23
|
|
9
50
|
|
|
10
51
|
### Added
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
- **Notes** - `rails notes` with git blame, author colors, date filtering, and sorting
|
|
22
22
|
- **About** - polished `rails about` output
|
|
23
23
|
- **Git Integration** - authors, diffs, branch origin, landing dates, uncommitted file indicators
|
|
24
|
-
- **Calendar View** - month separators
|
|
24
|
+
- **Calendar View** - month separators plus week ticks or full week rows, with optional per-section counts
|
|
25
25
|
- **Ghost Recovery** - uses the [mighost](https://github.com/amberpixels/mighost) gem (optional) to recover names, authors, and branch badges for `NO FILE` migrations
|
|
26
26
|
- **Smart Defaults** - auto-disables in CI, piped output, `NO_COLOR`, and LLM agents
|
|
27
27
|
|
|
@@ -105,6 +105,46 @@ Out of the box you get:
|
|
|
105
105
|
- **Affected tables** - color-coded table names extracted from migration files
|
|
106
106
|
- **Time filtering** - only the last 70 days shown by default (`since: 70d`)
|
|
107
107
|
- **Your migrations highlighted** - rows authored by you are visually distinct
|
|
108
|
+
- **Pending migrations greyed out** - a `down` row keeps its status glyph but loses its colors
|
|
109
|
+
|
|
110
|
+
#### Weekly view
|
|
111
|
+
|
|
112
|
+
By default each new ISO week is marked with a tick on the date column
|
|
113
|
+
(`calendar: wticks`). For a fuller weekly breakdown, `wdividers` gives every
|
|
114
|
+
week its own separator row, and `counts` appends how many migrations sit under
|
|
115
|
+
each separator, week or month:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
RBW_CALENDAR=wdividers,counts rake db:migrate:status
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Feb 2026 W06 · 2 migrations
|
|
123
|
+
↑↑ │ 20260202145343 │ 2026-02-02 14:53:43 │ Add weight field to animals ● animals
|
|
124
|
+
↑↑ │ 20260203134528 │ 2026-02-03 13:45:28 │ Create vaccination records ● vaccination_records
|
|
125
|
+
Feb 2026 W07 · 1 migration
|
|
126
|
+
↑↑ │ 20260210183902 │ 2026-02-10 18:39:02 │ Create pet tags ● pet_tags
|
|
127
|
+
Mar 2026 W10 · 1 migration
|
|
128
|
+
↑↑ │ 20260303120000 │ 2026-03-03 12:00:00 │ Add breed restrictions to… ● adoption_policies
|
|
129
|
+
Mar 2026 W11 · 1 migration
|
|
130
|
+
↑↑ │ 20260313132325 │ 2026-03-13 13:23:25 │ Create veterinary appointm… ⎇ PS-142 ● veterinary_appointments
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Every separator carries the month, so the week number always sits at the same
|
|
134
|
+
offset instead of shifting left on rows that open no new month, and every
|
|
135
|
+
separator is drawn in the same muted purple. Both labels are `strftime`
|
|
136
|
+
patterns: `label:` for months, `wlabel:` for weeks (defaulting to the same
|
|
137
|
+
pattern).
|
|
138
|
+
|
|
139
|
+
`RBW_CALENDAR` is the list of *week* markers, so an empty value leaves you with
|
|
140
|
+
month separators and nothing else:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
view: "calendar,tables"
|
|
144
|
+
calendar: "" # month separators only
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Dropping `calendar` from `view` instead removes the month separators too.
|
|
108
148
|
|
|
109
149
|
If the [mighost](https://github.com/amberpixels/mighost) gem is installed (`gem "mighost", group: [:development, :test]`), migrations whose files were deleted (e.g. after switching branches) show up with a 👻 status and their recovered name instead of a bare `********** NO FILE **********` row - Railbow talks to it through the stable `Mighost::API`. Each ghost row carries the most informative badge mighost can provide: `≡ <version>` when the migration was re-timestamped and lives on under another version (shown with a calmer 🪦 status), `⌥ <branch>` when a branch still holds the file, or `✂ deleted in:<sha>` pointing at the commit that removed it. Ghosts dismissed via `mighost:dismiss` (or hidden by `hide_superseded`) render as plain `NO FILE`.
|
|
110
150
|
|
|
@@ -181,7 +221,7 @@ Every option can also be set via `RBW_*` environment variables, which override c
|
|
|
181
221
|
| `RBW_DATE` | `full`, `rel`, `short`, `custom(%b %d)` | Date display format |
|
|
182
222
|
| `RBW_GIT` | `author:me,diff,mask:auto` | Git integration options |
|
|
183
223
|
| `RBW_VIEW` | `calendar,tables` | Enable calendar view and table detection |
|
|
184
|
-
| `RBW_CALENDAR` | `wticks` |
|
|
224
|
+
| `RBW_CALENDAR` | `wticks`, `wdividers,counts`, `` (empty) | Week markers: tick marks, full week separator rows, per-section counts. Empty means month separators only |
|
|
185
225
|
| `RBW_COMPACT` | `oneline,dense,noheader,maxw:80` | Compact display options |
|
|
186
226
|
| `RBW_VERB` | `GET,POST` | Filter routes by HTTP method |
|
|
187
227
|
| `RBW_SORT` | `file`, `date` | Sort order for notes |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
|
|
5
|
+
module Railbow
|
|
6
|
+
# Turns an ordered list of migration versions into the calendar furniture that
|
|
7
|
+
# gives the status table its shape: a separator row when the month changes,
|
|
8
|
+
# optionally one when the ISO week changes, and optionally a tick mark on the
|
|
9
|
+
# date column at each week boundary.
|
|
10
|
+
#
|
|
11
|
+
# A separator introduces the rows beneath it and runs until the next separator,
|
|
12
|
+
# so the optional count always answers "how many migrations in this section".
|
|
13
|
+
module Calendar
|
|
14
|
+
DEFAULT_MONTH_LABEL = "%b %Y W%V"
|
|
15
|
+
# Week rows carry the month too, so the week number sits at the same offset
|
|
16
|
+
# on every separator row rather than jumping left when the month is absent.
|
|
17
|
+
DEFAULT_WEEK_LABEL = DEFAULT_MONTH_LABEL
|
|
18
|
+
|
|
19
|
+
# What the renderer needs to draw the calendar: a label per separator row,
|
|
20
|
+
# and which rows open a week (for tick marks). Both keyed by row index.
|
|
21
|
+
class Furniture
|
|
22
|
+
attr_reader :separators, :tick_rows
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
@separators = {}
|
|
26
|
+
@tick_rows = Set.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
def none
|
|
33
|
+
Furniture.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build(versions, weeks: false, ticks: false, counts: false,
|
|
37
|
+
month_label: DEFAULT_MONTH_LABEL, week_label: DEFAULT_WEEK_LABEL)
|
|
38
|
+
dates = versions.map { |v| parse_date(v) }
|
|
39
|
+
furniture = none
|
|
40
|
+
|
|
41
|
+
dates.each_with_index do |date, i|
|
|
42
|
+
prev = (i > 0) ? dates[i - 1] : nil
|
|
43
|
+
next unless date && prev
|
|
44
|
+
|
|
45
|
+
opens_week = week_key(prev) != week_key(date)
|
|
46
|
+
|
|
47
|
+
if month_key(prev) != month_key(date)
|
|
48
|
+
furniture.separators[i] = date.strftime(month_label)
|
|
49
|
+
elsif weeks && opens_week
|
|
50
|
+
furniture.separators[i] = date.strftime(week_label)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
furniture.tick_rows << i if ticks && opens_week
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
append_counts(furniture.separators, dates.size) if counts
|
|
57
|
+
furniture
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_date(version)
|
|
61
|
+
v = version.to_s
|
|
62
|
+
Date.new(v[0..3].to_i, v[4..5].to_i, v[6..7].to_i)
|
|
63
|
+
rescue Date::Error
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def month_key(date)
|
|
68
|
+
[date.year, date.month]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# cwyear, not year: the ISO week of Jan 1 can belong to the year before.
|
|
72
|
+
def week_key(date)
|
|
73
|
+
[date.cwyear, date.cweek]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def append_counts(separators, total)
|
|
77
|
+
indices = separators.keys.sort
|
|
78
|
+
indices.each_with_index do |index, nth|
|
|
79
|
+
section_end = indices[nth + 1] || total
|
|
80
|
+
separators[index] = "#{separators[index]} · #{migration_count(section_end - index)}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Spelled out: a bare number next to a week label reads as part of the date.
|
|
85
|
+
def migration_count(count)
|
|
86
|
+
"#{count} #{(count == 1) ? "migration" : "migrations"}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
require "date"
|
|
4
4
|
require_relative "../formatters/base"
|
|
5
5
|
require_relative "../table"
|
|
6
|
+
require_relative "../calendar"
|
|
6
7
|
require_relative "../config"
|
|
8
|
+
require_relative "../params"
|
|
7
9
|
require_relative "fixtures"
|
|
8
10
|
|
|
9
11
|
module Railbow
|
|
@@ -20,7 +22,8 @@ module Railbow
|
|
|
20
22
|
puts "\n#{formatter.emoji(:status)} Database: #{formatter.cyan("db/development.sqlite3")}"
|
|
21
23
|
puts
|
|
22
24
|
|
|
23
|
-
#
|
|
25
|
+
# Fixed for the showcase: author:me, diff, tables, full date. The calendar
|
|
26
|
+
# options below come from the real params, so RBW_CALENDAR still applies.
|
|
24
27
|
date_format = "full"
|
|
25
28
|
tables_enabled = true
|
|
26
29
|
diff_enabled = true
|
|
@@ -28,7 +31,7 @@ module Railbow
|
|
|
28
31
|
git_email = Fixtures::DEMO_USER_EMAIL
|
|
29
32
|
git_name = Fixtures::DEMO_USER_NAME
|
|
30
33
|
|
|
31
|
-
# Latest migration date
|
|
34
|
+
# Latest migration date - for "fresh" landed badge detection
|
|
32
35
|
latest_version = migrations.last[:version]
|
|
33
36
|
latest_mig_date = parse_version_date(latest_version)
|
|
34
37
|
|
|
@@ -43,7 +46,7 @@ module Railbow
|
|
|
43
46
|
name_col_width = needs_name_truncation ? 60 : nil
|
|
44
47
|
|
|
45
48
|
table_columns = [
|
|
46
|
-
Railbow::Table::Column.new(label: "Status", max_width: 6, sticky: true),
|
|
49
|
+
Railbow::Table::Column.new(label: "Status", max_width: 6, sticky: true, accent: true),
|
|
47
50
|
Railbow::Table::Column.new(label: "Migration ID", sticky: true),
|
|
48
51
|
Railbow::Table::Column.new(label: "Created At"),
|
|
49
52
|
Railbow::Table::Column.new(label: "Migration Name",
|
|
@@ -57,7 +60,9 @@ module Railbow
|
|
|
57
60
|
|
|
58
61
|
# Build rows
|
|
59
62
|
highlight_rows = Set.new
|
|
63
|
+
down_rows = Set.new
|
|
60
64
|
rows = migrations.each_with_index.map do |m, idx|
|
|
65
|
+
down_rows << idx if m[:status] == "down"
|
|
61
66
|
colored_status = case m[:status]
|
|
62
67
|
when "up" then formatter.green_bold("up")
|
|
63
68
|
when "down" then formatter.yellow_bold("down")
|
|
@@ -106,40 +111,16 @@ module Railbow
|
|
|
106
111
|
[colored_status, m[:version], created_at, display_name, table_tags]
|
|
107
112
|
end
|
|
108
113
|
|
|
109
|
-
# Calendar
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
v = versions[i]
|
|
120
|
-
date = Date.new(v[0..3].to_i, v[4..5].to_i, v[6..7].to_i)
|
|
121
|
-
separators[i] = date.strftime(calendar_label_fmt)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Week ticks
|
|
127
|
-
tick_rows = Set.new
|
|
128
|
-
prev_week = nil
|
|
129
|
-
versions.each_with_index do |v, i|
|
|
130
|
-
y = v[0..3].to_i
|
|
131
|
-
m = v[4..5].to_i
|
|
132
|
-
d = v[6..7].to_i
|
|
133
|
-
next if y == 0 || m == 0 || d == 0
|
|
134
|
-
|
|
135
|
-
week = Date.new(y, m, d).cweek
|
|
136
|
-
|
|
137
|
-
if i > 0 && prev_week && week != prev_week
|
|
138
|
-
tick_rows << i
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
prev_week = week
|
|
142
|
-
end
|
|
114
|
+
# Calendar furniture. Driven by the real params, so RBW_CALENDAR can be
|
|
115
|
+
# tried out here without a Rails project.
|
|
116
|
+
calendar = Railbow::Calendar.build(
|
|
117
|
+
migrations.map { |m| m[:version] },
|
|
118
|
+
weeks: Railbow::Params.calendar_wdividers?,
|
|
119
|
+
ticks: Railbow::Params.calendar_wticks?,
|
|
120
|
+
counts: Railbow::Params.calendar_counts?,
|
|
121
|
+
month_label: Railbow::Params.calendar_label,
|
|
122
|
+
week_label: Railbow::Params.calendar_week_label
|
|
123
|
+
)
|
|
143
124
|
|
|
144
125
|
aliases = Railbow::Config.table_aliases
|
|
145
126
|
renderer = Railbow::Table::Renderer.new(
|
|
@@ -148,7 +129,10 @@ module Railbow
|
|
|
148
129
|
compact: {oneline: false, dense: false, noheader: false, maxw: nil, hidden_columns: []},
|
|
149
130
|
aliases: aliases
|
|
150
131
|
)
|
|
151
|
-
puts renderer.render(rows,
|
|
132
|
+
puts renderer.render(rows,
|
|
133
|
+
separators: calendar.separators,
|
|
134
|
+
highlight_rows: highlight_rows, dim_rows: down_rows,
|
|
135
|
+
tick_rows: calendar.tick_rows, tick_col: 2)
|
|
152
136
|
end
|
|
153
137
|
|
|
154
138
|
private
|
data/lib/railbow/init.rb
CHANGED
|
@@ -43,7 +43,13 @@ module Railbow
|
|
|
43
43
|
# View mode (comma-separated): calendar, tables
|
|
44
44
|
view: "calendar,tables"
|
|
45
45
|
|
|
46
|
-
# Calendar options
|
|
46
|
+
# Calendar options (comma-separated), all require view: calendar.
|
|
47
|
+
# Leave empty ("") for month separators only, with no week markers.
|
|
48
|
+
# wticks - week tick marks on the date column
|
|
49
|
+
# wdividers - a separator row per ISO week
|
|
50
|
+
# counts - append "· N migrations" to separator rows (per section)
|
|
51
|
+
# label:<fmt> - strftime for month separators (default: %b %Y W%V)
|
|
52
|
+
# wlabel:<fmt> - strftime for week separators (default: same as label)
|
|
47
53
|
calendar: "wticks"
|
|
48
54
|
|
|
49
55
|
# Date format: full, rel, short, or custom(%b %d, %Y)
|
data/lib/railbow/params.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
|
+
require_relative "calendar"
|
|
4
5
|
require_relative "config"
|
|
5
6
|
require_relative "name_formatter"
|
|
6
7
|
require_relative "name_collision_resolver"
|
|
@@ -226,8 +227,26 @@ module Railbow
|
|
|
226
227
|
calendar["wticks"] == true
|
|
227
228
|
end
|
|
228
229
|
|
|
230
|
+
# A separator row per ISO week, the fuller counterpart to wticks.
|
|
231
|
+
def calendar_wdividers?
|
|
232
|
+
return false unless view_calendar?
|
|
233
|
+
|
|
234
|
+
calendar["wdividers"] == true
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Append "· N migrations" to every separator row: how many it introduces.
|
|
238
|
+
def calendar_counts?
|
|
239
|
+
return false unless view_calendar?
|
|
240
|
+
|
|
241
|
+
calendar["counts"] == true
|
|
242
|
+
end
|
|
243
|
+
|
|
229
244
|
def calendar_label
|
|
230
|
-
calendar["label"] ||
|
|
245
|
+
calendar["label"] || Calendar::DEFAULT_MONTH_LABEL
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def calendar_week_label
|
|
249
|
+
calendar["wlabel"] || Calendar::DEFAULT_WEEK_LABEL
|
|
231
250
|
end
|
|
232
251
|
end
|
|
233
252
|
end
|
data/lib/railbow/table/column.rb
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
module Railbow
|
|
4
4
|
module Table
|
|
5
5
|
class Column
|
|
6
|
-
attr_reader :label, :width, :min_width, :max_width, :align, :truncate, :truncate_fn, :sticky
|
|
6
|
+
attr_reader :label, :width, :min_width, :max_width, :align, :truncate, :truncate_fn, :sticky, :accent
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
# accent: the column carries its own meaning through color (a status
|
|
9
|
+
# glyph, say), so it keeps that color when the row is dimmed.
|
|
10
|
+
def initialize(label:, width: :auto, min_width: nil, max_width: nil, align: :left, truncate: false, truncate_fn: nil, sticky: false, accent: false)
|
|
9
11
|
@label = label
|
|
10
12
|
@width = width
|
|
11
13
|
@min_width = min_width
|
|
@@ -14,6 +16,7 @@ module Railbow
|
|
|
14
16
|
@truncate = truncate
|
|
15
17
|
@truncate_fn = truncate_fn
|
|
16
18
|
@sticky = sticky
|
|
19
|
+
@accent = accent
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def fixed?
|
|
@@ -10,8 +10,9 @@ module Railbow
|
|
|
10
10
|
|
|
11
11
|
RESET = "\e[0m"
|
|
12
12
|
WHITE = "\e[97m"
|
|
13
|
-
GHOST_BG = "\e[48;5;52m" # deep red/maroon background
|
|
13
|
+
GHOST_BG = "\e[48;5;52m" # deep red/maroon background - stands out as abnormal
|
|
14
14
|
GHOST_FG = "\e[38;5;217m" # warm pink foreground for contrast
|
|
15
|
+
DIMMED_FG = "\e[38;5;242m" # muted grey - a row that is not currently in effect
|
|
15
16
|
|
|
16
17
|
attr_reader :columns, :theme
|
|
17
18
|
|
|
@@ -23,7 +24,7 @@ module Railbow
|
|
|
23
24
|
@theme = theme
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
def render(rows, separators: {}, highlight_rows: Set.new, ghost_rows: Set.new, tick_rows: Set.new, tick_col: nil)
|
|
27
|
+
def render(rows, separators: {}, highlight_rows: Set.new, ghost_rows: Set.new, dim_rows: Set.new, tick_rows: Set.new, tick_col: nil)
|
|
27
28
|
return "" if columns.empty?
|
|
28
29
|
|
|
29
30
|
# Remap rows if columns were hidden
|
|
@@ -64,12 +65,11 @@ module Railbow
|
|
|
64
65
|
rows.each_with_index do |row, i|
|
|
65
66
|
tc = (tick_rows.include?(i) && tick_col) ? tick_col : nil
|
|
66
67
|
if separators.key?(i) && theme.format_separator
|
|
67
|
-
|
|
68
|
-
sep_row[1] = theme.format_separator.call(separators[i]) if columns.size > 1
|
|
69
|
-
lines << render_row(sep_row, resolved, tick_col: tc, tick_cross: true)
|
|
68
|
+
lines << render_separator_row(separators[i], resolved, tick_col: tc)
|
|
70
69
|
tc = nil # tick already shown on separator row
|
|
71
70
|
end
|
|
72
|
-
formatted = render_row(row, resolved, tick_col: tc, highlight: highlight_rows.include?(i),
|
|
71
|
+
formatted = render_row(row, resolved, tick_col: tc, highlight: highlight_rows.include?(i),
|
|
72
|
+
ghost: ghost_rows.include?(i), dim: dim_rows.include?(i))
|
|
73
73
|
lines << formatted
|
|
74
74
|
end
|
|
75
75
|
lines.join("\n")
|
|
@@ -112,44 +112,65 @@ module Railbow
|
|
|
112
112
|
}.join(theme.header_col_separator)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
# A separator row is furniture, not data: the walls are drawn empty and the
|
|
116
|
+
# label is laid over them starting at the second column. Writing over the
|
|
117
|
+
# blanks rather than filling a cell means a label wider than its column
|
|
118
|
+
# spills into the empty space to its right instead of shifting the row.
|
|
119
|
+
def render_separator_row(label, widths, tick_col: nil)
|
|
120
|
+
pad = effective_padding
|
|
121
|
+
cells = widths.map { |w| "#{pad}#{" " * w}#{pad}" }
|
|
122
|
+
|
|
123
|
+
skeleton = cells.first.to_s.dup
|
|
124
|
+
(1...cells.size).each do |i|
|
|
125
|
+
skeleton << "#{separator_at(i - 1, tick_col, cross: true)}#{cells[i]}"
|
|
126
|
+
end
|
|
127
|
+
return skeleton if cells.size < 2 || label.nil? || label.empty?
|
|
128
|
+
|
|
129
|
+
offset = display_width(cells[0]) + display_width(separator_at(0, tick_col, cross: true)) + display_width(pad)
|
|
130
|
+
tail = skeleton[(offset + display_width(label))..] || ""
|
|
131
|
+
"#{skeleton[0, offset]}#{theme.format_separator.call(label)}#{tail}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# The separator character between column index and index + 1. Columns
|
|
135
|
+
# flanking the tick column get the tick variant so the week line reads
|
|
136
|
+
# as one continuous rule.
|
|
137
|
+
def separator_at(index, tick_col, cross: false)
|
|
138
|
+
flanks_tick = tick_col && (index == tick_col - 1 || index == tick_col)
|
|
139
|
+
return theme.col_separator unless flanks_tick
|
|
140
|
+
|
|
141
|
+
cross ? theme.tick_cross_separator : theme.tick_separator
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def render_row(row, widths, tick_col: nil, highlight: false, ghost: false, dim: false)
|
|
116
145
|
last = columns.size - 1
|
|
117
146
|
pad = effective_padding
|
|
118
|
-
default_sep = theme.col_separator
|
|
119
|
-
tick_sep = tick_cross ? theme.tick_cross_separator : theme.tick_separator
|
|
120
147
|
|
|
121
148
|
prefix_parts = row[0...last].each_with_index.map { |cell, i|
|
|
122
149
|
s = cell.to_s
|
|
123
150
|
cell_w = display_width(strip_ansi(s))
|
|
124
151
|
padding = " " * [widths[i] - cell_w, 0].max
|
|
125
152
|
content = (columns[i].align == :right) ? "#{padding}#{s}" : "#{s}#{padding}"
|
|
126
|
-
|
|
127
|
-
content = "#{GHOST_BG}#{GHOST_FG}#{content}#{RESET}"
|
|
128
|
-
elsif highlight
|
|
129
|
-
content = "#{WHITE}#{content}#{RESET}"
|
|
130
|
-
end
|
|
153
|
+
content = style_cell(content, highlight: highlight, ghost: ghost, dim: dim, accent: columns[i].accent)
|
|
131
154
|
"#{pad}#{content}#{RESET}#{pad}"
|
|
132
155
|
}
|
|
133
156
|
|
|
134
|
-
# Join prefix parts with per-position separators
|
|
135
|
-
# Loop index i joins column i-1 and column i (
|
|
136
|
-
#
|
|
137
|
-
|
|
157
|
+
# Join prefix parts with per-position separators.
|
|
158
|
+
# Loop index i joins column i-1 and column i (separator index = i-1).
|
|
159
|
+
# dup: appending to prefix_parts.first itself would corrupt the parts
|
|
160
|
+
# that render_last_cell measures for the wrapped-line indent.
|
|
161
|
+
prefix = prefix_parts.first.to_s.dup
|
|
138
162
|
(1...prefix_parts.size).each do |i|
|
|
139
|
-
|
|
140
|
-
sep = (tick_col && (sep_idx == tick_col - 1 || sep_idx == tick_col)) ? tick_sep : default_sep
|
|
141
|
-
prefix << "#{sep}#{prefix_parts[i]}"
|
|
163
|
+
prefix << "#{separator_at(i - 1, tick_col)}#{prefix_parts[i]}"
|
|
142
164
|
end
|
|
143
165
|
|
|
144
166
|
last_cell_raw = row[last].to_s
|
|
145
167
|
|
|
146
168
|
# Separator before the last column has index (last - 1)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
render_last_cell(prefix, prefix_parts, last_cell_raw, widths, last, col_sep: last_sep, highlight: highlight, ghost: ghost)
|
|
169
|
+
last_sep = separator_at(last - 1, tick_col)
|
|
170
|
+
render_last_cell(prefix, prefix_parts, last_cell_raw, widths, last, col_sep: last_sep, highlight: highlight, ghost: ghost, dim: dim)
|
|
150
171
|
end
|
|
151
172
|
|
|
152
|
-
def render_last_cell(prefix, prefix_parts, last_cell_raw, widths, last, col_sep: nil, highlight: false, ghost: false)
|
|
173
|
+
def render_last_cell(prefix, prefix_parts, last_cell_raw, widths, last, col_sep: nil, highlight: false, ghost: false, dim: false)
|
|
153
174
|
pad = effective_padding
|
|
154
175
|
sep = col_sep || theme.col_separator
|
|
155
176
|
|
|
@@ -167,7 +188,7 @@ module Railbow
|
|
|
167
188
|
if columns[last].truncate_fn && last_col_max &&
|
|
168
189
|
display_width(last_cell_plain) > last_col_max
|
|
169
190
|
last_cell_raw = columns[last].truncate_fn.call(last_cell_raw, last_col_max)
|
|
170
|
-
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost)
|
|
191
|
+
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost, dim: dim, accent: columns[last].accent)
|
|
171
192
|
return "#{prefix}#{sep}#{pad}#{last_cell_raw}#{RESET}#{pad}"
|
|
172
193
|
end
|
|
173
194
|
|
|
@@ -175,18 +196,18 @@ module Railbow
|
|
|
175
196
|
if columns[last].truncate && !columns[last].max_width && last_col_max &&
|
|
176
197
|
display_width(last_cell_plain) > last_col_max
|
|
177
198
|
last_cell_raw = truncate_by_words(last_cell_raw, last_col_max)
|
|
178
|
-
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost)
|
|
199
|
+
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost, dim: dim, accent: columns[last].accent)
|
|
179
200
|
return "#{prefix}#{sep}#{pad}#{last_cell_raw}#{RESET}#{pad}"
|
|
180
201
|
end
|
|
181
202
|
|
|
182
203
|
# In oneline mode, truncate instead of wrapping
|
|
183
204
|
if @compact[:oneline] && last_col_max && display_width(last_cell_plain) > last_col_max
|
|
184
205
|
last_cell_raw = truncate_by_words(last_cell_raw, last_col_max)
|
|
185
|
-
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost)
|
|
206
|
+
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost, dim: dim, accent: columns[last].accent)
|
|
186
207
|
return "#{prefix}#{sep}#{pad}#{last_cell_raw}#{RESET}#{pad}"
|
|
187
208
|
end
|
|
188
209
|
|
|
189
|
-
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost)
|
|
210
|
+
last_cell_raw = style_cell(last_cell_raw, highlight: highlight, ghost: ghost, dim: dim, accent: columns[last].accent)
|
|
190
211
|
|
|
191
212
|
if last_col_max && display_width(strip_ansi(last_cell_raw)) > last_col_max
|
|
192
213
|
blank_prefix = prefix_parts.map { |part|
|
|
@@ -201,9 +222,17 @@ module Railbow
|
|
|
201
222
|
end
|
|
202
223
|
end
|
|
203
224
|
|
|
204
|
-
def style_cell(content, highlight: false, ghost: false)
|
|
225
|
+
def style_cell(content, highlight: false, ghost: false, dim: false, accent: false)
|
|
205
226
|
if ghost
|
|
206
227
|
"#{GHOST_BG}#{GHOST_FG}#{content}#{RESET}"
|
|
228
|
+
elsif dim
|
|
229
|
+
# An accent column keeps its color - on a dimmed row it is the one
|
|
230
|
+
# cell still carrying meaning. Everything else drops its own colors:
|
|
231
|
+
# one flat grey is what reads as "not in effect", and the resets that
|
|
232
|
+
# end each inner color would break the grey run anyway.
|
|
233
|
+
return content if accent
|
|
234
|
+
|
|
235
|
+
"#{DIMMED_FG}#{strip_ansi(content)}#{RESET}"
|
|
207
236
|
elsif highlight
|
|
208
237
|
"#{WHITE}#{content}#{RESET}"
|
|
209
238
|
else
|
|
@@ -315,7 +344,7 @@ module Railbow
|
|
|
315
344
|
current << token
|
|
316
345
|
current_width += token_width
|
|
317
346
|
elsif current_width.zero?
|
|
318
|
-
# Single token wider than max
|
|
347
|
+
# Single token wider than max - try to break on underscores
|
|
319
348
|
broken = break_long_token(token, max_width)
|
|
320
349
|
lines.concat(broken[0...-1])
|
|
321
350
|
current = +broken.last
|
data/lib/railbow/table/theme.rb
CHANGED
|
@@ -8,7 +8,8 @@ module Railbow
|
|
|
8
8
|
:format_header_cell, :format_separator
|
|
9
9
|
|
|
10
10
|
def initialize(col_separator:, header_col_separator:, cell_padding:,
|
|
11
|
-
format_header_cell:, format_separator: nil,
|
|
11
|
+
format_header_cell:, format_separator: nil,
|
|
12
|
+
tick_separator: nil, tick_cross_separator: nil)
|
|
12
13
|
@col_separator = col_separator
|
|
13
14
|
@tick_separator = tick_separator || col_separator
|
|
14
15
|
@tick_cross_separator = tick_cross_separator || @tick_separator
|
|
@@ -23,7 +24,7 @@ module Railbow
|
|
|
23
24
|
RESET = "\e[0m"
|
|
24
25
|
BOLD = "\e[1m"
|
|
25
26
|
WHITE = "\e[97m"
|
|
26
|
-
PURPLE = "\e[38;5;
|
|
27
|
+
PURPLE = "\e[38;5;97m" # separator rows: present, but never louder than the data
|
|
27
28
|
BG_PURPLE = "\e[48;5;99m"
|
|
28
29
|
DIM = "\e[2m"
|
|
29
30
|
|
|
@@ -6,6 +6,7 @@ require_relative "../formatters/base"
|
|
|
6
6
|
require_relative "../migration_parser"
|
|
7
7
|
require_relative "../config"
|
|
8
8
|
require_relative "../table"
|
|
9
|
+
require_relative "../calendar"
|
|
9
10
|
require_relative "../logo"
|
|
10
11
|
|
|
11
12
|
# Override DatabaseTasks.migrate_status which is called by both
|
|
@@ -270,7 +271,7 @@ module Railbow
|
|
|
270
271
|
# 1. Filter out child branches: if branch A is an ancestor of branch B,
|
|
271
272
|
# the commit was introduced in A, not B.
|
|
272
273
|
# 2. Among remaining, prefer the branch with the MOST commits after the
|
|
273
|
-
# adding commit
|
|
274
|
+
# adding commit - it has been active longer since the commit was made,
|
|
274
275
|
# indicating it is the original branch (not a newer fork).
|
|
275
276
|
best = if branches.size == 1
|
|
276
277
|
branches.first
|
|
@@ -376,36 +377,44 @@ module Railbow
|
|
|
376
377
|
Units: d (days), w (weeks), mo/m (months), y (years)
|
|
377
378
|
|
|
378
379
|
RBW_DATE=<mode> Date column format (default: full):
|
|
379
|
-
full
|
|
380
|
-
rel
|
|
381
|
-
short
|
|
382
|
-
custom(…)
|
|
380
|
+
full - 2026-01-30 12:08:54 (column: Created At)
|
|
381
|
+
rel - ~3d ago
|
|
382
|
+
short - Jan 30 (column: Date)
|
|
383
|
+
custom(…) - user strftime, e.g. custom(%b %d, %Y)
|
|
383
384
|
|
|
384
385
|
RBW_VIEW=<options> Display options (comma-separated):
|
|
385
|
-
calendar
|
|
386
|
-
tables
|
|
386
|
+
calendar - show month/year separator lines + week ticks
|
|
387
|
+
tables - parse migration files, show Tables column
|
|
387
388
|
|
|
388
389
|
RBW_COMPACT=<options> Compact display (comma-separated):
|
|
389
|
-
oneline
|
|
390
|
-
dense
|
|
391
|
-
noheader
|
|
392
|
-
maxw:<n>
|
|
393
|
-
hide:<col>
|
|
394
|
-
|
|
395
|
-
RBW_CALENDAR=<options>
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
390
|
+
oneline - truncate instead of wrapping
|
|
391
|
+
dense - remove cell padding
|
|
392
|
+
noheader - hide table header row
|
|
393
|
+
maxw:<n> - cap column widths at n chars
|
|
394
|
+
hide:<col> - hide a column by name (repeatable)
|
|
395
|
+
|
|
396
|
+
RBW_CALENDAR=<options> Calendar sub-options (requires RBW_VIEW=calendar):
|
|
397
|
+
(empty) - month separators only, no week
|
|
398
|
+
markers at all
|
|
399
|
+
wticks - week tick marks on the date column
|
|
400
|
+
wdividers - a separator row per ISO week
|
|
401
|
+
counts - append "· N migrations" to every
|
|
402
|
+
separator row (that section)
|
|
403
|
+
label:<fmt> - strftime for month separators
|
|
404
|
+
(default: %b %Y W%V)
|
|
405
|
+
wlabel:<fmt> - strftime for week separators
|
|
406
|
+
(default: same as label, so the
|
|
407
|
+
week number never shifts)
|
|
399
408
|
|
|
400
409
|
RBW_GIT=<options> Git integration (comma-separated):
|
|
401
|
-
author
|
|
402
|
-
author:all
|
|
403
|
-
author:me
|
|
404
|
-
diff
|
|
405
|
-
base:<branch>
|
|
406
|
-
mask:<re>
|
|
410
|
+
author - add an Author column (same as author:all)
|
|
411
|
+
author:all - add an Author column
|
|
412
|
+
author:me - highlight your own migrations
|
|
413
|
+
diff - tag migrations by git origin
|
|
414
|
+
base:<branch> - base branch for diff (default: auto-detected)
|
|
415
|
+
mask:<re> - regex to extract branch label
|
|
407
416
|
e.g. mask:(WS-[^/]+)/
|
|
408
|
-
mask:auto
|
|
417
|
+
mask:auto - auto-extract ticket id from branch name
|
|
409
418
|
|
|
410
419
|
RBW_PLAIN=1 Disable Railbow formatting (plain Rails output)
|
|
411
420
|
|
|
@@ -419,6 +428,7 @@ module Railbow
|
|
|
419
428
|
\e[1mExamples:\e[0m
|
|
420
429
|
rake db:migrate:status
|
|
421
430
|
RBW_SINCE=2mo RBW_VIEW=calendar rake db:migrate:status
|
|
431
|
+
RBW_CALENDAR=wdividers,counts rake db:migrate:status
|
|
422
432
|
RBW_VIEW=tables RBW_GIT=author rake db:migrate:status
|
|
423
433
|
RBW_GIT=author:me RBW_SINCE=3mo rake db:migrate:status
|
|
424
434
|
RBW_DATE=rel rake db:migrate:status
|
|
@@ -481,7 +491,7 @@ module Railbow
|
|
|
481
491
|
|
|
482
492
|
skipped = total_count - db_list.size
|
|
483
493
|
if skipped > 0
|
|
484
|
-
puts formatter.dim(" (#{skipped} older migrations hidden
|
|
494
|
+
puts formatter.dim(" (#{skipped} older migrations hidden - SINCE=#{since_value})")
|
|
485
495
|
puts
|
|
486
496
|
end
|
|
487
497
|
end
|
|
@@ -545,7 +555,7 @@ module Railbow
|
|
|
545
555
|
end
|
|
546
556
|
|
|
547
557
|
# Build columns
|
|
548
|
-
# Latest migration ID date
|
|
558
|
+
# Latest migration ID date - used to determine "fresh" landed badges
|
|
549
559
|
latest_version = db_list.last&.dig(1).to_s
|
|
550
560
|
latest_mig_date = begin
|
|
551
561
|
Date.new(latest_version[0..3].to_i, latest_version[4..5].to_i, latest_version[6..7].to_i)
|
|
@@ -565,7 +575,7 @@ module Railbow
|
|
|
565
575
|
needs_name_truncation = tables_enabled || author_mode == "all" || diff_enabled || has_landed_tags
|
|
566
576
|
name_col_width = needs_name_truncation ? 60 : nil
|
|
567
577
|
table_columns = [
|
|
568
|
-
Railbow::Table::Column.new(label: "Status", max_width: 6, sticky: true),
|
|
578
|
+
Railbow::Table::Column.new(label: "Status", max_width: 6, sticky: true, accent: true),
|
|
569
579
|
Railbow::Table::Column.new(label: "Migration ID", sticky: true),
|
|
570
580
|
Railbow::Table::Column.new(label: (date_format == "full") ? "Created At" : "Date"),
|
|
571
581
|
Railbow::Table::Column.new(label: "Migration Name",
|
|
@@ -590,10 +600,14 @@ module Railbow
|
|
|
590
600
|
{}
|
|
591
601
|
end
|
|
592
602
|
|
|
593
|
-
# Build rows and track highlight/ghost indices
|
|
603
|
+
# Build rows and track highlight/ghost/down indices
|
|
594
604
|
highlight_rows = Set.new
|
|
595
605
|
ghost_rows = Set.new
|
|
606
|
+
down_rows = Set.new
|
|
596
607
|
rows = db_list.each_with_index.map do |(status, version, name), idx|
|
|
608
|
+
# A pending migration is not in effect yet: grey the whole row out so it
|
|
609
|
+
# reads as inactive next to the applied ones.
|
|
610
|
+
down_rows << idx if status == "down"
|
|
597
611
|
colored_status = case status
|
|
598
612
|
when "up" then formatter.green_bold("up")
|
|
599
613
|
when "down" then formatter.yellow_bold("down")
|
|
@@ -602,7 +616,7 @@ module Railbow
|
|
|
602
616
|
ghost_snapshot = name.include?("NO FILE") ? mighost_snapshots[version.to_s] : nil
|
|
603
617
|
if name.include?("NO FILE") && ghost_snapshot
|
|
604
618
|
ghost_rows << idx
|
|
605
|
-
# Mighost recovered this ghost migration
|
|
619
|
+
# Mighost recovered this ghost migration - show ghost status + name + badge.
|
|
606
620
|
# A superseded ghost lives on under another version: stale bookkeeping,
|
|
607
621
|
# not a lost migration, so it gets a calmer glyph.
|
|
608
622
|
colored_status = ghost_snapshot.superseded_by ? "🪦" : "👻"
|
|
@@ -701,7 +715,7 @@ module Railbow
|
|
|
701
715
|
filepath = version_to_file[version.to_s]
|
|
702
716
|
basename = filepath ? File.basename(filepath) : nil
|
|
703
717
|
|
|
704
|
-
# Uncommitted migrations have no git author
|
|
718
|
+
# Uncommitted migrations have no git author - treat them as mine.
|
|
705
719
|
# Match by email first; fall back to author name to handle cases where
|
|
706
720
|
# the commit email differs from git config (e.g. GitHub noreply emails
|
|
707
721
|
# after squash-merge, or mailmap rewrites).
|
|
@@ -736,44 +750,18 @@ module Railbow
|
|
|
736
750
|
row
|
|
737
751
|
end
|
|
738
752
|
|
|
739
|
-
# Calendar separators
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
date = Date.new(v[0..3].to_i, v[4..5].to_i, v[6..7].to_i)
|
|
752
|
-
separators[i] = date.strftime(calendar_label_fmt)
|
|
753
|
-
end
|
|
754
|
-
end
|
|
755
|
-
end
|
|
756
|
-
end
|
|
757
|
-
|
|
758
|
-
# Week tick separators: mark first row of each new ISO week
|
|
759
|
-
tick_rows = Set.new
|
|
760
|
-
if ticks_enabled
|
|
761
|
-
versions = db_list.map { |_, v, _| v.to_s }
|
|
762
|
-
prev_week = nil
|
|
763
|
-
versions.each_with_index do |v, i|
|
|
764
|
-
y = v[0..3].to_i
|
|
765
|
-
m = v[4..5].to_i
|
|
766
|
-
d = v[6..7].to_i
|
|
767
|
-
next if y == 0 || m == 0 || d == 0
|
|
768
|
-
|
|
769
|
-
week = Date.new(y, m, d).cweek
|
|
770
|
-
|
|
771
|
-
if i > 0 && prev_week && week != prev_week
|
|
772
|
-
tick_rows << i
|
|
773
|
-
end
|
|
774
|
-
|
|
775
|
-
prev_week = week
|
|
776
|
-
end
|
|
753
|
+
# Calendar furniture: month separators, week separators, week ticks
|
|
754
|
+
calendar = if calendar_enabled
|
|
755
|
+
Railbow::Calendar.build(
|
|
756
|
+
db_list.map { |_, v, _| v.to_s },
|
|
757
|
+
weeks: Railbow::Params.calendar_wdividers?,
|
|
758
|
+
ticks: ticks_enabled,
|
|
759
|
+
counts: Railbow::Params.calendar_counts?,
|
|
760
|
+
month_label: Railbow::Params.calendar_label,
|
|
761
|
+
week_label: Railbow::Params.calendar_week_label
|
|
762
|
+
)
|
|
763
|
+
else
|
|
764
|
+
Railbow::Calendar.none
|
|
777
765
|
end
|
|
778
766
|
|
|
779
767
|
renderer = Railbow::Table::Renderer.new(
|
|
@@ -783,7 +771,10 @@ module Railbow
|
|
|
783
771
|
aliases: Railbow::Config.table_aliases
|
|
784
772
|
)
|
|
785
773
|
tick_col = 2 # Date column index
|
|
786
|
-
puts renderer.render(rows,
|
|
774
|
+
puts renderer.render(rows,
|
|
775
|
+
separators: calendar.separators,
|
|
776
|
+
highlight_rows: highlight_rows, ghost_rows: ghost_rows, dim_rows: down_rows,
|
|
777
|
+
tick_rows: calendar.tick_rows, tick_col: tick_col)
|
|
787
778
|
end
|
|
788
779
|
end
|
|
789
780
|
end
|
data/lib/railbow/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: railbow
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eugene M
|
|
@@ -78,6 +78,7 @@ files:
|
|
|
78
78
|
- exe/railbow
|
|
79
79
|
- lib/railbow.rb
|
|
80
80
|
- lib/railbow/about_formatter.rb
|
|
81
|
+
- lib/railbow/calendar.rb
|
|
81
82
|
- lib/railbow/color_assigner.rb
|
|
82
83
|
- lib/railbow/config.rb
|
|
83
84
|
- lib/railbow/demo/fixtures.rb
|