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,150 @@
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::TermsList
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # Class extending the <i>Array</i> and providing specific attributes and
10
+ # methods for operating on a collection of instances of the class <i>Term</i>.
11
+ #
12
+ class TermsList < Array
13
+
14
+ # Subclasses of the <i>Term</i> class which <tt>self</tt> can contain.
15
+ #
16
+ # Each subclass symbol also represents a dynamically generated method name
17
+ # for <tt>self</tt> which can be called to return a new <tt>TermsList</tt>
18
+ # instance containing that subset of terms only, e.g.,
19
+ #
20
+ # my_terms_list.inputs #=> <AMEE::DataAbstraction::TermsList ... >
21
+ #
22
+ # my_terms_list.profiles #=> <AMEE::DataAbstraction::TermsList ... >
23
+ #
24
+ # These methods can be compounded:
25
+ #
26
+ # my_terms_list.inputs.drills #=> <AMEE::DataAbstraction::TermsList ... >
27
+ #
28
+ # my_terms_list.profiles.visible #=> <AMEE::DataAbstraction::TermsList ... >
29
+ #
30
+ TermClasses= [:profiles,:drills,:inputs,:outputs,:metadata,:usages]
31
+
32
+ TermClasses.each do |term|
33
+ define_method(term) do
34
+ self.class.new select{|x|x.is_a? AMEE::DataAbstraction::const_get(term.to_s.singularize.classify)}
35
+ end
36
+ end
37
+
38
+ # Boolean attributes of instances of the <i>Term</i> class.
39
+ #
40
+ # Each attribute symbol also represents a dynamically generated method name
41
+ # for <tt>self</tt> which can be called to return a new <tt>TermsList</tt>
42
+ # instance containing that subset of only those terms for which the attribute
43
+ # is true, e.g.,
44
+ #
45
+ # my_terms_list.visible #=> <AMEE::DataAbstraction::TermsList ... >
46
+ #
47
+ # my_terms_list.set #=> <AMEE::DataAbstraction::TermsList ... >
48
+ #
49
+ # These methods can be compounded:
50
+ #
51
+ # my_terms_list.drills.visible.set #=> <AMEE::DataAbstraction::TermsList ... >
52
+ #
53
+ TermFlags=[:set,:unset,:visible,:hidden,:fixed,
54
+ :optional,:compulsory,:enabled,:disabled,:drop_down,:text_box,:date]
55
+
56
+ TermFlags.each do |term|
57
+ define_method(term) do
58
+ self.class.new select{|x|x.send("#{term}?".to_sym)}
59
+ end
60
+ end
61
+
62
+ # Return a new <tt>TermsList</tt> instance containing that subset of terms
63
+ # which occur before the term labeled <tt>label</tt> in the owning
64
+ # calculation
65
+ #
66
+ def before(label)
67
+ self.class.new select{|x|x.before?(label)}
68
+ end
69
+
70
+ # Return a new <tt>TermsList</tt> instance containing that subset of terms
71
+ # which occur after the term labeled <tt>label</tt> in the owning
72
+ # calculation
73
+ #
74
+ def after(label)
75
+ self.class.new select{|x|x.after?(label)}
76
+ end
77
+
78
+ # Return a new <tt>TermsList</tt> instance containing that subset of terms
79
+ # which are optional in the owning calculation.
80
+ #
81
+ # If no argument is provided, the optional status of each term is defined
82
+ # according to the current usage of the parent caluclation. Otherwise,
83
+ # optional status is determined on the basis of the usage whose AMEE
84
+ # platform path matches <tt>usage</tt>
85
+ #
86
+ def optional(usage=nil)
87
+ self.class.new select{|x|x.optional?(usage)}
88
+ end
89
+
90
+ # Return a new <tt>TermsList</tt> instance containing that subset of terms
91
+ # which are compulsory in the owning calculation.
92
+ #
93
+ # If no argument is provided, the compulsory status of each term is defined
94
+ # according to the current usage of the parent caluclation. Otherwise,
95
+ # compulsory status is determined on the basis of the usage whose AMEE
96
+ # platform path matches <tt>usage</tt>
97
+ #
98
+ def compulsory(usage=nil)
99
+ self.class.new select{|x|x.compulsory?(usage)}
100
+ end
101
+
102
+ # Return a new <tt>TermsList</tt> instance containing that subset of terms
103
+ # which are either compulsory OR optional in the owning calculation, i.e.
104
+ # any which are NOT forbidden.
105
+ #
106
+ # If no argument is provided, the optional/compulsory status of each term
107
+ # is defined according to the current usage of the parent caluclation.
108
+ # Otherwise, optional/compulsory status is determined on the basis of the
109
+ # usage whose AMEE platform path matches <tt>usage</tt>
110
+ #
111
+ def in_use(usage=nil)
112
+ self.class.new select{|x|x.in_use?(usage)}
113
+ end
114
+
115
+ # Return a new <tt>TermsList</tt> instance containing that subset of terms
116
+ # which are neither compulsory OR optional in the owning calculation, i.e.
117
+ # those which are forbidden.
118
+ #
119
+ # If no argument is provided, the forbidden status of each term is defined
120
+ # according to the current usage of the parent caluclation. Otherwise,
121
+ # forbidden status is determined on the basis of the usage whose AMEE
122
+ # platform path matches <tt>usage</tt>
123
+ #
124
+ def out_of_use(usage=nil)
125
+ self.class.new select{|x|x.out_of_use?(usage)}
126
+ end
127
+
128
+ Selectors=TermClasses+TermFlags+[:before,:after,:optional,
129
+ :compulsory,:in_use,:out_of_use]
130
+
131
+ # Attributes of the class <i>Term</tt>.
132
+ #
133
+ # Each attribute symbol also defines a dynamically generated method which
134
+ # return arrays of the values of the named attribute for all terms, e.g.,
135
+ #
136
+ # my_terms_list.labels => [ :type, :fuel, :distance, :co2 ... ]
137
+ #
138
+ # my_terms_list.values => [ 'van;, 'petrol', 500, 25.4 ... ]
139
+ #
140
+ TermProperties=[:label,:name,:path,:value,:unit,:per_unit,:default_unit,:default_per_unit]
141
+
142
+ TermProperties.each do |term|
143
+ define_method(term.to_s.pluralize.to_sym) do
144
+ map{|x|x.send(term)}
145
+ end
146
+ end
147
+
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,90 @@
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::Usage
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # Subclass of <tt>Input</tt> providing methods and attributes appropriate for
10
+ # representing adjustable calculation usages specifically.
11
+ #
12
+ # Only one instance of <i>Usage</i> can be assocaited with a particular
13
+ # calucaltion object. When the value of <tt>self</tt> is changed, profile
14
+ # item value terms which are forbidden in the new usage will be inactivated
15
+ # and optional/compulsory flags are set on the remaining terms.
16
+ #
17
+ class Usage < Input
18
+
19
+ # Initialization of <i>Usage</i> objects follows that of the parent
20
+ # <i>Input</i> class, with a number of differences.
21
+ #
22
+ # If the parent caluclation already contains a usage term, a <i>TwoUsages</i>
23
+ # exception is raised.
24
+ #
25
+ # The <tt>label<tt> attribute is set by default to <tt>:usage</tt>.
26
+ #
27
+ # The <tt>interface</tt> attribute of <tt>self</tt> is set to
28
+ # <tt>:drop_down</tt> by default, but can be manually configured if
29
+ # required.
30
+ #
31
+ # The <tt>inactive</tt> property of <tt>self</tt> is set to <tt>:invisible</tt>
32
+ # by default.
33
+ #
34
+ def initialize(options={},&block)
35
+ raise Exceptions::TwoUsages if options[:parent].current_usage
36
+ label :usage
37
+ @inactive=:invisible
38
+ super
39
+ interface :drop_down unless interface
40
+ end
41
+
42
+ # Represents the method of handling forbidden terms. Should they be hidden
43
+ # in generated UIs or just disabled (greyed out)? Set the behaviour by
44
+ # passing either <tt>:invisible</tt> or <tt>:disabled</tt> as an argument.
45
+ # Retrieve the defined behaviour by calling without an argument.
46
+ #
47
+ attr_property :inactive
48
+
49
+ # Adjust the value of <tt>self</tt> indicating that a new usage should be
50
+ # switch to in the parent caluclation. This method has the effect of
51
+ # (de)activating terms in the parent calculation as appropriate.
52
+ #
53
+ def value(*args)
54
+ unless args.empty?
55
+ @value=args.first
56
+ activate_selected(value)
57
+ end
58
+ super
59
+ end
60
+
61
+ # Activate and deactivate terms in the parent calculation according to the
62
+ # compulsory/optional/forbidden status' of each in the usage indicated by
63
+ # <tt>usage</tt>
64
+ #
65
+ def activate_selected(usage=nil)
66
+ parent.profiles.in_use(usage).each do |term|
67
+ case @inactive
68
+ when :invisible
69
+ term.show!
70
+ when :disabled
71
+ term.enable!
72
+ end
73
+ end
74
+ parent.profiles.out_of_use(usage).each do |term|
75
+ case @inactive
76
+ when :invisible
77
+ term.hide!
78
+ when :disabled
79
+ term.disable!
80
+ end
81
+ end
82
+ end
83
+
84
+ # Returns an array of available valid values for <tt>self</tt>.
85
+ def choices
86
+ parent.amee_usages
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,129 @@
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 'quantify'
5
+
6
+ Quantify::Unit.configure do
7
+
8
+ # Remove from the system of known units, the units which are not used or not
9
+ # anticipated to be useful for interacting with AMEE. These either represent
10
+ # physical quantities which are not typically represented in emissions
11
+ # calculations (e.g. redioactivity, thermal resistance) or units which do
12
+ # represent appropriate physical quantities but are relatively obscure or
13
+ # inappropriate (e.g angstrom, calorie, cup, light year).
14
+ #
15
+ # Note: although AMEE supports 8 types of British thermal unit, only one is
16
+ # made available since it is unlikely that users would like to choose between
17
+ # each of the alternatives.
18
+
19
+ required_units = [ "1",
20
+ "Hz",
21
+ "Ohm",
22
+ "V",
23
+ "m/s",
24
+ "m/s²",
25
+ "A",
26
+ "K",
27
+ "m",
28
+ "mol",
29
+ "s",
30
+ "kg",
31
+ "g",
32
+ "km",
33
+ "m³",
34
+ "J",
35
+ "N",
36
+ "W",
37
+ "Pa",
38
+ "m²",
39
+ "atm",
40
+ "bar",
41
+ "ha",
42
+ "L",
43
+ "bbl",
44
+ "oz_fl_uk",
45
+ "gal_uk",
46
+ "gallon_dry_us",
47
+ "oz_fl",
48
+ "bbl_fl_us",
49
+ "gal",
50
+ "kWh",
51
+ "°C",
52
+ "°F",
53
+ "°R",
54
+ "ft",
55
+ "h",
56
+ "in",
57
+ "mi",
58
+ "min",
59
+ "month",
60
+ "nmi",
61
+ "oz",
62
+ "lb",
63
+ "t",
64
+ "week",
65
+ "yd",
66
+ "year",
67
+ "BTU_FiftyNineF",
68
+ "ton_us",
69
+ "ton_uk",
70
+ "d" ]
71
+
72
+ uneeded_units = []
73
+
74
+ units.each do |unit|
75
+ uneeded_units << unit.label unless required_units.include?(unit.label)
76
+ end
77
+
78
+ unload(uneeded_units)
79
+
80
+ end
81
+
82
+ Quantify::Unit::SI.configure do
83
+
84
+ # Load the commonly used SI unit/prefix combinations which are used in AMEE
85
+ prefix_and_load [:mega,:giga], :gram
86
+ prefix_and_load [:mega,:giga,:tera], :joule
87
+ prefix_and_load [:kilo,:mega], :watt
88
+
89
+ end
90
+
91
+ Quantify::Unit::Prefix::NonSI.configure do
92
+
93
+ # define the nonconventional 'M' (million) prefix which is used with British
94
+ # thermal units. This is similar, but distinct to the SI mega- prefix
95
+ load :name => 'million ', :symbol => 'M', :factor => 1e6
96
+
97
+ end
98
+
99
+ Quantify::Unit::NonSI.configure do
100
+
101
+ # Give the remaining (default) British thermal unit version a humanised name
102
+ # and symbol
103
+ Unit.BTU_FiftyNineF.configure_as_canonical do |unit|
104
+ unit.name = 'british thermal unit'
105
+ unit.symbol = 'BTU'
106
+ end
107
+
108
+ # Differentiate long and short ton symbols
109
+ Unit.ton_us.configure_as_canonical do |unit|
110
+ unit.symbol = 'ton (US)'
111
+ end
112
+
113
+ Unit.ton_uk.configure_as_canonical do |unit|
114
+ unit.symbol = 'ton (UK)'
115
+ end
116
+
117
+ # AMEE uses 'day' rather than 'd' as the label for the time period, day
118
+ Unit.day.canonical_label = 'day'
119
+
120
+ # Load prefixed version of British thermal unit, which is used in AMEE
121
+ prefix_and_load :M, :BTU_FiftyNineF
122
+
123
+ # Define and load the megawatt hour, an energy unit used in AMEE
124
+ construct_and_load(megawatt*hour) do |unit|
125
+ unit.symbol = 'MWh'
126
+ unit.label = 'MWh'
127
+ end
128
+
129
+ end
@@ -0,0 +1,27 @@
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: Class
5
+
6
+ class Class
7
+
8
+ # Syntactic sugar for providing instance level attributes. Similar to
9
+ # <tt>attr_accessor</tt> except the value of property is set without requiring
10
+ # "=" syntax, e.g.
11
+ #
12
+ # foo.propname 5 (rather than <tt>foo.attrname=5</tt>)
13
+ #
14
+ # foo.propname #=> 5
15
+ #
16
+ # In other words, setting is performed by specifing an argument, getting is
17
+ # performed using the same method call without an argument.
18
+ #
19
+ def attr_property(*accessors)
20
+ accessors.each do |m|
21
+ define_method(m) do |*val|
22
+ instance_variable_set("@#{m}",val.first) unless val.empty? #Array Hack to avoid warning
23
+ instance_variable_get("@#{m}")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,43 @@
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: Hash
5
+
6
+ class Hash
7
+
8
+ # Return a new instance of <i>Hash</i> which represents the same data as
9
+ # <tt>self</tt> but with all keys - including those of sub-hashes - symbolized
10
+ #
11
+ def recursive_symbolize_keys
12
+ new_hash = {}
13
+ self.each_pair do |k,v|
14
+ new_hash[k.to_sym] = value_or_symbolize_value(v)
15
+ end
16
+ new_hash
17
+ end
18
+
19
+ # Modify <tt>self</tt> in place, transforming all keys - including those of
20
+ # sub-hashes - in to symbols
21
+ #
22
+ def recursive_symbolize_keys!
23
+ self.each_pair do |k,v|
24
+ value = delete(k)
25
+ self[k.to_sym] = value_or_symbolize_value(value)
26
+ end
27
+ self
28
+ end
29
+
30
+ private
31
+
32
+ # Symbolize any hash key or sub-hash key containing within <tt>value</tt>.
33
+ def value_or_symbolize_value(value)
34
+ if value.is_a? Hash
35
+ return value.recursive_symbolize_keys
36
+ elsif value.is_a? Array
37
+ return value.map { |elem| value_or_symbolize_value(elem) }
38
+ else
39
+ return value
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,21 @@
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: OrderedHash
5
+
6
+ module ActiveSupport
7
+ class OrderedHash
8
+
9
+ # Version of enumerable#select for an OrderedHash which is order-preserving
10
+ # Output is an array of key-value pairs.
11
+ def stable_select(&block)
12
+ #Annoyingly, default ordered hash select is not stable
13
+ self.map{|k,v| block.call(k,v) ? [k,v] : nil}.compact
14
+ end
15
+
16
+ # Insert a given element at the beginning, not end, of an ordered hash.
17
+ def insert_at_start(key,value)
18
+ replace(OrderedHash[self.to_a.insert(0,[key,value])])
19
+ end
20
+ end
21
+ end