Module: CafeCar::Controller
Defined Under Namespace
Modules: AssociationAuthorization, Filtering
Constant Summary
collapse
- INDEX_VIEWS =
%w[table grid chart].freeze
Constants included
from Filtering
Filtering::CONTROL_PARAMS
Instance Method Summary
collapse
Instance Method Details
#batch ⇒ Object
Apply a bulk action, named by params[:bulk_action], to the selected records.
The action name is derived, not registered: the model policy's
permitted_bulk_actions is the whitelist (a name outside it is a bad request),
name? is the per-record authorization predicate, and name! the model bang
method applied. Every record is authorized ON ITS OWN — the candidate set is
first narrowed to the policy scope (rows the user may see), then each is checked
against name?; unauthorized rows are skipped, never bulk-bypassed. That
per-record check is the security boundary, so Pundit's blanket
verify_authorized is satisfied by skip_authorization after the fact.
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/cafe_car/controller.rb', line 131
def batch
skip_authorization action = permitted_bulk_action(params[:bulk_action])
unless action
skip_policy_scope return head(:bad_request)
end
records = policy_scope(model).where(id: Array(params[:ids]))
batched = records.select { |record| action_allowed?(record, action) }
batched.each { |record| record.public_send("#{action}!") }
redirect_to url_for(action: :index), success: batch_notice(action, batched.size)
end
|
#collection_action ⇒ Object
Run a policy-declared custom action over the collection:
POST //actions/:collection_action. Same derivation as
#member_action — permitted_collection_actions whitelists, name? (asked
of the model class) authorizes — then name! runs on the #filtered_scope,
which ActiveRecord delegates to a class method within that scoping. It runs
over the currently-viewed, filtered set (the button carries the active
filters, its label shows the count) — "Publish all" acts on exactly the
records the user is looking at. A host override (a public controller method
of the action's name) scopes its own query.
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
# File 'lib/cafe_car/controller.rb', line 174
def collection_action
skip_authorization name = permitted_custom_action(params[:collection_action], policy(model.new).attributes.actions.collection)
unless name
skip_policy_scope return head(:not_found)
end
authorize_action! model, name
return public_send(name) if respond_to?(name)
filtered_scope.public_send("#{name}!")
redirect_to url_for(action: :index), success: action_notice(:collection_action, name)
end
|
#create ⇒ Object
107
108
109
110
|
# File 'lib/cafe_car/controller.rb', line 107
def create
run_callbacks(:create) { object.save! }
respond_with object
end
|
#destroy ⇒ Object
117
118
119
120
|
# File 'lib/cafe_car/controller.rb', line 117
def destroy
run_callbacks(:destroy) { object.destroy! }
respond_with object
end
|
#edit ⇒ Object
105
|
# File 'lib/cafe_car/controller.rb', line 105
def edit = respond_with object
|
#index ⇒ Object
102
|
# File 'lib/cafe_car/controller.rb', line 102
def index = respond_with objects
|
#member_action ⇒ Object
Run a policy-declared custom action on one record:
POST //:id/actions/:member_action. The name resolves through the
model policy's permitted_member_actions whitelist (anything else is a
404), its name? predicate authorizes, then — by convention — the record's
name! bang method runs. A host overrides the behavior by defining a
public controller method of the action's name; it takes over after
authorization and owns the response.
153
154
155
156
157
158
159
160
161
162
163
|
# File 'lib/cafe_car/controller.rb', line 153
def member_action
skip_authorization name = permitted_custom_action(params[:member_action], policy(object).attributes.actions.member)
return head(:not_found) unless name
authorize_action! object, name
return public_send(name) if respond_to?(name)
object.public_send("#{name}!")
redirect_back_or_to href_for(object), success: action_notice(:member_action, name)
end
|
#new ⇒ Object
104
|
# File 'lib/cafe_car/controller.rb', line 104
def new = respond_with object
|
#options ⇒ Object
JSON typeahead feed for a searchable association select (Tom Select). Returns
[{value, text}] for the model, filtered by the ?q= keyword search and
capped at max_collection_options — so an association field can reach records
PAST the render cap. Authorized twice: index? gates list access at all, and
policy_scope narrows rows to those the user may see (never leaking hidden ones).
194
195
196
197
198
199
200
|
# File 'lib/cafe_car/controller.rb', line 194
def options
authorize model, :index?
scope = policy_scope(model)
scope = scope.query([ search_term ]) if search_term
records = scope.limit(CafeCar.max_collection_options)
render json: records.map { |record| { value: record.id, text: option_label(record) } }
end
|
#respond_with(*resources, **options, &block) ⇒ Object
202
203
204
|
# File 'lib/cafe_car/controller.rb', line 202
def respond_with(*resources, **options, &block)
super(*namespace, *resources, **options, &block)
end
|
#show ⇒ Object
103
|
# File 'lib/cafe_car/controller.rb', line 103
def show = respond_with object
|
#update ⇒ Object
112
113
114
115
|
# File 'lib/cafe_car/controller.rb', line 112
def update
run_callbacks(:update) { object.save! }
respond_with object
end
|