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
@@ -1,3 +1,3 @@
1
1
  module Compendium
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/compendium.rb CHANGED
@@ -1,5 +1,50 @@
1
- require "compendium/version"
1
+ require 'compendium/engine'
2
+ require 'compendium/version'
3
+ require 'active_support/configurable'
2
4
 
3
5
  module Compendium
6
+ autoload :AbstractChartProvider, 'compendium/abstract_chart_provider'
7
+ autoload :ChartProvider, 'compendium/abstract_chart_provider'
8
+ autoload :ContextWrapper, 'compendium/context_wrapper'
9
+ autoload :DSL, 'compendium/dsl'
10
+ autoload :Metric, 'compendium/metric'
11
+ autoload :Option, 'compendium/option'
12
+ autoload :Params, 'compendium/params'
13
+ autoload :Query, 'compendium/query'
14
+ autoload :ResultSet, 'compendium/result_set'
15
+ autoload :Report, 'compendium/report'
4
16
 
17
+ autoload :Param, 'compendium/param_types'
18
+ autoload :BooleanParam, 'compendium/param_types'
19
+ autoload :DateParam, 'compendium/param_types'
20
+ autoload :DropdownParam, 'compendium/param_types'
21
+ autoload :ParamWithChoices, 'compendium/param_types'
22
+ autoload :RadioParam, 'compendium/param_types'
23
+
24
+ def self.reports
25
+ @reports ||= []
26
+ end
27
+
28
+ # Configures global settings for Compendium
29
+ # Compendium.configure do |config|
30
+ # config.chart_provider = :AmCharts
31
+ # end
32
+ def self.configure(&block)
33
+ yield @config ||= Compendium::Configuration.new
34
+ end
35
+
36
+ def self.config
37
+ @config
38
+ end
39
+
40
+ # need a Class for 3.0
41
+ class Configuration #:nodoc:
42
+ include ActiveSupport::Configurable
43
+
44
+ config_accessor :chart_provider
45
+ end
46
+
47
+ configure do |config|
48
+ config.chart_provider = Compendium::AbstractChartProvider.find_chart_provider
49
+ end
5
50
  end
