amee-analytics 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,347 @@
1
+
2
+ # Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
3
+ # Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
4
+ #
5
+ # :title: Module: AMEE::DataAbstraction::TermsListAnalyticsSupport
6
+
7
+ module AMEE
8
+ module DataAbstraction
9
+
10
+ # Mixin module for the <i>AMEE::DataAbstraction::Term</i> class, providing
11
+ # methods for handling collections of calculations.
12
+ #
13
+ module TermsListAnalyticsSupport
14
+
15
+ def name
16
+ first.name if analogous?
17
+ end
18
+
19
+ # Returns <tt>true</tt> if all terms within the list have the same label.
20
+ # Otherwise, returns <tt>false</tt>.
21
+ #
22
+ # This enables a check as to whether all terms represent the same thing,
23
+ # i.e. same calculation component (i.e. the same drill choice, or profile
24
+ # item value, or return value, or metadata type).
25
+ #
26
+ def analogous?
27
+ labels.uniq.size == (1 or nil)
28
+ end
29
+
30
+ # Returns <tt>true</tt> if all terms within the list have the same label
31
+ # AND contain consistent units. Otherwise, returns <tt>false</tt>.
32
+ #
33
+ # This enables a term list to be manipulated numerically, for example, by
34
+ # producing a sum or a mean across all terms.
35
+ #
36
+ def homogeneous?
37
+ analogous? and homogeneous_units? and homogeneous_per_units?
38
+ end
39
+
40
+ # Returns <tt>true</tt> if TermsList is NOT homogeneous, i.e. it does NOT
41
+ # contain all analogous terms with corresponding units. Otherwise, returns
42
+ # <tt>false</tt>.
43
+ #
44
+ def heterogeneous?
45
+ !homogeneous?
46
+ end
47
+
48
+ # Returns <tt>true</tt> if all terms within the list are represented by the
49
+ # same unit or are all <tt>nil</tt>. Otherwise, returns <tt>false</tt>.
50
+ #
51
+ def homogeneous_units?
52
+ return true if all? { |term| term.unit.nil? } or
53
+ ( all? { |term| term.unit.is_a? Quantity::Unit::Base } and
54
+ map { |term| term.unit.label }.uniq.size == 1 )
55
+ return false
56
+ end
57
+
58
+ # Returns <tt>true</tt> if all terms within the list are represented by the
59
+ # same PER unit or are all <tt>nil</tt>. Otherwise, returns <tt>false</tt>.
60
+ #
61
+ def homogeneous_per_units?
62
+ return true if all? { |term| term.per_unit.nil? } or
63
+ ( all? { |term| term.per_unit.is_a? Quantity::Unit::Base } and
64
+ map { |term| term.per_unit.label }.uniq.size == 1 )
65
+ return false
66
+ end
67
+
68
+ # Returns the label which defines all terms in contained within <tt>self</tt>,
69
+ # if they are all the same. Otherwise, returns <tt>nil</tt>.
70
+ #
71
+ def label
72
+ first.label if analogous?
73
+ end
74
+
75
+ def +(other_list)
76
+ self.class.new(self.to_a + other_list.to_a)
77
+ end
78
+
79
+ def -(other_list)
80
+ other_list = [other_list].flatten
81
+ self.delete_if { |term| other_list.include?(term) }
82
+ end
83
+
84
+ def first_of_each_type
85
+ labels = self.labels.uniq
86
+ terms = labels.map {|label| find { |term| term.label == label } }
87
+ TermsList.new(terms)
88
+ end
89
+
90
+ # Returns the label of the unit which is predominantly used across all terms
91
+ # in the list, e.g.
92
+ #
93
+ # list.predominant_unit #=> kg
94
+ #
95
+ # list.predominant_unit #=> kWh
96
+ #
97
+ # Returns nil if all units are blank
98
+ #
99
+ def predominant_unit
100
+ terms = reject { |term| term.unit.nil? }
101
+ unit = terms.group_by { |term| term.unit.label }.
102
+ max {|a,b| a.last.size <=> b.last.size }.first unless terms.blank?
103
+ return unit
104
+ end
105
+
106
+ # Returns the label of the per unit which is predominantly used across all terms
107
+ # in the list, e.g.
108
+ #
109
+ # list.predominant_per_unit #=> h
110
+ #
111
+ # list.predominant_per_unit #=> kWh
112
+ #
113
+ # Returns nil if all per units are blank
114
+ #
115
+ def predominant_per_unit
116
+ terms = reject { |term| term.per_unit.nil? }
117
+ unit = terms.group_by { |term| term.per_unit.label }.
118
+ max {|a,b| a.last.size <=> b.last.size }.first unless terms.blank?
119
+ return unit
120
+ end
121
+
122
+ # Returns <tt>true</tt> if all terms in the list have numeric values.
123
+ # Otherwise, returns <tt>false</tt>.
124
+ #
125
+ def all_numeric?
126
+ all? { |term| term.has_numeric_value? }
127
+ end
128
+
129
+ # Returns a new instance of <i>TermsList</i> comprising only those terms
130
+ # belongong to <tt>self</tt> which have numeric values.
131
+ #
132
+ # This is useful for establishing which terms in a list to perform numerical
133
+ # operations on
134
+ #
135
+ def numeric_terms
136
+ TermsList.new select { |term| term.has_numeric_value? }
137
+ end
138
+
139
+ # Returns a new instance of <i>TermsList</i> with all units standardized and
140
+ # the respective term values adjusted accordingly.
141
+ #
142
+ # The unit and per units to be standardized to can be specified as the first
143
+ # and second arguments respectively. Either the unit name, symbol or label
144
+ # (as defined in the <i>Quantify</i> gem) can be used. If no arguments are
145
+ # specified, the standardized units represent those which are predominant
146
+ # in the list, e.g.
147
+ #
148
+ # list.standardize_units #=> <TermsList>
149
+ #
150
+ # list.standardize_units(:t,:kWh) #=> <TermsList>
151
+ #
152
+ # list.standardize_units('pound') #=> <TermsList>
153
+ #
154
+ # list.standardize_units(nil, 'BTU') #=> <TermsList>
155
+ #
156
+ def standardize_units(unit=nil,per_unit=nil)
157
+ return self if homogeneous? and ((unit.nil? or (first.unit and first.unit.label == unit)) and
158
+ (per_unit.nil? or (first.per_unit and first.per_unit.label == per_unit)))
159
+ unit = predominant_unit if unit.nil?
160
+ per_unit = predominant_per_unit if per_unit.nil?
161
+ new_terms = map { |term| term.convert_unit(:unit => unit, :per_unit => per_unit) }
162
+ TermsList.new new_terms
163
+ end
164
+
165
+ # Returns a new instance of <i>Result</i> which represents the sum of all
166
+ # term values within the list.
167
+ #
168
+ # Any terms within self which contain non-numeric values are ignored.
169
+ #
170
+ # If the terms within <tt>self</tt> do not contain consistent units, they
171
+ # are standardized by default to the unit (and per unit) which predominate
172
+ # in the list. Alternatively, the required unit and per units can be
173
+ # specified as arguments using the same conventions as the
174
+ # <tt>#standardize_units</tt> method.
175
+ #
176
+ def sum(unit=nil,per_unit=nil)
177
+ unit = predominant_unit if unit.nil?
178
+ per_unit = predominant_per_unit if per_unit.nil?
179
+ value = numeric_terms.standardize_units(unit,per_unit).inject(0.0) do |sum,term|
180
+ sum + term.value
181
+ end
182
+ template = self
183
+ Result.new { label template.label; value value; unit unit; per_unit per_unit; name template.name }
184
+ end
185
+
186
+ # Returns a new instance of <i>Result</i> which represents the mean of all
187
+ # term values within the list.
188
+ #
189
+ # Any terms within self which contain non-numeric values are ignored.
190
+ #
191
+ # If the terms within <tt>self</tt> do not contain consistent units, they
192
+ # are standardized by default to the unit (and per unit) which predominate
193
+ # in the list. Alternatively, the required unit and per units can be
194
+ # specified as arguments using the same conventions as the
195
+ # <tt>#standardize_units</tt> method.
196
+ #
197
+ def mean(unit=nil,per_unit=nil)
198
+ list = numeric_terms
199
+ sum = list.sum(unit,per_unit)
200
+ Result.new { label sum.label; value (sum.value/list.size); unit sum.unit; per_unit sum.per_unit; name sum.name }
201
+ end
202
+
203
+ # Returns a representation of the term with most prevalent value in
204
+ # <tt>self</tt>, i.e. the modal value. This method considers both numerical
205
+ # and text values.
206
+ #
207
+ # If only a single modal value is discovered an instance of the class
208
+ # <i>Result</i> is returning representing the modal value. Where multiple
209
+ # modal values occur a new instance of <i>TermsList</i> is returned
210
+ # containing <i>Result</i> representations of each modal value.
211
+ #
212
+ def mode
213
+ groups = standardize_units.reject { |term| term.value.nil? }.
214
+ group_by { |term| term.value }.map(&:last)
215
+ max_group_size = groups.max {|a,b| a.size <=> b.size }.size
216
+ max_groups = groups.select {|a| a.size == max_group_size}
217
+ if max_groups.size == 1
218
+ max_groups.first.first.to_result
219
+ else
220
+ TermsList.new max_groups.map { |group| group.first.to_result }
221
+ end
222
+ end
223
+
224
+ # Returns a representation of the term with median value in <tt>self</tt>.
225
+ # This method considers both numerical and text values.
226
+ #
227
+ # If <tt>self</tt> has an even-numbered size, the median is caluclated as
228
+ # the mean of the values of the two centrally placed terms (having been
229
+ # sorted according to their value attributes).
230
+ #
231
+ def median
232
+ new_list = standardize_units
233
+ midpoint = new_list.size/2
234
+ if new_list.size % 2.0 == 1
235
+ median_term = new_list.sort_by_value[midpoint]
236
+ elsif new_list.size % 2.0 == 0
237
+ median_term = new_list.sort_by_value[midpoint-1, 2].mean
238
+ else
239
+ raise
240
+ end
241
+ median_term.to_result
242
+ end
243
+
244
+ # Convenience method for initializing instances of the <i>Result</i> class.
245
+ # Intialize the new object with the attributes described by <tt>label</tt>,
246
+ # <tt>value</tt>, <tt>unit</tt> and <tt>per_unit</tt>. The unit and per_unit
247
+ # attributes default to <tt>nil</tt> if left unspecified.
248
+ #
249
+ def initialize_result(label,value,unit=nil,per_unit=nil)
250
+ Result.new { label label; value value; unit unit; per_unit per_unit }
251
+ end
252
+
253
+ # Sorts the terms list in place according to the term attribute indiated by
254
+ # <tt>attr</tt>, returning <tt>self</tt>.
255
+ #
256
+ # my_terms_list.sort_by! :value
257
+ #
258
+ # #=> <AMEE::DataAbstraction::TermsList ... >
259
+ #
260
+ def sort_by!(attr)
261
+ replace(sort_by(attr))
262
+ end
263
+
264
+ # Similar to <tt>#sort_by!</tt> but returns a new instance of
265
+ # <i>TermsList</i> arranged according to the values on the
266
+ # attribute <tt>attr</tt>. E.g.
267
+ #
268
+ # my_terms_list.sort_by :value
269
+ #
270
+ # #=> <AMEE::DataAbstraction::TermsList ... >
271
+ #
272
+ def sort_by(attr)
273
+ # Remove unset terms before sort and append at end
274
+ unset_terms = select { |term| term.unset? }
275
+ set_terms = select { |term| term.set? }
276
+ set_terms.sort! { |term,other_term| term.send(attr) <=> other_term.send(attr) }
277
+ TermsList.new(set_terms + unset_terms)
278
+ end
279
+
280
+ # Return an instance of <i>TermsList</i> containing only terms labelled
281
+ # :type.
282
+ #
283
+ # This method overrides the standard #type method (which is deprecated) and
284
+ # mimics the functionality provied by the first #method_missing method in
285
+ # dynamically retrieving a subset of terms according their labels.
286
+ #
287
+ def type
288
+ TermsList.new select{ |x| x.label == :type }
289
+ end
290
+
291
+ def respond_to?(method)
292
+ if labels.include? method.to_sym
293
+ return true
294
+ elsif method.to_s =~ /sort_by_(.*)!/ and self.class::TermProperties.include? $1.to_sym
295
+ return true
296
+ elsif method.to_s =~ /sort_by_(.*)/ and self.class::TermProperties.include? $1.to_sym
297
+ return true
298
+ else
299
+ super
300
+ end
301
+ end
302
+
303
+ # Syntactic sugar for several instance methods.
304
+ #
305
+ # ---
306
+ #
307
+ # Call a method on <tt>self</tt> which named after a specific term label
308
+ # contained within <tt>self</tt> and return a new instance of the
309
+ # <tt>TermsList</tt> class containing each of those terms. E.g.,
310
+ #
311
+ # my_terms = my_terms_list.type #=> <AMEE::DataAbstraction::TermsList>
312
+ # my_terms.label #=> :type
313
+ #
314
+ # my_terms = my_terms_list.mass #=> <AMEE::DataAbstraction::TermsList>
315
+ # my_terms.label #=> :mass
316
+ #
317
+ # my_terms = my_terms_list.co2 #=> <AMEE::DataAbstraction::TermsList>
318
+ # my_terms.label #=> :co2
319
+ #
320
+ # ---
321
+ #
322
+ # Call either the <tt>#sort_by</tt> or <tt>#sort_by!</tt> methods including
323
+ # the argument term as part of the method name, e.g.,
324
+ #
325
+ # my_calculation_collection.sort_by_value
326
+ #
327
+ # #=> <AMEE::DataAbstraction::TermsList ... >
328
+ #
329
+ # my_calculation_collection.sort_by_name!
330
+ #
331
+ # #=> <AMEE::DataAbstraction::TermsList ... >
332
+ #
333
+ def method_missing(method, *args, &block)
334
+ if labels.include? method
335
+ TermsList.new select{ |x| x.label == method }
336
+ elsif method.to_s =~ /sort_by_(.*)!/ and self.class::TermProperties.include? $1.to_sym
337
+ sort_by! $1.to_sym
338
+ elsif method.to_s =~ /sort_by_(.*)/ and self.class::TermProperties.include? $1.to_sym
339
+ sort_by $1.to_sym
340
+ else
341
+ super
342
+ end
343
+ end
344
+
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,8 @@
1
+ # Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
2
+ # Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
3
+
4
+ require 'rubygems'
5
+ gem 'amee-data-abstraction'
6
+ require 'amee-data-abstraction'
7
+
8
+ require 'amee/data_abstraction/result'
data/rails/init.rb ADDED
@@ -0,0 +1,9 @@
1
+
2
+ # Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
3
+ # Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
4
+
5
+ require 'amee-data-abstraction'
6
+
7
+ ::AMEE::DataAbstraction::CalculationCollection.class_eval { include AMEE::DataAbstraction::CalculationCollectionAnalyticsSupport }
8
+ ::AMEE::DataAbstraction::TermsList.class_eval { include AMEE::DataAbstraction::TermsListAnalyticsSupport }
9
+ ::AMEE::DataAbstraction::Term.class_eval { include AMEE::DataAbstraction::TermAnalyticsSupport }
@@ -0,0 +1,234 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include AMEE::DataAbstraction
4
+
5
+ describe CalculationCollection do
6
+
7
+ before(:each) do
8
+ initialize_calculation_set
9
+ @calcs = []
10
+ @calcs << add_elec_calc(500,240)
11
+ @calcs << add_elec_calc(1000,480)
12
+ @calcs << add_elec_calc(1234,600)
13
+ @coll = CalculationCollection.new @calcs
14
+ end
15
+
16
+ it "should add calcs to collection" do
17
+ @coll.should be_a CalculationCollection
18
+ @coll.first.should be_a AMEE::DataAbstraction::OngoingCalculation
19
+ @coll.size.should eql 3
20
+ end
21
+
22
+ it "should be homogeneous" do
23
+ @coll.should be_homogeneous
24
+ @coll.should_not be_heterogeneous
25
+ end
26
+
27
+ it "should be heterogeneous" do
28
+ @coll << add_transport_calc(1000,231)
29
+ @coll.should_not be_homogeneous
30
+ @coll.should be_heterogeneous
31
+ end
32
+
33
+ it "should hold all calculation terms" do
34
+ terms = @coll.terms
35
+ terms.should be_a AMEE::DataAbstraction::TermsList
36
+ terms.size.should eql 9
37
+ end
38
+
39
+ it "should return all like terms dynamically" do
40
+ terms = @coll.co2
41
+ terms.should be_a AMEE::DataAbstraction::TermsList
42
+ terms.size.should eql 3
43
+ terms.labels.uniq.should eql [:co2]
44
+ end
45
+
46
+ it "should delegate selector methods" do
47
+ terms = @coll.outputs
48
+ terms.all? {|term| term.is_a? AMEE::DataAbstraction::Output }.should be_true
49
+ end
50
+
51
+ it "should sum term values with homegeneous calcs" do
52
+ @coll.co2.sum.to_s.should eql "1320.0 t"
53
+ @coll.co2.mean.to_s.should eql "440.0 t"
54
+ @coll.usage.sum.to_s.should eql "2734.0 kWh"
55
+ end
56
+
57
+ it "should sum term values with heterogeneous calcs" do
58
+ @coll << add_transport_calc(1000,231)
59
+ @coll.co2.sum.to_s.should eql "1551.0 t"
60
+ @coll.distance.sum.to_s.should eql "1000.0 km"
61
+ @coll.usage.sum.to_s.should eql "2734.0 kWh"
62
+ end
63
+
64
+ it "should sort self by specified term" do
65
+ # check order
66
+ @coll.first['co2'].value.should eql 240
67
+ @coll[1]['co2'].value.should eql 480
68
+ @coll.last['co2'].value.should eql 600
69
+
70
+ # reverse and check order
71
+ @coll.reverse!
72
+ @coll.first['co2'].value.should eql 600
73
+ @coll[1]['co2'].value.should eql 480
74
+ @coll.last['co2'].value.should eql 240
75
+
76
+ # sort by co2 and check order
77
+ @coll.sort_by_co2!
78
+ @coll.first['co2'].value.should eql 240
79
+ @coll[1]['co2'].value.should eql 480
80
+ @coll.last['co2'].value.should eql 600
81
+
82
+ # reverse and check order
83
+ @coll.reverse!
84
+ @coll.first['co2'].value.should eql 600
85
+ @coll[1]['co2'].value.should eql 480
86
+ @coll.last['co2'].value.should eql 240
87
+
88
+ # sort by usage and check order
89
+ @coll.sort_by_usage!
90
+ @coll.first['co2'].value.should eql 240
91
+ @coll[1]['co2'].value.should eql 480
92
+ @coll.last['co2'].value.should eql 600
93
+ end
94
+
95
+ it "should sort by specified term and return new" do
96
+ # check order
97
+ @coll.first['co2'].value.should eql 240
98
+ @coll[1]['co2'].value.should eql 480
99
+ @coll.last['co2'].value.should eql 600
100
+
101
+ # reverse and check order
102
+ @coll.reverse!
103
+ @coll.first['co2'].value.should eql 600
104
+ @coll[1]['co2'].value.should eql 480
105
+ @coll.last['co2'].value.should eql 240
106
+
107
+ # instantiate new based on co2 and check order
108
+ coll = @coll.sort_by_co2
109
+ coll.first['co2'].value.should eql 240
110
+ coll[1]['co2'].value.should eql 480
111
+ coll.last['co2'].value.should eql 600
112
+
113
+ # check reversed order of original
114
+ @coll.first['co2'].value.should eql 600
115
+ @coll[1]['co2'].value.should eql 480
116
+ @coll.last['co2'].value.should eql 240
117
+
118
+ # instantiate new based on usage and check order
119
+ coll = @coll.sort_by_usage
120
+ coll.first['co2'].value.should eql 240
121
+ coll[1]['co2'].value.should eql 480
122
+ coll.last['co2'].value.should eql 600
123
+ end
124
+
125
+ it "should standardize units in place" do
126
+ @coll.first['usage'].unit 'J'
127
+ @coll.first['usage'].value.should eql 500
128
+ @coll.first['usage'].unit.label.should eql 'J'
129
+ @coll.standardize_units!(:usage,:kWh)
130
+ @coll.first['usage'].unit 'kWh'
131
+ @coll.first['usage'].value.should be_close 0.000138888888888889,0.000001
132
+ end
133
+
134
+ it "should standardize units returning new collection" do
135
+ @coll.first['co2'].value.should eql 240
136
+ @coll.first['co2'].unit.label.should eql 't'
137
+ coll = @coll.standardize_units(:co2,:lb)
138
+ coll.first['co2'].unit 'lb'
139
+ coll.first['co2'].value.should be_close 529109.429243706,0.01
140
+ end
141
+
142
+ it "should handle 'type' as a terms filter" do
143
+ calcs = []
144
+ calcs << add_transport_calc(500,240)
145
+ calcs << add_transport_calc(1000,480)
146
+ calcs << add_transport_calc(1234,600)
147
+ @coll = CalculationCollection.new calcs
148
+ @coll.each { |calc| calc['type'].value "car" }
149
+ terms = @coll.type
150
+ terms.should be_a TermsList
151
+ terms.values.all? {|val| val == "car"}.should be_true
152
+ end
153
+
154
+ it "should add calculation collections" do
155
+ calcs1 = []
156
+ calcs1 << add_transport_calc(500,240)
157
+ calcs1 << add_transport_calc(1000,480)
158
+ calcs1 << add_transport_calc(1234,600)
159
+ @coll1 = CalculationCollection.new(calcs1)
160
+ calcs2 = []
161
+ calcs2 << add_transport_calc(700,350)
162
+ calcs2 << add_transport_calc(5,11)
163
+ calcs2 << add_transport_calc(123,234)
164
+ @coll2 = CalculationCollection.new(calcs2)
165
+ @coll3 = @coll1 + @coll2
166
+ @coll3.should be_a CalculationCollection
167
+ @coll3.size.should eql 6
168
+ end
169
+
170
+ it "should subtract calculation collections" do
171
+ calcs1 = []
172
+ calcs1 << add_transport_calc(500,240)
173
+ calcs1 << add_transport_calc(1000,480)
174
+ calcs1 << add_transport_calc(1234,600)
175
+ @coll1 = CalculationCollection.new(calcs1)
176
+ calcs2 = []
177
+ calcs2 << add_transport_calc(500,240)
178
+ calcs2 << add_transport_calc(1000,480)
179
+ @coll2 = CalculationCollection.new(calcs2)
180
+ @coll3 = @coll1 - @coll2
181
+ @coll3.should be_a CalculationCollection
182
+ @coll3.size.should eql 1
183
+ @coll3.first['distance'].value.should eql 1234
184
+ end
185
+
186
+ it "should add to calculation collection using += syntax" do
187
+ @coll = CalculationCollection.new
188
+ @coll += add_transport_calc(500,240)
189
+ @coll.should be_a CalculationCollection
190
+ @coll += add_transport_calc(1000,480)
191
+ @coll.should be_a CalculationCollection
192
+ @coll += add_transport_calc(1234,600)
193
+ @coll.should be_a CalculationCollection
194
+ @coll.size.should eql 3
195
+ end
196
+
197
+ it "should subtract from calculation collection using -= syntax" do
198
+ calcs1 = []
199
+ calcs1 << add_transport_calc(500,240)
200
+ calcs1 << add_transport_calc(1000,480)
201
+ calcs1 << add_transport_calc(1234,600)
202
+ @coll1 = CalculationCollection.new(calcs1)
203
+ @coll1 -= add_transport_calc(500,240)
204
+ @coll1 -= add_transport_calc(1000,480)
205
+ @coll1.should be_a CalculationCollection
206
+ @coll1.size.should eql 1
207
+ @coll1.first['distance'].value.should eql 1234
208
+ end
209
+
210
+ it "should add all outputs" do
211
+ res = @coll.sum_all_outputs
212
+ res.instance_of?(TermsList).should be_true
213
+ res.first.value.should eql 1320.0
214
+ end
215
+
216
+ it "should respond to dynamic term methods" do
217
+ @coll.respond_to?(:co2).should be_true
218
+ @coll.respond_to?(:usage).should be_true
219
+ @coll.respond_to?(:distance).should be_false
220
+ end
221
+
222
+ it "should respond to dynamic sort methods" do
223
+ @coll.respond_to?(:sort_by_co2).should be_true
224
+ @coll.respond_to?(:sort_by_usage!).should be_true
225
+ @coll.respond_to?(:sort_by_distance).should be_false
226
+ end
227
+
228
+ it "should return co2 outputs" do
229
+ terms = @coll.co2_or_co2e_outputs
230
+ terms.size.should eql 3
231
+ terms.first.label.should eql :co2
232
+ end
233
+
234
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include AMEE::DataAbstraction
4
+
5
+ describe Term do
6
+ before(:each) do
7
+
8
+ end
9
+
10
+
11
+ end