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,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
+