plutonium 0.56.2 → 0.57.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/.claude/skills/plutonium-resource/SKILL.md +14 -0
- data/CHANGELOG.md +23 -0
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +2 -4
- data/app/assets/plutonium.js.map +2 -2
- data/app/assets/plutonium.min.js +1 -1
- data/app/assets/plutonium.min.js.map +2 -2
- data/docs/.vitepress/theme/components/HomeCta.vue +2 -2
- data/docs/public/templates/base.rb +1 -1
- data/docs/public/templates/lite.rb +9 -9
- data/docs/public/templates/plutonium.rb +10 -7
- data/docs/reference/resource/query.md +20 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/assets/assets_generator.rb +17 -0
- data/lib/plutonium/core/controllers/entity_scoping.rb +4 -1
- data/lib/plutonium/resource/query_object.rb +13 -1
- data/lib/plutonium/ui/layout/base.rb +8 -7
- data/lib/plutonium/ui/layout/icon_rail.rb +6 -2
- data/lib/plutonium/ui/layout/sidebar.rb +1 -1
- data/lib/plutonium/ui/modal/slideover.rb +1 -1
- data/lib/plutonium/ui/table/components/scopes_bar.rb +2 -1
- data/lib/plutonium/ui/table/components/scopes_pills.rb +4 -3
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/css/components.css +4 -1
- data/src/js/controllers/icon_rail_controller.js +3 -4
- metadata +2 -2
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
23
|
<div class="hc-term-wrap">
|
|
24
|
-
<pre class="pu-term hc-term"><span class="prompt">$</span>
|
|
24
|
+
<pre class="pu-term hc-term"><span class="prompt">$</span> {{ activeCommand }}<span class="pu-term-cursor"></span></pre>
|
|
25
25
|
<button class="hc-copy" :class="{ 'hc-copy--ok': copied }" @click="copy" :title="copied ? 'Copied' : 'Copy command'" :aria-label="copied ? 'Copied' : 'Copy command'">
|
|
26
26
|
<component :is="copied ? IconCheck : IconCopy" :size="16" :stroke-width="2" />
|
|
27
27
|
</button>
|
|
@@ -49,7 +49,7 @@ const options = [
|
|
|
49
49
|
]
|
|
50
50
|
const selected = ref("plutonium")
|
|
51
51
|
const activeUrl = computed(() => options.find(o => o.id === selected.value).url)
|
|
52
|
-
const activeCommand = computed(() => `rails new my_app -m ${activeUrl.value}`)
|
|
52
|
+
const activeCommand = computed(() => `rails new my_app -a propshaft -j esbuild -c tailwind -m ${activeUrl.value}`)
|
|
53
53
|
const copied = ref(false)
|
|
54
54
|
|
|
55
55
|
async function copy() {
|
|
@@ -2,51 +2,51 @@ after_bundle do
|
|
|
2
2
|
# SQLite infrastructure (replaces Redis/Postgres for simple deployments)
|
|
3
3
|
generate "pu:lite:setup"
|
|
4
4
|
git add: "."
|
|
5
|
-
git commit: %( -m 'setup sqlite') if `git status --porcelain`.present?
|
|
5
|
+
git commit: %( -m 'chore: setup sqlite') if `git status --porcelain`.present?
|
|
6
6
|
|
|
7
7
|
generate "pu:lite:tune"
|
|
8
8
|
git add: "."
|
|
9
|
-
git commit: %( -m 'tune sqlite pragmas') if `git status --porcelain`.present?
|
|
9
|
+
git commit: %( -m 'chore: tune sqlite pragmas') if `git status --porcelain`.present?
|
|
10
10
|
|
|
11
11
|
unless ENV["SKIP_SOLID_QUEUE"]
|
|
12
12
|
generate "pu:lite:solid_queue"
|
|
13
13
|
git add: "."
|
|
14
|
-
git commit: %( -m 'add solid_queue') if `git status --porcelain`.present?
|
|
14
|
+
git commit: %( -m 'chore: add solid_queue') if `git status --porcelain`.present?
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
unless ENV["SKIP_SOLID_CACHE"]
|
|
18
18
|
generate "pu:lite:solid_cache"
|
|
19
19
|
git add: "."
|
|
20
|
-
git commit: %( -m 'add solid_cache') if `git status --porcelain`.present?
|
|
20
|
+
git commit: %( -m 'chore: add solid_cache') if `git status --porcelain`.present?
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
unless ENV["SKIP_SOLID_CABLE"]
|
|
24
24
|
generate "pu:lite:solid_cable"
|
|
25
25
|
git add: "."
|
|
26
|
-
git commit: %( -m 'add solid_cable') if `git status --porcelain`.present?
|
|
26
|
+
git commit: %( -m 'chore: add solid_cable') if `git status --porcelain`.present?
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
unless ENV["SKIP_SOLID_ERRORS"]
|
|
30
30
|
generate "pu:lite:solid_errors"
|
|
31
31
|
git add: "."
|
|
32
|
-
git commit: %( -m 'add solid_errors') if `git status --porcelain`.present?
|
|
32
|
+
git commit: %( -m 'chore: add solid_errors') if `git status --porcelain`.present?
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
unless ENV["SKIP_LITESTREAM"]
|
|
36
36
|
generate "pu:lite:litestream"
|
|
37
37
|
git add: "."
|
|
38
|
-
git commit: %( -m 'add litestream') if `git status --porcelain`.present?
|
|
38
|
+
git commit: %( -m 'chore: add litestream') if `git status --porcelain`.present?
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
unless ENV["SKIP_RAILS_PULSE"]
|
|
42
42
|
generate "pu:lite:rails_pulse"
|
|
43
43
|
git add: "."
|
|
44
|
-
git commit: %( -m 'add rails_pulse') if `git status --porcelain`.present?
|
|
44
|
+
git commit: %( -m 'chore: add rails_pulse') if `git status --porcelain`.present?
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
unless ENV["SKIP_SQLITE_MAINTENANCE"]
|
|
48
48
|
generate "pu:lite:maintenance"
|
|
49
49
|
git add: "."
|
|
50
|
-
git commit: %( -m 'add sqlite maintenance job') if `git status --porcelain`.present?
|
|
50
|
+
git commit: %( -m 'chore: add sqlite maintenance job') if `git status --porcelain`.present?
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
after_bundle do
|
|
2
2
|
# We just installed Rails, let's create a commit
|
|
3
|
-
git(add: ".") && git(commit: %( -m 'initial commit' ))
|
|
3
|
+
git(add: ".") && git(commit: %( -m 'chore: initial commit' ))
|
|
4
4
|
|
|
5
5
|
# Run the base install
|
|
6
6
|
template_location = if ENV["LOCAL"]
|
|
@@ -12,20 +12,23 @@ after_bundle do
|
|
|
12
12
|
|
|
13
13
|
# Add development tools
|
|
14
14
|
generate "pu:gem:dotenv"
|
|
15
|
-
git(add: ".") && git(commit: %( -m 'add dotenv' ))
|
|
15
|
+
git(add: ".") && git(commit: %( -m 'chore: add dotenv' ))
|
|
16
16
|
|
|
17
17
|
generate "pu:gem:annotated"
|
|
18
|
-
git(add: ".") && git(commit: %( -m 'add annotate' ))
|
|
18
|
+
git(add: ".") && git(commit: %( -m 'chore: add annotate' ))
|
|
19
19
|
|
|
20
20
|
generate "pu:gem:standard"
|
|
21
|
-
git(add: ".") && git(commit: %( -m 'add standardrb' ))
|
|
21
|
+
git(add: ".") && git(commit: %( -m 'chore: add standardrb' ))
|
|
22
22
|
|
|
23
23
|
generate "pu:gem:letter_opener"
|
|
24
|
-
git(add: ".") && git(commit: %( -m 'add letter_opener' ))
|
|
24
|
+
git(add: ".") && git(commit: %( -m 'chore: add letter_opener' ))
|
|
25
25
|
|
|
26
26
|
generate "pu:gem:actual_db_schema"
|
|
27
|
-
git(add: ".") && git(commit: %( -m 'add actual_db_schema' ))
|
|
27
|
+
git(add: ".") && git(commit: %( -m 'chore: add actual_db_schema' ))
|
|
28
28
|
|
|
29
29
|
generate "pu:core:assets"
|
|
30
|
-
git(add: ".") && git(commit: %( -m 'integrate assets' ))
|
|
30
|
+
git(add: ".") && git(commit: %( -m 'chore: integrate assets' ))
|
|
31
|
+
|
|
32
|
+
generate "pu:skills:sync"
|
|
33
|
+
git(add: ".") && git(commit: %( -m 'chore: sync plutonium skills' ))
|
|
31
34
|
end
|
|
@@ -214,6 +214,26 @@ When a default is set:
|
|
|
214
214
|
- The default scope button is highlighted (not "All").
|
|
215
215
|
- Clicking "All" shows the unscoped collection.
|
|
216
216
|
|
|
217
|
+
### Conditional visibility — `condition:`
|
|
218
|
+
|
|
219
|
+
Like `condition:` on [actions](./actions), a scope can be **defined but only render its button when a runtime proc is truthy**. The scope (and its URL) stays live either way — `condition:` only toggles the button.
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
scope :admin_only, condition: -> { current_user.admin? }
|
|
223
|
+
scope :beta_feature, condition: -> { params[:beta] == "1" }
|
|
224
|
+
|
|
225
|
+
# Expose a scope's URL (API/programmatic) without surfacing a button
|
|
226
|
+
scope :internal, condition: -> { false }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The proc is evaluated against the view context so `current_user`, `params`, `request`, and `allowed_to?` are all available directly. There is no `object`/`record` — scopes have no single-record context.
|
|
230
|
+
|
|
231
|
+
::: danger `condition:` is NOT authorization
|
|
232
|
+
A hidden scope button still has a **live URL** anyone can navigate to. `condition:` decides whether the *button renders*, not whether the *records are accessible*.
|
|
233
|
+
|
|
234
|
+
Use `condition:` for UI relevance ("show this tab to admins only"). Use the policy's `relation_scope` to restrict which records a user can see at all.
|
|
235
|
+
:::
|
|
236
|
+
|
|
217
237
|
## Sorting
|
|
218
238
|
|
|
219
239
|
```ruby
|
|
@@ -12,6 +12,7 @@ module Pu
|
|
|
12
12
|
desc "Setup plutonium assets"
|
|
13
13
|
|
|
14
14
|
def start
|
|
15
|
+
verify_prerequisites
|
|
15
16
|
install_dependencies
|
|
16
17
|
copy_tailwind_config
|
|
17
18
|
configure_application
|
|
@@ -24,6 +25,22 @@ module Pu
|
|
|
24
25
|
|
|
25
26
|
private
|
|
26
27
|
|
|
28
|
+
# The asset pipeline assumes the app was generated with esbuild + Tailwind.
|
|
29
|
+
# Without those, `application.tailwind.css` doesn't exist and the generator
|
|
30
|
+
# later crashes with a cryptic inject_into_file error. Fail early with a fix.
|
|
31
|
+
def verify_prerequisites
|
|
32
|
+
return if File.exist?("app/assets/stylesheets/application.tailwind.css")
|
|
33
|
+
|
|
34
|
+
error <<~MSG
|
|
35
|
+
Plutonium assets require a Rails app generated with esbuild and Tailwind.
|
|
36
|
+
Expected app/assets/stylesheets/application.tailwind.css, but it is missing.
|
|
37
|
+
|
|
38
|
+
Re-create the app with the required flags:
|
|
39
|
+
rails new myapp -a propshaft -j esbuild -c tailwind \\
|
|
40
|
+
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
41
|
+
MSG
|
|
42
|
+
end
|
|
43
|
+
|
|
27
44
|
def copy_tailwind_config
|
|
28
45
|
copy_file "tailwind.config.js", force: true
|
|
29
46
|
copy_file "postcss.config.js", force: true
|
|
@@ -115,7 +115,10 @@ module Plutonium
|
|
|
115
115
|
def remember_scoped_entity
|
|
116
116
|
return unless scoped_to_entity?
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
entity = current_scoped_entity
|
|
119
|
+
return unless entity
|
|
120
|
+
|
|
121
|
+
session[scoped_entity_session_key] = entity.to_global_id.to_s
|
|
119
122
|
end
|
|
120
123
|
|
|
121
124
|
# Retrieves the remembered scoped entity from the session.
|
|
@@ -31,9 +31,11 @@ module Plutonium
|
|
|
31
31
|
#
|
|
32
32
|
# @param name [Symbol] The name of the scope.
|
|
33
33
|
# @param body [Proc, nil] The body of the scope.
|
|
34
|
-
|
|
34
|
+
# @param condition [Proc, nil] Display-only visibility gate (same semantics as condition: on actions).
|
|
35
|
+
def define_scope(name, body = nil, condition: nil, **)
|
|
35
36
|
body ||= name
|
|
36
37
|
scope_definitions[name] = build_query(body)
|
|
38
|
+
scope_conditions[name] = condition if condition
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Defines a sort with the given name and body.
|
|
@@ -118,6 +120,16 @@ module Plutonium
|
|
|
118
120
|
|
|
119
121
|
def scope_definitions = @scope_definitions ||= {}.with_indifferent_access
|
|
120
122
|
|
|
123
|
+
def scope_conditions = @scope_conditions ||= {}.with_indifferent_access
|
|
124
|
+
|
|
125
|
+
# Display-only visibility gate for a scope, mirroring condition: on actions.
|
|
126
|
+
# Returns true when no condition is set.
|
|
127
|
+
def scope_visible?(name, view_context)
|
|
128
|
+
condition = scope_conditions[name]
|
|
129
|
+
return true if condition.nil?
|
|
130
|
+
Plutonium::Action::ConditionContext.new(view_context, nil).instance_exec(&condition)
|
|
131
|
+
end
|
|
132
|
+
|
|
121
133
|
# Returns true if user explicitly selected "All" scope (no filtering)
|
|
122
134
|
def all_scope_selected? = @all_scope_selected
|
|
123
135
|
|
|
@@ -44,11 +44,12 @@ module Plutonium
|
|
|
44
44
|
# preferences read from localStorage:
|
|
45
45
|
# - Color mode: applies `dark` class on <html> so dark theme renders
|
|
46
46
|
# from the first frame instead of flashing light.
|
|
47
|
-
# - Rail-pin:
|
|
48
|
-
# on
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
47
|
+
# - Rail-pin: the rail is pinned by default, so this applies
|
|
48
|
+
# `pu-rail-pinned` on <body> (when present) and on every incoming
|
|
49
|
+
# body via turbo:before-render unless the user explicitly collapsed
|
|
50
|
+
# it (localStorage "false"), so a Turbo.visit (e.g. the redirect
|
|
51
|
+
# after a form submit) doesn't flash the rail into its collapsed
|
|
52
|
+
# state before the icon-rail Stimulus controller can restore it.
|
|
52
53
|
def render_pre_paint_scripts
|
|
53
54
|
script do
|
|
54
55
|
raw(safe(<<~JS))
|
|
@@ -62,10 +63,10 @@ module Plutonium
|
|
|
62
63
|
} catch (e) {}
|
|
63
64
|
|
|
64
65
|
try {
|
|
65
|
-
if (localStorage.getItem("pu_rail_pinned")
|
|
66
|
+
if (localStorage.getItem("pu_rail_pinned") === "false") return;
|
|
66
67
|
if (document.body) document.body.classList.add("pu-rail-pinned");
|
|
67
68
|
document.addEventListener("turbo:before-render", function (event) {
|
|
68
|
-
if (localStorage.getItem("pu_rail_pinned")
|
|
69
|
+
if (localStorage.getItem("pu_rail_pinned") !== "false") {
|
|
69
70
|
event.detail.newBody.classList.add("pu-rail-pinned");
|
|
70
71
|
}
|
|
71
72
|
});
|
|
@@ -39,7 +39,7 @@ module Plutonium
|
|
|
39
39
|
id: "sidebar-navigation",
|
|
40
40
|
data: {controller: "sidebar icon-rail"},
|
|
41
41
|
aria: {label: "Sidebar Navigation"},
|
|
42
|
-
class: "fixed top-0 left-0 z-40 h-
|
|
42
|
+
class: "fixed top-0 left-0 z-40 h-dvh " \
|
|
43
43
|
"bg-[var(--pu-surface)] border-r border-[var(--pu-border)] " \
|
|
44
44
|
"flex flex-col transition-[width] duration-200 overflow-x-hidden " \
|
|
45
45
|
"-translate-x-full lg:translate-x-0"
|
|
@@ -54,7 +54,11 @@ module Plutonium
|
|
|
54
54
|
|
|
55
55
|
def render_brand_section
|
|
56
56
|
div(class: "h-12 flex items-center justify-center border-b border-[var(--pu-border)] shrink-0") do
|
|
57
|
-
|
|
57
|
+
next unless brand_slot?
|
|
58
|
+
|
|
59
|
+
a(href: root_path, aria: {label: "Home"}, class: "flex items-center justify-center") do
|
|
60
|
+
render brand_slot
|
|
61
|
+
end
|
|
58
62
|
end
|
|
59
63
|
end
|
|
60
64
|
|
|
@@ -18,7 +18,7 @@ module Plutonium
|
|
|
18
18
|
data: {controller: "sidebar"},
|
|
19
19
|
id: "sidebar-navigation",
|
|
20
20
|
aria: {label: "Sidebar Navigation"},
|
|
21
|
-
class: "fixed top-0 left-0 z-40 w-64 h-
|
|
21
|
+
class: "fixed top-0 left-0 z-40 w-64 h-dvh pt-14 transition-transform -translate-x-full lg:translate-x-0"
|
|
22
22
|
) do
|
|
23
23
|
div(
|
|
24
24
|
id: "sidebar-navigation-content",
|
|
@@ -33,7 +33,7 @@ module Plutonium
|
|
|
33
33
|
# showModal() and is dropped when the dialog leaves the top layer
|
|
34
34
|
# on close(), so it still covers the panel's slide-out. Mirrors
|
|
35
35
|
# the .pu-dialog::backdrop rule in components.css.
|
|
36
|
-
"fixed top-0 right-0 bottom-0 left-auto m-0 h-
|
|
36
|
+
"fixed top-0 right-0 bottom-0 left-auto m-0 h-dvh max-w-full max-h-dvh " \
|
|
37
37
|
"bg-[var(--pu-surface)] border-l border-[var(--pu-border)] " \
|
|
38
38
|
"backdrop:bg-black/60 backdrop:backdrop-blur-sm " \
|
|
39
39
|
"rounded-none p-0 " \
|
|
@@ -14,6 +14,7 @@ module Plutonium
|
|
|
14
14
|
div(class: "flex flex-wrap items-center gap-2") do
|
|
15
15
|
render_all_scope_button
|
|
16
16
|
current_query_object.scope_definitions.each_key do |name|
|
|
17
|
+
next unless current_query_object.scope_visible?(name, view_context)
|
|
17
18
|
render_scope_button(name)
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -60,7 +61,7 @@ module Plutonium
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
def render?
|
|
63
|
-
current_query_object.scope_definitions.
|
|
64
|
+
current_query_object.scope_definitions.any? { |name, _| current_query_object.scope_visible?(name, view_context) }
|
|
64
65
|
end
|
|
65
66
|
end
|
|
66
67
|
end
|
|
@@ -10,7 +10,8 @@ module Plutonium
|
|
|
10
10
|
|
|
11
11
|
nav(role: "tablist",
|
|
12
12
|
aria: {label: "Scope"},
|
|
13
|
-
class: "flex items-center gap-1 px-4 py-2 border-b border-[var(--pu-border)]"
|
|
13
|
+
class: "flex flex-nowrap items-center gap-1 px-4 py-2 border-b border-[var(--pu-border)] " \
|
|
14
|
+
"overflow-x-auto whitespace-nowrap [scrollbar-width:none] [&::-webkit-scrollbar]:hidden") do
|
|
14
15
|
render_all_pill
|
|
15
16
|
scopes.each_key { |key| render_pill(key) }
|
|
16
17
|
end
|
|
@@ -43,7 +44,7 @@ module Plutonium
|
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def pill_classes(active)
|
|
46
|
-
base = "px-3 py-1 rounded-md text-sm transition-colors"
|
|
47
|
+
base = "shrink-0 px-3 py-1 rounded-md text-sm transition-colors"
|
|
47
48
|
state = if active
|
|
48
49
|
"bg-primary-100 text-primary-700 dark:bg-primary-950/40 dark:text-primary-300"
|
|
49
50
|
else
|
|
@@ -58,7 +59,7 @@ module Plutonium
|
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def scopes
|
|
61
|
-
@scopes ||= current_query_object.scope_definitions
|
|
62
|
+
@scopes ||= current_query_object.scope_definitions.select { |name, _| current_query_object.scope_visible?(name, view_context) }
|
|
62
63
|
end
|
|
63
64
|
end
|
|
64
65
|
end
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
data/src/css/components.css
CHANGED
|
@@ -780,8 +780,11 @@ body.pu-rail-pinned .icon-rail-pin-expand {
|
|
|
780
780
|
box-shadow: var(--pu-shadow-lg);
|
|
781
781
|
padding: 6px;
|
|
782
782
|
animation: pu-rail-flyout-in 120ms ease-out;
|
|
783
|
-
/* Cap to the viewport so long menus scroll instead of overflowing.
|
|
783
|
+
/* Cap to the viewport so long menus scroll instead of overflowing.
|
|
784
|
+
dvh tracks the visible viewport so the cap holds on mobile (where
|
|
785
|
+
100vh sits behind the browser chrome); 100vh is the fallback. */
|
|
784
786
|
max-height: calc(100vh - 16px);
|
|
787
|
+
max-height: calc(100dvh - 16px);
|
|
785
788
|
overflow-y: auto;
|
|
786
789
|
overscroll-behavior: contain;
|
|
787
790
|
}
|
|
@@ -9,10 +9,9 @@ export default class extends Controller {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
connect() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
12
|
+
// Pinned is the default; only an explicit "false" collapses the rail.
|
|
13
|
+
const pinned = localStorage.getItem(this.storageKeyValue) !== "false"
|
|
14
|
+
document.body.classList.toggle("pu-rail-pinned", pinned)
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
togglePin() {
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.57.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-06-
|
|
10
|
+
date: 2026-06-09 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: zeitwerk
|