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.
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