plutonium 0.33.0 → 0.34.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/# Plutonium: The pre-alpha demo.md +4 -2
- data/.claude/skills/assets/SKILL.md +416 -0
- data/.claude/skills/connect-resource/SKILL.md +112 -0
- data/.claude/skills/controller/SKILL.md +302 -0
- data/.claude/skills/create-resource/SKILL.md +240 -0
- data/.claude/skills/definition/SKILL.md +218 -0
- data/.claude/skills/definition-actions/SKILL.md +386 -0
- data/.claude/skills/definition-fields/SKILL.md +474 -0
- data/.claude/skills/definition-query/SKILL.md +334 -0
- data/.claude/skills/forms/SKILL.md +439 -0
- data/.claude/skills/installation/SKILL.md +300 -0
- data/.claude/skills/interaction/SKILL.md +382 -0
- data/.claude/skills/model/SKILL.md +267 -0
- data/.claude/skills/model-features/SKILL.md +286 -0
- data/.claude/skills/nested-resources/SKILL.md +274 -0
- data/.claude/skills/package/SKILL.md +191 -0
- data/.claude/skills/policy/SKILL.md +352 -0
- data/.claude/skills/portal/SKILL.md +400 -0
- data/.claude/skills/resource/SKILL.md +281 -0
- data/.claude/skills/rodauth/SKILL.md +452 -0
- data/.claude/skills/views/SKILL.md +563 -0
- data/Appraisals +46 -4
- data/CHANGELOG.md +36 -0
- data/app/assets/plutonium.css +2 -2
- data/config/brakeman.ignore +239 -0
- data/config/initializers/action_policy.rb +1 -1
- data/docs/.vitepress/config.ts +132 -47
- data/docs/concepts/architecture.md +226 -0
- data/docs/concepts/auto-detection.md +254 -0
- data/docs/concepts/index.md +61 -0
- data/docs/concepts/packages-portals.md +304 -0
- data/docs/concepts/resources.md +224 -0
- data/docs/cookbook/blog.md +412 -0
- data/docs/cookbook/index.md +289 -0
- data/docs/cookbook/saas.md +481 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +146 -0
- data/docs/getting-started/tutorial/01-setup.md +118 -0
- data/docs/getting-started/tutorial/02-first-resource.md +180 -0
- data/docs/getting-started/tutorial/03-authentication.md +246 -0
- data/docs/getting-started/tutorial/04-authorization.md +170 -0
- data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
- data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
- data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
- data/docs/getting-started/tutorial/index.md +64 -0
- data/docs/guides/adding-resources.md +420 -0
- data/docs/guides/authentication.md +551 -0
- data/docs/guides/authorization.md +468 -0
- data/docs/guides/creating-packages.md +380 -0
- data/docs/guides/custom-actions.md +523 -0
- data/docs/guides/index.md +45 -0
- data/docs/guides/multi-tenancy.md +302 -0
- data/docs/guides/nested-resources.md +411 -0
- data/docs/guides/search-filtering.md +266 -0
- data/docs/guides/theming.md +321 -0
- data/docs/index.md +67 -26
- data/docs/public/CLAUDE.md +64 -21
- data/docs/reference/assets/index.md +496 -0
- data/docs/reference/controller/index.md +363 -0
- data/docs/reference/definition/actions.md +400 -0
- data/docs/reference/definition/fields.md +350 -0
- data/docs/reference/definition/index.md +252 -0
- data/docs/reference/definition/query.md +342 -0
- data/docs/reference/generators/index.md +469 -0
- data/docs/reference/index.md +49 -0
- data/docs/reference/interaction/index.md +445 -0
- data/docs/reference/model/features.md +248 -0
- data/docs/reference/model/index.md +219 -0
- data/docs/reference/policy/index.md +385 -0
- data/docs/reference/portal/index.md +382 -0
- data/docs/reference/views/forms.md +396 -0
- data/docs/reference/views/index.md +479 -0
- data/gemfiles/rails_7.gemfile +9 -2
- data/gemfiles/rails_7.gemfile.lock +146 -111
- data/gemfiles/rails_8.0.gemfile +20 -0
- data/gemfiles/rails_8.0.gemfile.lock +417 -0
- data/gemfiles/rails_8.1.gemfile +20 -0
- data/gemfiles/rails_8.1.gemfile.lock +419 -0
- data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
- data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
- data/lib/generators/pu/pkg/portal/USAGE +65 -0
- data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
- data/lib/generators/pu/res/conn/USAGE +71 -0
- data/lib/generators/pu/res/model/USAGE +106 -110
- data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
- data/lib/generators/pu/res/scaffold/USAGE +85 -0
- data/lib/generators/pu/rodauth/install_generator.rb +2 -6
- data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
- data/lib/generators/pu/skills/sync/USAGE +14 -0
- data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
- data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
- data/lib/plutonium/core/controller.rb +2 -2
- data/lib/plutonium/interaction/base.rb +1 -0
- data/lib/plutonium/package/engine.rb +2 -2
- data/lib/plutonium/query/adhoc_block.rb +6 -2
- data/lib/plutonium/query/model_scope.rb +1 -1
- data/lib/plutonium/railtie.rb +4 -0
- data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
- data/lib/plutonium/resource/controllers/crud_actions.rb +21 -18
- data/lib/plutonium/resource/controllers/interactive_actions.rb +21 -25
- data/lib/plutonium/resource/query_object.rb +38 -8
- data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
- data/lib/plutonium/version.rb +1 -1
- data/lib/tasks/release.rake +19 -4
- data/package.json +1 -1
- metadata +76 -39
- data/brakeman.ignore +0 -28
- data/docs/api-examples.md +0 -49
- data/docs/guide/claude-code-guide.md +0 -74
- data/docs/guide/deep-dive/authorization.md +0 -189
- data/docs/guide/deep-dive/multitenancy.md +0 -256
- data/docs/guide/deep-dive/resources.md +0 -390
- data/docs/guide/getting-started/01-installation.md +0 -165
- data/docs/guide/index.md +0 -28
- data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
- data/docs/guide/introduction/02-core-concepts.md +0 -440
- data/docs/guide/tutorial/01-project-setup.md +0 -75
- data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
- data/docs/guide/tutorial/03-defining-resources.md +0 -90
- data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
- data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
- data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
- data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
- data/docs/markdown-examples.md +0 -85
- data/docs/modules/action.md +0 -244
- data/docs/modules/authentication.md +0 -236
- data/docs/modules/configuration.md +0 -599
- data/docs/modules/controller.md +0 -443
- data/docs/modules/core.md +0 -316
- data/docs/modules/definition.md +0 -1308
- data/docs/modules/display.md +0 -759
- data/docs/modules/form.md +0 -495
- data/docs/modules/generator.md +0 -400
- data/docs/modules/index.md +0 -167
- data/docs/modules/interaction.md +0 -642
- data/docs/modules/package.md +0 -151
- data/docs/modules/policy.md +0 -176
- data/docs/modules/portal.md +0 -710
- data/docs/modules/query.md +0 -297
- data/docs/modules/resource_record.md +0 -618
- data/docs/modules/routing.md +0 -690
- data/docs/modules/table.md +0 -301
- data/docs/modules/ui.md +0 -631
|
@@ -28,9 +28,9 @@ module Plutonium
|
|
|
28
28
|
build_interactive_record_action_interaction
|
|
29
29
|
|
|
30
30
|
if helpers.current_turbo_frame == "remote_modal"
|
|
31
|
-
render layout: false
|
|
31
|
+
render layout: false, formats: [:html]
|
|
32
32
|
else
|
|
33
|
-
render :interactive_record_action
|
|
33
|
+
render :interactive_record_action, formats: [:html]
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -39,11 +39,7 @@ module Plutonium
|
|
|
39
39
|
build_interactive_record_action_interaction
|
|
40
40
|
|
|
41
41
|
if params[:pre_submit]
|
|
42
|
-
|
|
43
|
-
format.html do
|
|
44
|
-
render :interactive_record_action, status: :unprocessable_content
|
|
45
|
-
end
|
|
46
|
-
end
|
|
42
|
+
render :interactive_record_action, formats: [:html], status: :unprocessable_content
|
|
47
43
|
return
|
|
48
44
|
end
|
|
49
45
|
|
|
@@ -54,8 +50,6 @@ module Plutonium
|
|
|
54
50
|
if outcome.success?
|
|
55
51
|
return_url = redirect_url_after_action_on(resource_record!)
|
|
56
52
|
|
|
57
|
-
format.any { redirect_to return_url, status: :see_other }
|
|
58
|
-
|
|
59
53
|
if helpers.current_turbo_frame == "remote_modal"
|
|
60
54
|
format.turbo_stream do
|
|
61
55
|
render turbo_stream: [
|
|
@@ -63,16 +57,16 @@ module Plutonium
|
|
|
63
57
|
]
|
|
64
58
|
end
|
|
65
59
|
end
|
|
60
|
+
|
|
61
|
+
format.any { redirect_to return_url, status: :see_other }
|
|
66
62
|
else
|
|
67
|
-
format.html do
|
|
68
|
-
render :interactive_record_action, status: :unprocessable_content
|
|
63
|
+
format.any(:html, :turbo_stream) do
|
|
64
|
+
render :interactive_record_action, formats: [:html], status: :unprocessable_content
|
|
69
65
|
end
|
|
70
|
-
|
|
71
66
|
format.any do
|
|
72
67
|
@errors = @interaction.errors
|
|
73
68
|
render "errors", status: :unprocessable_content
|
|
74
69
|
end
|
|
75
|
-
|
|
76
70
|
end
|
|
77
71
|
end
|
|
78
72
|
end
|
|
@@ -83,10 +77,14 @@ module Plutonium
|
|
|
83
77
|
skip_verify_current_authorized_scope!
|
|
84
78
|
build_interactive_resource_action_interaction
|
|
85
79
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
respond_to do |format|
|
|
81
|
+
format.any(:html, :turbo_stream) do
|
|
82
|
+
if helpers.current_turbo_frame == "remote_modal"
|
|
83
|
+
render layout: false, formats: [:html]
|
|
84
|
+
else
|
|
85
|
+
render :interactive_resource_action, formats: [:html]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
90
88
|
end
|
|
91
89
|
end
|
|
92
90
|
|
|
@@ -97,8 +95,8 @@ module Plutonium
|
|
|
97
95
|
|
|
98
96
|
if params[:pre_submit]
|
|
99
97
|
respond_to do |format|
|
|
100
|
-
format.html do
|
|
101
|
-
render :interactive_resource_action, status: :unprocessable_content
|
|
98
|
+
format.any(:html, :turbo_stream) do
|
|
99
|
+
render :interactive_resource_action, formats: [:html], status: :unprocessable_content
|
|
102
100
|
end
|
|
103
101
|
end
|
|
104
102
|
return
|
|
@@ -111,8 +109,6 @@ module Plutonium
|
|
|
111
109
|
if outcome.success?
|
|
112
110
|
return_url = redirect_url_after_action_on(resource_class)
|
|
113
111
|
|
|
114
|
-
format.any { redirect_to return_url, status: :see_other }
|
|
115
|
-
|
|
116
112
|
if helpers.current_turbo_frame == "remote_modal"
|
|
117
113
|
format.turbo_stream do
|
|
118
114
|
render turbo_stream: [
|
|
@@ -120,16 +116,16 @@ module Plutonium
|
|
|
120
116
|
]
|
|
121
117
|
end
|
|
122
118
|
end
|
|
119
|
+
|
|
120
|
+
format.any { redirect_to return_url, status: :see_other }
|
|
123
121
|
else
|
|
124
|
-
format.html do
|
|
125
|
-
render :interactive_resource_action, status: :unprocessable_content
|
|
122
|
+
format.any(:html, :turbo_stream) do
|
|
123
|
+
render :interactive_resource_action, formats: [:html], status: :unprocessable_content
|
|
126
124
|
end
|
|
127
|
-
|
|
128
125
|
format.any do
|
|
129
126
|
@errors = @interaction.errors
|
|
130
127
|
render "errors", status: :unprocessable_content
|
|
131
128
|
end
|
|
132
|
-
|
|
133
129
|
end
|
|
134
130
|
end
|
|
135
131
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Plutonium
|
|
2
2
|
module Resource
|
|
3
3
|
class QueryObject
|
|
4
|
-
attr_reader :search_filter, :search_query
|
|
4
|
+
attr_reader :search_filter, :search_query, :default_scope_name
|
|
5
5
|
attr_accessor :default_sort_config
|
|
6
6
|
|
|
7
7
|
# Initializes a QueryObject with the given resource_class and parameters.
|
|
@@ -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 default [Boolean] Whether this scope is the default.
|
|
35
|
+
def define_scope(name, body = nil, default: false, **)
|
|
35
36
|
body ||= name
|
|
36
37
|
scope_definitions[name] = build_query(body)
|
|
38
|
+
@default_scope_name = name.to_s if default
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Defines a sort with the given name and body.
|
|
@@ -68,31 +70,53 @@ module Plutonium
|
|
|
68
70
|
q = {}
|
|
69
71
|
|
|
70
72
|
q[:search] = options.key?(:search) ? options[:search].presence : search_query
|
|
71
|
-
q[:scope] = options.key?(:scope)
|
|
73
|
+
q[:scope] = if options.key?(:scope)
|
|
74
|
+
options[:scope].presence
|
|
75
|
+
else
|
|
76
|
+
selected_scope_filter
|
|
77
|
+
end
|
|
72
78
|
|
|
73
79
|
q[:sort_directions] = selected_sort_directions.dup
|
|
74
80
|
q[:sort_fields] = selected_sort_fields.dup
|
|
75
81
|
handle_sort_options!(q, options)
|
|
76
82
|
|
|
77
83
|
q.merge! params.slice(*filter_definitions.keys)
|
|
78
|
-
|
|
84
|
+
compacted = deep_compact({q: q})
|
|
85
|
+
|
|
86
|
+
# Preserve explicit "All" selection (scope: nil in options means show all)
|
|
87
|
+
if options.key?(:scope) && options[:scope].nil?
|
|
88
|
+
compacted[:q] ||= {}
|
|
89
|
+
compacted[:q][:scope] = ""
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
query_params = compacted.to_param
|
|
79
93
|
"#{@request_path}?#{query_params}"
|
|
80
94
|
end
|
|
81
95
|
|
|
82
96
|
# Applies the defined filters and sorts to the given scope.
|
|
83
97
|
#
|
|
84
98
|
# @param scope [Object] The initial scope to which filters and sorts are applied.
|
|
99
|
+
# @param params [Hash] The query parameters.
|
|
100
|
+
# @param context [Object] Optional context (e.g., controller) for executing scope blocks.
|
|
85
101
|
# @return [Object] The modified scope.
|
|
86
|
-
def apply(scope, params)
|
|
102
|
+
def apply(scope, params, context: nil)
|
|
87
103
|
params = deep_compact(params.with_indifferent_access)
|
|
88
104
|
scope = search_filter.apply(scope, search: params[:search]) if search_filter && params[:search]
|
|
89
|
-
|
|
105
|
+
# Use selected_scope which includes the default when no explicit selection
|
|
106
|
+
effective_scope = @selected_scope_filter
|
|
107
|
+
scope = scope_definitions[effective_scope].apply(scope, context:) if effective_scope && scope_definitions[effective_scope]
|
|
90
108
|
scope = apply_sorts(scope, params)
|
|
91
109
|
apply_filters(scope, params)
|
|
92
110
|
end
|
|
93
111
|
|
|
94
112
|
def scope_definitions = @scope_definitions ||= {}.with_indifferent_access
|
|
95
113
|
|
|
114
|
+
# Returns true if user explicitly selected "All" scope (no filtering)
|
|
115
|
+
def all_scope_selected? = @all_scope_selected
|
|
116
|
+
|
|
117
|
+
# Returns the currently selected scope (may be default if none explicitly selected)
|
|
118
|
+
def selected_scope = @selected_scope_filter
|
|
119
|
+
|
|
96
120
|
def filter_definitions = @filter_definitions ||= {}.with_indifferent_access
|
|
97
121
|
|
|
98
122
|
def sort_definitions = @sort_definitions ||= {}.with_indifferent_access
|
|
@@ -125,8 +149,14 @@ module Plutonium
|
|
|
125
149
|
#
|
|
126
150
|
# @param params [Hash] The parameters to extract.
|
|
127
151
|
def extract_filter_params
|
|
128
|
-
@search_query = params[:search]
|
|
129
|
-
|
|
152
|
+
@search_query = params[:search].presence&.strip
|
|
153
|
+
# Track if user explicitly selected "all" (scope param present but blank)
|
|
154
|
+
@all_scope_selected = params.key?(:scope) && params[:scope].blank?
|
|
155
|
+
@selected_scope_filter = if @all_scope_selected
|
|
156
|
+
nil # User clicked "All"
|
|
157
|
+
else
|
|
158
|
+
params[:scope].presence || default_scope_name
|
|
159
|
+
end
|
|
130
160
|
end
|
|
131
161
|
|
|
132
162
|
# Extracts sort parameters from the given params.
|
|
@@ -14,39 +14,9 @@ module Plutonium
|
|
|
14
14
|
"flex flex-wrap justify-between items-center gap-4 mb-4"
|
|
15
15
|
) do
|
|
16
16
|
div(class: "flex flex-wrap items-center gap-2") do
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
id: "#{name}-scope",
|
|
21
|
-
href: current_query_object.build_url(scope: nil),
|
|
22
|
-
class:
|
|
23
|
-
"px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
|
24
|
-
) { name.humanize }
|
|
25
|
-
else
|
|
26
|
-
a(
|
|
27
|
-
id: "#{name}-scope",
|
|
28
|
-
href: current_query_object.build_url(scope: nil),
|
|
29
|
-
class:
|
|
30
|
-
"px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
|
|
31
|
-
) { name.humanize }
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
current_query_object.scope_definitions.each do |name, definition|
|
|
35
|
-
if name == current_scope
|
|
36
|
-
a(
|
|
37
|
-
id: "#{name}-scope",
|
|
38
|
-
href: current_query_object.build_url(scope: name),
|
|
39
|
-
class:
|
|
40
|
-
"px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
|
41
|
-
) { name.humanize }
|
|
42
|
-
else
|
|
43
|
-
a(
|
|
44
|
-
id: "#{name}-scope",
|
|
45
|
-
href: current_query_object.build_url(scope: name),
|
|
46
|
-
class:
|
|
47
|
-
"px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
|
|
48
|
-
) { name.humanize }
|
|
49
|
-
end
|
|
17
|
+
render_all_scope_button
|
|
18
|
+
current_query_object.scope_definitions.each_key do |name|
|
|
19
|
+
render_scope_button(name)
|
|
50
20
|
end
|
|
51
21
|
end
|
|
52
22
|
|
|
@@ -124,7 +94,42 @@ module Plutonium
|
|
|
124
94
|
|
|
125
95
|
private
|
|
126
96
|
|
|
127
|
-
def
|
|
97
|
+
def render_all_scope_button
|
|
98
|
+
active = all_scope_active?
|
|
99
|
+
a(
|
|
100
|
+
id: "all-scope",
|
|
101
|
+
href: current_query_object.build_url(scope: nil),
|
|
102
|
+
class: active ? active_scope_class : inactive_scope_class
|
|
103
|
+
) { "All" }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def render_scope_button(name)
|
|
107
|
+
active = name.to_s == current_scope
|
|
108
|
+
a(
|
|
109
|
+
id: "#{name}-scope",
|
|
110
|
+
href: current_query_object.build_url(scope: name),
|
|
111
|
+
class: active ? active_scope_class : inactive_scope_class
|
|
112
|
+
) { name.to_s.humanize }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def current_scope
|
|
116
|
+
# Use the effective scope (includes default when no selection)
|
|
117
|
+
current_query_object.selected_scope
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def all_scope_active?
|
|
121
|
+
# Active if user explicitly selected "All" OR no scope param and no default
|
|
122
|
+
current_query_object.all_scope_selected? ||
|
|
123
|
+
(!raw_resource_query_params.key?(:scope) && current_query_object.default_scope_name.blank?)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def active_scope_class
|
|
127
|
+
"px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def inactive_scope_class
|
|
131
|
+
"px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
|
|
132
|
+
end
|
|
128
133
|
|
|
129
134
|
def render?
|
|
130
135
|
current_query_object.scope_definitions.present?
|
data/lib/plutonium/version.rb
CHANGED
data/lib/tasks/release.rake
CHANGED
|
@@ -6,10 +6,25 @@ namespace :release do
|
|
|
6
6
|
current_version = Plutonium::VERSION
|
|
7
7
|
puts "Current version: #{current_version}"
|
|
8
8
|
|
|
9
|
+
# Find the tag to compare against
|
|
10
|
+
version_tag = "v#{current_version}"
|
|
11
|
+
tag_exists = system("git rev-parse #{version_tag} >/dev/null 2>&1")
|
|
12
|
+
|
|
13
|
+
unless tag_exists
|
|
14
|
+
# Fall back to most recent tag
|
|
15
|
+
version_tag = `git describe --tags --abbrev=0 2>/dev/null`.strip
|
|
16
|
+
if version_tag.empty?
|
|
17
|
+
puts "No tags found, comparing against initial commit"
|
|
18
|
+
version_tag = `git rev-list --max-parents=0 HEAD`.strip
|
|
19
|
+
else
|
|
20
|
+
puts "Tag v#{current_version} not found, comparing against #{version_tag}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
9
24
|
# Check for breaking changes, features, or fixes since last tag
|
|
10
|
-
breaking = `git log
|
|
11
|
-
features = `git log
|
|
12
|
-
fixes = `git log
|
|
25
|
+
breaking = `git log #{version_tag}..HEAD --oneline | grep -i "BREAKING CHANGE"`.strip
|
|
26
|
+
features = `git log #{version_tag}..HEAD --oneline | grep "^[a-f0-9]* feat"`.strip
|
|
27
|
+
fixes = `git log #{version_tag}..HEAD --oneline | grep "^[a-f0-9]* fix"`.strip
|
|
13
28
|
|
|
14
29
|
major, minor, patch = current_version.split(".").map(&:to_i)
|
|
15
30
|
|
|
@@ -54,7 +69,7 @@ namespace :release do
|
|
|
54
69
|
package_json_file = "package.json"
|
|
55
70
|
if File.exist?(package_json_file)
|
|
56
71
|
package_content = File.read(package_json_file)
|
|
57
|
-
updated_package = package_content.gsub(/"version":\s*"[\d.]+"/, %
|
|
72
|
+
updated_package = package_content.gsub(/"version":\s*"[\d.]+"/, %("version": "#{version}"))
|
|
58
73
|
File.write(package_json_file, updated_package)
|
|
59
74
|
puts "✓ Updated #{package_json_file}"
|
|
60
75
|
end
|
data/package.json
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.34.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: zeitwerk
|
|
@@ -399,6 +399,26 @@ extensions: []
|
|
|
399
399
|
extra_rdoc_files: []
|
|
400
400
|
files:
|
|
401
401
|
- "# Plutonium: The pre-alpha demo.md"
|
|
402
|
+
- ".claude/skills/assets/SKILL.md"
|
|
403
|
+
- ".claude/skills/connect-resource/SKILL.md"
|
|
404
|
+
- ".claude/skills/controller/SKILL.md"
|
|
405
|
+
- ".claude/skills/create-resource/SKILL.md"
|
|
406
|
+
- ".claude/skills/definition-actions/SKILL.md"
|
|
407
|
+
- ".claude/skills/definition-fields/SKILL.md"
|
|
408
|
+
- ".claude/skills/definition-query/SKILL.md"
|
|
409
|
+
- ".claude/skills/definition/SKILL.md"
|
|
410
|
+
- ".claude/skills/forms/SKILL.md"
|
|
411
|
+
- ".claude/skills/installation/SKILL.md"
|
|
412
|
+
- ".claude/skills/interaction/SKILL.md"
|
|
413
|
+
- ".claude/skills/model-features/SKILL.md"
|
|
414
|
+
- ".claude/skills/model/SKILL.md"
|
|
415
|
+
- ".claude/skills/nested-resources/SKILL.md"
|
|
416
|
+
- ".claude/skills/package/SKILL.md"
|
|
417
|
+
- ".claude/skills/policy/SKILL.md"
|
|
418
|
+
- ".claude/skills/portal/SKILL.md"
|
|
419
|
+
- ".claude/skills/resource/SKILL.md"
|
|
420
|
+
- ".claude/skills/rodauth/SKILL.md"
|
|
421
|
+
- ".claude/skills/views/SKILL.md"
|
|
402
422
|
- ".cliff.toml"
|
|
403
423
|
- ".node-version"
|
|
404
424
|
- ".rspec"
|
|
@@ -486,8 +506,8 @@ files:
|
|
|
486
506
|
- app/views/rodauth/webauthn_autofill.html.erb
|
|
487
507
|
- app/views/rodauth/webauthn_remove.html.erb
|
|
488
508
|
- app/views/rodauth/webauthn_setup.html.erb
|
|
489
|
-
- brakeman.ignore
|
|
490
509
|
- config.ru
|
|
510
|
+
- config/brakeman.ignore
|
|
491
511
|
- config/initializers/action_policy.rb
|
|
492
512
|
- config/initializers/hotwire_turbo_monkey_patches.rb
|
|
493
513
|
- config/initializers/pagy.rb
|
|
@@ -496,43 +516,35 @@ files:
|
|
|
496
516
|
- docs/.vitepress/config.ts
|
|
497
517
|
- docs/.vitepress/theme/custom.css
|
|
498
518
|
- docs/.vitepress/theme/index.ts
|
|
499
|
-
- docs/
|
|
500
|
-
- docs/
|
|
501
|
-
- docs/
|
|
502
|
-
- docs/
|
|
503
|
-
- docs/
|
|
504
|
-
- docs/
|
|
505
|
-
- docs/
|
|
506
|
-
- docs/
|
|
507
|
-
- docs/
|
|
508
|
-
- docs/
|
|
509
|
-
- docs/
|
|
510
|
-
- docs/
|
|
511
|
-
- docs/
|
|
512
|
-
- docs/
|
|
513
|
-
- docs/
|
|
514
|
-
- docs/
|
|
519
|
+
- docs/concepts/architecture.md
|
|
520
|
+
- docs/concepts/auto-detection.md
|
|
521
|
+
- docs/concepts/index.md
|
|
522
|
+
- docs/concepts/packages-portals.md
|
|
523
|
+
- docs/concepts/resources.md
|
|
524
|
+
- docs/cookbook/blog.md
|
|
525
|
+
- docs/cookbook/index.md
|
|
526
|
+
- docs/cookbook/saas.md
|
|
527
|
+
- docs/getting-started/index.md
|
|
528
|
+
- docs/getting-started/installation.md
|
|
529
|
+
- docs/getting-started/tutorial/01-setup.md
|
|
530
|
+
- docs/getting-started/tutorial/02-first-resource.md
|
|
531
|
+
- docs/getting-started/tutorial/03-authentication.md
|
|
532
|
+
- docs/getting-started/tutorial/04-authorization.md
|
|
533
|
+
- docs/getting-started/tutorial/05-custom-actions.md
|
|
534
|
+
- docs/getting-started/tutorial/06-nested-resources.md
|
|
535
|
+
- docs/getting-started/tutorial/07-customizing-ui.md
|
|
536
|
+
- docs/getting-started/tutorial/index.md
|
|
537
|
+
- docs/guides/adding-resources.md
|
|
538
|
+
- docs/guides/authentication.md
|
|
539
|
+
- docs/guides/authorization.md
|
|
540
|
+
- docs/guides/creating-packages.md
|
|
541
|
+
- docs/guides/custom-actions.md
|
|
542
|
+
- docs/guides/index.md
|
|
543
|
+
- docs/guides/multi-tenancy.md
|
|
544
|
+
- docs/guides/nested-resources.md
|
|
545
|
+
- docs/guides/search-filtering.md
|
|
546
|
+
- docs/guides/theming.md
|
|
515
547
|
- docs/index.md
|
|
516
|
-
- docs/markdown-examples.md
|
|
517
|
-
- docs/modules/action.md
|
|
518
|
-
- docs/modules/authentication.md
|
|
519
|
-
- docs/modules/configuration.md
|
|
520
|
-
- docs/modules/controller.md
|
|
521
|
-
- docs/modules/core.md
|
|
522
|
-
- docs/modules/definition.md
|
|
523
|
-
- docs/modules/display.md
|
|
524
|
-
- docs/modules/form.md
|
|
525
|
-
- docs/modules/generator.md
|
|
526
|
-
- docs/modules/index.md
|
|
527
|
-
- docs/modules/interaction.md
|
|
528
|
-
- docs/modules/package.md
|
|
529
|
-
- docs/modules/policy.md
|
|
530
|
-
- docs/modules/portal.md
|
|
531
|
-
- docs/modules/query.md
|
|
532
|
-
- docs/modules/resource_record.md
|
|
533
|
-
- docs/modules/routing.md
|
|
534
|
-
- docs/modules/table.md
|
|
535
|
-
- docs/modules/ui.md
|
|
536
548
|
- docs/public/CLAUDE.md
|
|
537
549
|
- docs/public/android-chrome-192x192.png
|
|
538
550
|
- docs/public/android-chrome-512x512.png
|
|
@@ -554,10 +566,29 @@ files:
|
|
|
554
566
|
- docs/public/tutorial/plutonium-posts-detail-customized.png
|
|
555
567
|
- docs/public/tutorial/plutonium-posts-detail.png
|
|
556
568
|
- docs/public/tutorial/plutonium-publish-post.png
|
|
569
|
+
- docs/reference/assets/index.md
|
|
570
|
+
- docs/reference/controller/index.md
|
|
571
|
+
- docs/reference/definition/actions.md
|
|
572
|
+
- docs/reference/definition/fields.md
|
|
573
|
+
- docs/reference/definition/index.md
|
|
574
|
+
- docs/reference/definition/query.md
|
|
575
|
+
- docs/reference/generators/index.md
|
|
576
|
+
- docs/reference/index.md
|
|
577
|
+
- docs/reference/interaction/index.md
|
|
578
|
+
- docs/reference/model/features.md
|
|
579
|
+
- docs/reference/model/index.md
|
|
580
|
+
- docs/reference/policy/index.md
|
|
581
|
+
- docs/reference/portal/index.md
|
|
582
|
+
- docs/reference/views/forms.md
|
|
583
|
+
- docs/reference/views/index.md
|
|
557
584
|
- esbuild.config.js
|
|
558
585
|
- exe/pug
|
|
559
586
|
- gemfiles/rails_7.gemfile
|
|
560
587
|
- gemfiles/rails_7.gemfile.lock
|
|
588
|
+
- gemfiles/rails_8.0.gemfile
|
|
589
|
+
- gemfiles/rails_8.0.gemfile.lock
|
|
590
|
+
- gemfiles/rails_8.1.gemfile
|
|
591
|
+
- gemfiles/rails_8.1.gemfile.lock
|
|
561
592
|
- lib/active_model/validations/array_validator.rb
|
|
562
593
|
- lib/active_model/validations/attached_validator.rb
|
|
563
594
|
- lib/active_model/validations/url_validator.rb
|
|
@@ -633,6 +664,7 @@ files:
|
|
|
633
664
|
- lib/generators/pu/pkg/package/templates/app/presenters/resource_presenter.rb.tt
|
|
634
665
|
- lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt
|
|
635
666
|
- lib/generators/pu/pkg/package/templates/lib/engine.rb.tt
|
|
667
|
+
- lib/generators/pu/pkg/portal/USAGE
|
|
636
668
|
- lib/generators/pu/pkg/portal/portal_generator.rb
|
|
637
669
|
- lib/generators/pu/pkg/portal/templates/app/controllers/concerns/controller.rb.tt
|
|
638
670
|
- lib/generators/pu/pkg/portal/templates/app/controllers/dashboard_controller.rb.tt
|
|
@@ -646,6 +678,7 @@ files:
|
|
|
646
678
|
- lib/generators/pu/pkg/portal/templates/app/views/package/dashboard/index.html.erb
|
|
647
679
|
- lib/generators/pu/pkg/portal/templates/config/routes.rb.tt
|
|
648
680
|
- lib/generators/pu/pkg/portal/templates/lib/engine.rb.tt
|
|
681
|
+
- lib/generators/pu/res/conn/USAGE
|
|
649
682
|
- lib/generators/pu/res/conn/conn_generator.rb
|
|
650
683
|
- lib/generators/pu/res/conn/templates/.keep
|
|
651
684
|
- lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt
|
|
@@ -659,6 +692,7 @@ files:
|
|
|
659
692
|
- lib/generators/pu/res/model/templates/create_table_migration.rb.tt
|
|
660
693
|
- lib/generators/pu/res/model/templates/model.rb.tt
|
|
661
694
|
- lib/generators/pu/res/model/templates/module.rb.tt
|
|
695
|
+
- lib/generators/pu/res/scaffold/USAGE
|
|
662
696
|
- lib/generators/pu/res/scaffold/scaffold_generator.rb
|
|
663
697
|
- lib/generators/pu/res/scaffold/templates/.keep
|
|
664
698
|
- lib/generators/pu/res/scaffold/templates/controller.rb.tt
|
|
@@ -719,6 +753,7 @@ files:
|
|
|
719
753
|
- lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_added.text.erb
|
|
720
754
|
- lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_removed.text.erb
|
|
721
755
|
- lib/generators/pu/rodauth/templates/config/initializers/rodauth.rb.tt
|
|
756
|
+
- lib/generators/pu/rodauth/templates/config/initializers/url_options.rb
|
|
722
757
|
- lib/generators/pu/rodauth/templates/db/migrate/create_rodauth.rb.tt
|
|
723
758
|
- lib/generators/pu/rodauth/templates/db/migrate/install_rodauth.rb.tt
|
|
724
759
|
- lib/generators/pu/rodauth/templates/lib/tasks/rodauth_admin.rake.tt
|
|
@@ -732,6 +767,8 @@ files:
|
|
|
732
767
|
- lib/generators/pu/service/sidekiq/templates/app/sidekiq/sidekiq_job.rb
|
|
733
768
|
- lib/generators/pu/service/sidekiq/templates/config/initializers/sidekiq.rb
|
|
734
769
|
- lib/generators/pu/service/sidekiq/templates/config/sidekiq.yml
|
|
770
|
+
- lib/generators/pu/skills/sync/USAGE
|
|
771
|
+
- lib/generators/pu/skills/sync/sync_generator.rb
|
|
735
772
|
- lib/plutonium.rb
|
|
736
773
|
- lib/plutonium/action/README.md
|
|
737
774
|
- lib/plutonium/action/base.rb
|
data/brakeman.ignore
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ignored_warnings": [
|
|
3
|
-
{
|
|
4
|
-
"warning_type": "Cross-Site Request Forgery",
|
|
5
|
-
"warning_code": 7,
|
|
6
|
-
"fingerprint": "1cb8570b8c91f38317cdf909e01e7016359846174f427e86011633c344d30fc3",
|
|
7
|
-
"check_name": "ForgerySetting",
|
|
8
|
-
"message": "`protect_from_forgery` should be called in `Plutonium::Resource::Controller`",
|
|
9
|
-
"file": "lib/plutonium/resource/controller.rb",
|
|
10
|
-
"line": 10,
|
|
11
|
-
"link": "https://brakemanscanner.org/docs/warning_types/cross-site_request_forgery/",
|
|
12
|
-
"code": null,
|
|
13
|
-
"render_path": null,
|
|
14
|
-
"location": {
|
|
15
|
-
"type": "controller",
|
|
16
|
-
"controller": "Plutonium::Resource::Controller"
|
|
17
|
-
},
|
|
18
|
-
"user_input": null,
|
|
19
|
-
"confidence": "High",
|
|
20
|
-
"cwe_id": [
|
|
21
|
-
352
|
|
22
|
-
],
|
|
23
|
-
"note": "Tests confirm csrf tokens are being verified. False flag."
|
|
24
|
-
}
|
|
25
|
-
],
|
|
26
|
-
"updated": "2024-02-24 17:32:51 +0000",
|
|
27
|
-
"brakeman_version": "6.1.2"
|
|
28
|
-
}
|
data/docs/api-examples.md
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
outline: deep
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Runtime API Examples
|
|
6
|
-
|
|
7
|
-
This page demonstrates usage of some of the runtime APIs provided by VitePress.
|
|
8
|
-
|
|
9
|
-
The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files:
|
|
10
|
-
|
|
11
|
-
```md
|
|
12
|
-
<script setup>
|
|
13
|
-
import { useData } from 'vitepress'
|
|
14
|
-
|
|
15
|
-
const { theme, page, frontmatter } = useData()
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
## Results
|
|
19
|
-
|
|
20
|
-
### Theme Data
|
|
21
|
-
<pre>{{ theme }}</pre>
|
|
22
|
-
|
|
23
|
-
### Page Data
|
|
24
|
-
<pre>{{ page }}</pre>
|
|
25
|
-
|
|
26
|
-
### Page Frontmatter
|
|
27
|
-
<pre>{{ frontmatter }}</pre>
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
<script setup>
|
|
31
|
-
import { useData } from 'vitepress'
|
|
32
|
-
|
|
33
|
-
const { site, theme, page, frontmatter } = useData()
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
## Results
|
|
37
|
-
|
|
38
|
-
### Theme Data
|
|
39
|
-
<pre>{{ theme }}</pre>
|
|
40
|
-
|
|
41
|
-
### Page Data
|
|
42
|
-
<pre>{{ page }}</pre>
|
|
43
|
-
|
|
44
|
-
### Page Frontmatter
|
|
45
|
-
<pre>{{ frontmatter }}</pre>
|
|
46
|
-
|
|
47
|
-
## More
|
|
48
|
-
|
|
49
|
-
Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
|