amee-data-abstraction 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 (53) hide show
  1. data/.rvmrc +1 -0
  2. data/CHANGELOG.txt +4 -0
  3. data/Gemfile +16 -0
  4. data/Gemfile.lock +41 -0
  5. data/LICENSE.txt +27 -0
  6. data/README.txt +188 -0
  7. data/Rakefile +102 -0
  8. data/VERSION +1 -0
  9. data/amee-data-abstraction.gemspec +115 -0
  10. data/examples/_calculator_form.erb +27 -0
  11. data/examples/calculation_controller.rb +16 -0
  12. data/init.rb +4 -0
  13. data/lib/amee-data-abstraction.rb +30 -0
  14. data/lib/amee-data-abstraction/calculation.rb +236 -0
  15. data/lib/amee-data-abstraction/calculation_set.rb +101 -0
  16. data/lib/amee-data-abstraction/drill.rb +63 -0
  17. data/lib/amee-data-abstraction/exceptions.rb +47 -0
  18. data/lib/amee-data-abstraction/input.rb +197 -0
  19. data/lib/amee-data-abstraction/metadatum.rb +58 -0
  20. data/lib/amee-data-abstraction/ongoing_calculation.rb +545 -0
  21. data/lib/amee-data-abstraction/output.rb +16 -0
  22. data/lib/amee-data-abstraction/profile.rb +108 -0
  23. data/lib/amee-data-abstraction/prototype_calculation.rb +350 -0
  24. data/lib/amee-data-abstraction/term.rb +506 -0
  25. data/lib/amee-data-abstraction/terms_list.rb +150 -0
  26. data/lib/amee-data-abstraction/usage.rb +90 -0
  27. data/lib/config/amee_units.rb +129 -0
  28. data/lib/core-extensions/class.rb +27 -0
  29. data/lib/core-extensions/hash.rb +43 -0
  30. data/lib/core-extensions/ordered_hash.rb +21 -0
  31. data/lib/core-extensions/proc.rb +15 -0
  32. data/rails/init.rb +32 -0
  33. data/spec/amee-data-abstraction/calculation_set_spec.rb +54 -0
  34. data/spec/amee-data-abstraction/calculation_spec.rb +75 -0
  35. data/spec/amee-data-abstraction/drill_spec.rb +38 -0
  36. data/spec/amee-data-abstraction/input_spec.rb +77 -0
  37. data/spec/amee-data-abstraction/metadatum_spec.rb +17 -0
  38. data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +494 -0
  39. data/spec/amee-data-abstraction/profile_spec.rb +39 -0
  40. data/spec/amee-data-abstraction/prototype_calculation_spec.rb +256 -0
  41. data/spec/amee-data-abstraction/term_spec.rb +385 -0
  42. data/spec/amee-data-abstraction/terms_list_spec.rb +53 -0
  43. data/spec/config/amee_units_spec.rb +71 -0
  44. data/spec/core-extensions/class_spec.rb +25 -0
  45. data/spec/core-extensions/hash_spec.rb +44 -0
  46. data/spec/core-extensions/ordered_hash_spec.rb +12 -0
  47. data/spec/core-extensions/proc_spec.rb +12 -0
  48. data/spec/fixtures/electricity.rb +35 -0
  49. data/spec/fixtures/electricity_and_transport.rb +55 -0
  50. data/spec/fixtures/transport.rb +26 -0
  51. data/spec/spec.opts +2 -0
  52. data/spec/spec_helper.rb +244 -0
  53. metadata +262 -0
