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,27 @@
1
+ <% form_id = "drill-form-#{rand(1e8)}" %>
2
+ <% form_tag({:action=>nil},{:id=>form_id}) do%>
3
+ <%= hidden_field_tag 'form_id', form_id %>
4
+ <table>
5
+ <%@calculation.drills.each_value do |input|%>
6
+ <tr>
7
+ <td><%=label :entry,input.label,input.name%></td>
8
+ <td>
9
+ <%= select_tag "entry[#{input.label}]",
10
+ options_for_select(input.options_for_select,input.set? ? input.value : nil),:disabled=>input.disabled?%>
11
+ </td>
12
+ </tr>
13
+ <%end%>
14
+ <%@calculation.profiles.each_value do |input|%>
15
+ <tr><td><%=label :entry,input.label,input.name%></td><td><%= text_field :entry, input.label, "size" => 20, :value => input.value_if_given %></td></tr>
16
+ <%end%>
17
+ <%@calculation.chosen_outputs.each_value do |result|%>
18
+ <tr><td><%=label :entry,result.label,result.name%></td><td><%= result.value_if_given %></td></tr>
19
+ <%end%>
20
+ </table>
21
+ <%end%>
22
+ <%= observe_form form_id, :url => {:action => 'result'}%>
23
+ <% javascript_tag do %>
24
+ $('#<%=form_id%>').submit(function() {
25
+ return false;
26
+ });
27
+ <%end%>
@@ -0,0 +1,16 @@
1
+ class CalculationController < ApplicationController
2
+ def index
3
+ @calculations=Calculations.calculations.values
4
+ end
5
+
6
+ def enter
7
+ @calculation=Calculations[params[:calculation]]
8
+ end
9
+
10
+ def result
11
+ @calculation=Calculations[params[:calculation]].clone
12
+ @calculation.choose!(params['entry'])
13
+ @calculation.calculate!
14
+ end
15
+
16
+ end
data/init.rb ADDED
@@ -0,0 +1,4 @@
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 File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,30 @@
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 'amee'
5
+ require 'uuidtools'
6
+ require 'quantify'
7
+
8
+ require File.dirname(__FILE__) + '/core-extensions/class'
9
+ require File.dirname(__FILE__) + '/core-extensions/ordered_hash'
10
+ require File.dirname(__FILE__) + '/core-extensions/hash'
11
+ require File.dirname(__FILE__) + '/core-extensions/proc'
12
+ require File.dirname(__FILE__) + '/config/amee_units'
13
+ require File.dirname(__FILE__) + '/amee-data-abstraction/exceptions'
14
+ require File.dirname(__FILE__) + '/amee-data-abstraction/terms_list'
15
+ require File.dirname(__FILE__) + '/amee-data-abstraction/calculation'
16
+ require File.dirname(__FILE__) + '/amee-data-abstraction/ongoing_calculation'
17
+ require File.dirname(__FILE__) + '/amee-data-abstraction/prototype_calculation'
18
+ require File.dirname(__FILE__) + '/amee-data-abstraction/calculation_set'
19
+ require File.dirname(__FILE__) + '/amee-data-abstraction/term'
20
+ require File.dirname(__FILE__) + '/amee-data-abstraction/input'
21
+ require File.dirname(__FILE__) + '/amee-data-abstraction/drill'
22
+ require File.dirname(__FILE__) + '/amee-data-abstraction/profile'
23
+ require File.dirname(__FILE__) + '/amee-data-abstraction/usage'
24
+ require File.dirname(__FILE__) + '/amee-data-abstraction/output'
25
+ require File.dirname(__FILE__) + '/amee-data-abstraction/metadatum'
26
+
27
+ module AMEE::DataAbstraction
28
+ # Connection to AMEE server.
29
+ mattr_accessor :connection
30
+ end
@@ -0,0 +1,236 @@
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::Calculation
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # Base class providing attributes and methods for representing a calculation
10
+ # which can be made using the AMEE platform. An instance of <i>Calculation</i>
11
+ # will typically be associated with a specific AMEE category.
12
+ #
13
+ # Instances of <i>Calculation</i> are represented by several primary attributes:
14
+ #
15
+ # label:: Symbol representing the unique, machine-readable name for the
16
+ # calculation
17
+ #
18
+ # name:: String representing a human-readable name for the calculation
19
+ #
20
+ # path:: String representing the AMEE platform path to the data category
21
+ # which is associated with <tt>self</tt>
22
+ #
23
+ # fixed_usage:: String representing the AMEE platform path for the usage to
24
+ # be used by <tt>self</tt>, if defined
25
+ #
26
+ # An instance of <i>Calculation</i> also holds an arbitrary number of objects
27
+ # of the class <i>Term</i>. These represent values associated with the
28
+ # calculation, e.g. inputs, outputs, metadatum, etc. These can be accessed
29
+ # using the <tt>#terms</tt> methods or the term subset methods provided by
30
+ # the <tt>TermsList</tt> class (e.g. <tt>#profiles</tt>, <tt>#visible</tt>)
31
+ #
32
+ # Two classes inherit the <i>Calculation</i> class:
33
+ # * <i>PrototypeCalculation</i> : provides a templating for a specific calculation
34
+ # type with defined, but blank, terms (i.e. inputs, outputs, etc.)
35
+ #
36
+ # * <i>OngoingCalculation</i> : represents a particular calculation - possibly
37
+ # incomplete - which can be updated, submitted for calculation, and saved
38
+ #
39
+ class Calculation
40
+
41
+ public
42
+
43
+ # Symbol representing the unique, machine-readable name for <tt>self</tt>.
44
+ # Set a value by passing an argument. Retrieve a value by calling without
45
+ # an argument, e.g.,
46
+ #
47
+ # my_calculation.label :fuel
48
+ #
49
+ # my_calculation.label #=> :fuel
50
+ #
51
+ attr_property :label
52
+
53
+ # String representing a human-readable name for <tt>self</tt>. Set a
54
+ # value by passing an argument. Retrieve a value by calling without an
55
+ # argument, e.g.,
56
+ #
57
+ # my_calculation.name 'Domestic fuel consumption'
58
+ #
59
+ # my_calculation.name #=> 'Domestic fuel consumption'
60
+ #
61
+ attr_property :name
62
+
63
+ # String representing the AMEE platform path to the data category which is
64
+ # associated with <tt>self</tt>. Set a value by passing an argument. Retrieve
65
+ # a value by calling without an argument, e.g.,
66
+ #
67
+ # my_calculation.path '/some/path/in/amee/'
68
+ #
69
+ # my_calculation.path #=> '/some/path/in/amee/'
70
+ #
71
+ attr_property :path
72
+
73
+ # String representing the AMEE platform path for the usage to be used by
74
+ # <tt>self</tt>, if defined. Set a value by passing an argument. Retrieve
75
+ # a value by calling without an argument, e.g.,
76
+ #
77
+ # my_calculation.fixed_usage 'byMass'
78
+ #
79
+ # my_calculation.fixed_usage #=> 'byMass'
80
+ #
81
+ attr_property :fixed_usage
82
+
83
+ # Calculations contain a list of "terms" of the base class <i>Term</i>,
84
+ # representing inputs, outputs, metadatum, etc. which are associated with
85
+ # <tt>self</tt>.
86
+ #
87
+ # Returns all associated terms as an instance of the <i>TermsList</i> class
88
+ #
89
+ def terms
90
+ TermsList.new(@contents.values)
91
+ end
92
+
93
+ # Retrieve the terms associated with <tt>self</tt> as a hash from labels to terms.
94
+ attr_accessor :contents
95
+
96
+ # Shorthand method for retrieving the term assocaited with <tt>self</tt> which has a
97
+ # label matching <tt>sym</tt>
98
+ #
99
+ def [](sym)
100
+ @contents[sym.to_sym]
101
+ end
102
+
103
+ # Syntactic sugar to enable the return of a subset of associated terms according
104
+ # to their type or status (e.g. drills, profiles, set, unset, visible). See
105
+ # <i>TermsList::Selectors</i> for valid variants
106
+ #
107
+ TermsList::Selectors.each do |sel|
108
+ delegate sel,:to=>:terms
109
+ end
110
+
111
+ # Prettyprint a string representation of <tt>self</tt>, together with associated terms
112
+ def inspect
113
+ elements = {:label => label.inspect, :terms => terms.map{|t| "<#{t.class.name.demodulize} label:#{t.label}, value:#{t.value.inspect}>"}}
114
+ attr_list = elements.map {|k,v| "#{k}: #{v}" } * ', '
115
+ "<#{self.class.name} #{attr_list}>"
116
+ end
117
+
118
+ def initialize_copy(source)
119
+ super
120
+ @contents=ActiveSupport::OrderedHash.new
121
+ source.contents.each do |k,v|
122
+ @contents[k]=v.clone
123
+ @contents[k].parent=self
124
+ end
125
+ end
126
+
127
+ # Return a string representing the AMEE Explorer URL which is assocaited
128
+ # with <tt>self</tt>
129
+ #
130
+ def explorer_url
131
+ "http://explorer.amee.com/categories#{path}"
132
+ end
133
+
134
+ protected
135
+
136
+ def initialize
137
+ @contents=ActiveSupport::OrderedHash.new
138
+ end
139
+
140
+ # Methods which will be memoized at application start, as they do not
141
+ # change over application instance lifetime
142
+ #
143
+ AmeeMemoised=[:amee_data_category, :amee_item_definition, :amee_ivds,
144
+ :amee_return_values, :amee_usages]
145
+
146
+ # Return all the values of the memoized quantities
147
+ def saved_amee
148
+ AmeeMemoised.map{|x|instance_variable_get("@#{x.to_s}")}
149
+ end
150
+
151
+ # Save the memoized quantities
152
+ def save_amee(values)
153
+ AmeeMemoised.zip(values).each do |prop,val|
154
+ instance_variable_set("@#{prop.to_s}",val)
155
+ end
156
+ end
157
+
158
+ private
159
+
160
+ # Return the global <i>AMEE::Connection</i> object. This is configured in
161
+ # /config/amee.yml
162
+ #
163
+ def connection
164
+ AMEE::DataAbstraction.connection
165
+ end
166
+
167
+ # Return the <i>AMEE::Data::Category</i> object associated with <tt>self</tt>
168
+ def amee_data_category
169
+ @amee_data_category||=AMEE::Data::Category.get(connection, "/data#{path}")
170
+ end
171
+
172
+ # Return the <i>AMEE::Admin::ItemDefinition</i> object associated with <tt>self</tt>
173
+ def amee_item_definition
174
+ @amee_item_definition||=amee_data_category.item_definition
175
+ end
176
+
177
+ # Return the <i>AMEE::Admin::ReturnValueDefinitionList</i> object associated
178
+ # with <tt>self</tt>. This represents each of the return value definitions which are
179
+ # associated with the calculation
180
+ #
181
+ def amee_return_values
182
+ @amee_return_values||=AMEE::Admin::ReturnValueDefinitionList.new(connection,amee_item_definition.uid)
183
+ end
184
+
185
+ # Return the instance of <i>Term</i> class associated with <tt>self</tt> and contains
186
+ # a path attribute matching <tt>path</tt>, e.g.
187
+ #
188
+ # my_calculation.by_path('distance') #=> <AMEE::DataAbstraction::Profile ... >
189
+ #
190
+ # my_calculation.by_path('type') #=> <AMEE::DataAbstraction::Drill ... >
191
+ #
192
+ def by_path(path)
193
+ terms.detect { |v| v.path==path }
194
+ end
195
+
196
+ # Return the instance of <i>Drill</i> class associated with <tt>self</tt> and contains
197
+ # a path attribute matching <tt>path</tt>, e.g.
198
+ #
199
+ # my_calculation.by_path('type') #=> <AMEE::DataAbstraction::Drill ... >
200
+ #
201
+ def drill_by_path(path)
202
+ drills.detect { |v| v.path==path }
203
+ end
204
+
205
+ public
206
+
207
+ # Return the <i>AMEE::Admin::ItemValueDefinitionList</i> object associated
208
+ # with <tt>self</tt>. This represents each of the item value definitions which are
209
+ # associated with the calculation
210
+ #
211
+ def amee_ivds
212
+ @amee_ivds||=amee_item_definition.item_value_definition_list.select{|x|x.versions.include?("2.0")}
213
+ end
214
+
215
+ # Returns a String representing the AMEE platform path for the usage currently
216
+ # used by <tt>self</tt>. If not usage is defined, returns nil
217
+ #
218
+ # my_calculation.current_usage #=> 'byMass'
219
+ #
220
+ def current_usage
221
+ usages.empty? ? fixed_usage : usages.first.value
222
+ end
223
+
224
+ # Returns an Array containing the AMEE platform paths for all valid usage
225
+ # available to <tt>self</tt> according to those defined under #item_definition. If
226
+ # no usage(s) is defined, returns nil, e.g.
227
+ #
228
+ # my_calculation.amee_usages #=> [ 'byMass', 'byEnergy' ]
229
+ #
230
+ def amee_usages
231
+ @amee_usages||=amee_item_definition.usages
232
+ end
233
+
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,101 @@
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::CalculationSet
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # The <i>CalculationSet</i> class represents a collection of prototype
10
+ # calculations (of the class <i>ProtptypeCalculation</i>.
11
+ #
12
+ # Prototype calculations are contained within the @calculations instance variable
13
+ # ordered hash. Calculations can be added manually to the @calculations hash or
14
+ # initialized in place using the <tt>#calculation</tt> method which takes an
15
+ # options hash or block for specifying the prototype calculation properties.
16
+ #
17
+ # Typical usage is to initialize the <i>CalculationSet</i> and its daughter
18
+ # prototype calculations together using block syntax, thus:
19
+ #
20
+ # Calculations = CalculationSet.new {
21
+ #
22
+ # calculation {
23
+ # label :electricity
24
+ # path "/some/path/for/electricity"
25
+ # ...
26
+ # }
27
+ #
28
+ # calculation {
29
+ # label :transport
30
+ # path "a/transport/path"
31
+ # ...
32
+ # }
33
+ #
34
+ # ...
35
+ # }
36
+ #
37
+ class CalculationSet
38
+
39
+ # Initialize a <i>CalculationSet</i> with a "DSL" block, i.e. a block to be
40
+ # evaluated in the context of the instantiated CalculationSet
41
+ #
42
+ def initialize(options={},&block)
43
+ @calculations=ActiveSupport::OrderedHash.new
44
+ @all_blocks=[]
45
+ @all_options={}
46
+ instance_eval(&block) if block
47
+ end
48
+
49
+ # Access the @calculations instance variable ordered hash. Keys are labels
50
+ # assocaited with each prototype calculation; values are the instantiated
51
+ # <i>PrototypeCalculation</i> objects
52
+ #
53
+ attr_accessor :calculations
54
+
55
+ # Shorthand method for returning the prototype calculation which is represented
56
+ # by a label matching <tt>sym</tt>
57
+ #
58
+ def [](sym)
59
+ @calculations[sym.to_sym]
60
+ end
61
+
62
+ # Instantiate a <i>PrototypeCalculation</i> within this calculation set,
63
+ # initializing with the supplied DSL block to be evaluated in the context
64
+ # of the newly created calculation
65
+ #
66
+ def calculation(options={},&block)
67
+ new_content=PrototypeCalculation.new(options.merge(@all_options),&block)
68
+ @all_blocks.each {|all_block| new_content.instance_eval(&all_block) }
69
+ new_content.name new_content.label.to_s.humanize unless new_content.name
70
+ @calculations[new_content.label]=new_content
71
+ end
72
+
73
+ # Append the supplied block to the DSL block of ALL calculations in this
74
+ # calculation set. This is useful for configuration which is required
75
+ # across all calculations (e.g. overriding human readable names or adding
76
+ # globally applicable metadatum)
77
+ #
78
+ def all_calculations(options={},&dsl_block)
79
+ @all_blocks.push dsl_block
80
+ @all_options.merge(options)
81
+ end
82
+
83
+ # Instantiate several prototype calculations, by loading each possible usage
84
+ # for the category with path given in <tt>apath</tt>.
85
+ #
86
+ # Each instantiated calculation is customised on the basis of the supplied
87
+ # DSL block. The usage is given as a parameter to the DSL block
88
+ #
89
+ def calculations_all_usages(apath,options={},&dsl_block)
90
+ dummycalc=PrototypeCalculation.new{path apath}
91
+ dummycalc.amee_usages.each do |usage|
92
+ calculation(options){
93
+ path apath
94
+ instance_exec(usage,&dsl_block)
95
+ }
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,63 @@
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::Drill
5
+
6
+ module AMEE
7
+ module DataAbstraction
8
+
9
+ # Subclass of <tt>Input</tt> providing methods and attributes appropriate for
10
+ # representing AMEE drill down choices and selections specifically
11
+ #
12
+ class Drill < Input
13
+
14
+ public
15
+
16
+ # Returns <tt>true</tt> if the UI element of <tt>self</tt> is disabled.
17
+ # Otherwise, returns <tt>false</tt>.
18
+ #
19
+ # A drill is considered disabled if it either (1) explicitly set using the
20
+ # <tt>#disable!</tt> method; (2) has a <i>fixed</i> value; or (3) is not the
21
+ # next drill (because drill should be chosen in order).
22
+ #
23
+ def disabled?
24
+ super || (!set? && !next?)
25
+ end
26
+
27
+ # Initialization of <i>Drill</i> objects follows that of the parent
28
+ # <i>Input</i> class. The <tt>interface</tt> attribute of <tt>self</tt> is
29
+ # set to <tt>:drop_down</tt> by default for <tt>Drill</tt> instances, but
30
+ # can be manually configured if required.
31
+ #
32
+ def initialize(options={},&block)
33
+ interface :drop_down
34
+ super
35
+ choice_validation_message
36
+ end
37
+
38
+ private
39
+
40
+ # Returns <tt>true</tt> if the value set for <tt>self</tt> is one of the
41
+ # available choices. Otherwise, returns <tt>false</tt>.
42
+ #
43
+ def valid?
44
+ super && (choices.include? value)
45
+ end
46
+
47
+ # Returns the list of available choices for <tt>self</tt>.
48
+ def choices
49
+ c=parent.amee_drill(:before=>label).choices
50
+ c.length==1 ? [value] : c #Intention is to get autodrilled, drill will result in a UID
51
+ end
52
+
53
+ # Returns <tt>true</tt> if <tt>self</tt> is the "first unset" drill, i.e.
54
+ # the one which should be set next
55
+ #
56
+ def next?
57
+ unset? && parent.drills.before(label).all?(&:set?)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+