compendium 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +70 -4
  3. data/Rakefile +4 -0
  4. data/app/assets/stylesheets/compendium/_metrics.css.scss +92 -0
  5. data/app/assets/stylesheets/compendium/options.css.scss +41 -0
  6. data/app/assets/stylesheets/compendium/report.css.scss +22 -0
  7. data/app/classes/compendium/presenters/base.rb +30 -0
  8. data/app/classes/compendium/presenters/chart.rb +31 -0
  9. data/app/classes/compendium/presenters/metric.rb +19 -0
  10. data/app/classes/compendium/presenters/option.rb +97 -0
  11. data/app/classes/compendium/presenters/query.rb +23 -0
  12. data/app/classes/compendium/presenters/settings/query.rb +22 -0
  13. data/app/classes/compendium/presenters/settings/table.rb +23 -0
  14. data/app/classes/compendium/presenters/table.rb +81 -0
  15. data/app/controllers/compendium/reports_controller.rb +52 -0
  16. data/app/helpers/compendium/reports_helper.rb +21 -0
  17. data/app/views/compendium/reports/run.haml +1 -0
  18. data/app/views/compendium/reports/setup.haml +14 -0
  19. data/compendium.gemspec +7 -1
  20. data/config/initializers/rails/active_record/connection_adapters/quoting.rb +14 -0
  21. data/config/initializers/ruby/numeric.rb +26 -0
  22. data/config/locales/en.yml +5 -0
  23. data/lib/compendium/abstract_chart_provider.rb +30 -0
  24. data/lib/compendium/chart_provider/amcharts.rb +20 -0
  25. data/lib/compendium/context_wrapper.rb +27 -0
  26. data/lib/compendium/dsl.rb +79 -0
  27. data/lib/compendium/engine/mount.rb +13 -0
  28. data/lib/compendium/engine.rb +8 -0
  29. data/lib/compendium/metric.rb +29 -0
  30. data/lib/compendium/open_hash.rb +68 -0
  31. data/lib/compendium/option.rb +37 -0
  32. data/lib/compendium/param_types.rb +91 -0
  33. data/lib/compendium/params.rb +40 -0
  34. data/lib/compendium/query.rb +94 -0
  35. data/lib/compendium/report.rb +56 -0
  36. data/lib/compendium/result_set.rb +24 -0
  37. data/lib/compendium/version.rb +1 -1
  38. data/lib/compendium.rb +46 -1
  39. data/spec/context_wrapper_spec.rb +71 -0
  40. data/spec/dsl_spec.rb +90 -0
  41. data/spec/metric_spec.rb +84 -0
  42. data/spec/option_spec.rb +12 -0
  43. data/spec/param_types_spec.rb +147 -0
  44. data/spec/params_spec.rb +28 -0
  45. data/spec/presenters/base_spec.rb +20 -0
  46. data/spec/presenters/option_spec.rb +49 -0
  47. data/spec/query_spec.rb +33 -0
  48. data/spec/report_spec.rb +93 -0
  49. data/spec/spec_helper.rb +1 -0
  50. metadata +135 -14
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDMzZWY3N2Y4MWU5MGUxODg2YzFjZWY2ZDgzNzE0ZTU0MmNkN2IyYw==
5
+ data.tar.gz: !binary |-
6
+ ZDBjNTEwN2NjNGNlZWIxMzhiMWQ5MGRmMzg0NmY4NGQyNTlhYTAwNA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MDQ1M2YwZDVjMWEyN2M2NjM4ZDYxOGY4ZTEwOTQ0M2Y0MWIzM2UxNDM2MGVm
10
+ YzIwMDFkYzM1ZjliNDUxMjBjNzQxNDkyMTIzYzlhNWQyNjViYTMwYmRhYmUy
11
+ ODBiODRhZjRkOGQ0MWVjNDdiNjE4MmE3MjM2OGNkNDZlZTE0NDk=
12
+ data.tar.gz: !binary |-
13
+ NjIzZGIwNWY1NTU3MzViOTU5Njc3MzFiNTdiYzdhZWViYjIwOGVlYzZmMDhm
14
+ NWI5NzkwNDJjYTUzYjIwNDg4ZjZkZDJhYzE5MDE0ZGQyM2U0Y2JlMGIzNThk
15
+ ODY0NDFkY2NhMDg1MWZkYmU3MjlmNjdhMzIxZWUzYWFjNjIyMzU=
data/README.md CHANGED
@@ -2,6 +2,72 @@
2
2
 