@@ -0,0 +1,16 @@
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
+ # :title: Class: AMEE::DataAbstraction::Output
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # Subclass of <tt>Term</tt> providing methods and attributes appropriate for
10
+ # representing AMEE return values specifically
11
+ #
12
+ class Output < Term
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,108 @@
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
+ # :title: Class: AMEE::DataAbstraction::Profile
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # Subclass of <tt>Input</tt> providing methods and attributes appropriate for
10
+ # representing AMEE profile item values specifically
11
+ #
12
+ class Profile < Input
13
+
14
+ # Represents a list of acceptable choices for the value of <tt>self</tt>.
15
+ # Set the list of choices by passing an argument. Retrieve the choices by
16
+ # calling without an argument, e.g.,
17
+ #
18
+ # my_term.choices 'industrial', 'commercial', 'residential'
19
+ #
20
+ # my_term.choices #=> [ 'industrial',
21
+ # 'commercial',
22
+ # 'residential' ]
23
+ #
24
+ # A single value of <tt>nil</tt> represents an unrestricted value
25
+ # .
26
+ attr_property :choices
27
+
28
+ # Initialization of <i>Input</i> objects follows that of the parent
29
+ # <i>Term</i> class. The <tt>interface</tt> attribute of <tt>self</tt> is
30
+ # set to <tt>:drop_down</tt> by default if a list of choices is defined
31
+ # using the <tt>choices</tt> attribute. Otherwise the <tt>interface</tt>
32
+ # attribute is set to <tt>:test_box</tt>, but can be manually configured if
33
+ # required.
34
+ #
35
+ def initialize(options={},&block)
36
+ super
37
+ interface :drop_down unless choices.blank?
38
+ choice_validation_message unless choices.blank?
39
+ interface :text_box unless interface
40
+ end
41
+
42
+ # Return <tt>true</tt> if the value of <tt>self</tt> is NOT required before
43
+ # the parent calculation can be calculated. Otherwise, return <tt>false</tt>.
44
+ #
45
+ # If no argument is provided, optional status is determined according to the
46
+ # current usage of the parent calculation. Optionality can be determined for
47
+ # a specific usage by passing in the usage path as an argument
48
+ #
49
+ def optional?(usage=nil)
50
+ usage||=parent.current_usage
51
+ usage ? amee_ivd.optional?(usage) : super()
52
+ end
53
+
54
+ # Return <tt>true</tt> if the value of <tt>self</tt> is required before
55
+ # the parent calculation can be calculated. Otherwise, return <tt>false</tt>.
56
+ #
57
+ # If no argument is provided, compulsory status is determined according to
58
+ # the current usage of the parent calculation. Compulsory status can be
59
+ # determined for a specific usage by passing in the usage path as an argument
60
+ #
61
+ def compulsory?(usage=nil)
62
+ usage||=parent.current_usage
63
+ usage ? amee_ivd.compulsory?(usage) : super()
64
+ end
65
+
66
+ # Return <tt>true</tt> if the value of <tt>self</tt> is either compulsory
67
+ # OR optional in the owning calculation, i.e. is NOT forbidden.
68
+ #
69
+ # If no argument is provided, the optional/compulsory status is defined
70
+ # according to the current usage of the parent caluclation. Otherwise,
71
+ # optional/compulsory status is determined on the basis of the usage whose
72
+ # AMEE platform path matches <tt>usage</tt>
73
+ #
74
+ def in_use?(usage)
75
+ compulsory?(usage)||optional?(usage)
76
+ end
77
+
78
+ # Return <tt>true</tt> if the value of <tt>self</tt> is neither compulsory
79
+ # OR optional in the owning calculation, i.e. is forbidden.
80
+ #
81
+ # If no argument is provided, forbbiden status is defined according to the
82
+ # current usage of the parent caluclation. Otherwise, it is determined on
83
+ # the basis of the usage whose AMEE platform path matches <tt>usage</tt>
84
+ #
85
+ def out_of_use?(usage)
86
+ !in_use?(usage)
87
+ end
88
+
89
+ # Return the <i>AMEE::Admin::ItemValueDefinition</i> object associated
90
+ # with <tt>self</tt>.
91
+ #
92
+ def amee_ivd
93
+ parent.amee_ivds.detect{|x|x.path==path}
94
+ end
95
+
96
+ # Returns <tt>true</tt> if the value set for <tt>self</tt> is valid. If
97
+ # <tt>self</tt> contains neither a custom validation pattern nor any
98
+ # defined choices, <tt>true</tt> is returned. Otherwise, validity depends
99
+ # on the custom validation being successful (if present) and the the value
100
+ # of <tt>self</tt> matching one of the entries in the <tt>choices</tt>
101
+ # attribute (if defined). Otherwise, returns <tt>false</tt>.
102
+ #
103
+ def valid?
104
+ super && (choices.blank? || choices.include?(value))
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,350 @@
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
+ # :title: Class: AMEE::DataAbstraction::PrototypeCalculation
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # The <i>PrototypeCalculation</i> class represents a template for a potential
10
+ # calculation within the AMEE platfom.
11
+ #
12
+ # The class inherits from the <i>Calculation</i> class and is therefore primarly
13
+ # characterised by the <tt>label</tt>, <tt>name</tt>, and <tt>path</tt> attributes,
14
+ # as well as an associated instance of the <tt>TermsList</tt> class which represents
15
+ # each of the values (input, outputs, metdata) involved in the calculation. Unlike
16
+ # the <i>OngoingCalculation</i>, the terms associated with an instance of
17
+ # <i>PrototypeCalculation</i> will typically contains blank (nil) values.
18
+ #
19
+ # Objects of the class <i>PrototypeCalculation</tt> are typically instantiated
20
+ # using block ('DSL') syntax, within which each of the attributes and associated
21
+ # terms are defined. Thus,
22
+ #
23
+ # calculation = PrototypeCalculation.new {
24
+ #
25
+ # label :electricity
26
+ # name "Domestic electricity consumption"
27
+ # path "some/path/in/amee"
28
+ # drill { ... }
29
+ # profile { ... }
30
+ # ...
31
+ #
32
+ # }
33
+ #
34
+ class PrototypeCalculation < Calculation
35
+
36
+ public
37
+
38
+ # Initialize a new instance of <i>PrototypeCalculation</i>.
39
+ #
40
+ # The calculation can be configured in place by passing a block (evaluated
41
+ # in the context of the new instance) which defines the calculation properties
42
+ # using the macro-style instance helper methods.
43
+ #
44
+ # calculation = PrototypeCalculation.new {
45
+ #
46
+ # label :transport
47
+ # path "some/other/path/in/amee"
48
+ # terms_from_amee
49
+ # metadatum { ... }
50
+ # start_and_end_dates
51
+ # ...
52
+ #
53
+ # }
54
+ #
55
+ def initialize(options={},&block)
56
+ super()
57
+ instance_eval(&block) if block
58
+ end
59
+
60
+ # Associate a new instance of the <i>Profile</i> class (subclass of the
61
+ # <i>Term</i> class) with <tt>self</tt>, for representing an AMEE profile item input
62
+ #
63
+ # The newly instantiated <i>Term</i> object is configured according to the
64
+ # ('DSL') block passed in.
65
+ #
66
+ # my_protptype.profile {
67
+ # label :energy_used
68
+ # path 'energyUsed'
69
+ # default_unit :kWh
70
+ # }
71
+ #
72
+ def profile(options={},&block)
73
+ construct(Profile,options,&block)
74
+ end
75
+
76
+ # Associate a new instance of the <i>Drill</i> class (subclass of the
77
+ # <i>Term</i> class) with <tt>self</tt>, for representing an AMEE drill down choice
78
+ #
79
+ # The newly instantiated <i>Term</i> object is configured according to the
80
+ # ('DSL') block passed in.
81
+ #
82
+ # my_protptype.drill {
83
+ # label :fuel_type
84
+ # path 'fuelType'
85
+ # fixed 'diesel'
86
+ # }
87
+ #
88
+ def drill(options={},&block)
89
+ construct(Drill,options,&block)
90
+ end
91
+
92
+ # Associate a new instance of the <i>Output</i> class (subclass of the
93
+ # <i>Term</i> class) with <tt>self</tt>, for representing an AMEE return value
94
+ #
95
+ # The newly instantiated <i>Term</i> object is configured according to the
96
+ # ('DSL') block passed in.
97
+ #
98
+ # my_protptype.output {
99
+ # label :co2
100
+ # path 'CO2'
101
+ # }
102
+ #
103
+ def output(options={},&block)
104
+ construct(Output,options,&block)
105
+ end
106
+
107
+ # Associate a new instance of the <i>Metadatum</i> class (subclass of the
108
+ # <i>Term</i> class) with <tt>self</tt>, for representing arbitrary metadata which
109
+ # is to be associated with each calculation. These may represent unique
110
+ # references to location, equipment (vehicles, furnaces), reporting periods,
111
+ # for example.
112
+ #
113
+ # The newly instantiated <i>Term</i> object is configured according to the
114
+ # ('DSL') block passed in.
115
+ #
116
+ # my_protptype.metadatum {
117
+ # label :reporting_period
118
+ # value "July 2010"
119
+ # }
120
+ #
121
+ def metadatum(options={},&block)
122
+ construct(Metadatum,options,&block)
123
+ end
124
+
125
+ # Helper method for automatically instantiating <i>Drill</i> class term
126
+ # objects representing all drill down choices based on those associated with
127
+ # the AMEE platform category with which <tt>self</tt> corresponds.
128
+ #
129
+ def all_drills
130
+ # Need to use #drill_downs rather than simply finding drills
131
+ # directly from #amee_ivds in order to establish drill order
132
+ amee_item_definition.drill_downs.each do |apath|
133
+ amee_ivds.each do |ivd|
134
+ next unless ivd.path == apath
135
+ drill {
136
+ path ivd.path
137
+ name ivd.name
138
+ }
139
+ end
140
+ end
141
+ end
142
+
143
+ # Helper method for automatically instantiating <i>Profile</i> class term
144
+ # objects representing all profile item values based on those associated with
145
+ # the AMEE platform category with which <tt>self</tt> corresponds.
146
+ #
147
+ # Each term is instantiated with path, name, choices, default_unit and
148
+ # default_per_unit attributes corresponding to those defined in the AMEE
149
+ # platform.
150
+ #
151
+ def all_profiles
152
+ amee_ivds.each do |ivd|
153
+ next unless ivd.profile?
154
+ construct_from_ivd(Profile,ivd)
155
+ end
156
+ end
157
+
158
+ # Helper method for automatically instantiating <i>Output</i> class term
159
+ # objects representing all return values based on those associated with
160
+ # the AMEE platform category with which <tt>self</tt> corresponds.
161
+ #
162
+ # Each term is instantiated with path, default_unit and default_per_unit
163
+ # attributes corresponding to those defined in the AMEE platform.
164
+ #
165
+ def all_outputs
166
+ amee_return_values.each do |rvd|
167
+ output {
168
+ path rvd.name
169
+ default_unit rvd.unit
170
+ default_per_unit rvd.perunit
171
+ }
172
+ end
173
+ end
174
+
175
+ # Helper method for automatically instantiating <i>Profile</i> class term
176
+ # objects representing only the profile item values associated with a
177
+ # particular usage (specified by <tt>usage</tt>) for the AMEE platform
178
+ # category with which <tt>self</tt> corresponds.
179
+ #
180
+ # This method does not permit dynamic usage switching during run-time.
181
+ #
182
+ # Each term is instantiated with path, name, choices, default_unit and
183
+ # default_per_unit attributes corresponding to those defined in the AMEE
184
+ # platform.
185
+ #
186
+ def profiles_from_usage(usage)
187
+ self.fixed_usage usage
188
+ amee_ivds.each do |ivd|
189
+ next unless ivd.profile?
190
+ construct_from_ivd(Profile,ivd) if ivd.compulsory?(usage) || ivd.optional?(usage)
191
+ end
192
+ end
193
+
194
+ # Helper method for automatically instantiating <i>Profile</i>, <i>Drill</i>
195
+ # and <i>Output</i> class term objects representing all profile item values,
196
+ # drill choices and return values associated with the AMEE platform category
197
+ # with which <tt>self</tt> corresponds.
198
+ #
199
+ # Optionally, instantiate only those profile terms corresponding to a
200
+ # particular usage by passing the path of the required usage as an argument.
201
+ # The latter case does not allow dynamic usage switching at run-time.
202
+ #
203
+ # Each term is instantiated with path, name, choices, default_unit and
204
+ # default_per_unit attributes corresponding to those defined in the AMEE
205
+ # platform.
206
+ #
207
+ def terms_from_amee(usage=nil)
208
+ all_drills
209
+ if usage
210
+ profiles_from_usage(usage)
211
+ else
212
+ all_profiles
213
+ end
214
+ all_outputs
215
+ end
216
+
217
+ # Helper method for automatically instantiating <i>Profile</i>, <i>Drill</i>
218
+ # and <i>Output</i> class term objects representing all profile item values,
219
+ # drill choices and return values associated with the AMEE platform category
220
+ # with which <tt>self</tt> corresponds.
221
+ #
222
+ # Also automatically defines a usage term for the usage represented by
223
+ # <tt>ausage</tt> to enable dynamic usage switching. The profile terms
224
+ # associated with the specified usage are automatically activated and
225
+ # deactivated as appropriate, but this can be switched at run-time by
226
+ # changing the value of the instantiated usage term.
227
+ #
228
+ # Each term is instantiated with path, name, choices, default_unit and
229
+ # default_per_unit attributes (where appropriate) corresponding to those
230
+ # defined in the AMEE platform.
231
+ #
232
+ def terms_from_amee_dynamic_usage(ausage)
233
+ all_drills
234
+ usage{ value ausage}
235
+ all_outputs
236
+ end
237
+
238
+ # Helper method for automatically instantiating <i>Profile</i> class term
239
+ # objects representing all profile item values associated with the AMEE
240
+ # platform category represented by <tt>self</tt>, and instantiating a new instance
241
+ # of the <i>Usage</i> term class which can be used for dynamically switching
242
+ # usages at run-time.
243
+ #
244
+ # Each term is instantiated with path, name, choices, default_unit and
245
+ # default_per_unit attributes corresponding to those defined in the AMEE
246
+ # platform.
247
+ #
248
+ # The newly instantiated <i>Usage</i> object can be configured in place
249
+ # according to the ('DSL') block passed in, e.g.,
250
+ #
251
+ # my_protptype.usage {
252
+ # inactive :disabled
253
+ # value nil
254
+ # }
255
+ #
256
+ def usage(options={},&block)
257
+ all_profiles
258
+ construct(Usage,options.merge(:first=>true),&block)
259
+ end
260
+
261
+ # Helper method for automatically instantiating <i>Metadatum</i> class term
262
+ # objects explicitly configured for storing start and end dates for an AMEE
263
+ # platform profile item.
264
+ #
265
+ def start_and_end_dates
266
+ metadatum {
267
+ path 'start_date'
268
+ name 'Start date'
269
+ interface :date
270
+ type :datetime
271
+ validation lambda{|v| v.is_a?(Time) || v.is_a?(DateTime) || (v.is_a?(String) && Date.parse(v) rescue false)}
272
+ }
273
+ metadatum {
274
+ path 'end_date'
275
+ name 'End date'
276
+ interface :date
277
+ type :datetime
278
+ validation lambda{|v| v.is_a?(Time) || v.is_a?(DateTime) || (v.is_a?(String) && Date.parse(v) rescue false)}
279
+ }
280
+ end
281
+
282
+ # Helper method for reopening and modifying the definition of the term with
283
+ # the label attribute matching <tt>label</tt>. Modification is specified in
284
+ # the passed block, which is evaluated in the context of the respective term
285
+ # instance.
286
+ #
287
+ # This is typically used to override (customize) the attributes and behaviour
288
+ # of term autoloaded from the AMEE platform using one of the instance helper
289
+ # methods of <tt>self</tt>.
290
+ #
291
+ def correcting(label,&block)
292
+ return unless contents[label]
293
+ contents[label].instance_eval(&block)
294
+ end
295
+
296
+ #Instantiate an OngoingCalculation based on this prototype, ready for
297
+ #communication with AMEE.
298
+ def begin_calculation
299
+ result=OngoingCalculation.new
300
+ contents.each do |k,v|
301
+ result.contents[k]=v.clone
302
+ result.contents[k].parent=result
303
+ end
304
+ result.path path
305
+ result.name name
306
+ result.label label
307
+ result.fixed_usage fixed_usage
308
+ result.save_amee saved_amee
309
+ result
310
+ end
311
+
312
+ private
313
+
314
+ def construct_from_ivd(klass,ivd)
315
+ # Initialize boolean values as ruby boolean objects
316
+ if (ivd.valuetype == "BOOLEAN") && (ivd.default == ("true"||"TRUE"))
317
+ default_value = true
318
+ elsif (ivd.valuetype == "BOOLEAN") && (ivd.default == ("false"||"FALSE"))
319
+ default_value = false
320
+ else
321
+ default_value = ivd.default
322
+ end
323
+
324
+ construct(klass) {
325
+ path ivd.path
326
+ name ivd.name
327
+ note ivd.meta.wikidoc
328
+ type ivd.valuetype.downcase
329
+ value default_value
330
+ choices ivd.choices
331
+ default_unit ivd.unit
332
+ default_per_unit ivd.perunit
333
+ }
334
+ end
335
+
336
+ # Construct a term of class klass, and evaluate a DSL block in its context.
337
+ def construct(klass,options={},&block)
338
+ new_content=klass.new(options.merge(:parent=>self),&block)
339
+ raise Exceptions::DSL.new(
340
+ "Attempt to create #{klass} without a label") unless new_content.label
341
+ if options[:first]
342
+ @contents.insert_at_start(new_content.label,new_content)
343
+ else
344
+ @contents[new_content.label]=new_content
345
+ end
346
+ end
347
+
348
+ end
349
+ end
350
+ end