The idea for this HOWTO is based on the request [#141] where a user suggested to use a project drop-down to be able to switch between projects more efficiently.
Based on the attached patch (thanks, Kou) I put together a short step-by-step recipe for extending existing Retrospectiva with additional functionality without touching the core. The resulting code adds a simple project drop-down at the bottom of the page, it can be found here:
Identify the to be extended code
We want to add something into the page footer, so let’s find out how the footer is rendered. By looking into app/views/layouts/_footer.rhtml we can see that the left part of is rendered by a helper method called @render_left_side_footer, which can be found in app/helpers/application_helper.rb.
By looking closer we can see, that this method contains 3 extendable interfaces:
elements += view_extensions(:footer, :left, :before, :join => false)
...
elements += view_extensions(:footer, :left, :between, :join => false)
...
elements += view_extensions(:footer, :left, :after, :join => false)
Create the plugin
First, we create a directory in the extensions folder, in our case: extensions/project_drop_down. Next, we create a view-partial that will store the extension code:
01: <% if Project.current && User.current.projects.size > 1 -%>
02: <span class="quiet">
03: <%=_ 'Projects' %>
04: <select onchange="window.location.href=this.value;">
05: <%
06: choices = User.current.projects.map do |project|
07: [h(project.name), project_path(:project_name => project.short_name)]
08: end
09: selected = project_path(:project_name => Project.current.short_name)
10: -%>
11: <%= options_for_select choices, selected %>
12: </select>
13: </span>
14: <% end -%>
- In line 1, we make sure that the code only applies if a project is selected and the logged-in user is able to access more than one project.
- In line 3, we add a translatable label Projects right in front of the drop-down.
- Line 4 opens the
selecttag, which contains a JS callback to follow the right URL on-change. - In lines 6-8 we generate the drop-down choices array
- In lines 9 we identify the selected choice (currently selected project)
- Line 11 actually renders the choices, see RubyOnRails Documentation for details
All partials need to be saved within the view directory in the extension folder. Partial paths need to be unique across all extensions, therefore it makes sense to save it in a nested folder structure that includes the extension name, i.e. (in our case) extensions/project_drop_down/views/project_drop_down/_footer.html.erb.
Load the partial
In our application_helper.rb we had the following line: view_extensions(:footer, :left, :between, :join => false), remember? Let’s assume we want our partial appear at this position of the page footer, we need to create the ext_info.rb file in our extension directory with the following content:
RetroEM::Views.register_extension('project_drop_down/footer', :footer, :left, :between)
The ext_info.rb is loaded once (together with the plugin), in our case we associate the relative partial path project_drop_down/footer with the [:footer, :left, :between] extension interface slot, where it will appear when the page is loaded.
Install the plugin, clean up, enjoy!
To install the plugin, we simply call ruby script/rxm project_drop_down. Done!
The only thing that can still be improved is the partial. Lines 5-11 contain processing logic, which is not only bad practice but also quite messy and difficult to read. How about externalising this bit into a nice and simple helper method? No problem at all, we just need to extend application_helper.rb. To do so, just create a file ext/application_helper.rb file and add the helper method (i,e. options_for_project_drop_down) to it. Now just change the partial to use the helper method instead of the messy inline code and … enjoy!
More, more, more
Do you want to create an extension and require more interfaces? Just file a ticket and I will extend the core with the additional requirements.


RSS feeds