I'm trying to display all the users that aren't administrators in my users dashboard.
def index
super
@resources = User.where(admin: false).page(params[:page])
end
This is essentially from the boilerplate provided. The collections partial is rendering first and then the index method is being hit - it doesn't pass my @resources into the partial. Anything I am missing?
Hey, @taylor-d.
Here's the default code for the index action. As you can see, the resources are populated by the Administrate::Search object, and are passed to the views through local variables.
If you want to override the resources that are passed to the views, you'll have to overwrite the entire method - try copying the entire index action into your controller, and editing line 5.
I'm starting to question whether local variables are really the best option - they came up in #318 as well. It seems like they might make it difficult to override behavior like this.
I came across this again today as well as in #318 with a scenario of having a logged in user only allowed to access certain elements in the collection. I can use the cancan gem on the front end to remove buttons and links and protect urls, but there should be an easier way to edit the finder for the collection than overriding the entire method.
Luckily, arel was made for this kind of thing. Why not provide a simple hook at the end of your resources arel chain to allow the dashboard to tack on a clause? Something like
...
resources = resources.page(params[:page]).per(records_per_page)
resources = finder_chain_additions(resources) # <==== NEW LINE
page = Administrate::Page::Collection.new(dashboard, order: order)
...
def finder_chain_additions
resources
end
This would allow any DashboardController subclass a way to do things like authorization, custom filtering etc... simply by overriding the finder_chain_additions method.
Thoughts?
I was just trying to solve this very same problem. To me this is an absolutely critical feature to support if I am to adopt Administrate for my needs, do you know if this will be addressed in any upcoming releases?
+1 critical feature
I don't think this is a critical feature because as shown above, it's just Ruby and you can just pull the body of the Administrate::ApplicationController#index method into the commented out index method in the generated dashboard controller.
The issue here, which @graysonwright didn't address above, is that the comments lie. The comments show you a technique that just doesn't work. The comments shouldn't lie.
So, to move forward, should we change the comments to being something correct or do we have to extract a helper method or show a different technique?
If we're going to call super, rendering needs to be taken care of in a different way. Any suggestions of how to do this? Could resources be a method that we override?
Here is some sample code that works but would probably break a bunch of apps
# app/controllers/administrate/application_controller.rb
module Administrate
class ApplicationController < ActionController::Base
def index
page = Administrate::Page::Collection.new(dashboard, order: order)
render locals: {
resources: resources,
search_term: search_term,
page: page,
}
end
# ...
private
def resources
r = Administrate::Search.new(resource_resolver, search_term).run
r = order.apply(r)
r.page(params[:page]).per(records_per_page)
end
def search_term
params[:search].to_s.strip
end
then in your controller
def resources
SomeModel.some_scope.page(params[:page]).per(records_per_page)
end
This is the same idea that we see in the Administrate::ApplicationController#find_resource method.
What do you think?
@nickcharlton We should look into this one
Thanks @pferdefleisch. We are using this fix to support nested routes.
You can also just put the override in your Admin::ApplicationController < Administrate::ApplicationController. However, to make it work I had to add show_search_bar: show_search_bar? to the locals hash.
def index
page = Administrate::Page::Collection.new(dashboard, order: order)
render locals: {
resources: resources,
search_term: search_term,
page: page,
show_search_bar: show_search_bar?
}
end
Took a stab at this in #1007 using the hint from @graysonwright to revert local variables back to instance variables.
I just bumped into this issue too. I notice the default implementation has a private scoped_resource method. It would be very useful if that method was not private, and thus a candidate for overwriting by sub-classes.
The end result would work very similarly to the begin_of_association_chain method from the inherited_resources gem -- which worked well in my experience. This allows you to add scoping/filtering to a model, in way that gets picked up not just by the index method, but also show, edit, update and destroy methods.
Ah, interesting. I noticed now that @anhari's PR hadn't moved along much recently.
@masonhale Do you have any thoughts on that implementation vs. your proposed one?
@nickcharlton I've used inherited_resources in the past I really appreciated how it automatically handled the "normal"-case for REST-style controller methods, but it also got out of the way easily and incrementally.
I say incrementally because if you want to say add a filter to the data seen in the controller (say by using the has_scope gem), then you could use begin_of_association_chain or end_of_association_chain to plug that in without having to override the entire controller. In the same way if you did decide to override a controller method like update, you could take full responsibility for the entire method, or if you wanted to just insert some logic to trigger a background update task (for example), you could still call update! (a method provided by the inherited_resources gem) to have it pick up the rest of the work.
When I look at the administrate project, I see the same potential -- to provide a framework that does the simple things very easily, but that gets out of the way when you need to do something more complex. Administrate already does a good job of this with the ability to add custom field types and to override views and view partials, but I see some more work to do to bring the same level of "incremental override" to the controller layer.
I think making scoped_resource protected so that it exposes an API for overriding is a good step -- but realize you are creating an API, so it worth taking a step back and looking at the bigger picture. In fact, if it worked exactly like inherited_resources I would be happy, because I already know how that works, and the API has been battle-tested and evolved so is less likely to result in churn. (I realize that the Dashboard based implementation model makes actually using inherited_resources itself probably unfeasible).
I'd support the points pushing towards a resources method which one can override for the purpose of providing additional scopes.
Fixing the resource scope customization challenge with instance variables (like #1096) has downsides vs. the current setup of render with locals. Instance variables can be referenced anywhere in controller before/after hooks and the stack of views (index.html.erb -> partial_a.html.erb -> partial_b.html.erb) - so partial_b can end up breaking it's encapsulation. Instance variables also tend to make partials less reusable (two controllers/actions are then coupled with the need to set a common name). Couple of interesting posts on this theme:
https://thepugautomatic.com/2013/05/locals/
http://www.carlosramireziii.com/stop-using-instance-variables-in-partials.html
Reading the source it looks like there's already a method which can be overridden to apply scopes for #index:
If you override #scoped_resource then you can apply scopes as required. Here's the default implmentation:
https://github.com/thoughtbot/administrate/blob/31852e392e945b9bf6a57a41970c93b68f34764f/app/controllers/administrate/application_controller.rb#L121-L123
For @tylrd's case that'd be:
def scoped_resource
User.where(admin: false) # or resource_class.where(admin: false)
end
I think the solution provided by @odlp completely solves the use case and should be a matter of updating the comments in the template https://github.com/thoughtbot/administrate/blob/master/lib/generators/administrate/dashboard/templates/controller.rb.erb to document that solution/example.
Nice! It looks like this has been merged in.
Thank you, @odlp and @vasco !
Most helpful comment
+1 critical feature