Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions app/controllers/administrate/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def index
resources = paginate_resources(resources)
@resources = resources
page = Administrate::Page::Collection.new(dashboard, order: order)
page.context = self
Comment thread
pablobm marked this conversation as resolved.
filters = Administrate::Search.new(scoped_resource, dashboard, search_term).valid_filters

render locals: {
Expand All @@ -24,29 +25,38 @@ def index

def show
@resource = resource = requested_resource
page = Administrate::Page::Show.new(dashboard, resource)
page.context = self
render locals: {
page: Administrate::Page::Show.new(dashboard, resource)
page: page
}
end

def new
@resource = resource = new_resource
authorize_resource(resource)
@resource = resource = new_resource.tap do |resource|
authorize_resource(resource)
end

page = Administrate::Page::Form.new(dashboard, resource)
page.context = self
render locals: {
page: Administrate::Page::Form.new(dashboard, resource)
page: page
}
end

def edit
@resource = resource = requested_resource
page = Administrate::Page::Form.new(dashboard, resource)
page.context = self
render locals: {
page: Administrate::Page::Form.new(dashboard, resource)
page: page
}
end

def create
@resource = resource = new_resource(resource_params)
authorize_resource(resource)
@resource = resource = new_resource(resource_params).tap do |resource|
authorize_resource(resource)
end

if resource.save
yield(resource) if block_given?
Expand All @@ -55,8 +65,10 @@ def create
notice: translate_with_resource("create.success")
)
else
page = Administrate::Page::Form.new(dashboard, resource)
page.context = self
render :new, locals: {
page: Administrate::Page::Form.new(dashboard, resource)
page: page
}, status: :unprocessable_entity
end
end
Expand All @@ -70,8 +82,10 @@ def update
status: :see_other
)
else
page = Administrate::Page::Form.new(dashboard, resource)
page.context = self
render :edit, locals: {
page: Administrate::Page::Form.new(dashboard, resource)
page: page
}, status: :unprocessable_entity
end
end
Expand Down Expand Up @@ -178,7 +192,9 @@ def sorting_params
end

def dashboard
@dashboard ||= dashboard_class.new
@dashboard ||= dashboard_class.new.tap do |d|
d.context = self
end
end

def requested_resource
Expand Down
4 changes: 2 additions & 2 deletions docs/customizing_controller_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class Admin::FoosController < Admin::ApplicationController
# Foo.find_by!(slug: param)
# end

# Override this if you have certain roles that require a subset
# this will be used to set the records shown on the `index` action.
# Override this if you have certain roles that require a subset.
# This will be used in all actions except for the `new` and `create` actions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as #2457 (comment). Create a separate PR, let me know, and I'll merge it straightaway.

#
# def scoped_resource
# if current_user.super_admin?
Expand Down
4 changes: 3 additions & 1 deletion lib/administrate/base_dashboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def specific_form_attributes_for(action)
end

def permitted_attributes(action = nil)
attributes = form_attributes action
attributes = form_attributes(action)

if attributes.is_a? Hash
attributes = attributes.values.flatten
Expand Down Expand Up @@ -120,6 +120,8 @@ def item_associations
attribute_associated attributes
end

attr_accessor :context

private

def attribute_not_found_message(attr)
Expand Down
4 changes: 3 additions & 1 deletion lib/administrate/field/associative.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ def html_controller
private

def associated_dashboard
"#{associated_class_name}Dashboard".constantize.new
"#{associated_class_name}Dashboard".constantize.new.tap do |d|
d.context = context
end
end

def association_primary_key
Expand Down
11 changes: 8 additions & 3 deletions lib/administrate/field/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,22 @@ def self.local_partial_prefixes(look: :default)
end
end

def initialize(attribute, data, page, options = {})
def initialize(attribute, raw_data, page, options = {})
@attribute = attribute
@page = page
@resource = options.delete(:resource)
@options = options
@data = read_value(data)
@raw_data = raw_data
end

def html_class
self.class.html_class
end

def data
read_value(@raw_data)
end

def html_controller
nil
end
Expand Down Expand Up @@ -133,7 +137,8 @@ def required?
end
end

attr_reader :attribute, :data, :options, :page, :resource
attr_reader :attribute, :options, :page, :resource
attr_accessor :context
end
end
end
17 changes: 9 additions & 8 deletions lib/administrate/field/has_many.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def associated_collection(order = self.order)
associated_dashboard,
order: order,
collection_attributes: options[:collection_attributes]
)
).tap do |page|
page.context = context
end
end

def attribute_key
Expand Down Expand Up @@ -76,7 +78,7 @@ def more_than_limit?
end

def data
@data ||= associated_class.none
super || associated_class.none
end

def order_from_params(params)
Expand Down Expand Up @@ -106,12 +108,11 @@ def includes
end

def candidate_resources
if options.key?(:includes)
includes = options.fetch(:includes)
associated_class.includes(*includes).all
else
associated_class.all
end
scope = options[:scope] ? options[:scope].call(self) : associated_class.all
scope = scope.includes(options.fetch(:includes, []))

order = options[:order]
order ? scope.reorder(order) : scope
end

def display_candidate_resource(resource)
Expand Down
8 changes: 6 additions & 2 deletions lib/administrate/field/has_one.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ def nested_form
@nested_form ||= Administrate::Page::Form.new(
resolver.dashboard_class.new,
data || resolver.resource_class.new
)
).tap do |page|
page.context = context
end
end

