amee-data-abstraction 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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