amee-data-abstraction 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +3 -0
- data/README.txt +26 -14
- data/VERSION +1 -1
- data/amee-data-abstraction.gemspec +7 -6
- data/lib/amee-data-abstraction/calculation.rb +1 -1
- data/lib/amee-data-abstraction/calculation_set.rb +152 -10
- data/lib/amee-data-abstraction/input.rb +10 -0
- data/lib/amee-data-abstraction/ongoing_calculation.rb +1 -1
- data/lib/amee-data-abstraction/term.rb +31 -13
- data/spec/amee-data-abstraction/calculation_set_spec.rb +247 -9
- data/spec/amee-data-abstraction/calculation_spec.rb +24 -19
- data/spec/amee-data-abstraction/drill_spec.rb +14 -9
- data/spec/amee-data-abstraction/input_spec.rb +113 -73
- data/spec/amee-data-abstraction/metadatum_spec.rb +1 -1
- data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +38 -30
- data/spec/amee-data-abstraction/profile_spec.rb +4 -2
- data/spec/amee-data-abstraction/prototype_calculation_spec.rb +13 -8
- data/spec/amee-data-abstraction/term_spec.rb +45 -4
- data/spec/amee-data-abstraction/terms_list_spec.rb +23 -12
- data/spec/config/amee_units_spec.rb +1 -2
- data/spec/core-extensions/class_spec.rb +18 -18
- data/spec/core-extensions/hash_spec.rb +1 -2
- data/spec/core-extensions/ordered_hash_spec.rb +1 -2
- data/spec/core-extensions/proc_spec.rb +1 -1
- data/spec/fixtures/config/calculations/electricity.rb +35 -0
- data/spec/fixtures/config/calculations/electricity_and_transport.rb +53 -0
- data/spec/fixtures/{transport.rb → config/calculations/transport.rb} +2 -2
- data/spec/fixtures/{electricity.rb → config/electricity.rb} +1 -1
- data/spec/spec_helper.rb +30 -2
- metadata +28 -27
- data/spec/fixtures/electricity_and_transport.rb +0 -55
data/CHANGELOG.txt
CHANGED
data/README.txt
CHANGED
@@ -25,9 +25,9 @@ Documentation: http://rubydoc.info/gems/amee-data-abstraction
|
|
25
25
|
All gem requirements should be installed as part of the rubygems installation process
|
26
26
|
above, but are listed here for completeness.
|
27
27
|
|
28
|
-
* amee ~>
|
28
|
+
* amee ~> 4.1
|
29
29
|
* uuidtools = 2.1.2
|
30
|
-
* quantify =
|
30
|
+
* quantify = 2.0.0
|
31
31
|
|
32
32
|
== USAGE
|
33
33
|
|
@@ -117,16 +117,10 @@ Submit to AMEE for calculation
|
|
117
117
|
|
118
118
|
Typical practice is initialize the calculation prototypes required for an
|
119
119
|
application via a configuration file which creates the required calculation
|
120
|
-
templates within an instance of CalculationSet.
|
121
|
-
|
122
|
-
initializing new calculations and templating view structures (e.g. tables, forms)
|
123
|
-
from anywhere in the application.
|
120
|
+
templates within an instance of CalculationSet. Such a configuration file can
|
121
|
+
be structured the follow DSL:
|
124
122
|
|
125
|
-
|
126
|
-
|
127
|
-
# e.g. /config/initializers/calculations.rb
|
128
|
-
|
129
|
-
Calculations = AMEE::DataAbstraction::CalculationSet {
|
123
|
+
# e.g. /config/calculations/my_emissions_calculations.rb
|
130
124
|
|
131
125
|
calculation {
|
132
126
|
label :electricity
|
@@ -148,17 +142,35 @@ Adding a configuration to /config or /config/initializers may be appropriate
|
|
148
142
|
path "/some/fuel/associated/path/in/amee"
|
149
143
|
terms_from_amee
|
150
144
|
}
|
151
|
-
|
145
|
+
|
146
|
+
The default location for such files within Rails applications is under
|
147
|
+
/config/calculations. If such an approach is taken, the configuration can be
|
148
|
+
read and a calculation set generated by using the filename, thus:
|
149
|
+
|
150
|
+
CalculationSet.find('my_emissions_calculations')
|
151
|
+
|
152
|
+
#=> <AMEE::DataAbstraction::CalculationSet ... >
|
153
|
+
|
154
|
+
Otherwise, the path to the configuration file can be provided:
|
155
|
+
|
156
|
+
CalculationSet.find('some/directory/my_emissions_calculations')
|
157
|
+
|
158
|
+
#=> <AMEE::DataAbstraction::CalculationSet ... >
|
159
|
+
|
160
|
+
The calculation set is accessible as follow using the same argument as used
|
161
|
+
by the Find method (filename if conventional Rails location, path otherwise):
|
162
|
+
|
163
|
+
CalculationSet.sets['my_emissions_calculations']
|
152
164
|
|
153
165
|
#=> <AMEE::DataAbstraction::CalculationSet ... >
|
154
166
|
|
155
167
|
From this global calculation set, initialize a new calculation
|
156
168
|
|
157
|
-
my_fuel_calculation =
|
169
|
+
my_fuel_calculation = CalculationSet.sets['my_emissions_calcualtions'][:fuel].begin_calculation
|
158
170
|
|
159
171
|
#=> <AMEE::DataAbstraction::OngoingCalculation ... >
|
160
172
|
|
161
|
-
a_different_transport_calculation =
|
173
|
+
a_different_transport_calculation = CalculationSet.sets['my_emissions_calcualtions'][:transport].begin_calculation
|
162
174
|
|
163
175
|
#=> <AMEE::DataAbstraction::OngoingCalculation ... >
|
164
176
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{amee-data-abstraction}
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["James Hetherington", "Andrew Berkeley", "James Smith", "George Palmer"]
|
12
|
-
s.date = %q{2011-10-
|
12
|
+
s.date = %q{2011-10-18}
|
13
13
|
s.description = %q{Part of the AMEEappkit this gem provides a data abstraction layer, decreasing the amount and detail of development required}
|
14
14
|
s.email = %q{help@amee.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -62,16 +62,17 @@ Gem::Specification.new do |s|
|
|
62
62
|
"spec/core-extensions/hash_spec.rb",
|
63
63
|
"spec/core-extensions/ordered_hash_spec.rb",
|
64
64
|
"spec/core-extensions/proc_spec.rb",
|
65
|
-
"spec/fixtures/electricity.rb",
|
66
|
-
"spec/fixtures/electricity_and_transport.rb",
|
67
|
-
"spec/fixtures/transport.rb",
|
65
|
+
"spec/fixtures/config/calculations/electricity.rb",
|
66
|
+
"spec/fixtures/config/calculations/electricity_and_transport.rb",
|
67
|
+
"spec/fixtures/config/calculations/transport.rb",
|
68
|
+
"spec/fixtures/config/electricity.rb",
|
68
69
|
"spec/spec.opts",
|
69
70
|
"spec/spec_helper.rb"
|
70
71
|
]
|
71
72
|
s.homepage = %q{http://github.com/AMEE/amee-data-abstraction}
|
72
73
|
s.licenses = ["BSD 3-Clause"]
|
73
74
|
s.require_paths = ["lib"]
|
74
|
-
s.rubygems_version = %q{1.
|
75
|
+
s.rubygems_version = %q{1.5.3}
|
75
76
|
s.summary = %q{Calculation and form building tool hiding details of AMEEconnect}
|
76
77
|
|
77
78
|
if s.respond_to? :specification_version then
|
@@ -132,7 +132,7 @@ module AMEE
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def explorer_url
|
135
|
-
::Rails.logger.info "#explorer_url method deprecated. Use #discover_url" if defined? Rails
|
135
|
+
::Rails.logger.info "#explorer_url method deprecated. Use #discover_url" if defined?(Rails) && ::Rails.logger.present?
|
136
136
|
discover_url
|
137
137
|
end
|
138
138
|
|
@@ -36,22 +36,95 @@ module AMEE
|
|
36
36
|
#
|
37
37
|
class CalculationSet
|
38
38
|
|
39
|
-
#
|
40
|
-
#
|
39
|
+
# Class variable holding all instantiated calculation sets keyed on the set
|
40
|
+
# name.
|
41
41
|
#
|
42
|
-
|
43
|
-
|
42
|
+
@@sets = {}
|
43
|
+
|
44
|
+
# Convenience method for accessing the @@sets class variable
|
45
|
+
def self.sets
|
46
|
+
@@sets
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieve a calculation set on the basis of a configuration file name or
|
50
|
+
# relatiev/absolute file path. If configuration files are location within
|
51
|
+
# the default Rails location under '/config/calculations' then the path and
|
52
|
+
# the .rb extenstion can be omitted from the name.
|
53
|
+
#
|
54
|
+
def self.find(name)
|
55
|
+
@@sets[name.to_sym] or load_set(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Regenerate a configuration lock file assocaited with the master
|
59
|
+
# configuration file <tt>name</tt>. Optionally set a custom path for the
|
60
|
+
# lock file as <tt>output_path</tt>, otherwise the lock file path and
|
61
|
+
# filename will be based upon the master file with the extension .lock.rb.
|
62
|
+
#
|
63
|
+
def self.regenerate_lock_file(name,output_path=nil)
|
64
|
+
set = CalculationSet.find(name)
|
65
|
+
set.generate_lock_file(output_path)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Find a specific prototype calculation instance without specifying the set
|
69
|
+
# to which it belongs.
|
70
|
+
#
|
71
|
+
def self.find_prototype_calculation(label)
|
72
|
+
@@sets.each_pair do |name,set|
|
73
|
+
set = find(name)
|
74
|
+
return set[label] if set[label]
|
75
|
+
end
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
# Load a calculation set based on a filename or full path.
|
82
|
+
def self.load_set(name)
|
83
|
+
CalculationSet.new(name,:file => name) do
|
84
|
+
instance_eval(File.open(self.config_path).read)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
DEFFAULT_RAILS_CONFIG_DIR = "config/calculations"
|
89
|
+
|
90
|
+
# Find the config file assocaited with <tt>name</tt>. The method first checks
|
91
|
+
# the default Rails configuration location (config/calculations) then the
|
92
|
+
# file path described by <tt>name</tt> relative to the Rails root and by
|
93
|
+
# absolute path.
|
94
|
+
def self.find_config_file(name)
|
95
|
+
default_config_dir = defined?(::Rails) ? "#{::Rails.root}/#{DEFFAULT_RAILS_CONFIG_DIR}" : nil
|
96
|
+
if defined?(::Rails) && File.exists?("#{default_config_dir}/#{name.to_s}.rb")
|
97
|
+
"#{default_config_dir}/#{name.to_s}.rb"
|
98
|
+
elsif defined?(::Rails) && File.exists?("#{default_config_dir}/#{name.to_s}")
|
99
|
+
"#{default_config_dir}/#{name.to_s}"
|
100
|
+
elsif defined?(::Rails) && File.exists?("#{::Rails.root}/#{name}")
|
101
|
+
"#{::Rails.root}/#{name}"
|
102
|
+
elsif File.exists?(name)
|
103
|
+
name
|
104
|
+
else
|
105
|
+
raise ArgumentError, "The config file '#{name}' could not be located"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
public
|
110
|
+
|
111
|
+
attr_accessor :calculations, :name, :file
|
112
|
+
|
113
|
+
# Initialise a new Calculation set. Specify the name of the calculation set
|
114
|
+
# as the first argument. This name is used as the set key within the class
|
115
|
+
# variable @@sets hash.
|
116
|
+
#
|
117
|
+
def initialize(name,options={},&block)
|
118
|
+
raise ArgumentError, "Calculation set must have a name" unless name
|
119
|
+
@name = name
|
120
|
+
@file = CalculationSet.find_config_file(options[:file]) if options[:file]
|
121
|
+
@calculations = ActiveSupport::OrderedHash.new
|
44
122
|
@all_blocks=[]
|
45
123
|
@all_options={}
|
46
124
|
instance_eval(&block) if block
|
125
|
+
@@sets[@name.to_sym] = self
|
47
126
|
end
|
48
127
|
|
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
128
|
# Shorthand method for returning the prototype calculation which is represented
|
56
129
|
# by a label matching <tt>sym</tt>
|
57
130
|
#
|
@@ -94,8 +167,77 @@ module AMEE
|
|
94
167
|
instance_exec(usage,&dsl_block)
|
95
168
|
}
|
96
169
|
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns the path to the configuration file for <tt>self</tt>. If a .lock
|
173
|
+
# file exists, this takes precedence, otherwise the master config file
|
174
|
+
# described by the <tt>#file</tt> attribute is returned.
|
175
|
+
#
|
176
|
+
def config_path
|
177
|
+
lock_file_exists? ? lock_file_path : @file
|
178
|
+
end
|
97
179
|
|
180
|
+
# Returns the path to the configuration lock file
|
181
|
+
def lock_file_path
|
182
|
+
@file.gsub(".rb",".lock.rb") rescue nil
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns <tt>true</tt> if a configuration lock file exists. Otherwise,
|
186
|
+
# returns <tt>false</tt>.
|
187
|
+
#
|
188
|
+
def lock_file_exists?
|
189
|
+
File.exists?(lock_file_path)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Generates a lock file for the calcuation set configuration. If no argument
|
193
|
+
# is provided the, the lock file is generated using the filename and path
|
194
|
+
# described by the <tt>#lock_file_path</tt> method. If a custom output
|
195
|
+
# location is required, this can be provided optionally as an argument.
|
196
|
+
#
|
197
|
+
def generate_lock_file(output_path=nil)
|
198
|
+
file = output_path || lock_file_path or raise ArgumentError,
|
199
|
+
"No path for lock file known. Either set path for the master config file using the #file accessor method or provide as an argument"
|
200
|
+
string = ""
|
201
|
+
@calculations.values.each do |prototype_calculation|
|
202
|
+
string += "calculation {\n\n"
|
203
|
+
string += " name \"#{prototype_calculation.name}\"\n"
|
204
|
+
string += " label :#{prototype_calculation.label}\n"
|
205
|
+
string += " path \"#{prototype_calculation.path}\"\n\n"
|
206
|
+
prototype_calculation.terms.each do |term|
|
207
|
+
string += " #{term.class.to_s.split("::").last.downcase} {\n"
|
208
|
+
string += " name \"#{term.name}\"\n" unless term.name.blank?
|
209
|
+
string += " label :#{term.label}\n" unless term.label.blank?
|
210
|
+
string += " path \"#{term.path}\"\n" unless term.path.blank?
|
211
|
+
string += " value \"#{term.value}\"\n" unless term.value.blank?
|
212
|
+
|
213
|
+
if term.is_a?(AMEE::DataAbstraction::Input)
|
214
|
+
string += " fixed \"#{term.value}\"\n" if term.fixed? && !term.value.blank?
|
215
|
+
if term.is_a?(AMEE::DataAbstraction::Drill)
|
216
|
+
string += " choices \"#{term.choices.join('","')}\"\n" if term.instance_variable_defined?("@choices") && !term.choices.blank?
|
217
|
+
elsif term.is_a?(AMEE::DataAbstraction::Profile)
|
218
|
+
string += " choices [\"#{term.choices.join('","')}\"]\n" if term.instance_variable_defined?("@choices") && !term.choices.blank?
|
219
|
+
end
|
220
|
+
string += " optional!\n" if term.optional?
|
221
|
+
end
|
222
|
+
|
223
|
+
string += " default_unit :#{term.default_unit.label}\n" unless term.default_unit.blank?
|
224
|
+
string += " default_per_unit :#{term.default_per_unit.label}\n" unless term.default_per_unit.blank?
|
225
|
+
string += " alternative_units :#{term.alternative_units.map(&:label).join(', :')}\n" unless term.alternative_units.blank?
|
226
|
+
string += " alternative_per_units :#{term.alternative_per_units.map(&:label).join(', :')}\n" unless term.alternative_per_units.blank?
|
227
|
+
string += " unit :#{term.unit.label}\n" unless term.unit.blank?
|
228
|
+
string += " per_unit :#{term.per_unit.label}\n" unless term.per_unit.blank?
|
229
|
+
string += " type :#{term.type}\n" unless term.type.blank?
|
230
|
+
string += " interface :#{term.interface}\n" unless term.interface.blank?
|
231
|
+
string += " note \"#{term.note}\"\n" unless term.note.blank?
|
232
|
+
string += " disable!\n" if !term.is_a?(AMEE::DataAbstraction::Drill) && term.disabled?
|
233
|
+
string += " hide!\n" if term.hidden?
|
234
|
+
string += " }\n\n"
|
235
|
+
end
|
236
|
+
string += "}\n\n"
|
237
|
+
end
|
238
|
+
File.open(file,'w') { |f| f.write string }
|
98
239
|
end
|
240
|
+
|
99
241
|
end
|
100
242
|
end
|
101
243
|
end
|
@@ -158,6 +158,16 @@ module AMEE
|
|
158
158
|
!optional?(usage)
|
159
159
|
end
|
160
160
|
|
161
|
+
# Manually set the term as optional
|
162
|
+
def optional!
|
163
|
+
@optional=true
|
164
|
+
end
|
165
|
+
|
166
|
+
# Manually set the term as compuslory
|
167
|
+
def compulsory!
|
168
|
+
@optional=false
|
169
|
+
end
|
170
|
+
|
161
171
|
# Check that the value of <tt>self</tt> is valid. If invalid, and is defined
|
162
172
|
# as part of a calculation, add the invalidity message to the parent
|
163
173
|
# calculation's error list. Otherwise, raise a <i>ChoiceValidation</i>
|
@@ -258,7 +258,7 @@ module AMEE
|
|
258
258
|
def load_outputs
|
259
259
|
outputs.each do |output|
|
260
260
|
res=nil
|
261
|
-
if output.path
|
261
|
+
if output.path.to_s=='default'
|
262
262
|
res= profile_item.amounts.find{|x| x[:default] == true}
|
263
263
|
else
|
264
264
|
res= profile_item.amounts.find{|x| x[:type] == output.path}
|
@@ -84,16 +84,6 @@ module AMEE
|
|
84
84
|
#
|
85
85
|
attr_property :path
|
86
86
|
|
87
|
-
# String representing an annotation for <tt>self</tt>. Set a value by
|
88
|
-
# passing an argument. Retrieve a value by calling without an argument,
|
89
|
-
# e.g.,
|
90
|
-
#
|
91
|
-
# my_term.note 'Enter the mass of cement produced in the reporting period'
|
92
|
-
#
|
93
|
-
# my_term.note #=> 'Enter the mass of cement ...'
|
94
|
-
#
|
95
|
-
attr_property :note
|
96
|
-
|
97
87
|
# Symbol representing the owning parent calculation of <tt>self</tt>. Set
|
98
88
|
# the owning calculation object by passing as an argument. Retrieve it by
|
99
89
|
# calling without an argument, e.g.,
|
@@ -211,6 +201,19 @@ module AMEE
|
|
211
201
|
end
|
212
202
|
@value
|
213
203
|
end
|
204
|
+
|
205
|
+
# String representing an annotation for <tt>self</tt>. Set a value by
|
206
|
+
# passing an argument. Retrieve a value by calling without an argument,
|
207
|
+
# e.g.,
|
208
|
+
#
|
209
|
+
# my_term.note 'Enter the mass of cement produced in the reporting period'
|
210
|
+
#
|
211
|
+
# my_term.note #=> 'Enter the mass of cement ...'
|
212
|
+
#
|
213
|
+
def note(string=nil)
|
214
|
+
instance_variable_set("@note",string.gsub('"',"'")) unless string.nil?
|
215
|
+
instance_variable_get("@note")
|
216
|
+
end
|
214
217
|
|
215
218
|
# Symbols representing the attributes of <tt>self</tt> which are concerned
|
216
219
|
# with quantity units.
|
@@ -264,19 +267,33 @@ module AMEE
|
|
264
267
|
end
|
265
268
|
|
266
269
|
[:unit,:per_unit].each do |field|
|
270
|
+
|
271
|
+
# If no argument provided, returns the alternative units which are valid
|
272
|
+
# for <tt>self</tt>. If a list of units are provided as an argument, these
|
273
|
+
# override the dynamically assigned alternative units for <tt>self</tt>.
|
274
|
+
#
|
267
275
|
define_method("alternative_#{field}s") do |*args|
|
268
276
|
ivar = "@alternative_#{field}s"
|
269
|
-
default = send("default_#{field}".to_sym)
|
270
277
|
unless args.empty?
|
271
|
-
args << default if default
|
272
278
|
units = args.map {|arg| Unit.for(arg) }
|
273
279
|
Term.validate_dimensional_equivalence?(*units)
|
274
280
|
instance_variable_set(ivar, units)
|
275
281
|
else
|
276
282
|
return instance_variable_get(ivar) if instance_variable_get(ivar)
|
277
|
-
|
283
|
+
default = send("default_#{field}".to_sym)
|
284
|
+
return instance_variable_set(ivar, (default.alternatives)) if default
|
278
285
|
end
|
279
286
|
end
|
287
|
+
|
288
|
+
# Returns the list of unit choices for <tt>self</tt>, including both the
|
289
|
+
# default unit and all alternative units.
|
290
|
+
#
|
291
|
+
define_method("#{field}_choices") do |*args|
|
292
|
+
choices = send("alternative_#{field}s".to_sym)
|
293
|
+
default = send("default_#{field}".to_sym)
|
294
|
+
choices = [default] + choices if default
|
295
|
+
return choices
|
296
|
+
end
|
280
297
|
end
|
281
298
|
|
282
299
|
# Returns <tt>true</tt> if <tt>self</tt> has a populated value attribute.
|
@@ -503,6 +520,7 @@ module AMEE
|
|
503
520
|
else value
|
504
521
|
end
|
505
522
|
end
|
523
|
+
|
506
524
|
end
|
507
525
|
end
|
508
526
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
|
+
|
2
3
|
class CalculationSet
|
3
4
|
def call_me
|
4
5
|
#stub, because flexmock doesn't work for new instances during constructor
|
@@ -8,25 +9,260 @@ class CalculationSet
|
|
8
9
|
@@called
|
9
10
|
end
|
10
11
|
end
|
12
|
+
|
11
13
|
describe CalculationSet do
|
12
|
-
|
13
|
-
|
14
|
+
|
15
|
+
before :all do
|
16
|
+
CalculationSet.sets.clear
|
17
|
+
@calc_set = CalculationSet.find("electricity_and_transport")
|
14
18
|
end
|
19
|
+
|
15
20
|
it 'can create an instance' do
|
16
|
-
|
21
|
+
@calc_set.calculations.should be_a ActiveSupport::OrderedHash
|
22
|
+
@calc_set.should be_a CalculationSet
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can create an instance and find calcs' do
|
26
|
+
@calc_set.calculations.should be_a ActiveSupport::OrderedHash
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can access class sets hash" do
|
30
|
+
CalculationSet.sets[:electricity_and_transport].should be_a CalculationSet
|
31
|
+
end
|
32
|
+
|
33
|
+
it "is included in class sets hash if initialised by find method" do
|
34
|
+
CalculationSet.sets[:electricity_and_transport].should be_a CalculationSet
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has file attribute if initialised by find method" do
|
38
|
+
CalculationSet.sets[:electricity_and_transport].file.should eql "#{Rails.root}/config/calculations/electricity_and_transport.rb"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "has name attribute if initialised by find method" do
|
42
|
+
CalculationSet.sets[:electricity_and_transport].name.should eql "electricity_and_transport"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "is included in class sets hash if initialised manually" do
|
46
|
+
CalculationSet.new('my_set') {calculation {label :mycalc}}
|
47
|
+
CalculationSet.sets[:my_set].should be_a CalculationSet
|
17
48
|
end
|
49
|
+
|
50
|
+
it "has name" do
|
51
|
+
CalculationSet.new('my_set') {calculation {label :mycalc}}.name.should eql "my_set"
|
52
|
+
end
|
53
|
+
|
18
54
|
it 'can access a calculation by key' do
|
19
|
-
|
55
|
+
@calc_set[:transport].should be_a PrototypeCalculation
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "initialising from file" do
|
59
|
+
|
60
|
+
after(:each) do
|
61
|
+
CalculationSet.sets.clear
|
62
|
+
delete_lock_files
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should find config file in default Rails location using just file name" do
|
66
|
+
CalculationSet.find_config_file("electricity").should eql "#{Rails.root}/config/calculations/electricity.rb"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should find config file in default Rails location using file name and extension" do
|
70
|
+
CalculationSet.find_config_file("electricity.rb").should eql "#{Rails.root}/config/calculations/electricity.rb"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should find config file in other Rails location using relative path" do
|
74
|
+
CalculationSet.find_config_file("config/electricity.rb").should eql "#{Rails.root}/config/electricity.rb"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should raise error if config file not found" do
|
78
|
+
lambda{CalculationSet.find_config_file("fuel")}.should raise_error
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should call load_set if no set exists in class hash" do
|
82
|
+
CalculationSet.sets[:transport].should be_nil
|
83
|
+
flexmock(AMEE::DataAbstraction::CalculationSet) do |mock|
|
84
|
+
mock.should_receive(:load_set).once
|
85
|
+
end
|
86
|
+
set = CalculationSet.find('transport')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not call load_set if set exists in class hash" do
|
90
|
+
CalculationSet.sets[:transport].should be_nil
|
91
|
+
set = CalculationSet.find('transport')
|
92
|
+
CalculationSet.sets[:transport].should be_a CalculationSet
|
93
|
+
flexmock(AMEE::DataAbstraction::CalculationSet) do |mock|
|
94
|
+
mock.should_receive(:load_set).never
|
95
|
+
end
|
96
|
+
set = CalculationSet.find('transport')
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should generate set from file name using find method" do
|
100
|
+
CalculationSet.sets[:transport].should be_nil
|
101
|
+
set = CalculationSet.find('transport')
|
102
|
+
set.should be_a CalculationSet
|
103
|
+
CalculationSet.sets[:transport].should be_a CalculationSet
|
104
|
+
set.name.should eql 'transport'
|
105
|
+
set.file.should eql "#{Rails.root}/config/calculations/transport.rb"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should regenerate lock file at default location" do
|
109
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
110
|
+
File.exist?(lock_file).should be_false
|
111
|
+
CalculationSet.sets[:transport].should be_nil
|
112
|
+
set = CalculationSet.find('transport')
|
113
|
+
File.exist?(lock_file).should be_false
|
114
|
+
CalculationSet.sets[:transport].should be_a CalculationSet
|
115
|
+
|
116
|
+
set.generate_lock_file
|
117
|
+
File.exist?(lock_file).should be_true
|
118
|
+
|
119
|
+
# lock file content
|
120
|
+
content = File.open(lock_file).read
|
121
|
+
|
122
|
+
# clear lock file to test for regenerated data
|
123
|
+
File.open(lock_file,'w') {|file| file.write "overwrite content"}
|
124
|
+
File.open(lock_file).read.should eql "overwrite content"
|
125
|
+
File.exist?(lock_file).should be_true
|
126
|
+
|
127
|
+
# regenerate and test content matches original
|
128
|
+
CalculationSet.regenerate_lock_file('transport')
|
129
|
+
File.exist?(lock_file).should be_true
|
130
|
+
File.open(lock_file).read.should eql content
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should regenerate lock file at custom location" do
|
134
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
135
|
+
File.exist?(lock_file).should be_false
|
136
|
+
CalculationSet.sets[:transport].should be_nil
|
137
|
+
set = CalculationSet.find('transport')
|
138
|
+
|
139
|
+
File.exist?(lock_file).should be_false
|
140
|
+
CalculationSet.sets[:transport].should be_a CalculationSet
|
141
|
+
|
142
|
+
set.generate_lock_file
|
143
|
+
File.exist?(lock_file).should be_true
|
144
|
+
|
145
|
+
content = File.open(lock_file).read
|
146
|
+
File.open(lock_file,'w') {|file| file.write "overwrite content"}
|
147
|
+
File.open(lock_file).read.should eql "overwrite content"
|
148
|
+
File.exist?(lock_file).should be_true
|
149
|
+
|
150
|
+
CalculationSet.regenerate_lock_file('transport', "#{Rails.root}/transport.lock.rb")
|
151
|
+
File.exist?(lock_file).should be_true
|
152
|
+
File.exist?("#{Rails.root}/transport.lock.rb").should be_true
|
153
|
+
File.open(lock_file).read.should eql "overwrite content"
|
154
|
+
File.open("#{Rails.root}/transport.lock.rb").read.should eql content
|
155
|
+
|
156
|
+
File.delete("#{Rails.root}/transport.lock.rb")
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return a lock file path based on master config file" do
|
160
|
+
set = CalculationSet.find('transport')
|
161
|
+
set.lock_file_path.should eql "#{Rails.root}/config/calculations/transport.lock.rb"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should return lock file path if lock file exists" do
|
165
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
166
|
+
File.exist?(lock_file).should be_false
|
167
|
+
set = CalculationSet.find('transport')
|
168
|
+
File.exist?(lock_file).should be_false
|
169
|
+
set.generate_lock_file
|
170
|
+
File.exist?(lock_file).should be_true
|
171
|
+
set.config_path.should eql lock_file
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should return master file path if lock file does not exist" do
|
175
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
176
|
+
File.exist?(lock_file).should be_false
|
177
|
+
set = CalculationSet.find('transport')
|
178
|
+
File.exist?(lock_file).should be_false
|
179
|
+
set.config_path.should eql "#{Rails.root}/config/calculations/transport.rb"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should know if lock file exists" do
|
183
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
184
|
+
File.exist?(lock_file).should be_false
|
185
|
+
set = CalculationSet.find('transport')
|
186
|
+
File.exist?(lock_file).should be_false
|
187
|
+
set.generate_lock_file
|
188
|
+
File.exist?(lock_file).should be_true
|
189
|
+
set.lock_file_exists?.should be_true
|
190
|
+
File.delete(lock_file)
|
191
|
+
File.exist?(lock_file).should be_false
|
192
|
+
set.lock_file_exists?.should be_false
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should generate lock file" do
|
196
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
197
|
+
File.exist?(lock_file).should be_false
|
198
|
+
|
199
|
+
set = CalculationSet.find('transport')
|
200
|
+
File.exist?(lock_file).should be_false
|
201
|
+
set.lock_file_exists?.should be_false
|
202
|
+
|
203
|
+
set.generate_lock_file
|
204
|
+
File.exist?(lock_file).should be_true
|
205
|
+
set.lock_file_exists?.should be_true
|
206
|
+
|
207
|
+
content = File.open(lock_file).read
|
208
|
+
|
209
|
+
File.delete(lock_file)
|
210
|
+
File.exist?(lock_file).should be_false
|
211
|
+
set.lock_file_exists?.should be_false
|
212
|
+
|
213
|
+
set.generate_lock_file
|
214
|
+
File.exist?(lock_file).should be_true
|
215
|
+
set.lock_file_exists?.should be_true
|
216
|
+
File.open(lock_file).read.should eql content
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should generate lock file at custom location" do
|
220
|
+
lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
|
221
|
+
File.exist?(lock_file).should be_false
|
222
|
+
|
223
|
+
set = CalculationSet.find('transport')
|
224
|
+
File.exist?(lock_file).should be_false
|
225
|
+
set.lock_file_exists?.should be_false
|
226
|
+
|
227
|
+
set.generate_lock_file("#{Rails.root}/transport.lock.rb")
|
228
|
+
File.exist?(lock_file).should be_false
|
229
|
+
set.lock_file_exists?.should be_false
|
230
|
+
File.exist?("#{Rails.root}/transport.lock.rb").should be_true
|
231
|
+
|
232
|
+
File.delete("#{Rails.root}/transport.lock.rb")
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
it "can find a prototype calc without calc set" do
|
238
|
+
CalculationSet.new('my_set') {
|
239
|
+
calculation {label :my_calc}
|
240
|
+
calculation {label :my_other_calc}
|
241
|
+
}
|
242
|
+
CalculationSet.new('your_set') {
|
243
|
+
calculation {label :your_calc}
|
244
|
+
calculation {label :your_other_calc}
|
245
|
+
}
|
246
|
+
CalculationSet.find_prototype_calculation(:transport).should be_a PrototypeCalculation
|
247
|
+
CalculationSet.find_prototype_calculation(:your_calc).should be_a PrototypeCalculation
|
248
|
+
CalculationSet.find_prototype_calculation(:my_other_calc).should be_a PrototypeCalculation
|
249
|
+
end
|
250
|
+
|
251
|
+
it "returns nil where no prototype calcualtion is found" do
|
252
|
+
CalculationSet.find_prototype_calculation(:fuel).should be_nil
|
20
253
|
end
|
254
|
+
|
21
255
|
it 'can construct a calculation' do
|
22
|
-
CalculationSet.new {calculation {label :mycalc}}[:mycalc].should be_a PrototypeCalculation
|
256
|
+
CalculationSet.new('my_set') {calculation {label :mycalc}}[:mycalc].should be_a PrototypeCalculation
|
23
257
|
end
|
258
|
+
|
24
259
|
it 'can be initialized with a DSL block' do
|
25
|
-
CalculationSet.new {call_me}
|
260
|
+
CalculationSet.new('my_set') {call_me}
|
26
261
|
CalculationSet.called.should be_true
|
27
262
|
end
|
263
|
+
|
28
264
|
it 'can have terms added to all calculations' do
|
29
|
-
cs=CalculationSet.new {
|
265
|
+
cs=CalculationSet.new('my_set') {
|
30
266
|
all_calculations {
|
31
267
|
drill {label :energetic}
|
32
268
|
}
|
@@ -37,6 +273,7 @@ describe CalculationSet do
|
|
37
273
|
}
|
38
274
|
cs[:mycalc].drills.labels.should eql [:remarkably,:energetic]
|
39
275
|
end
|
276
|
+
|
40
277
|
it 'can make multiple calculations quickly, one for each usage' do
|
41
278
|
mocker=AMEEMocker.new(self,:path=>'something')
|
42
279
|
mocker.item_value_definitions.usages(['bybob','byfrank']).
|
@@ -44,7 +281,7 @@ describe CalculationSet do
|
|
44
281
|
item_value_definition('first',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
|
45
282
|
item_value_definition('second',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
|
46
283
|
item_value_definition('third',['byfrank'],[],['bybob'],[],nil,nil,true,false,nil,"TEXT")
|
47
|
-
cs=CalculationSet.new {
|
284
|
+
cs=CalculationSet.new('my_set') {
|
48
285
|
calculations_all_usages('/something') { |usage|
|
49
286
|
label usage.to_sym
|
50
287
|
profiles_from_usage usage
|
@@ -53,4 +290,5 @@ describe CalculationSet do
|
|
53
290
|
cs[:bybob].profiles.labels.should eql [:first,:second]
|
54
291
|
cs[:byfrank].profiles.labels.should eql [:third]
|
55
292
|
end
|
293
|
+
|
56
294
|
end
|