Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract reports from controller #1250

Conversation

isaacfreeman
Copy link

When we have more than a handful of reports for a Solidus site, we typically end up with a long and messy decorator for the reports controller. This PR introduces a structure that allows us to extract the logic for each report into its own class, and declare which reports we want to be active in an initializer.

I appreciate this isn't ready to merge yet, but I'm opening the pull request for comment on whether this is a desirable approach. It's worked well for me with clients, but I don't know how universal my experience is. What do you think?

Notes:

  • Specs currently aren't passing
  • I've aimed to maintain backwards compatibility with existing decorators that use Spree::ReportsController.add_available_report!
  • There's some metaprogramming to generate an action in the controller for each generated report. This could perhaps be made less magical by simply using a show method that receives the desired report as a param, but this would break backwards compatibility.
  • I've added a second report showing variants that are out of stock. I'm not assuming that this specific report needs to also be merged into Solidus, and I'm open to removing it or moving it to a separate PR even if there's consensus on the general approach. I'll leave it in for now to illustrate how additional reports can be added.
  • The seed data currently doesn't have any out-of-stock variants, so you'll see no rows in the table until you make one. In the console for the sandbox app, run Spree::StockItem.first.set_count_on_hand(0).
  • The out-of-stock variants report uses a table_report view that's intended to be generally useful for any report that generates a table.

@@ -0,0 +1,3 @@
Rails.application.config.spree.add_class("reports")

Choose a reason for hiding this comment

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

Indentation of first line in file detected.

@mtomov
Copy link
Contributor

mtomov commented Jun 21, 2016

I like the approach! My suggestions would be:

  • keep it to a single show action - it gets messy to have a controller action for every report. Metaprogramming helps indeed, but why not do it properly? I'm not sure that you will even be breaking compatibility, if you just add show, old reports with custom actions should theoretically work, no?
  • have a base Reporter class, which would define the common interface self.description, self.template, new, rows .., and can contain some helpful methods, such as def spree_routes; Spree::Core::Engine.routes.url_helpers; end;
  • rename def locals to something more revealing, like data, or something better
  • table report is great!

<table style="table-layout: fixed">
<colgroup>
<% @headers.each do |header| %>
<col style="width: <%= header.values.first.length * 0.8 %>em">
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't table-layout: fixed take care of the column widths?

@isaacfreeman
Copy link
Author

@mtomov: Thanks! Agree on all your points. In particular, you're right about the show method – I was too quick to assume I couldn't do it for backward compatibility reasons, but of course you're right that it's not a problem.

How would you feel about simply using BaseReport as a parent class name rather than Reporter? It's closer to the domain object as understood by users, and the :base: prefix is used elsewhere in Solidus for similar purposes.

Also, would it be more convenient to continue with this PR, or close it while I follow up and open a new one later?

@mtomov
Copy link
Contributor

mtomov commented Jun 21, 2016

Yes, either Spree::BaseReporter or maybe even Spree::Reports::Base. Maybe the singular is even better - Spree::Report::Base - your call.

By all means, do continue with this one. We can squash it all at the end.

Time.current.beginning_of_month
end

def parse_end_time(end_time)

Choose a reason for hiding this comment

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

Unused method argument - end_time. If it's necessary, use _ or _end_time as an argument name to indicate that it won't be used. You can also write as parse_end_time(*) if you want the method to accept any arguments but don't care about them.

Isaac Freeman added 5 commits June 22, 2016 14:09
- code specific to the Sales Total report mvoed to its own class
- initializer to declare which reports should be available
- reports controller  uses define_method metaprogramming to set up an action for each report
- GET and POST routes generated for each declared report
- optional title argument for Spree::Admin::ReportsController.add_available_report!
- adds Spree::Reports::OutOfStockVariants
- generic view file for table-based reports
- Removes redundant title code, in favour of just using the class name
- Code improvements recommended by Hound
- plus some refactoring in SalesTotal
- uses a show method in ReportsController instead of generating an action for each report
- extracts Spree::Reports::Base
- fixes broken translation arguments in Spree::Reports::SalesTotal
- removes unnecessary colgroup in table report view
Isaac Freeman added 2 commits June 22, 2016 16:44
- removes unused title key from ReportsController#add_available_report
- adjusts translation namespace for ReportsController#add_available_report
- adjusts ReportsControllerSpec for sales_total to use show action
- code style improvements
@isaacfreeman isaacfreeman force-pushed the refactor/extract_reports_from_controller branch from c10905a to fbc357f Compare June 22, 2016 04:47
@mtomov
Copy link
Contributor

mtomov commented Jun 22, 2016

Btw, if we are to consider an external dependency, this gem Dossier was recommended to me.

@isaacfreeman
Copy link
Author

@mtomov: Dossier looks good, but as you say it's an extra dependency, and I'd rather not add dependencies to the main Solidus distribution. However, if we go ahead with #1262 and move reports to their own spree_reports gem, I'd feel a lot more comfortable adding dependencies to that. We'd know then that people are opting in to using the reports, and we wouldn't be adding the dependency for people who'll never use it.

- moves SalesTotal report specs out of controller spec
- controller specs for OutOfStockVariants report
- handle case where report can't be found
- adjustments to SalesTotal to better match existing specs
report.content.each do |key, value|
instance_variable_set("@#{key}", value)
report_class = "Spree::Report::#{params[:id].camelize}".safe_constantize
if report_class.present?
Copy link
Member

Choose a reason for hiding this comment

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

This can just be if report_class

Copy link
Author

Choose a reason for hiding this comment

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

I've always found .present? a bit more readable, but I'm happy to match code style elsewhere in Solidus.

@jhawthorn jhawthorn added this to the 2.2.0 milestone Dec 8, 2016
@jhawthorn jhawthorn removed this from the 2.2.0 milestone Apr 13, 2017
@tvdeyen
Copy link
Member

tvdeyen commented Oct 4, 2017

We want to extract reports into it's own gem like stated in #1262

@tvdeyen tvdeyen closed this Oct 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants