amee-analytics 1.0.1

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.
@@ -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