Ruby on Rails: How do you list the partial paths that are rendered for a page?
ruby, ruby-on-rails, ruby-on-rails-3
Solution
Yes, there is so totally a way to do this in Rails!
As bdares pointed out in his comment, the lines for template rendering appear in the logs. But how do they even get there in the first place? Well, that has to do with a little something in Active Support called `LogSubscriber`. The documentation for this is pretty thorough:
ActiveSupport::LogSubscriber is an object set to consume ActiveSupport::Notifications with the sole purpose of logging them. The log subscriber dispatches notifications to a registered object based on its given namespace.
The thing that's putting the "Rendered ..." lines in the logs, is `ActionView::LogSubscriber` which is defined like this within Rails 3.2. Take a moment to read the code. It's nice and neat.
What this is doing is subscribing to a couple of events within Rails, namely the `render_template`, `render_partial` and `render_collection` methods. It's doing this by calling the `ActiveSupport::LogSubscriber.attach_to` method, which will attach a log subscriber to some events. That method is also interesting, and defined like this:
def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
log_subscribers << log_subscriber
@@flushable_loggers = nil
log_subscriber.public_methods(false).each do |event|
next if 'call' == event.to_s
notifier.subscribe("#{event}.#{namespace}", log_subscriber)
end
end
I've put this code in line because it's extremely relevant to our interests. What this is doing is calling `subscribe` on the `notifier` object. The `notifier` object is, by default `ActiveSupport::Notifications`, which is important to remember. The documentation for that class in Rails is in the API too.
When `subscribe` is called, it passes through the name of the `event`. What is this? Well, it's every single public method inside the class. When the `LogSubscriber` subclass is defined, it defines a couple of public methods: `render_template`, `render_partial` and `render_collection`. These are the events which will be subscribed to for this log subscriber.
Now, that's relevant to our interests because we're going to use the same thing to subscribe to these events inside the view. The first step is letting `ActiveSupport::Notfications` that we want to subscribe to all partial rendering events. We can do this by creating a new `before_filter` within `ApplicationController`:
before_filter :log_partial_events
def log_partial_events
@partial_events = []
ActiveSupport::Notifications.subscribe("render_partial.action_view") do |event_name, start_at, end_at, id, payload|
@partial_events << payload[:identifier]
end
The event we're subscribing to is the `render_partial` event within the `action_view` namespace, which is exactly one of the events that `ActionView::LogSubscriber` is subscribing to. What will happen when a partial is rendered is that this block will be called and passed in some data.
`event_name`: The name of the event. `start_at`: The time the event started. `end_at`: The time the event ended. `id`: A unique identifier for this event. `payload`: Some payload information about this event.
When the hook's called, it'll add the `identifier` key from the `payload` to the array defined at the top of the method.
Then in the view, to access a list of these you can do this:
<%= debug @partial_events %>
Problem
I'm looking to list the partials rendered for each page in my app. For example, when the app/tasks/index.html.erb page is shown, I'd like to show something like the following to the user: Partials rendered for this page: tasks/_list.html.erb tasks/_button.html.erb tasks/_navigation.html.erb Is there any way to do so in Ruby on Rails?