def nested_show
@nested_show ||= Administrate::Page::Show.new(
resolver.dashboard_class.new,
data || resolver.resource_class.new
)
) do |page|
page.context = context
end
end

def linkable?
Expand Down
6 changes: 4 additions & 2 deletions lib/administrate/field/polymorphic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ def selected_global_id
private

def associated_dashboard(klass = data.class)
"#{klass.name}Dashboard".constantize.new
"#{klass.name}Dashboard".constantize.new.tap do |d|
d.context = context
end
end

def classes
klasses = options.fetch(:classes, [])
klasses.respond_to?(:call) ? klasses.call : klasses
klasses.respond_to?(:call) ? klasses.call(self) : klasses
end

private
Expand Down
6 changes: 5 additions & 1 deletion lib/administrate/page/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ def item_associations
dashboard.try(:item_associations) || []
end

attr_accessor :context

private

def attribute_field(dashboard, resource, attribute_name, page)
field = dashboard.attribute_type_for(attribute_name)
field.new(attribute_name, nil, page, resource: resource)
field.new(attribute_name, nil, page, resource: resource).tap do |f|
f.context = context
end
end

attr_reader :dashboard, :options
Expand Down
20 changes: 18 additions & 2 deletions spec/controllers/admin/customers_controller_spec.rb

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the changes to this file? They seem unrelated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review.
Previously, this spec used a policy scope that did not narrow records, so the index results were effectively unchanged in the “all records” case.
Since this PR updates the customer policy scope, I used multiple customer records so any scope-related change in index results becomes visible.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
describe Admin::CustomersController, type: :controller do
describe "GET index" do
it "passes all customers to the view" do
customer = create(:customer)
customers = create_list(:customer, 5)

locals = capture_view_locals { get :index }
expect(locals[:resources]).to eq([customer])
expect(locals[:resources]).to eq(customers)
end

it "applies any scope overrides" do
Expand Down Expand Up @@ -47,6 +47,22 @@
expect(locals[:resources].map(&:id)).to eq customers.map(&:id).sort
end

context "when the user is an admin" do

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
context "when the user is an admin" do
context "when the user is not an admin" do

controller(Admin::CustomersController) do
def authenticate_admin
@current_user = Customer.find_by!(name: "Current User")
end
end

let!(:current_user) { create(:customer, name: "Current User") }
let!(:other_customers) { create_list(:customer, 5) }

it "passes one customers to the view" do
locals = capture_view_locals { get :index }
expect(locals[:resources]).to eq([current_user])
end
end

context "with alternate sorting attributes" do
controller(Admin::CustomersController) do
def default_sorting_attribute
Expand Down
33 changes: 31 additions & 2 deletions spec/controllers/admin/orders_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def pundit_user
end

describe "GET new" do
it "raises a Pundit error" do
expect { get :new }.to raise_error(Pundit::NotAuthorizedError)
it "allows access to /new" do
expect { get :new }.not_to raise_error

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something that I'm not sure about with this is that it removes a place where this type of authorization error is tested. Although the specs are a mess anyway already so things like this are difficult to avoid.

Perhaps a case for Pundit::NotAuthorizedError could be created somewhere else? For example test payments/new.

Also, the context setup at the top (context "with namespaced Punditize concern" do) can now lose the include and the def policy_namespace as I don't think they affect the test any more. (I think this was the case with the include already).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other problem here is that these changes remove the intent of the context block: testing namespaced policies. The policy_namespace doesn't do anything any more in these examples because the default policy (OrderPolicy) now behaves in a way much closer to how the namespaced policy (Own::OrderPolicy) does.

So either this context needs a new example that tests the different behaviour of the namespaced policy, or a new context testing namespaced policies has to be added elsewhere (eg: payments controller, like it was done with the previous comment).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another problem from this: the new context you added below ("when the user is not an admin") doesn't do anything that this one couldn't do. Eg: the "POST create" block below could work up here too.

end
end

Expand Down Expand Up @@ -106,5 +106,34 @@ def send_request(order:)
end
end
end

context "when the user is not an admin" do
controller(Admin::OrdersController) do
def pundit_user
Customer.find_by(name: "Current User")
end
end

let!(:user) { create(:customer, name: "Current User") }

describe "GET new" do
it "allows access to /new" do
expect { get :new }.not_to raise_error
end
end

describe "POST create" do
it "allows creating records with the current customer" do
post(
:create,
params: {
order: attributes_for(:order)
}
)
expect(response).to redirect_to([:admin, (order = Order.last)])
expect(order.customer).to eq(user)
end
end
end
end
# standard:enable Lint/ConstantDefinitionInBlock
22 changes: 22 additions & 0 deletions spec/controllers/admin/payments_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require "rails_helper"

# Ensure we keep explicit coverage for authorization errors on `new` actions.
# standard:disable Lint/ConstantDefinitionInBlock

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# standard:disable Lint/ConstantDefinitionInBlock

Leftover from a previous implementation, perhaps before using controller?

RSpec.describe Admin::PaymentsController, type: :controller do
controller(Admin::PaymentsController) do
include Administrate::Punditize

def pundit_user
Customer.find_by(name: "Current User")
end
end

let!(:user) { create(:customer, name: "Current User") }

describe "GET new" do
it "raises a Pundit error" do
expect { get :new }.to raise_error(Pundit::NotAuthorizedError)
end
end
end
# standard:enable Lint/ConstantDefinitionInBlock

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# standard:enable Lint/ConstantDefinitionInBlock

Same as above.

Loading