@@ -0,0 +1,71 @@
1
+ require 'compendium/context_wrapper'
2
+
3
+ class Wrapper1
4
+ def test_val
5
+ 123
6
+ end
7
+ end
8
+
9
+ class Wrapper2
10
+ def wrapped
11
+ true
12
+ end
13
+ end
14
+
15
+ class Wrapper3
16
+ def wrapper_num
17
+ 3
18
+ end
19
+ end
20
+
21
+ class Wrapper4
22
+ def wrapper_num
23
+ 4
24
+ end
25
+ end
26
+
27
+ describe Compendium::ContextWrapper do
28
+ describe ".wrap" do
29
+ let(:w1) { Wrapper1.new }
30
+ let(:w2) { Wrapper2.new }
31
+ let(:w3) { Wrapper3.new }
32
+ let(:w4) { Wrapper4.new }
33
+
34
+ subject { described_class.wrap(w2, w1) }
35
+
36
+ it { should respond_to :test_val }
37
+ it { should respond_to :wrapped }
38
+
39
+ its(:test_val) { should == 123 }
40
+ its(:wrapped) { should == true }
41
+
42
+ it "should not affect the original objects" do
43
+ subject
44
+ w1.should_not respond_to :wrapped
45
+ w2.should_not respond_to :test_val
46
+ end
47
+
48
+ it "should yield a block if given" do
49
+ described_class.wrap(w2, w1) { test_val }.should == 123
50
+ end
51
+
52
+ context "overriding methods" do
53
+ subject { described_class.wrap(w4, w3) }
54
+ its(:wrapper_num) { should == 4 }
55
+ end
56
+
57
+ context "nested wrapping" do
58
+ let(:inner) { described_class.wrap(w2, w1) }
59
+ subject { described_class.wrap(inner, w3) }
60
+
61
+ it { should respond_to :test_val }
62
+ it { should respond_to :wrapped }
63
+ it { should respond_to :wrapper_num }
64
+
65
+ it "should not extend the inner wrap" do
66
+ subject
67
+ inner.should_not respond_to :wrapper_num
68
+ end
69
+ end
70
+ end
71
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'compendium'
2
+ require 'compendium/dsl'
3
+
4
+ describe Compendium::DSL do
5
+ subject do
6
+ Class.new do
7
+ extend Compendium::DSL
8
+ end
9
+ end
10
+
11
+ describe "#option" do
12
+ before { subject.option :starting_on, :date }
13
+
14
+ its(:options) { should include :starting_on }
15
+ specify { subject.options[:starting_on].should be_date }
16
+
17
+ it "should allow previously defined options to be redefined" do
18
+ subject.option :starting_on, :boolean
19
+ subject.options[:starting_on].should be_boolean
20
+ subject.options[:starting_on].should_not be_date
21
+ end
22
+ end
23
+
24
+ describe "#query" do
25
+ subject do
26
+ Class.new(Compendium::Report) do
27
+ query :test
28
+ end
29
+ end
30
+
31
+ its(:queries) { should include :test }
32
+
33
+ it "should relate the new query back to the report instance" do
34
+ r = subject.new
35
+ r.test.report.should == r
36
+ end
37
+
38
+ it "should not relate a query to the report class" do
39
+ subject.test.report.should be_nil
40
+ end
41
+ end
42
+
43
+ describe "#chart" do
44
+ before { subject.chart(:chart) }
45
+
46
+ its(:queries) { should include :chart }
47
+ end
48
+
49
+ describe "#data" do
50
+ before { subject.data(:data) }
51
+
52
+ its(:queries) { should include :data }
53
+ end
54
+
55
+ describe "#metric" do
56
+ let(:metric_proc) { ->{ :metric } }
57
+
58
+ before do
59
+ subject.query :test
60
+ subject.metric :test_metric, metric_proc, through: :test
61
+ end
62
+
63
+ it "should add a metric to the given query" do
64
+ subject.queries[:test].metrics.first.name.should == :test_metric
65
+ end
66
+
67
+ it "should set the metric command" do
68
+ subject.queries[:test].metrics.first.command.should == metric_proc
69
+ end
70
+
71
+ it "should raise an error if through is not specified" do
72
+ expect{ subject.metric :test_metric, metric_proc }.to raise_error ArgumentError, 'through option must be specified for metric'
73
+ end
74
+
75
+ it "should raise an error if specified for an invalid query" do
76
+ expect{ subject.metric :test_metric, metric_proc, through: :fake }.to raise_error ArgumentError, 'query fake is not defined'
77
+ end
78
+ end
79
+
80
+ it "should allow previously defined queries to be redefined by name" do
81
+ subject.query :test_query
82
+ subject.test_query foo: :bar
83
+ subject.queries[:test_query].options.should == { foo: :bar }
84
+ end
85
+
86
+ it "should allow previously defined queries to be accessed by name" do
87
+ subject.query :test_query
88
+ subject.test_query.should == subject.queries[:test_query]
89
+ end
90
+ end
@@ -0,0 +1,84 @@
1
+ require 'compendium/metric'
2
+
3
+ class MetricContext
4
+ def calculate(data)
5
+ data.first.first
6
+ end
7
+ end
8
+
9
+ describe Compendium::Metric do
10
+ let(:ctx) { MetricContext.new }
11
+ let(:data) { [[1, 2, 3], [4, 5, 6]] }
12
+
13
+ subject { described_class.new(:test_metric, :query, nil) }
14
+
15
+ describe "#run" do
16
+ it "should delegate the command to the context when the command is a symbol" do
17
+ subject.command = :calculate
18
+ subject.run(ctx, data).should == 1
19
+ end
20
+
21
+ it "should call the command when it is a proc" do
22
+ subject.command = -> d { d.flatten.inject(:+) }
23
+ subject.run(ctx, data).should == 21
24
+ end
25
+
26
+ it "should allow procs that refer back to the context" do
27
+ subject.command = -> d { calculate(d) * 2 }
28
+ subject.run(ctx, data).should == 2
29
+ end
30
+
31
+ context "when an if proc is given" do
32
+ before { subject.command = -> * { 100 } }
33
+
34
+ it "should calculate the metric if the proc evaluates to true" do
35
+ subject.options[:if] = ->{ true }
36
+ subject.run(ctx, data).should == 100
37
+ end
38
+
39
+ it "should not calculate the metric if the proc evaluates to false" do
40
+ subject.options[:if] = ->{ false }
41
+ subject.run(ctx, data).should be_nil
42
+ end
43
+
44
+ it "should clear the result if the proc evaluates to false" do
45
+ subject.options[:if] = ->{ false }
46
+ subject.result = 123
47
+ subject.run(ctx, data)
48
+ subject.result.should be_nil
49
+ end
50
+ end
51
+
52
+ context "when an unless proc is given" do
53
+ before { subject.command = -> * { 100 } }
54
+
55
+ it "should calculate the metric if the proc evaluates to false" do
56
+ subject.options[:unless] = ->{ false }
57
+ subject.run(ctx, data).should == 100
58
+ end
59
+
60
+ it "should not calculate the metric if the proc evaluates to true" do
61
+ subject.options[:unless] = ->{ true }
62
+ subject.run(ctx, data).should be_nil
63
+ end
64
+
65
+ it "should clear the result if the proc evaluates to false" do
66
+ subject.options[:unless] = ->{ true }
67
+ subject.result = 123
68
+ subject.run(ctx, data)
69
+ subject.result.should be_nil
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "#ran?" do
75
+ it "should return true if there are any results" do
76
+ subject.stub(result: 123)
77
+ subject.should have_ran
78
+ end
79
+
80
+ it "should return false if there are no results" do
81
+ subject.should_not have_ran
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,12 @@
1
+ require 'compendium/option'
2
+
3
+ describe Compendium::Option do
4
+ it "should raise an ArgumentError if no name is given" do
5
+ expect { described_class.new }.to raise_error ArgumentError, "name must be provided"
6
+ end
7
+
8
+ it "should set up type predicates from the type option" do
9
+ o = described_class.new(name: :option, type: :date)
10
+ o.should be_date
11
+ end
12
+ end
@@ -0,0 +1,147 @@
1
+ require 'compendium/param_types'
2
+
3
+ describe Compendium::Param do
4
+ subject{ described_class.new(:test) }
5
+
6
+ it { should_not be_boolean }
7
+ it { should_not be_date }
8
+ it { should_not be_dropdown }
9
+ it { should_not be_radio }
10
+
11
+ describe "#==" do
12
+ it "should compare to the param's value" do
13
+ subject.stub(value: :test_value)
14
+ subject.should == :test_value
15
+ end
16
+ end
17
+ end
18
+
19
+ describe Compendium::ParamWithChoices do
20
+ subject{ described_class.new(0, %w(a b c)) }
21
+
22
+ it { should_not be_boolean }
23
+ it { should_not be_date }
24
+ it { should_not be_dropdown }
25
+ it { should_not be_radio }
26
+
27
+ it "should return the index when given an index" do
28
+ p = described_class.new(1, [:foo, :bar, :baz])
29
+ p.should == 1
30
+ end
31
+
32
+ it "should return the index when given a value" do
33
+ p = described_class.new(:foo, [:foo, :bar, :baz])
34
+ p.should == 0
35
+ end
36
+
37
+ it "should return the index when given a string value" do
38
+ p = described_class.new("2", [:foo, :bar, :baz])
39
+ p.should == 2
40
+ end
41
+
42
+ it "should raise an error if given an invalid index" do
43
+ expect { described_class.new(3, [:foo, :bar, :baz]) }.to raise_error IndexError
44
+ end
45
+
46
+ it "should raise an error if given a value that is not included in the choices" do
47
+ expect { described_class.new(:quux, [:foo, :bar, :baz]) }.to raise_error IndexError
48
+ end
49
+
50
+ describe "#value" do
51
+ it "should return the value of the given choice" do
52
+ p = described_class.new(2, [:foo, :bar, :baz])
53
+ p.value.should == :baz
54
+ end
55
+ end
56
+ end
57
+
58
+ describe Compendium::BooleanParam do
59
+ subject{ described_class.new(true) }
60
+
61
+ it { should be_boolean }
62
+ it { should_not be_date }
63
+ it { should_not be_dropdown }
64
+ it { should_not be_radio }
65
+
66
+ it "should pass along 0 and 1" do
67
+ described_class.new(0).should == 0
68
+ described_class.new(1).should == 1
69
+ end
70
+
71
+ it "should convert a numeric string to a number" do
72
+ described_class.new('0').should == 0
73
+ described_class.new('1').should == 1
74
+ end
75
+
76
+ it "should return 0 for a truthy value" do
77
+ described_class.new(true).should == 0
78
+ described_class.new(:abc).should == 0
79
+ end
80
+
81
+ it "should return 1 for a falsey value" do
82
+ described_class.new(false).should == 1
83
+ described_class.new(nil).should == 1
84
+ end
85
+
86
+ describe "#value" do
87
+ it "should return true for a truthy value" do
88
+ described_class.new(true).value.should == true
89
+ described_class.new(:abc).value.should == true
90
+ described_class.new(0).value.should == true
91
+ end
92
+
93
+ it "should return false for a falsey value" do
94
+ described_class.new(false).value.should == false
95
+ described_class.new(nil).value.should == false
96
+ described_class.new(1).value.should == false
97
+ end
98
+ end
99
+
100
+ describe "#!" do
101
+ it "should return false if the boolean is true" do
102
+ !described_class.new(true).should == false
103
+ end
104
+
105
+ it "should return true if the boolean is false" do
106
+
107
+ end
108
+ end
109
+ end
110
+
111
+ describe Compendium::DateParam do
112
+ subject{ described_class.new(Date.today) }
113
+
114
+ it { should_not be_boolean }
115
+ it { should be_date }
116
+ it { should_not be_dropdown }
117
+ it { should_not be_radio }
118
+
119
+ it "should convert date strings to date objects" do
120
+ p = described_class.new("2010-05-20")
121
+ p.should == Date.new(2010, 5, 20)
122
+ end
123
+
124
+ it "should convert other date/time formats to date objects" do
125
+ described_class.new(DateTime.new(2010, 5, 20, 10, 30, 59)) == Date.new(2010, 5, 20)
126
+ described_class.new(Time.new(2010, 5, 20, 10, 30, 59)) == Date.new(2010, 5, 20)
127
+ described_class.new(Date.new(2010, 5, 20)) == Date.new(2010, 5, 20)
128
+ end
129
+ end
130
+
131
+ describe Compendium::DropdownParam do
132
+ subject{ described_class.new(0, %w(a b c)) }
133
+
134
+ it { should_not be_boolean }
135
+ it { should_not be_date }
136
+ it { should be_dropdown }
137
+ it { should_not be_radio }
138
+ end
139
+
140
+ describe Compendium::RadioParam do
141
+ subject{ described_class.new(0, %w(a b c)) }
142
+
143
+ it { should_not be_boolean }
144
+ it { should_not be_date }
145
+ it { should_not be_dropdown }
146
+ it { should be_radio }
147
+ end
@@ -0,0 +1,28 @@
1
+ require 'compendium/params'
2
+
3
+ describe Compendium::Params do
4
+ let(:options) { {
5
+ starting_on: Compendium::Option.new(name: :starting_on, type: :date, default: ->{ Date.today }),
6
+ ending_on: Compendium::Option.new(name: :ending_on, type: :date),
7
+ report_type: Compendium::Option.new(name: :report_type, type: :radio, choices: [:big, :small]),
8
+ boolean: Compendium::Option.new(name: :boolean, type: :boolean),
9
+ another_boolean: Compendium::Option.new(name: :another_boolean, type: :boolean)
10
+ } }
11
+
12
+ subject{ described_class.new(@params, options) }
13
+
14
+ it "should only allow keys that are given as options" do
15
+ @params = { starting_on: '2013-10-15', foo: :bar }
16
+ subject.keys.should_not include :foo
17
+ end
18
+
19
+ it "should set missing options to their default value" do
20
+ @params = {}
21
+ subject.starting_on.should == Date.today
22
+ end
23
+
24
+ it "should set missing options to nil if there is no default value" do
25
+ @params = {}
26
+ subject.ending_on.should be_nil
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'compendium/presenters/base'
3
+
4
+ TestPresenter = Class.new(Compendium::Presenters::Base) do
5
+ presents :test_obj
6
+ end
7
+
8
+ describe Compendium::Presenters::Base do
9
+ let(:template) { double("Template", delegated?: true) }
10
+ subject { TestPresenter.new(template, :test) }
11
+
12
+ it "should allow the object name to be overridden" do
13
+ subject.test_obj.should == :test
14
+ end
15
+
16
+ it "should delegate missing methods to the template object" do
17
+ template.should_receive(:delegated?)
18
+ subject.should be_delegated
19
+ end
20
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'compendium/presenters/option'
3
+ require 'compendium/option'
4
+
5
+ describe Compendium::Presenters::Option do
6
+ let(:template) do
7
+ t = double('Template')
8
+ t.stub(:t) { |key| key } # Stub I18n.t to just return the given value
9
+ t
10
+ end
11
+
12
+ let(:option) { Compendium::Option.new(name: :test_option) }
13
+
14
+ subject { described_class.new(template, option) }
15
+
16
+ describe "#name" do
17
+ it "should pass the name through I18n" do
18
+ template.should_receive(:t).with(:test_option)
19
+ subject.name
20
+ end
21
+ end
22
+
23
+ describe "#note" do
24
+ before { template.stub(:content_tag) }
25
+
26
+ it "should return nil if no note is specified" do
27
+ subject.note.should be_nil
28
+ end
29
+
30
+ it "should pass to I18n if the note option is set to true" do
31
+ option.merge!(note: true)
32
+ template.should_receive(:t).with(:test_option_note)
33
+ subject.note
34
+ end
35
+
36
+ it "should pass to I18n if the note option is set" do
37
+ option.merge!(note: :the_note)
38
+ template.should_receive(:t).with(:the_note)
39
+ subject.note
40
+ end
41
+
42
+ it "should create the note within a div with class option-note" do
43
+ option.merge!(note: true)
44
+ template.should_receive(:content_tag).with(:div, anything, class: 'option-note')
45
+ subject.note
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,33 @@
1
+ require 'compendium/query'
2
+
3
+ describe Compendium::Query do
4
+ describe "#run" do
5
+ let(:query) { described_class.new(:test, {}, -> * { [1, 2, 3] }) }
6
+ before { query.stub(:fetch_results) { |c| c } }
7
+
8
+ it "should return the result of the query" do
9
+ query.run(nil).should == [1, 2, 3]
10
+ end
11
+
12
+ it "should mark the query as having ran" do
13
+ query.run(nil)
14
+ query.should have_run
15
+ end
16
+
17
+ it "should not affect any cloned queries" do
18
+ q2 = query.clone
19
+ query.run(nil)
20
+ q2.should_not have_run
21
+ end
22
+ end
23
+
24
+ describe "#nil?" do
25
+ it "should return true if the query's proc is nil" do
26
+ Compendium::Query.new(:test, {}, nil).should be_nil
27
+ end
28
+
29
+ it "should return false if the query's proc is not nil" do
30
+ Compendium::Query.new(:test, {}, ->{}).should_not be_nil
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,93 @@
1
+ require 'compendium/report'
2
+
3
+ describe Compendium::Report do
4
+ subject { described_class }
5
+
6
+ its(:queries) { should be_empty }
7
+ its(:options) { should be_empty }
8
+
9
+ it "should not do anything when run" do
10
+ report = subject.new
11
+ report.run
12
+ report.results.should be_empty
13
+ end
14
+
15
+ context "with multiple instances" do
16
+ let(:report_class) do
17
+ Class.new(Compendium::Report) do
18
+ query :test
19
+ metric :test_metric, ->{}, through: :test
20
+ end
21
+ end
22
+
23
+ subject { report_class.new }
24
+ let(:report2) { report_class.new }
25
+
26
+ its(:queries) { should_not equal report2.queries }
27
+ its(:queries) { should_not equal report_class.queries }
28
+ its(:metrics) { should_not equal report2.metrics }
29
+ end
30
+
31
+ describe "#run" do
32
+ let(:report_class) do
33
+ Class.new(Compendium::Report) do
34
+ option :first, :date
35
+ option :second, :date
36
+
37
+ query :test do |params|
38
+ [params[:first].__getobj__, params[:second].__getobj__]
39
+ end
40
+
41
+ metric :test_metric, -> results { results.to_a.max }, through: :test
42
+ end
43
+ end
44
+
45
+ subject { report_class.new(first: '2010-10-10', second: '2011-11-11') }
46
+ let!(:report2) { report_class.new }
47
+
48
+ before do
49
+ Compendium::Query.any_instance.stub(:fetch_results) { |c| c }
50
+ subject.run
51
+ end
52
+
53
+ its('test_results.records') { should == [Date.new(2010, 10, 10), Date.new(2011, 11, 11)] }
54
+
55
+ it "should run its metrics" do
56
+ subject.test.metrics[:test_metric].result.should == Date.new(2011, 11, 11)
57
+ subject.metrics[:test_metric].result.should == Date.new(2011, 11, 11)
58
+ end
59
+
60
+ it "should not affect other instances of the report class" do
61
+ report2.test.results.should be_nil
62
+ report2.metrics[:test_metric].result.should be_nil
63
+ end
64
+
65
+ it "should not affect the class collections" do
66
+ report_class.test.results.should be_nil
67
+ end
68
+
69
+ context "with through queries" do
70
+ let(:report_class) do
71
+ Class.new(Compendium::Report) do
72
+ option :first, :boolean, default: false
73
+ query(:test) { |params| !!params[:first] ? [100, 200, 400, 800] : [1600, 3200, 6400]}
74
+ query(:through, through: :test) { |results| [results.first] }
75
+ end
76
+ end
77
+
78
+ subject { report_class.new(first: true) }
79
+
80
+ its('through.results') { should == [100] }
81
+
82
+ it "should not mark other instances' queries as ran" do
83
+ report2.test.should_not have_run
84
+ end
85
+
86
+ it "should not affect other instances" do
87
+ report2.queries.each { |q| q.stub(:fetch_results) { |c| c } }
88
+ report2.run
89
+ report2.through.results.should == [1600]
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1 @@
1
+ $:.unshift File.expand_path("../../app/classes", __FILE__)