studio-engine 0.8.0 → 0.9.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 +18 -0
- data/app/models/studio/enumeral.rb +85 -0
- data/db/migrate/20260623130000_create_studio_enumerals.rb +30 -0
- data/lib/studio/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31ed2941026e5bf0114872ba16dd7a9b94b1a472d5407ff82ec604ba2e659962
|
|
4
|
+
data.tar.gz: 1fda4b1328315323b8afcee644a68acc8678e756762a88b289ea10e0fc46a6cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6431e1dafbd0a614e1c0a6abe25004ab3888a3d1e7c263ab97d7883e10bcedde60da03f16c98b262d9926173749343b00c33d6a5580db9fc299c8b1b355a9665
|
|
7
|
+
data.tar.gz: 7513d2735c2535ed520530eab4adc0b1ce60de617488e4fa934e43beb31ef330e7e49c8837e3310aa92467559dbabf714fab43b81d86a0d52cebce8f85674e35
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.9.0 — 2026-06-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`Studio::Enumeral`** — a shared, DB-backed enumeration table for the whole
|
|
11
|
+
ecosystem. Every "list of fixed, labeled, colored values" (Pokémon types first;
|
|
12
|
+
statuses/tiers/roles later) is rows in ONE `studio_enumerals` table, grouped by
|
|
13
|
+
`category`: a stable machine `key`, a human `label`, a `color` hex, a display
|
|
14
|
+
`position`, a sparse `rank` (gappy 100/200/… domain ranking — e.g. most-common
|
|
15
|
+
first — distinct from display order), and `metadata` (jsonb) for
|
|
16
|
+
category-specific extras. Class helpers `catalog`, `lookup`, `color_map` (one
|
|
17
|
+
query → `{ key => color }`), `color_for(category, key, fallback:)`, and a
|
|
18
|
+
metadata-backed `emoji` reader + `emoji_map` (decorative glyphs live in
|
|
19
|
+
`metadata`, not a column); scopes `ordered` / `by_rank`; `available?` degrades
|
|
20
|
+
to empty when the table isn't installed. Shipped by the gem like `Studio::Link` /
|
|
21
|
+
`Studio::EmailDelivery` — reference migration `create_studio_enumerals`
|
|
22
|
+
installed per app (copy into `db/migrate`). No behavior is attached: a consumer
|
|
23
|
+
adopts a new category by seeding rows, no code change.
|
|
24
|
+
|
|
7
25
|
## 0.8.0 — 2026-06-21
|
|
8
26
|
|
|
9
27
|
### Added
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Studio
|
|
2
|
+
# A shared, DB-backed enumeration table for the whole ecosystem. Every "list of
|
|
3
|
+
# fixed, labeled, colored values" — Pokémon types (its first use), and later
|
|
4
|
+
# statuses, tiers, roles, … — is just rows in ONE studio_enumerals table,
|
|
5
|
+
# grouped by `category`. Each row is identity + presentation: a stable machine
|
|
6
|
+
# `key` ("fire"), a human `label` ("Fire"), a `color` hex ("#EE8130"), and a
|
|
7
|
+
# `position` for ordering within its category. `metadata` (jsonb) carries any
|
|
8
|
+
# category-specific extras off the columns.
|
|
9
|
+
#
|
|
10
|
+
# Shipped by the gem like Studio::Link / Studio::EmailDelivery: the model lives
|
|
11
|
+
# here, the table lives in each consumer app (copy the reference migration into
|
|
12
|
+
# db/migrate). No behavior is attached — it is pure reference data the apps
|
|
13
|
+
# read, so a consumer adopts a new category by seeding rows, no code change.
|
|
14
|
+
class Enumeral < ApplicationRecord
|
|
15
|
+
self.table_name = "studio_enumerals"
|
|
16
|
+
|
|
17
|
+
HEX = /\A#(?:\h{3}|\h{6})\z/
|
|
18
|
+
|
|
19
|
+
validates :category, presence: true
|
|
20
|
+
validates :key, presence: true,
|
|
21
|
+
uniqueness: { scope: :category, case_sensitive: false }
|
|
22
|
+
validates :color, format: { with: HEX, message: "must be a hex color like #EE8130" },
|
|
23
|
+
allow_blank: true
|
|
24
|
+
|
|
25
|
+
scope :in_category, ->(category) { where(category: category.to_s) }
|
|
26
|
+
scope :ordered, -> { order(:position, :key) }
|
|
27
|
+
# By `rank` (a sparse 100/200/… domain ranking), nulls last — for categories
|
|
28
|
+
# ordered by something other than display position, e.g. most-common-first.
|
|
29
|
+
scope :by_rank, -> { order(:rank) }
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# True only when the table is actually migrated in this app's DB, so a
|
|
33
|
+
# consumer that hasn't installed the migration degrades to "empty" instead
|
|
34
|
+
# of crashing (mirrors Studio::EmailDelivery.available?).
|
|
35
|
+
def available?
|
|
36
|
+
connection.data_source_exists?(table_name)
|
|
37
|
+
rescue ActiveRecord::ActiveRecordError, NoMethodError
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The ordered list of a category's enumerals — the relation a view iterates
|
|
42
|
+
# (one query). Returns an empty relation when the table isn't installed yet,
|
|
43
|
+
# so callers can `.each` safely pre-migration.
|
|
44
|
+
def catalog(category)
|
|
45
|
+
return none unless available?
|
|
46
|
+
|
|
47
|
+
in_category(category).ordered
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The single enumeral for (category, key), or nil.
|
|
51
|
+
def lookup(category, key)
|
|
52
|
+
return nil unless available?
|
|
53
|
+
|
|
54
|
+
in_category(category).find_by(key: key.to_s)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# { key => color } for a category in ONE query — the shape a view wants:
|
|
58
|
+
# build it once, then look up each cell with no extra queries (avoids an
|
|
59
|
+
# N+1 when rendering many rows, e.g. the /pokemon type badges).
|
|
60
|
+
def color_map(category)
|
|
61
|
+
catalog(category).pluck(:key, :color).to_h
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Just the color for a single (category, key), with an optional fallback
|
|
65
|
+
# when the key is unknown or the table isn't installed yet.
|
|
66
|
+
def color_for(category, key, fallback: nil)
|
|
67
|
+
return fallback unless available?
|
|
68
|
+
|
|
69
|
+
in_category(category).where(key: key.to_s).limit(1).pick(:color) || fallback
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# { key => emoji } for a category in ONE query — the metadata sibling of
|
|
73
|
+
# color_map, for decorative glyphs kept in `metadata` rather than a column.
|
|
74
|
+
def emoji_map(category)
|
|
75
|
+
catalog(category).pluck(:key, :metadata).to_h { |key, meta| [key, meta && meta["emoji"]] }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# A decorative glyph for the value, kept in `metadata` (not a column) since
|
|
80
|
+
# it's a presentation extra — e.g. a Pokémon type's emoji. Nil when unset.
|
|
81
|
+
def emoji
|
|
82
|
+
metadata && metadata["emoji"]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Reference migration for the Studio::Enumeral model. Like the engine's
|
|
2
|
+
# studio_links / studio_email_deliveries migrations, each consumer app installs
|
|
3
|
+
# its own copy of this into db/migrate so the table is created in the app's
|
|
4
|
+
# database (the model is shipped by the gem; the table lives per app).
|
|
5
|
+
class CreateStudioEnumerals < ActiveRecord::Migration[7.2]
|
|
6
|
+
def change
|
|
7
|
+
create_table :studio_enumerals do |t|
|
|
8
|
+
# `category` groups a fixed set of values (e.g. "pokemon_type"); `key` is
|
|
9
|
+
# the stable machine value within it (e.g. "fire"). label/color/position
|
|
10
|
+
# are presentation; metadata (jsonb) carries any category-specific extras.
|
|
11
|
+
t.string :category, null: false
|
|
12
|
+
t.string :key, null: false
|
|
13
|
+
t.string :label
|
|
14
|
+
t.string :color
|
|
15
|
+
t.integer :position, null: false, default: 0
|
|
16
|
+
# A sparse, gappy ordinal (100, 200, …) for a domain ranking distinct from
|
|
17
|
+
# display `position` — e.g. Pokémon types ranked most-common → least-common.
|
|
18
|
+
# The gaps leave room to insert a value between two ranks without
|
|
19
|
+
# renumbering. Nullable: not every enumeral is ranked.
|
|
20
|
+
t.integer :rank
|
|
21
|
+
t.jsonb :metadata, null: false, default: {}
|
|
22
|
+
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add_index :studio_enumerals, [:category, :key], unique: true
|
|
27
|
+
add_index :studio_enumerals, [:category, :position]
|
|
28
|
+
add_index :studio_enumerals, [:category, :rank]
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/studio/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -174,6 +174,7 @@ files:
|
|
|
174
174
|
- app/models/image_cache.rb
|
|
175
175
|
- app/models/session_context.rb
|
|
176
176
|
- app/models/studio/email_delivery.rb
|
|
177
|
+
- app/models/studio/enumeral.rb
|
|
177
178
|
- app/models/studio/link.rb
|
|
178
179
|
- app/models/theme_setting.rb
|
|
179
180
|
- app/services/google_oauth_validator.rb
|
|
@@ -235,6 +236,7 @@ files:
|
|
|
235
236
|
- db/migrate/20260614000000_create_studio_email_deliveries.rb
|
|
236
237
|
- db/migrate/20260620000001_create_studio_links.rb
|
|
237
238
|
- db/migrate/20260620000002_allow_null_image_cache_owner.rb
|
|
239
|
+
- db/migrate/20260623130000_create_studio_enumerals.rb
|
|
238
240
|
- lib/studio-engine.rb
|
|
239
241
|
- lib/studio.rb
|
|
240
242
|
- lib/studio/color_scale.rb
|