Class: RubyUIAdmin::BaseResource
- Inherits:
-
Object
- Object
- RubyUIAdmin::BaseResource
- Defined in:
- lib/ruby_ui_admin/base_resource.rb
Overview
Base class for all resources. Provides the declarative resource DSL (fields, panels, tabs, scopes, actions and filters).
Instance Attribute Summary collapse
-
#params ⇒ Object
Returns the value of attribute params.
-
#record ⇒ Object
Returns the value of attribute record.
-
#user ⇒ Object
Returns the value of attribute user.
-
#view ⇒ Object
Returns the value of attribute view.
Class Method Summary collapse
- .abstract? ⇒ Boolean
- .derive_model_class ⇒ Object
- .model_class ⇒ Object
- .model_class=(value) ⇒ Object
- .navigation_label ⇒ Object
-
.resource_name ⇒ Object
"buyer".
-
.resource_name_plural ⇒ Object
"buyers".
-
.route_key ⇒ Object
Route key used by the dynamic router and path helpers, e.g.
- .singular_route_key ⇒ Object
Instance Method Summary collapse
-
#action(klass, **options) ⇒ Object
---- Actions DSL ----.
- #action_items ⇒ Object
- #actions ⇒ Object
-
#actions_for(view: nil, record: nil) ⇒ Object
Instantiated actions visible in the given view (optionally for a record).
-
#apply_defaults(record) ⇒ Object
Applies field-level
default:values to a new record (literal or proc). -
#base_query ⇒ Object
---- Querying ----.
-
#current_user ⇒ Object
Alias so resource DSL (e.g. field defaults/options evaluated eagerly) can use
current_user. - #default_scope_entry ⇒ Object
-
#discover_associations(only: nil, except: []) ⇒ Object
Auto-declares fields from the model's associations.
-
#discover_columns(only: nil, except: []) ⇒ Object
Auto-declares fields from the model's database columns.
- #divider ⇒ Object
-
#field(id, as: :text, **options, &block) ⇒ Object
---- Fields DSL ----.
-
#field_authorized?(field, view) ⇒ Boolean
Whether the current user may see this field in the given view.
-
#field_structure(view: nil) ⇒ Object
The structured item tree (fields + panels + tabs) for the given view.
-
#fields ⇒ Object
Overridden by subclasses to declare fields.
-
#filter(klass, **options) ⇒ Object
---- Filters DSL ----.
- #filter_items ⇒ Object
- #filters ⇒ Object
- #find_action_entry(key) ⇒ Object
- #find_field(id) ⇒ Object
- #find_record(id, query: nil) ⇒ Object
- #find_scope(key) ⇒ Object
- #get_actions ⇒ Object
-
#get_fields(view: nil) ⇒ Object
Flat list of field leaves visible in the given view (used by the index, param permitting and record filling).
- #get_filters ⇒ Object
- #get_scopes ⇒ Object
- #hydrate(record: nil, view: nil, user: nil, params: nil) ⇒ Object
-
#initialize(record: nil, view: nil, user: nil, params: nil) ⇒ BaseResource
constructor
A new instance of BaseResource.
- #instantiate_action(entry, view: nil, record: nil, records: nil) ⇒ Object
- #model_class ⇒ Object
- #navigation_label ⇒ Object
- #new_record ⇒ Object
-
#panel(name = nil, **opts, &block) ⇒ Object
Groups fields in a card-like panel.
- #record_title(target = record) ⇒ Object
-
#remove_scope_all ⇒ Object
Callable inside
def scopesto hide the "All" tab. - #remove_scope_all? ⇒ Boolean
- #resource_name ⇒ Object
- #route_key ⇒ Object
-
#scope(klass, **options) ⇒ Object
scope Klassorscope Klass, default: true(mark default at attachment time). - #scope_items ⇒ Object
-
#scopes ⇒ Object
Overridden by subclasses to declare named index scopes.
- #scopes? ⇒ Boolean
- #singular_route_key ⇒ Object
-
#tab(name, description: nil, &block) ⇒ Object
A single tab inside a
tabsblock. -
#tabs(&block) ⇒ Object
Declares a set of tabs.
Constructor Details
#initialize(record: nil, view: nil, user: nil, params: nil) ⇒ BaseResource
Returns a new instance of BaseResource.
78 79 80 81 82 83 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 78 def initialize(record: nil, view: nil, user: nil, params: nil) @record = record @view = view @user = user @params = params end |
Instance Attribute Details
#params ⇒ Object
Returns the value of attribute params.
76 77 78 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 76 def params @params end |
#record ⇒ Object
Returns the value of attribute record.
76 77 78 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 76 def record @record end |
#user ⇒ Object
Returns the value of attribute user.
76 77 78 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 76 def user @user end |
#view ⇒ Object
Returns the value of attribute view.
76 77 78 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 76 def view @view end |
Class Method Details
.abstract? ⇒ Boolean
46 47 48 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 46 def abstract? self == RubyUIAdmin::BaseResource end |
.derive_model_class ⇒ Object
40 41 42 43 44 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 40 def derive_model_class name.to_s.demodulize.constantize rescue NameError nil end |
.model_class ⇒ Object
34 35 36 37 38 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 34 def model_class return @model_class if defined?(@model_class) && @model_class @model_class = derive_model_class end |
.model_class=(value) ⇒ Object
30 31 32 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 30 def model_class=(value) @model_class = value.is_a?(String) ? value.constantize : value end |
.navigation_label ⇒ Object
71 72 73 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 71 def resource_name_plural.titleize end |
.resource_name ⇒ Object
"buyer"
51 52 53 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 51 def resource_name name.to_s.demodulize.underscore end |
.resource_name_plural ⇒ Object
"buyers"
56 57 58 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 56 def resource_name_plural resource_name.pluralize end |
.route_key ⇒ Object
Route key used by the dynamic router and path helpers, e.g. "buyers". Derived from the RESOURCE name (not the model) so multiple resources backed by the same model get distinct routes. Override per-resource if needed.
63 64 65 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 63 def route_key resource_name_plural end |
.singular_route_key ⇒ Object
67 68 69 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 67 def singular_route_key resource_name end |
Instance Method Details
#action(klass, **options) ⇒ Object
---- Actions DSL ----
399 400 401 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 399 def action(klass, **) action_items << {klass: klass, options: } end |
#action_items ⇒ Object
403 404 405 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 403 def action_items @action_items ||= [] end |
#actions ⇒ Object
407 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 407 def actions; end |
#actions_for(view: nil, record: nil) ⇒ Object
Instantiated actions visible in the given view (optionally for a record).
416 417 418 419 420 421 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 416 def actions_for(view: nil, record: nil) get_actions.filter_map do |entry| action = instantiate_action(entry, view: view, record: record) action if action.visible_in_view?(view) end end |
#apply_defaults(record) ⇒ Object
Applies field-level default: values to a new record (literal or proc).
194 195 196 197 198 199 200 201 202 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 194 def apply_defaults(record) get_fields(view: :new).each do |field| next unless field.has_default? field.fill_value(record, field.default_value(record)) end record end |
#base_query ⇒ Object
---- Querying ----
466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 466 def base_query query = model_class.all query = query.includes(*self.class.includes) if self.class.includes.present? if self.class.index_query query = ExecutionContext.new( target: self.class.index_query, query: query, params: params ).handle end query end |
#current_user ⇒ Object
Alias so resource DSL (e.g. field defaults/options evaluated eagerly) can use
current_user. user is set on hydrate.
99 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 99 def current_user = user |
#default_scope_entry ⇒ Object
267 268 269 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 267 def default_scope_entry get_scopes.find(&:default?) end |
#discover_associations(only: nil, except: []) ⇒ Object
Auto-declares fields from the model's associations.
156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 156 def discover_associations(only: nil, except: []) except = Array(except).map(&:to_sym) only = only && Array(only).map(&:to_sym) model_class.reflect_on_all_associations.each do |association| name = association.name next if except.include?(name) next if only && !only.include?(name) type = association_field_type(association) field name, as: type if type end end |
#discover_columns(only: nil, except: []) ⇒ Object
Auto-declares fields from the model's database columns. discover_columns(only: %i[id name], except: %i)
141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 141 def discover_columns(only: nil, except: []) except = Array(except).map(&:to_sym) only = only && Array(only).map(&:to_sym) model_class.columns.each do |column| name = column.name.to_sym next if except.include?(name) next if only && !only.include?(name) next if name.to_s.end_with?("_id") && only.nil? # foreign keys -> use discover_associations field name, as: field_type_for_column(column, name) end end |
#divider ⇒ Object
137 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 137 def divider(**); end |
#field(id, as: :text, **options, &block) ⇒ Object
---- Fields DSL ----
104 105 106 107 108 109 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 104 def field(id, as: :text, **, &block) field_class = Fields.field_class_for(as) instance = field_class.new(id, resource: self, **, &block) current_collector << instance instance end |
#field_authorized?(field, view) ⇒ Boolean
Whether the current user may see this field in the given view.
A field is visible unless the resource's policy explicitly defines a matching rule that returns false. Candidate rules, most specific first:
index -> index_<id>? ; show -> show_<id>? ; new -> new_<id>?/edit_<id>? ;
edit -> edit_<id>? ; then the generic view_<id>? for any view.
An undefined rule means "no opinion" and the field stays visible (so enabling explicit_authorization does NOT silently hide every unconfigured field).
212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 212 def (field, view) return true unless RubyUIAdmin.configuration. policy = self.class. return true if policy.nil? service = Services::AuthorizationService.new(user, @record, policy_class: policy) rule = candidate_field_rules(field.id, view).find { |candidate| service.defines_rule?(candidate) } return true if rule.nil? service.(rule, record: @record, raise_exception: false) end |
#field_structure(view: nil) ⇒ Object
The structured item tree (fields + panels + tabs) for the given view.
174 175 176 177 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 174 def field_structure(view: nil) build_items filter_item_tree(@root, view) end |
#fields ⇒ Object
Overridden by subclasses to declare fields.
171 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 171 def fields; end |
#filter(klass, **options) ⇒ Object
---- Filters DSL ----
446 447 448 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 446 def filter(klass, **) filter_items << {klass: klass, options: } end |
#filter_items ⇒ Object
450 451 452 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 450 def filter_items @filter_items ||= [] end |
#filters ⇒ Object
454 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 454 def filters; end |
#find_action_entry(key) ⇒ Object
423 424 425 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 423 def find_action_entry(key) get_actions.find { |entry| entry[:klass].action_key == key.to_s } end |
#find_field(id) ⇒ Object
189 190 191 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 189 def find_field(id) get_fields.find { |field| field.id == id.to_sym } end |
#find_record(id, query: nil) ⇒ Object
481 482 483 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 481 def find_record(id, query: nil) (query || base_query).find(id) end |
#find_scope(key) ⇒ Object
263 264 265 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 263 def find_scope(key) get_scopes.find { |scope| scope.key == key.to_s } end |
#get_actions ⇒ Object
409 410 411 412 413 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 409 def get_actions @action_items = [] actions action_items end |
#get_fields(view: nil) ⇒ Object
Flat list of field leaves visible in the given view (used by the index, param permitting and record filling). Filters by view visibility AND by field-level authorization.
182 183 184 185 186 187 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 182 def get_fields(view: nil) build_items leaves = flatten_fields(@root) leaves = leaves.select { |field| field.visible_in_view?(view) && field.visible?(view: view) && (field, view) } if view leaves end |
#get_filters ⇒ Object
456 457 458 459 460 461 462 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 456 def get_filters @filter_items = [] filters filter_items.map do |entry| entry[:klass].new(arguments: entry.dig(:options, :arguments) || {}) end end |
#get_scopes ⇒ Object
239 240 241 242 243 244 245 246 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 239 def get_scopes @scope_items = [] @remove_scope_all_called = false scopes scope_items.map do |entry| entry[:klass].new(params: params || {}, default_override: entry[:options][:default]) end end |
#hydrate(record: nil, view: nil, user: nil, params: nil) ⇒ Object
85 86 87 88 89 90 91 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 85 def hydrate(record: nil, view: nil, user: nil, params: nil) @record = record unless record.nil? @view = view unless view.nil? @user = user unless user.nil? @params = params unless params.nil? self end |
#instantiate_action(entry, view: nil, record: nil, records: nil) ⇒ Object
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 427 def instantiate_action(entry, view: nil, record: nil, records: nil) records = records || (record ? [record] : []) # Expose the acted-on record as `resource.record` inside the action's `fields` # (a single-record/show action sees its record; a bulk action sees the # first selected record as a representative). hydrate(record: records.first) if records.first action = entry[:klass].new( view: view, resource: self, user: user, arguments: entry.dig(:options, :arguments) || {} ) action.records = records action end |
#model_class ⇒ Object
93 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 93 def model_class = self.class.model_class |
#navigation_label ⇒ Object
100 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 100 def = self.class. |
#new_record ⇒ Object
485 486 487 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 485 def new_record model_class.new end |
#panel(name = nil, **opts, &block) ⇒ Object
Groups fields in a card-like panel. Accepts the name either positionally (panel "X")
or as a keyword (panel name: "X") for Avo compatibility.
113 114 115 116 117 118 119 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 113 def panel(name = nil, **opts, &block) name = opts[:name] if name.nil? container = Structure::Panel.new(name) current_collector << container within(container.items, &block) container end |
#record_title(target = record) ⇒ Object
489 490 491 492 493 494 495 496 497 498 499 500 501 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 489 def record_title(target = record) return nil if target.nil? title_attr = self.class.title if title_attr.respond_to?(:call) ExecutionContext.new(target: title_attr, record: target, resource: self).handle elsif target.respond_to?(title_attr) target.public_send(title_attr) else "#{model_class.model_name.human} ##{target.id}" end end |
#remove_scope_all ⇒ Object
Callable inside def scopes to hide the "All" tab. The class
attribute self.remove_scope_all = true also works.
250 251 252 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 250 def remove_scope_all @remove_scope_all_called = true end |
#remove_scope_all? ⇒ Boolean
254 255 256 257 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 254 def remove_scope_all? get_scopes # ensures `scopes` ran (which may call `remove_scope_all`) @remove_scope_all_called || self.class.remove_scope_all end |
#resource_name ⇒ Object
96 97 98 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 96 def resource_name = self.class.resource_name # Alias so resource DSL (e.g. field defaults/options evaluated eagerly) can use # `current_user`. `user` is set on hydrate. |
#route_key ⇒ Object
94 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 94 def route_key = self.class.route_key |
#scope(klass, **options) ⇒ Object
scope Klass or scope Klass, default: true (mark default at attachment time).
228 229 230 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 228 def scope(klass, **) scope_items << {klass: klass, options: } end |
#scope_items ⇒ Object
232 233 234 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 232 def scope_items @scope_items ||= [] end |
#scopes ⇒ Object
Overridden by subclasses to declare named index scopes.
237 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 237 def scopes; end |
#scopes? ⇒ Boolean
259 260 261 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 259 def scopes? get_scopes.any? end |
#singular_route_key ⇒ Object
95 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 95 def singular_route_key = self.class.singular_route_key |
#tab(name, description: nil, &block) ⇒ Object
A single tab inside a tabs block.
130 131 132 133 134 135 |
# File 'lib/ruby_ui_admin/base_resource.rb', line 130 def tab(name, description: nil, &block) container = Structure::Tab.new(name, description: description) current_collector << container within(container.items, &block) container end |