3
3
  Ruby on Rails framework for making reporting easy.
4
4
 
5
+ ## Usage
6
+
7
+ Compendium is a reporting framework for Rails which makes it easy to create and render reports (with charts and tables).
8
+
9
+ A Compendium report is a subclass of `Compendium::Report`. Reports can be defined using the simple DSL:
10
+
11
+ class MyReport < Compendium::Report
12
+ # Options define which parameters your report will accept when being set up.
13
+ # An option is defined with a name, a type, and some settings (ie. default value, choices for radio buttons and
14
+ # dropdowns, etc.)
15
+ option :starting_on, :date, default: -> { Date.today - 1.month }
16
+ option :ending_on, :date, default: -> { Date.today }
17
+ option :currency, :radio, choices: [:USD, :CAD, :GBP]
18
+
19
+ # By default, queries are converted to SQL and executed instead of returning AR models
20
+ # The query definition block gets the report's current parameters
21
+ # totals: true means that the last row returned should be interpretted as a row of totals
22
+ query :deliveries, totals: true do |params|
23
+ Items.where(delivered: true, purchased_at: (params[:starting_on]..params[:ending_on]))
24
+ end
25
+
26
+ # Define a query which collects data by using AR directly
27
+ query :on_hand_inventory, collect: :active_record do |params|
28
+ Items.where(in_stock: true)
29
+ end
30
+
31
+ # Define a query that works on another query's result set
32
+ # Note: chart and data are aliases for query
33
+ chart :deliveries_over_time, through: :deliveries do |results|
34
+ results.group_by(&:purchased_at)
35
+ end
36
+
37
+ # Queries can also be used to drive metrics
38
+ metric :shipping_time, -> results { results.last['shipping_time'] }, through: :deliveries
39
+ end
40
+
41
+ Reports can then also be simply instantiated (which is done automatically if using the supplied
42
+ `Compendium::ReportsController`):
43
+
44
+ report = MyReport.new(starting_on: '2013-06-01')
45
+ report.run(self) # The parameter is the context to run the report in; usually this should be
46
+ # a controller context so that methods like current_user can be used
47
+
48
+ Compendium also comes with a variety of different presenters, for rendering the setup page, and displaying charts
49
+ (`report.render_chart`), tables (`report.render_table`) and metrics for your report. Charting is delegated through a
50
+ `ChartProvider` to a charting gem (amcharts.rb is currently supported).
51
+
52
+ ### Tying into your Rails application
53
+
54
+ Compendium has a `Rails::Engine`, which adds a default controller and some views. If desired, the controller can be
55
+ subclassed so that filters and the like can be added. The controller (which extends `ApplicationController`
56
+ automatically) has two actions: `setup` (collect options for the report) and `run` (execute and render the report),
57
+ with accompanying views. The `setup` view can be included inside your own view using the `render_report_setup`
58
+ method (*NOTE:* you have to pass `local_assigns` into it if you want locals to be passed along).
59
+
60
+ Routes are not automatically added to your application. In order to do so, you can use the `mount_compendium` helper
61
+ within your `config/routes.rb` file
62
+
63
+ mount_compendium at: '/report', controller: 'reports' # controller defaults to compendium/reports
64
+
65
+ ### Interaction with other gems
66
+ * If [accessible_tooltip](https://github.com/dvandersluis/accessible_tooltip) is present, option notes will be rendered
67
+ in a tooltip rather than as straight text.
68
+ * [AmCharts.rb](https://github.com/dvandersluis/amcharts.rb) is currently the only chart provider (please create a pull
69
+ request if you'd like to create another one...)
70
+
5
71
  ## Installation
6
72
 
7
73
  Add this line to your application's Gemfile:
@@ -16,10 +82,6 @@ Or install it yourself as:
16
82
 
17
83
  $ gem install compendium
18
84
 
19
- ## Usage
20
-
21
- TODO: Write usage instructions here
22
-
23
85
  ## Contributing
24
86
 
25
87
  1. Fork it
@@ -27,3 +89,7 @@ TODO: Write usage instructions here
27
89
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
90
  4. Push to the branch (`git push origin my-new-feature`)
29
91
  5. Create new Pull Request
92
+
93
+ ## Acknowledgments
94
+
95
+ * Special thanks to [TalentNest](http://github.com/talentnest), who sponsored this gem's development.
data/Rakefile CHANGED
@@ -1 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,92 @@
1
+ @import "compass/css3/border-radius";
2
+
3
+ div.metrics
4
+ {
5
+ div.metric
6
+ {
7
+ float: left;
8
+ margin: 10px;
9
+ padding: 5px;
10
+ border: 2px solid #575757;
11
+ width: 150px;
12
+ height: 150px;
13
+ font-size: 12px;
14
+ text-align: center;
15
+ color: #575757;
16
+ @include border-radius(10px);
17
+
18
+ .metric-label
19
+ {
20
+ height: 30%;
21
+ font-size: 150%;
22
+ padding-top: 10px;
23
+ padding-bottom: 5px;
24
+ font-weight: bold;
25
+ }
26
+
27
+ .metric-data
28
+ {
29
+ height: 50%;
30
+ position: relative;
31
+ font-size: 600%;
32
+ font-weight: bold;
33
+ color: black;
34
+
35
+ .metric-data-inner
36
+ {
37
+ display: inline;
38
+ position: relative;
39
+ line-height: 0;
40
+ top: 50%;
41
+ margin: auto;
42
+ padding: 0;
43
+ }
44
+ }
45
+
46
+ .metric-label-small
47
+ {
48
+ height: 20%;
49
+ font_size: 90%;
50
+ }
51
+
52
+ &.small
53
+ {
54
+ width: 115px;
55
+ height: 115px;
56
+ font-size: 10px;
57
+
58
+ .metric-label
59
+ {
60
+ padding-top: 0px;
61
+ }
62
+
63
+ .metric-label-small
64
+ {
65
+ font-size: 100%;
66
+ }
67
+ }
68
+
69
+ &.big
70
+ {
71
+ border-width: 3px;
72
+ width: 280px;
73
+ height: 280px;
74
+ font-size: 22px;
75
+
76
+ .metric-label
77
+ {
78
+ padding-top: 25px;
79
+ }
80
+
81
+ .metric-label-small
82
+ {
83
+ margin-top: -25px;
84
+ }
85
+
86
+ .metric-data
87
+ {
88
+ top: -10px;
89
+ }
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,41 @@
1
+ .option
2
+ {
3
+ .option-label
4
+ {
5
+ margin: 0px 0px 0px 0px;
6
+ font-family: "Arial";
7
+ font-size: 14px;
8
+ font-weight: bold;
9
+ color: #575757;
10
+ }
11
+
12
+ .option-note
13
+ {
14
+ font-style: italic;
15
+ color: #575757;
16
+ }
17
+
18
+ .option-element-group
19
+ {
20
+ margin-top: 3px;
21
+ }
22
+
23
+ .option-radio
24
+ {
25
+ input[type=radio]
26
+ {
27
+ margin: 2px 6px 2px 0;
28
+
29
+ & + label
30
+ {
31
+ position: relative;
32
+ top: 1px;
33
+ }
34
+ }
35
+ }
36
+
37
+ & + &, & + input[type=submit]
38
+ {
39
+ margin-top: 15px;
40
+ }
41
+ }
@@ -0,0 +1,22 @@
1
+ .report
2
+ {
3
+ @import 'metrics';
4
+
5
+ .title
6
+ {
7
+ font-size: 200%;
8
+ font-weight: bold;
9
+ }
10
+
11
+ .subtitle
12
+ {
13
+ font-size: 150%;
14
+ font-weight: bold;
15
+ }
16
+
17
+ .setting, .option
18
+ {
19
+ font-weight: bold;
20
+ color: #575757;
21
+ }
22
+ }
@@ -0,0 +1,30 @@
1
+ module Compendium::Presenters
2
+ class Base
3
+ def initialize(template, object)
4
+ @object = object
5
+ @template = template
6
+ end
7
+
8
+ def to_s
9
+ "#<#{self.class.name}:0x00#{'%x' % (object_id << 1)}>"
10
+ end
11
+
12
+ private
13
+
14
+ def self.presents(name)
15
+ define_method(name) do
16
+ @object
17
+ end
18
+ end
19
+
20
+ def method_missing(*args, &block)
21
+ return @template.send(*args, &block) if @template.respond_to?(args.first)
22
+ super
23
+ end
24
+
25
+ def respond_to_missing?(*args)
26
+ return true if @template.respond_to?(*args)
27
+ super
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module Compendium::Presenters
2
+ class Chart < Query
3
+ attr_reader :data, :chart_provider
4
+
5
+ def initialize(template, object, type, container = nil, &setup)
6
+ super(template, object)
7
+
8
+ @data = results.records
9
+ @data = @data[0...-1] if query.options[:totals]
10
+
11
+ @container = container || query.name
12
+
13
+ initialize_chart_provider(type, &setup)
14
+ end
15
+
16
+ def render
17
+ chart_provider.render(@template, @container)
18
+ end
19
+
20
+ private
21
+
22
+ def provider
23
+ provider = Compendium.config.chart_provider
24
+ provider.is_a?(Class) ? provider : Compendium::ChartProvider.const_get(provider)
25
+ end
26
+
27
+ def initialize_chart_provider(type, &setup)
28
+ @chart_provider = provider.new(type, @data, &setup)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ module Compendium::Presenters
2
+ class Metric < Base
3
+ presents :metric
4
+
5
+ delegate :name, :query, :ran?, to: :metric
6
+
7
+ def label
8
+ t("#{query}.#{name}")
9
+ end
10
+
11
+ def result(number_format = '%0.1f', display_nil_as = :na)
12
+ if metric.result
13
+ sprintf(number_format, metric.result)
14
+ else
15
+ t(display_nil_as)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,97 @@
1
+ module Compendium::Presenters
2
+ class Option < Base
3
+ MISSING_CHOICES_ERROR = "choices must be specified"
4
+
5
+ presents :option
6
+
7
+ def name
8
+ t(option.name)
9
+ end
10
+
11
+ def label(form)
12
+ label = case option.type.to_sym
13
+ when :boolean, :radio
14
+ name
15
+
16
+ else
17
+ form.label option.name, name
18
+ end
19
+
20
+ out = ActiveSupport::SafeBuffer.new
21
+ out << content_tag(:span, label, class: 'option-label')
22
+
23
+ if option.note?
24
+ note = t(option.note == true ? :"#{option.name}_note" : option.note)
25
+
26
+ if defined?(AccessibleTooltip)
27
+ return accessible_tooltip(:help, label: out, title: t("#{option.name}_note_title", default: '')) { note }
28
+ else
29
+ out << content_tag(:div, note, class: 'option-note')
30
+ end
31
+ end
32
+
33
+ out
34
+ end
35
+
36
+ def note
37
+ if option.note?
38
+ key = option.note === true ? :"#{option.name}_note" : option.note
39
+ content_tag(:div, t(key), class: 'option-note')
40
+ end
41
+ end
42
+
43
+ def input(ctx, form)
44
+ out = ActiveSupport::SafeBuffer.new
45
+
46
+ case option.type.to_sym
47
+ when :date
48
+ out << date_field(form)
49
+
50
+ when :dropdown
51
+ raise ArgumentError, MISSING_CHOICES_ERROR unless option.choices?
52
+
53
+ options = option.choices
54
+ options = ctx.instance_exec(&options) if options.respond_to?(:call)
55
+ out << dropdown(form, options)
56
+
57
+ when :boolean, :radio
58
+ choices = if option.radio?
59
+ raise ArgumentError, MISSING_CHOICES_ERROR unless option.choices?
60
+ option.choices
61
+ else
62
+ %w(true false)
63
+ end
64
+
65
+ choices.each.with_index { |choice, index| out << radio_button(form, choice, index) }
66
+ end
67
+
68
+ out
69
+ end
70
+
71
+ private
72
+
73
+ def date_field(form, include_time = false)
74
+ content_tag('div', class: 'option-date') do
75
+ if defined?(CalendarDateSelect)
76
+ form.calendar_date_select option.name, time: include_time, popup: 'force'
77
+ else
78
+ form.text_field option.name
79
+ end
80
+ end
81
+ end
82
+
83
+ def dropdown(form, choices = {})
84
+ content_tag('div', class: 'option-dropdown') do
85
+ form.select option.name, choices
86
+ end
87
+ end
88
+
89
+ def radio_button(form, label, value)
90
+ content_tag('div', class: 'option-radio') do
91
+ div_content = ActiveSupport::SafeBuffer.new
92
+ div_content << form.radio_button(option.name, value)
93
+ div_content << form.label(option.name, t(label), value: value)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,23 @@
1
+ module Compendium::Presenters
2
+ class Query < Base
3
+ presents :query
4
+
5
+ def initialize(template, object)
6
+ super(template, object)
7
+ end
8
+
9
+ def render
10
+ raise NotImplementedError
11
+ end
12
+
13
+ private
14
+
15
+ def results
16
+ query.results
17
+ end
18
+
19
+ def settings_class
20
+ Settings.const_get(self.class.name.demodulize) rescue Settings::Query
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module Compendium::Presenters::Settings
2
+ class Query
3
+ delegate :[], :fetch, to: :@settings
4
+
5
+ def initialize
6
+ @settings = {}.with_indifferent_access
7
+ end
8
+
9
+ def method_missing(name, *args, &block)
10
+ if block_given?
11
+ @settings[name] = block.call(*args)
12
+ elsif !args.empty?
13
+ @settings[name] = args.length == 1 ? args.first : args
14
+ elsif name.to_s.end_with?('?')
15
+ prefix = name.to_s.gsub(/\?\z/, '')
16
+ @settings.key?(prefix)
17
+ else
18
+ @settings[name]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Compendium::Presenters::Settings
2
+ class Table < Query
3
+ attr_reader :headings
4
+
5
+ def initialize(headings)
6
+ super()
7
+ @headings = Hash[headings.zip(headings)].with_indifferent_access
8
+ end
9
+
10
+ def override_heading(col, label)
11
+ @headings[col] = label
12
+ end
13
+
14
+ def format(column, &block)
15
+ @settings[:formatters] ||= {}
16
+ @settings[:formatters][column] = block
17
+ end
18
+
19
+ def formatters
20
+ (@settings[:formatters] || {})
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,81 @@
1
+ module Compendium::Presenters
2
+ class Table < Query
3
+ attr_reader :records, :totals
4
+
5
+ def initialize(*)
6
+ super
7
+
8
+ @records = results.records
9
+ @totals = @records.pop if has_totals_row?
10
+
11
+ @settings = settings_class.new(results.keys)
12
+ yield @settings if block_given?
13
+ end
14
+
15
+ def render
16
+ content_tag(:table, class: 'results') do
17
+ table = ActiveSupport::SafeBuffer.new
18
+ table << content_tag(:thead, build_heading_row)
19
+ table << content_tag(:tbody) do
20
+ tbody = ActiveSupport::SafeBuffer.new
21
+ records.each { |row| tbody << build_data_row(row) }
22
+ tbody
23
+ end
24
+ table << content_tag(:tfoot, build_totals_row) if has_totals_row?
25
+ table
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def headings
32
+ @settings.headings
33
+ end
34
+
35
+ def has_totals_row?
36
+ query.options.fetch(:totals, false)
37
+ end
38
+
39
+ def build_data_row(row)
40
+ build_row(row, 'data') { |key, val| formatted_value(key, val) }
41
+ end
42
+
43
+ def build_heading_row
44
+ build_row(headings, 'headings', :th) { |key, val| t(val) }
45
+ end
46
+
47
+ def build_totals_row
48
+ totals[totals.keys.first] = t(:total)
49
+ build_row(totals, 'totals', :th) { |key, val| formatted_value(key, val) }
50
+ end
51
+
52
+ def build_row(row, row_class, cell_type = :td)
53
+ content_tag('tr', class: row_class) do
54
+ out = ActiveSupport::SafeBuffer.new
55
+
56
+ row.each.with_index do |(key, val), i|
57
+ val = yield key, val, i if block_given?
58
+ out << content_tag(cell_type, val)
59
+ end
60
+
61
+ out
62
+ end
63
+ end
64
+
65
+ def formatted_value(k, v)
66
+ if @settings.formatters[k]
67
+ @settings.formatters[k].call(v)
68
+ else
69
+ if v.numeric?
70
+ if v.zero? and @settings.display_zero_as?
71
+ @settings.display_zero_as
72
+ else
73
+ sprintf(@settings.number_format || '%0.2f', v)
74
+ end
75
+ elsif v.nil?
76
+ @settings.display_nil_as
77
+ end
78
+ end || v
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,52 @@
1
+ module Compendium
2
+ class ReportsController < ::ApplicationController
3
+ helper Compendium::ReportsHelper
4
+
5
+ before_filter :find_report
6
+ before_filter :run_report, only: :run
7
+
8
+ def setup
9
+ render locals: { report: setup_report, prefix: @prefix }
10
+ end
11
+
12
+ def run
13
+ template = template_exists?(@prefix, get_template_prefixes) ? @prefix : 'run'
14
+ render action: template, locals: { report: @report }
15
+ end
16
+
17
+ private
18
+
19
+ def find_report
20
+ @prefix = params[:report_name]
21
+ @report_name = "#{@prefix}_report"
22
+
23
+ begin
24
+ require(@report_name) unless Rails.env.development? or Module.const_defined?(@report_name.classify)
25
+ @report_class = @report_name.camelize.constantize
26
+ rescue LoadError
27
+ flash[:error] = t(:invalid_report)
28
+ redirect_to action: :index
29
+ end
30
+ end
31
+
32
+ def setup_report
33
+ @report_class.new(params[:report] || {})
34
+ end
35
+
36
+ def run_report
37
+ @report = @report_class.new(params[:report]).run(self)
38
+ end
39
+
40
+ def get_template_prefixes
41
+ paths = []
42
+ klass = self.class
43
+
44
+ begin
45
+ paths << klass.name.underscore.gsub(/_controller$/, '')
46
+ klass = klass.superclass
47
+ end while(klass != ActionController::Base)
48
+
49
+ paths
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ module Compendium
2
+ module ReportsHelper
3
+ def expose(*args)
4
+ klass = args.pop if args.last.is_a?(Class)
5
+ klass ||= "Compendium::Presenters::#{args.first.class}".constantize
6
+ presenter = klass.new(self, *(args.empty? ? [nil] : args))
7
+ yield presenter if block_given?
8
+ presenter
9
+ end
10
+
11
+ def render_report_setup(assigns)
12
+ render file: "#{Compendium::Engine.root}/app/views/compendium/reports/setup", locals: assigns
13
+ end
14
+
15
+ def render_if_exists(options = {})
16
+ if lookup_context.template_exists?(options[:partial] || options[:template], options[:path], options.key?(:partial))
17
+ render options
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1 @@
1
+ = t(:results, report_name: @prefix)
@@ -0,0 +1,14 @@
1
+ - content_for :stylesheets do
2
+ = stylesheet_link_tag 'compendium/options'
3
+
4
+ = render_if_exists partial: 'report_header', path: 'compendium/reports'
5
+
6
+ .options
7
+ = form_for report, as: :report, url: compendium_reports_run_path do |f|
8
+ - report.options.values.each do |option|
9
+ - expose option, Compendium::Presenters::Option do |opt|
10
+ .option
11
+ = opt.label(f)
12
+ .option-element-group= opt.input(self, f)
13
+
14
+ = f.submit t(:generate_report, scope: 'compendium.reports')