amee-data-abstraction 2.1.1 → 2.2.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 (31) hide show
  1. data/CHANGELOG.txt +3 -0
  2. data/README.txt +26 -14
  3. data/VERSION +1 -1
  4. data/amee-data-abstraction.gemspec +7 -6
  5. data/lib/amee-data-abstraction/calculation.rb +1 -1
  6. data/lib/amee-data-abstraction/calculation_set.rb +152 -10
  7. data/lib/amee-data-abstraction/input.rb +10 -0
  8. data/lib/amee-data-abstraction/ongoing_calculation.rb +1 -1
  9. data/lib/amee-data-abstraction/term.rb +31 -13
  10. data/spec/amee-data-abstraction/calculation_set_spec.rb +247 -9
  11. data/spec/amee-data-abstraction/calculation_spec.rb +24 -19
  12. data/spec/amee-data-abstraction/drill_spec.rb +14 -9
  13. data/spec/amee-data-abstraction/input_spec.rb +113 -73
  14. data/spec/amee-data-abstraction/metadatum_spec.rb +1 -1
  15. data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +38 -30
  16. data/spec/amee-data-abstraction/profile_spec.rb +4 -2
  17. data/spec/amee-data-abstraction/prototype_calculation_spec.rb +13 -8
  18. data/spec/amee-data-abstraction/term_spec.rb +45 -4
  19. data/spec/amee-data-abstraction/terms_list_spec.rb +23 -12
  20. data/spec/config/amee_units_spec.rb +1 -2
  21. data/spec/core-extensions/class_spec.rb +18 -18
  22. data/spec/core-extensions/hash_spec.rb +1 -2
  23. data/spec/core-extensions/ordered_hash_spec.rb +1 -2
  24. data/spec/core-extensions/proc_spec.rb +1 -1
  25. data/spec/fixtures/config/calculations/electricity.rb +35 -0
  26. data/spec/fixtures/config/calculations/electricity_and_transport.rb +53 -0
  27. data/spec/fixtures/{transport.rb → config/calculations/transport.rb} +2 -2
  28. data/spec/fixtures/{electricity.rb → config/electricity.rb} +1 -1
  29. data/spec/spec_helper.rb +30 -2
  30. metadata +28 -27
  31. data/spec/fixtures/electricity_and_transport.rb +0 -55
@@ -1,4 +1,7 @@
1
1
  = Changelog
2
2
 
3
+ == 1.3.0
4
+ * Reconfigure storage and API for Calculation Sets
5
+
3
6
  == 1.0.0
4
7
  * Initial public release
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 ~> 3.0
28
+ * amee ~> 4.1
29
29
  * uuidtools = 2.1.2
30
- * quantify = 1.1.0
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. If the calculation set is assigned
121
- to a global variable or constant, the set of prototypes is available for
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
- Adding a configuration to /config or /config/initializers may be appropriate
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 = Calculations[:fuel].begin_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 = Calculations[:transport].begin_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.1
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.1.1"
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}
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.4.2}
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
- # Initialize a <i>CalculationSet</i> with a "DSL" block, i.e. a block to be
40
- # evaluated in the context of the instantiated CalculationSet
39
+ # Class variable holding all instantiated calculation sets keyed on the set
40
+ # name.
41
41
  #
42
- def initialize(options={},&block)
43
- @calculations=ActiveSupport::OrderedHash.new
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==:default
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
- return instance_variable_set(ivar, (default.alternatives << default)) if default
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 File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
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
- it 'can create an instance' do
13
- ElectricityAndTransport.should be_a CalculationSet
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
- ElectricityAndTransport.calculations.should be_a ActiveSupport::OrderedHash
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
- ElectricityAndTransport[:transport].should be_a PrototypeCalculation
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