ruby_ui_admin 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +165 -0
- data/app/assets/stylesheets/ruby_ui_admin/application.tailwind.css +111 -0
- data/app/components/ruby_ui_admin/ui/base.rb +26 -0
- data/app/components/ruby_ui_admin/ui/icon.rb +79 -0
- data/app/components/ruby_ui_admin/ui/native_select.rb +37 -0
- data/app/components/ruby_ui_admin/ui/native_select_group.rb +18 -0
- data/app/components/ruby_ui_admin/ui/native_select_icon.rb +42 -0
- data/app/components/ruby_ui_admin/ui/native_select_option.rb +18 -0
- data/app/components/ruby_ui_admin/ui/pagination.rb +62 -0
- data/app/components/ruby_ui_admin/ui/pagination_content.rb +18 -0
- data/app/components/ruby_ui_admin/ui/pagination_ellipsis.rb +45 -0
- data/app/components/ruby_ui_admin/ui/pagination_item.rb +32 -0
- data/app/components/ruby_ui_admin/ui/select.rb +29 -0
- data/app/components/ruby_ui_admin/ui/toast_action.rb +30 -0
- data/app/components/ruby_ui_admin/ui/toast_cancel.rb +30 -0
- data/app/components/ruby_ui_admin/ui/toast_close.rb +43 -0
- data/app/components/ruby_ui_admin/ui/toast_description.rb +21 -0
- data/app/components/ruby_ui_admin/ui/toast_icon.rb +69 -0
- data/app/components/ruby_ui_admin/ui/toast_item.rb +75 -0
- data/app/components/ruby_ui_admin/ui/toast_region.rb +147 -0
- data/app/components/ruby_ui_admin/ui/toast_title.rb +21 -0
- data/app/components/ruby_ui_admin/ui.rb +12 -0
- data/app/components/ruby_ui_admin/views/action.rb +37 -0
- data/app/components/ruby_ui_admin/views/action_form.rb +104 -0
- data/app/components/ruby_ui_admin/views/action_trigger.rb +148 -0
- data/app/components/ruby_ui_admin/views/actions_menu.rb +34 -0
- data/app/components/ruby_ui_admin/views/base.rb +301 -0
- data/app/components/ruby_ui_admin/views/card_component.rb +69 -0
- data/app/components/ruby_ui_admin/views/dashboard.rb +40 -0
- data/app/components/ruby_ui_admin/views/docs.rb +91 -0
- data/app/components/ruby_ui_admin/views/field_input.rb +156 -0
- data/app/components/ruby_ui_admin/views/field_value.rb +301 -0
- data/app/components/ruby_ui_admin/views/filter_bar.rb +111 -0
- data/app/components/ruby_ui_admin/views/form.rb +151 -0
- data/app/components/ruby_ui_admin/views/home.rb +35 -0
- data/app/components/ruby_ui_admin/views/index.rb +324 -0
- data/app/components/ruby_ui_admin/views/path_helpers.rb +36 -0
- data/app/components/ruby_ui_admin/views/rails_helpers.rb +15 -0
- data/app/components/ruby_ui_admin/views/show.rb +77 -0
- data/app/components/ruby_ui_admin/views/show_fields.rb +37 -0
- data/app/components/ruby_ui_admin/views/show_tab.rb +58 -0
- data/app/components/ruby_ui_admin/views/structure_renderer.rb +148 -0
- data/app/components/ruby_ui_admin/views/translation.rb +12 -0
- data/app/controllers/ruby_ui_admin/actions_controller.rb +162 -0
- data/app/controllers/ruby_ui_admin/application_controller.rb +81 -0
- data/app/controllers/ruby_ui_admin/dashboards_controller.rb +12 -0
- data/app/controllers/ruby_ui_admin/docs_controller.rb +166 -0
- data/app/controllers/ruby_ui_admin/home_controller.rb +18 -0
- data/app/controllers/ruby_ui_admin/resources_controller.rb +259 -0
- data/config/locales/ruby_ui_admin.en.yml +59 -0
- data/config/locales/ruby_ui_admin.pt-BR.yml +59 -0
- data/config/routes/dynamic_routes.rb +11 -0
- data/config/routes.rb +23 -0
- data/docs/README.md +53 -0
- data/docs/actions/overview.md +112 -0
- data/docs/authorization/action-policy.md +72 -0
- data/docs/authorization/adapters.md +101 -0
- data/docs/authorization/explicit-mode.md +45 -0
- data/docs/authorization/field-authorization.md +53 -0
- data/docs/authorization/policies.md +62 -0
- data/docs/authorization/scopes.md +48 -0
- data/docs/customization/controllers.md +62 -0
- data/docs/customization/ejecting.md +55 -0
- data/docs/customization/javascript.md +161 -0
- data/docs/customization/theming-rubyui.md +80 -0
- data/docs/dashboards/overview.md +65 -0
- data/docs/fields/overview.md +94 -0
- data/docs/filters/overview.md +73 -0
- data/docs/generators-and-tasks/overview.md +42 -0
- data/docs/getting-started/authentication.md +98 -0
- data/docs/getting-started/configuration.md +174 -0
- data/docs/getting-started/installation.md +201 -0
- data/docs/getting-started/internationalization.md +50 -0
- data/docs/getting-started/practical-guide.md +310 -0
- data/docs/resources/associations.md +66 -0
- data/docs/resources/index-customization.md +68 -0
- data/docs/resources/overview.md +50 -0
- data/docs/resources/scopes.md +54 -0
- data/docs/resources/tabs-panels.md +65 -0
- data/lib/generators/ruby_ui_admin/action/action_generator.rb +21 -0
- data/lib/generators/ruby_ui_admin/action/templates/action.rb.tt +24 -0
- data/lib/generators/ruby_ui_admin/assets/assets_generator.rb +71 -0
- data/lib/generators/ruby_ui_admin/card/card_generator.rb +28 -0
- data/lib/generators/ruby_ui_admin/card/templates/chart_card.rb.tt +14 -0
- data/lib/generators/ruby_ui_admin/card/templates/metric_card.rb.tt +14 -0
- data/lib/generators/ruby_ui_admin/card/templates/partial_card.rb.tt +13 -0
- data/lib/generators/ruby_ui_admin/components/components_generator.rb +43 -0
- data/lib/generators/ruby_ui_admin/controller/controller_generator.rb +30 -0
- data/lib/generators/ruby_ui_admin/controller/templates/controller.rb.tt +24 -0
- data/lib/generators/ruby_ui_admin/dashboard/dashboard_generator.rb +18 -0
- data/lib/generators/ruby_ui_admin/dashboard/templates/dashboard.rb.tt +14 -0
- data/lib/generators/ruby_ui_admin/eject/eject_generator.rb +55 -0
- data/lib/generators/ruby_ui_admin/filter/filter_generator.rb +28 -0
- data/lib/generators/ruby_ui_admin/filter/templates/boolean_filter.rb.tt +13 -0
- data/lib/generators/ruby_ui_admin/filter/templates/select_filter.rb.tt +19 -0
- data/lib/generators/ruby_ui_admin/filter/templates/text_filter.rb.tt +13 -0
- data/lib/generators/ruby_ui_admin/install/install_generator.rb +62 -0
- data/lib/generators/ruby_ui_admin/install/templates/initializer.rb.tt +41 -0
- data/lib/generators/ruby_ui_admin/locales/locales_generator.rb +22 -0
- data/lib/generators/ruby_ui_admin/policy/policy_generator.rb +18 -0
- data/lib/generators/ruby_ui_admin/policy/templates/policy.rb.tt +24 -0
- data/lib/generators/ruby_ui_admin/resource/resource_generator.rb +84 -0
- data/lib/generators/ruby_ui_admin/resource/templates/resource.rb.tt +15 -0
- data/lib/generators/ruby_ui_admin/scope/scope_generator.rb +18 -0
- data/lib/generators/ruby_ui_admin/scope/templates/scope.rb.tt +11 -0
- data/lib/ruby_ui_admin/authorization/action_policy_adapter.rb +69 -0
- data/lib/ruby_ui_admin/authorization/adapter.rb +80 -0
- data/lib/ruby_ui_admin/authorization/can_can_can_adapter.rb +70 -0
- data/lib/ruby_ui_admin/authorization/pundit_adapter.rb +86 -0
- data/lib/ruby_ui_admin/authorization.rb +40 -0
- data/lib/ruby_ui_admin/base_action.rb +211 -0
- data/lib/ruby_ui_admin/base_dashboard.rb +70 -0
- data/lib/ruby_ui_admin/base_policy.rb +38 -0
- data/lib/ruby_ui_admin/base_resource.rb +503 -0
- data/lib/ruby_ui_admin/cards/base_card.rb +59 -0
- data/lib/ruby_ui_admin/cards/chart_card.rb +16 -0
- data/lib/ruby_ui_admin/cards/metric_card.rb +22 -0
- data/lib/ruby_ui_admin/cards/partial_card.rb +14 -0
- data/lib/ruby_ui_admin/configuration.rb +177 -0
- data/lib/ruby_ui_admin/current.rb +20 -0
- data/lib/ruby_ui_admin/dashboard_manager.rb +52 -0
- data/lib/ruby_ui_admin/engine.rb +80 -0
- data/lib/ruby_ui_admin/execution_context.rb +36 -0
- data/lib/ruby_ui_admin/fields/association_field.rb +67 -0
- data/lib/ruby_ui_admin/fields/badge_field.rb +19 -0
- data/lib/ruby_ui_admin/fields/base_field.rb +208 -0
- data/lib/ruby_ui_admin/fields/belongs_to_field.rb +49 -0
- data/lib/ruby_ui_admin/fields/boolean_field.rb +13 -0
- data/lib/ruby_ui_admin/fields/boolean_group_field.rb +54 -0
- data/lib/ruby_ui_admin/fields/code_field.rb +18 -0
- data/lib/ruby_ui_admin/fields/date_field.rb +34 -0
- data/lib/ruby_ui_admin/fields/date_time_field.rb +37 -0
- data/lib/ruby_ui_admin/fields/field_manager.rb +27 -0
- data/lib/ruby_ui_admin/fields/file_field.rb +67 -0
- data/lib/ruby_ui_admin/fields/files_field.rb +62 -0
- data/lib/ruby_ui_admin/fields/has_and_belongs_to_many_field.rb +9 -0
- data/lib/ruby_ui_admin/fields/has_many_field.rb +24 -0
- data/lib/ruby_ui_admin/fields/has_one_field.rb +13 -0
- data/lib/ruby_ui_admin/fields/hidden_field.rb +14 -0
- data/lib/ruby_ui_admin/fields/id_field.rb +19 -0
- data/lib/ruby_ui_admin/fields/key_value_field.rb +41 -0
- data/lib/ruby_ui_admin/fields/number_field.rb +13 -0
- data/lib/ruby_ui_admin/fields/password_field.rb +21 -0
- data/lib/ruby_ui_admin/fields/record_link_field.rb +45 -0
- data/lib/ruby_ui_admin/fields/select_field.rb +71 -0
- data/lib/ruby_ui_admin/fields/status_field.rb +21 -0
- data/lib/ruby_ui_admin/fields/text_field.rb +14 -0
- data/lib/ruby_ui_admin/fields/textarea_field.rb +13 -0
- data/lib/ruby_ui_admin/fields/url_field.rb +21 -0
- data/lib/ruby_ui_admin/filters/base_filter.rb +81 -0
- data/lib/ruby_ui_admin/filters/boolean_filter.rb +9 -0
- data/lib/ruby_ui_admin/filters/multiple_select_filter.rb +11 -0
- data/lib/ruby_ui_admin/filters/select_filter.rb +9 -0
- data/lib/ruby_ui_admin/filters/text_filter.rb +9 -0
- data/lib/ruby_ui_admin/menu/builder.rb +94 -0
- data/lib/ruby_ui_admin/resource_manager.rb +82 -0
- data/lib/ruby_ui_admin/scopes/base_scope.rb +107 -0
- data/lib/ruby_ui_admin/services/authorization_service.rb +38 -0
- data/lib/ruby_ui_admin/structure.rb +40 -0
- data/lib/ruby_ui_admin/tailwind_source.rb +65 -0
- data/lib/ruby_ui_admin/version.rb +5 -0
- data/lib/ruby_ui_admin/view.rb +34 -0
- data/lib/ruby_ui_admin.rb +110 -0
- data/lib/tasks/ruby_ui_admin.rake +71 -0
- data/public/ruby-ui-admin-assets/application.css +2 -0
- data/public/ruby-ui-admin-assets/controllers/index.js +36 -0
- data/public/ruby-ui-admin-assets/controllers/rua--bulk-select_controller.js +19 -0
- data/public/ruby-ui-admin-assets/controllers/rua--confirm_controller.js +51 -0
- data/public/ruby-ui-admin-assets/controllers/rua--dialog_controller.js +55 -0
- data/public/ruby-ui-admin-assets/controllers/rua--row-link_controller.js +16 -0
- data/public/ruby-ui-admin-assets/controllers/rua--tabs_controller.js +36 -0
- data/public/ruby-ui-admin-assets/controllers/ruby_ui--combobox_controller.js +197 -0
- data/public/ruby-ui-admin-assets/controllers/ruby_ui--sheet_content_controller.js +7 -0
- data/public/ruby-ui-admin-assets/controllers/ruby_ui--sheet_controller.js +15 -0
- data/public/ruby-ui-admin-assets/controllers/ruby_ui--sidebar_controller.js +67 -0
- data/public/ruby-ui-admin-assets/controllers/ruby_ui--toast_controller.js +151 -0
- data/public/ruby-ui-admin-assets/controllers/ruby_ui--toaster_controller.js +314 -0
- data/public/ruby-ui-admin-assets/vendor/stimulus.js +2567 -0
- data/public/ruby-ui-admin-assets/vendor/turbo.js +7178 -0
- metadata +379 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/class/attribute"
|
|
4
|
+
|
|
5
|
+
module RubyUIAdmin
|
|
6
|
+
# Base class for custom actions: declarative fields, response helpers and `handle`.
|
|
7
|
+
# Action execution is wired through ActionsController.
|
|
8
|
+
class BaseAction
|
|
9
|
+
class_attribute :message, instance_accessor: false, default: nil
|
|
10
|
+
class_attribute :confirm_button_label, instance_accessor: false, default: "Run"
|
|
11
|
+
class_attribute :cancel_button_label, instance_accessor: false, default: "Cancel"
|
|
12
|
+
class_attribute :standalone, instance_accessor: false, default: false
|
|
13
|
+
class_attribute :no_confirmation, instance_accessor: false, default: false
|
|
14
|
+
class_attribute :visible, instance_accessor: false, default: nil
|
|
15
|
+
class_attribute :authorize, instance_accessor: false, default: true
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
# Class-level `self.name =` with fallback to the real class name.
|
|
19
|
+
def name=(value)
|
|
20
|
+
@display_name = value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def name
|
|
24
|
+
@display_name || super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def action_name
|
|
28
|
+
name
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# URL-safe identifier for this action, stable across namespaces, e.g.
|
|
32
|
+
# RubyUIAdmin::Actions::Users::ResetPassword -> "users_reset_password".
|
|
33
|
+
def action_key
|
|
34
|
+
to_s.sub(/^RubyUIAdmin::Actions::/, "").gsub("::", "_").underscore
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# NOTE: `fields` is the DSL declaration method (below), so submitted values are
|
|
39
|
+
# stored separately as `field_values`.
|
|
40
|
+
attr_accessor :view, :resource, :user, :records, :field_values, :arguments, :current_user
|
|
41
|
+
attr_reader :response
|
|
42
|
+
# The controller running the action, used to expose route helpers inside `handle`.
|
|
43
|
+
attr_writer :controller
|
|
44
|
+
|
|
45
|
+
def initialize(view: nil, resource: nil, user: nil, arguments: {})
|
|
46
|
+
@view = view
|
|
47
|
+
@resource = resource
|
|
48
|
+
@user = user
|
|
49
|
+
@current_user = user
|
|
50
|
+
@arguments = arguments || {}
|
|
51
|
+
@records = []
|
|
52
|
+
@response = default_response
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# ---- Action form fields DSL ----
|
|
56
|
+
|
|
57
|
+
def field(id, as: :text, **options, &block)
|
|
58
|
+
field_class = Fields.field_class_for(as)
|
|
59
|
+
action_fields << field_class.new(id, **options, &block)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def action_fields
|
|
63
|
+
@action_fields ||= []
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Overridden by subclasses to declare action form fields.
|
|
67
|
+
def fields; end
|
|
68
|
+
|
|
69
|
+
def get_fields
|
|
70
|
+
@action_fields = []
|
|
71
|
+
fields
|
|
72
|
+
action_fields
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def has_fields?
|
|
76
|
+
get_fields.any?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# ---- Handler ----
|
|
80
|
+
|
|
81
|
+
# Subclasses implement `handle`. Two signatures are supported:
|
|
82
|
+
# def handle(query:, fields:, current_user:, resource:, **); end
|
|
83
|
+
# def handle(args); end # args[:records], args[:fields], args[:current_user]
|
|
84
|
+
def handle(**args); end
|
|
85
|
+
|
|
86
|
+
def name
|
|
87
|
+
self.class.name
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The confirmation message shown in the action modal. Accepts a literal or a proc
|
|
91
|
+
# evaluated with `resource`/`record`/`records` available (dynamic message).
|
|
92
|
+
def message
|
|
93
|
+
msg = self.class.message
|
|
94
|
+
return msg unless msg.respond_to?(:call)
|
|
95
|
+
|
|
96
|
+
ExecutionContext.new(
|
|
97
|
+
target: msg,
|
|
98
|
+
view: View.wrap(@view),
|
|
99
|
+
resource: resource,
|
|
100
|
+
record: records&.first,
|
|
101
|
+
records: records,
|
|
102
|
+
current_user: current_user
|
|
103
|
+
).handle
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# ---- Route helpers (available inside `handle`) ----
|
|
107
|
+
# Expose `main_app.*` (host routes) and the engine route proxy (`ruby_ui_admin.*`),
|
|
108
|
+
# plus bare `*_path`/`*_url` helpers resolved against either.
|
|
109
|
+
|
|
110
|
+
def main_app
|
|
111
|
+
@controller&.main_app
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def ruby_ui_admin
|
|
115
|
+
return @controller.ruby_ui_admin if @controller.respond_to?(:ruby_ui_admin)
|
|
116
|
+
|
|
117
|
+
RubyUIAdmin::Engine.routes.url_helpers if defined?(RubyUIAdmin::Engine)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def standalone?
|
|
121
|
+
!!self.class.standalone
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def no_confirmation?
|
|
125
|
+
!!self.class.no_confirmation
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def visible_in_view?(current_view)
|
|
129
|
+
return true if self.class.visible.nil?
|
|
130
|
+
|
|
131
|
+
ExecutionContext.new(
|
|
132
|
+
target: self.class.visible,
|
|
133
|
+
view: View.wrap(current_view),
|
|
134
|
+
resource: resource,
|
|
135
|
+
record: (records&.first)
|
|
136
|
+
).handle
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# ---- Response helpers (mutating @response) ----
|
|
140
|
+
|
|
141
|
+
def succeed(text, **opts)
|
|
142
|
+
add_message(:success, text, **opts)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def error(text, **opts)
|
|
146
|
+
add_message(:error, text, **opts)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def inform(text, **opts)
|
|
150
|
+
add_message(:info, text, **opts)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def warn(text, **opts)
|
|
154
|
+
add_message(:warning, text, **opts)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def redirect_to(path = nil, **opts, &block)
|
|
158
|
+
@response[:type] = :redirect
|
|
159
|
+
@response[:path] = block || path
|
|
160
|
+
@response[:redirect_options] = opts
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def reload
|
|
164
|
+
@response[:type] = :reload
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def keep_modal_open
|
|
168
|
+
@response[:keep_modal_open] = true
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def silent
|
|
172
|
+
@response[:messages] = []
|
|
173
|
+
@response[:silent] = true
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def download(content, filename)
|
|
177
|
+
@response[:type] = :download
|
|
178
|
+
@response[:download] = {content: content, filename: filename}
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Resolves bare `*_path`/`*_url` calls in `handle` against the engine routes first,
|
|
182
|
+
# then the host app (`main_app`), so e.g. `resources_posts_path` or a host
|
|
183
|
+
# `login_url(...)` work without a prefix.
|
|
184
|
+
def method_missing(name, *args, **kwargs, &block)
|
|
185
|
+
if route_helper?(name)
|
|
186
|
+
[ruby_ui_admin, main_app].compact.each do |helpers|
|
|
187
|
+
return helpers.public_send(name, *args, **kwargs) if helpers.respond_to?(name)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
super
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def respond_to_missing?(name, include_private = false)
|
|
194
|
+
(route_helper?(name) && [ruby_ui_admin, main_app].compact.any? { |h| h.respond_to?(name) }) || super
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
private
|
|
198
|
+
|
|
199
|
+
def route_helper?(name)
|
|
200
|
+
name.to_s.end_with?("_path", "_url")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def default_response
|
|
204
|
+
{type: :reload, messages: [], keep_modal_open: false, silent: false}
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def add_message(kind, text, **opts)
|
|
208
|
+
@response[:messages] << {type: kind, body: text, **opts}
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUIAdmin
|
|
4
|
+
# Base class for dashboards. Host dashboards (RubyUIAdmin::Dashboards::*) inherit
|
|
5
|
+
# this and declare cards in `def cards`.
|
|
6
|
+
class BaseDashboard
|
|
7
|
+
class << self
|
|
8
|
+
# Class-level `self.name =` with fallback to the real class name.
|
|
9
|
+
def name=(value)
|
|
10
|
+
@display_name = value
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def name
|
|
14
|
+
@display_name || super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def id=(value)
|
|
18
|
+
@id = value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def id
|
|
22
|
+
(defined?(@id) && @id) || to_s.demodulize.underscore
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def description=(value)
|
|
26
|
+
@description = value
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def description
|
|
30
|
+
defined?(@description) ? @description : nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def grid_columns=(value)
|
|
34
|
+
@grid_columns = value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def grid_columns
|
|
38
|
+
(defined?(@grid_columns) && @grid_columns) || 3
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Display title (falls back to a humanized class name).
|
|
42
|
+
def title
|
|
43
|
+
@display_name || to_s.demodulize.titleize
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def abstract?
|
|
47
|
+
self == RubyUIAdmin::BaseDashboard
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Overridden by subclasses to declare cards.
|
|
52
|
+
def cards; end
|
|
53
|
+
|
|
54
|
+
def card(klass, **options)
|
|
55
|
+
card_items << {klass: klass, options: options}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def divider(**); end
|
|
59
|
+
|
|
60
|
+
def card_items
|
|
61
|
+
@card_items ||= []
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def get_cards
|
|
65
|
+
@card_items = []
|
|
66
|
+
cards
|
|
67
|
+
card_items.map { |entry| entry[:klass].new(**(entry[:options] || {})) }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_policy"
|
|
4
|
+
|
|
5
|
+
module RubyUIAdmin
|
|
6
|
+
# Base class for admin policies. Host policies (RubyUIAdmin::Policies::*) inherit
|
|
7
|
+
# from this. It lives outside the `RubyUIAdmin::Policies` namespace so Zeitwerk can
|
|
8
|
+
# fully own that namespace for host-defined policies.
|
|
9
|
+
#
|
|
10
|
+
# Migration note: a host's existing base policy maps to this class.
|
|
11
|
+
#
|
|
12
|
+
# `manage?` is action_policy's default rule (fallback for any rule the host policy
|
|
13
|
+
# doesn't define). We defer it to `explicit_authorization`: when explicit_authorization
|
|
14
|
+
# is false, undefined rules are allowed; when true, they are denied.
|
|
15
|
+
class BasePolicy < ActionPolicy::Base
|
|
16
|
+
# `allow_nil: true` so policies can be evaluated with no signed-in user (rules
|
|
17
|
+
# guard with `user&.`); avoids AuthorizationContextMissing in that edge case.
|
|
18
|
+
authorize :user, allow_nil: true
|
|
19
|
+
# The real (non-impersonated) user, for policies that gate on it. Defaults to `user`.
|
|
20
|
+
authorize :true_user, allow_nil: true
|
|
21
|
+
|
|
22
|
+
def manage?
|
|
23
|
+
!RubyUIAdmin.configuration.explicit_authorization
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# `index?` and `create?` are concretely defined as `false` by action_policy's
|
|
27
|
+
# Defaults module, so redirect them to the same fallback as every other rule.
|
|
28
|
+
def index? = manage?
|
|
29
|
+
|
|
30
|
+
def create? = manage?
|
|
31
|
+
|
|
32
|
+
# NOTE: no default `relation_scope` is declared here on purpose. The Rails
|
|
33
|
+
# `relation_scope` alias is only registered after the action_policy railtie
|
|
34
|
+
# loads (post-boot), whereas this base class is required at gem load time.
|
|
35
|
+
# When a host policy defines no scope, AuthorizationService#apply_policy
|
|
36
|
+
# rescues the UnknownScope error and returns the relation untouched.
|
|
37
|
+
end
|
|
38
|
+
end
|