Class: CurrentScope::PermissionGrid

Inherits:
Object
  • Object
show all
Defined in:
lib/current_scope/permission_grid.rb

Overview

Presents the route-derived permission catalog as an ALIGNED matrix for the role editor: fixed columns, one row per controller, blank cells where a controller doesn't route a column's actions (never a shifted cell).

By default the columns are CRUD groups (config.permission_grid_groups): ticking one grants every routed action in the group, so the RESTful form actions fold into their mutation (new→create, edit→update) and index+show read as one. Actions outside any group (e.g. "approve") get their own column. With groups set to nil/{} every raw action becomes its own column — still aligned.

Defined Under Namespace

Classes: Cell, Column

Instance Method Summary collapse

Constructor Details

#initialize(catalog: CurrentScope.catalog, groups: CurrentScope.config.permission_grid_groups) ⇒ PermissionGrid

Returns a new instance of PermissionGrid.



16
17
18
19
# File 'lib/current_scope/permission_grid.rb', line 16

def initialize(catalog: CurrentScope.catalog, groups: CurrentScope.config.permission_grid_groups)
  @grouped = catalog.grouped # { "controller" => ["action", ...] }
  @groups  = groups || {}
end

Instance Method Details

#actions_for(controller) ⇒ Object



75
76
77
# File 'lib/current_scope/permission_grid.rb', line 75

def actions_for(controller)
  @grouped[controller] || []
end

#cell(controller, column, granted) ⇒ Object

One cell for (controller, column) against a role's granted key set. Blank when the controller routes none of the column's actions. Otherwise a checkbox.

A GROUP cell is only checked when EVERY routed action is granted — a partial group is rendered unchecked+indeterminate and its existing keys are preserved verbatim via hidden inputs (see the edit view). This is the escalation guard: a checked group token expands to the whole group on save, so treating "some granted" as checked would silently promote a partial grant to a full one just by re-saving the role. granted_keys carries the exact subset to round-trip for a partial cell.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/current_scope/permission_grid.rb', line 45

def cell(controller, column, granted)
  routed = column.actions & actions_for(controller)
  return Cell.new(blank: true) if routed.empty?

  keys = routed.map { |action| "#{controller}##{action}" }
  present_keys = keys.select { |key| granted.include?(key) }
  partial = present_keys.any? && present_keys.size < keys.size
  Cell.new(
    blank: false,
    group: column.group,
    name:  column.group ? "role[permission_groups][]" : "role[permission_keys][]",
    value: column.group ? "#{controller}:#{column.label}" : keys.first,
    checked: column.group ? (present_keys.any? && !partial) : present_keys.any?,
    partial: partial,
    granted_keys: partial ? present_keys : []
  )
end

#columnsObject

Ordered columns: config groups that apply to at least one controller (in config order), then leftover actions not covered by any group (sorted).



27
28
29
30
31
32
# File 'lib/current_scope/permission_grid.rb', line 27

def columns
  grouped = @groups.filter_map do |label, actions|
    Column.new(label: label, actions: actions, group: true) if any_controller_has?(actions)
  end
  grouped + leftover_actions.map { |action| Column.new(label: action, actions: [ action ], group: false) }
end

#controllersObject



21
22
23
# File 'lib/current_scope/permission_grid.rb', line 21

def controllers
  @grouped.keys.sort
end

#expand(tokens) ⇒ Object

Expand submitted "controller:group" tokens into routed permission keys. Unknown groups/controllers and unrouted actions drop out.



65
66
67
68
69
70
71
72
73
# File 'lib/current_scope/permission_grid.rb', line 65

def expand(tokens)
  Array(tokens).flat_map do |token|
    controller, label = token.to_s.split(":", 2)
    actions = @groups[label]
    next [] if actions.nil?

    (actions & actions_for(controller)).map { |action| "#{controller}##{action}" }
  end
end