amee-data-abstraction 1.2.0 → 1.3.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 (33) hide show
  1. data/CHANGELOG.txt +3 -0
  2. data/README.txt +28 -15
  3. data/Rakefile +1 -16
  4. data/VERSION +1 -1
  5. data/amee-data-abstraction.gemspec +6 -5
  6. data/lib/amee-data-abstraction/calculation.rb +1 -1
  7. data/lib/amee-data-abstraction/calculation_set.rb +152 -10
  8. data/lib/amee-data-abstraction/drill.rb +8 -2
  9. data/lib/amee-data-abstraction/input.rb +24 -0
  10. data/lib/amee-data-abstraction/ongoing_calculation.rb +13 -9
  11. data/lib/amee-data-abstraction/term.rb +31 -13
  12. data/spec/amee-data-abstraction/calculation_set_spec.rb +244 -8
  13. data/spec/amee-data-abstraction/calculation_spec.rb +23 -18
  14. data/spec/amee-data-abstraction/drill_spec.rb +34 -7
  15. data/spec/amee-data-abstraction/input_spec.rb +113 -73
  16. data/spec/amee-data-abstraction/metadatum_spec.rb +1 -1
  17. data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +45 -29
  18. data/spec/amee-data-abstraction/profile_spec.rb +2 -2
  19. data/spec/amee-data-abstraction/prototype_calculation_spec.rb +12 -8
  20. data/spec/amee-data-abstraction/term_spec.rb +49 -5
  21. data/spec/amee-data-abstraction/terms_list_spec.rb +16 -12
  22. data/spec/config/amee_units_spec.rb +1 -2
  23. data/spec/core-extensions/class_spec.rb +18 -18
  24. data/spec/core-extensions/hash_spec.rb +1 -2
  25. data/spec/core-extensions/ordered_hash_spec.rb +1 -2
  26. data/spec/core-extensions/proc_spec.rb +1 -1
  27. data/spec/fixtures/{electricity.rb → config/calculations/electricity.rb} +2 -2
  28. data/spec/fixtures/config/calculations/electricity_and_transport.rb +53 -0
  29. data/spec/fixtures/{transport.rb → config/calculations/transport.rb} +2 -2
  30. data/spec/fixtures/config/electricity.rb +35 -0
  31. data/spec/spec_helper.rb +38 -6
  32. metadata +8 -7
  33. data/spec/fixtures/electricity_and_transport.rb +0 -55
@@ -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
@@ -7,24 +8,257 @@ class CalculationSet
7
8
  cattr_accessor :called
8
9
  end
9
10
  describe CalculationSet do
11
+
12
+ before :all do
13
+ CalculationSet.sets.clear
14
+ @calc_set = CalculationSet.find("electricity_and_transport")
15
+ end
16
+
10
17
  it 'can create an instance' do
11
- ElectricityAndTransport.should be_a CalculationSet
18
+ @calc_set.should be_a CalculationSet
12
19
  end
20
+
13
21
  it 'can create an instance' do
14
- ElectricityAndTransport.calculations.should be_a ActiveSupport::OrderedHash
22
+ @calc_set.calculations.should be_a ActiveSupport::OrderedHash
23
+ end
24
+
25
+ it "can access class sets hash" do
26
+ CalculationSet.sets[:electricity_and_transport].should be_a CalculationSet
27
+ end
28
+
29
+ it "is included in class sets hash if initialised by find method" do
30
+ CalculationSet.sets[:electricity_and_transport].should be_a CalculationSet
31
+ end
32
+
33
+ it "has file attribute if initialised by find method" do
34
+ CalculationSet.sets[:electricity_and_transport].file.should eql "#{Rails.root}/config/calculations/electricity_and_transport.rb"
35
+ end
36
+
37
+ it "has name attribute if initialised by find method" do
38
+ CalculationSet.sets[:electricity_and_transport].name.should eql "electricity_and_transport"
39
+ end
40
+
41
+ it "is included in class sets hash if initialised manually" do
42
+ CalculationSet.new('my_set') {calculation {label :mycalc}}
43
+ CalculationSet.sets[:my_set].should be_a CalculationSet
15
44
  end
45
+
46
+ it "has name" do
47
+ CalculationSet.new('my_set') {calculation {label :mycalc}}.name.should eql "my_set"
48
+ end
49
+
16
50
  it 'can access a calculation by key' do
17
- ElectricityAndTransport[:transport].should be_a PrototypeCalculation
51
+ @calc_set[:transport].should be_a PrototypeCalculation
52
+ end
53
+
54
+ describe "initialising from file" do
55
+
56
+ after(:each) do
57
+ CalculationSet.sets.clear
58
+ delete_lock_files
59
+ end
60
+
61
+ it "should find config file in default Rails location using just file name" do
62
+ CalculationSet.find_config_file("electricity").should eql "#{Rails.root}/config/calculations/electricity.rb"
63
+ end
64
+
65
+ it "should find config file in default Rails location using file name and extension" do
66
+ CalculationSet.find_config_file("electricity.rb").should eql "#{Rails.root}/config/calculations/electricity.rb"
67
+ end
68
+
69
+ it "should find config file in other Rails location using relative path" do
70
+ CalculationSet.find_config_file("config/electricity.rb").should eql "#{Rails.root}/config/electricity.rb"
71
+ end
72
+
73
+ it "should raise error if config file not found" do
74
+ lambda{CalculationSet.find_config_file("fuel")}.should raise_error
75
+ end
76
+
77
+ it "should call load_set if no set exists in class hash" do
78
+ CalculationSet.sets[:transport].should be_nil
79
+ flexmock(AMEE::DataAbstraction::CalculationSet) do |mock|
80
+ mock.should_receive(:load_set).once
81
+ end
82
+ set = CalculationSet.find('transport')
83
+ end
84
+
85
+ it "should not call load_set if set exists in class hash" do
86
+ CalculationSet.sets[:transport].should be_nil
87
+ set = CalculationSet.find('transport')
88
+ CalculationSet.sets[:transport].should be_a CalculationSet
89
+ flexmock(AMEE::DataAbstraction::CalculationSet) do |mock|
90
+ mock.should_receive(:load_set).never
91
+ end
92
+ set = CalculationSet.find('transport')
93
+ end
94
+
95
+ it "should generate set from file name using find method" do
96
+ CalculationSet.sets[:transport].should be_nil
97
+ set = CalculationSet.find('transport')
98
+ set.should be_a CalculationSet
99
+ CalculationSet.sets[:transport].should be_a CalculationSet
100
+ set.name.should eql 'transport'
101
+ set.file.should eql "#{Rails.root}/config/calculations/transport.rb"
102
+ end
103
+
104
+ it "should regenerate lock file at default location" do
105
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
106
+ File.exist?(lock_file).should be_false
107
+ CalculationSet.sets[:transport].should be_nil
108
+ set = CalculationSet.find('transport')
109
+ File.exist?(lock_file).should be_false
110
+ CalculationSet.sets[:transport].should be_a CalculationSet
111
+
112
+ set.generate_lock_file
113
+ File.exist?(lock_file).should be_true
114
+
115
+ # lock file content
116
+ content = File.open(lock_file).read
117
+
118
+ # clear lock file to test for regenerated data
119
+ File.open(lock_file,'w') {|file| file.write "overwrite content"}
120
+ File.open(lock_file).read.should eql "overwrite content"
121
+ File.exist?(lock_file).should be_true
122
+
123
+ # regenerate and test content matches original
124
+ CalculationSet.regenerate_lock_file('transport')
125
+ File.exist?(lock_file).should be_true
126
+ File.open(lock_file).read.should eql content
127
+ end
128
+
129
+ it "should regenerate lock file at custom location" do
130
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
131
+ File.exist?(lock_file).should be_false
132
+ CalculationSet.sets[:transport].should be_nil
133
+ set = CalculationSet.find('transport')
134
+ File.exist?(lock_file).should be_false
135
+ CalculationSet.sets[:transport].should be_a CalculationSet
136
+
137
+ set.generate_lock_file
138
+ File.exist?(lock_file).should be_true
139
+
140
+ content = File.open(lock_file).read
141
+ File.open(lock_file,'w') {|file| file.write "overwrite content"}
142
+ File.open(lock_file).read.should eql "overwrite content"
143
+ File.exist?(lock_file).should be_true
144
+
145
+ CalculationSet.regenerate_lock_file('transport', "#{Rails.root}/transport.lock.rb")
146
+ File.exist?(lock_file).should be_true
147
+ File.exist?("#{Rails.root}/transport.lock.rb").should be_true
148
+ File.open(lock_file).read.should eql "overwrite content"
149
+ File.open("#{Rails.root}/transport.lock.rb").read.should eql content
150
+
151
+ File.delete("#{Rails.root}/transport.lock.rb")
152
+ end
153
+
154
+ it "should return a lock file path based on master config file" do
155
+ set = CalculationSet.find('transport')
156
+ set.lock_file_path.should eql "#{Rails.root}/config/calculations/transport.lock.rb"
157
+ end
158
+
159
+ it "should return lock file path if lock file exists" do
160
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
161
+ File.exist?(lock_file).should be_false
162
+ set = CalculationSet.find('transport')
163
+ File.exist?(lock_file).should be_false
164
+ set.generate_lock_file
165
+ File.exist?(lock_file).should be_true
166
+ set.config_path.should eql lock_file
167
+ end
168
+
169
+ it "should return master file path if lock file does not exist" do
170
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
171
+ File.exist?(lock_file).should be_false
172
+ set = CalculationSet.find('transport')
173
+ File.exist?(lock_file).should be_false
174
+ set.config_path.should eql "#{Rails.root}/config/calculations/transport.rb"
175
+ end
176
+
177
+ it "should know if lock file exists" do
178
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
179
+ File.exist?(lock_file).should be_false
180
+ set = CalculationSet.find('transport')
181
+ File.exist?(lock_file).should be_false
182
+ set.generate_lock_file
183
+ File.exist?(lock_file).should be_true
184
+ set.lock_file_exists?.should be_true
185
+ File.delete(lock_file)
186
+ File.exist?(lock_file).should be_false
187
+ set.lock_file_exists?.should be_false
188
+ end
189
+
190
+ it "should generate lock file" do
191
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
192
+ File.exist?(lock_file).should be_false
193
+
194
+ set = CalculationSet.find('transport')
195
+ File.exist?(lock_file).should be_false
196
+ set.lock_file_exists?.should be_false
197
+
198
+ set.generate_lock_file
199
+ File.exist?(lock_file).should be_true
200
+ set.lock_file_exists?.should be_true
201
+
202
+ content = File.open(lock_file).read
203
+
204
+ File.delete(lock_file)
205
+ File.exist?(lock_file).should be_false
206
+ set.lock_file_exists?.should be_false
207
+
208
+ set.generate_lock_file
209
+ File.exist?(lock_file).should be_true
210
+ set.lock_file_exists?.should be_true
211
+ File.open(lock_file).read.should eql content
212
+ end
213
+
214
+ it "should generate lock file at custom location" do
215
+ lock_file = "#{Rails.root}/config/calculations/transport.lock.rb"
216
+ File.exist?(lock_file).should be_false
217
+
218
+ set = CalculationSet.find('transport')
219
+ File.exist?(lock_file).should be_false
220
+ set.lock_file_exists?.should be_false
221
+
222
+ set.generate_lock_file("#{Rails.root}/transport.lock.rb")
223
+ File.exist?(lock_file).should be_false
224
+ set.lock_file_exists?.should be_false
225
+
226
+ File.exist?("#{Rails.root}/transport.lock.rb").should be_true
227
+
228
+ File.delete("#{Rails.root}/transport.lock.rb")
229
+ end
230
+
231
+ end
232
+
233
+ it "can find a prototype calc without calc set" do
234
+ CalculationSet.new('my_set') {
235
+ calculation {label :my_calc}
236
+ calculation {label :my_other_calc}
237
+ }
238
+ CalculationSet.new('your_set') {
239
+ calculation {label :your_calc}
240
+ calculation {label :your_other_calc}
241
+ }
242
+ CalculationSet.find_prototype_calculation(:transport).should be_a PrototypeCalculation
243
+ CalculationSet.find_prototype_calculation(:your_calc).should be_a PrototypeCalculation
244
+ CalculationSet.find_prototype_calculation(:my_other_calc).should be_a PrototypeCalculation
245
+ end
246
+
247
+ it "returns nil where no prototype calcualtion is found" do
248
+ CalculationSet.find_prototype_calculation(:fuel).should be_nil
18
249
  end
250
+
19
251
  it 'can construct a calculation' do
20
- CalculationSet.new {calculation {label :mycalc}}[:mycalc].should be_a PrototypeCalculation
252
+ CalculationSet.new('my_set') {calculation {label :mycalc}}[:mycalc].should be_a PrototypeCalculation
21
253
  end
254
+
22
255
  it 'can be initialized with a DSL block' do
23
- CalculationSet.new {call_me}
256
+ CalculationSet.new('my_set') {call_me}
24
257
  CalculationSet.called.should be_true
25
258
  end
259
+
26
260
  it 'can have terms added to all calculations' do
27
- cs=CalculationSet.new {
261
+ cs=CalculationSet.new('my_set') {
28
262
  all_calculations {
29
263
  drill {label :energetic}
30
264
  }
@@ -35,6 +269,7 @@ describe CalculationSet do
35
269
  }
36
270
  cs[:mycalc].drills.labels.should eql [:remarkably,:energetic]
37
271
  end
272
+
38
273
  it 'can make multiple calculations quickly, one for each usage' do
39
274
  mocker=AMEEMocker.new(self,:path=>'something')
40
275
  mocker.item_value_definitions.usages(['bybob','byfrank']).
@@ -42,7 +277,7 @@ describe CalculationSet do
42
277
  item_value_definition('first',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
43
278
  item_value_definition('second',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
44
279
  item_value_definition('third',['byfrank'],[],['bybob'],[],nil,nil,true,false,nil,"TEXT")
45
- cs=CalculationSet.new {
280
+ cs=CalculationSet.new('my_set') {
46
281
  calculations_all_usages('/something') { |usage|
47
282
  label usage.to_sym
48
283
  profiles_from_usage usage
@@ -51,4 +286,5 @@ describe CalculationSet do
51
286
  cs[:bybob].profiles.labels.should eql [:first,:second]
52
287
  cs[:byfrank].profiles.labels.should eql [:third]
53
288
  end
289
+
54
290
  end
@@ -1,53 +1,58 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
+
2
3
  describe Calculation do
3
4
 
5
+ before :all do
6
+ @calc = CalculationSet.find("transport")[:transport]
7
+ end
8
+
4
9
  it 'can create an instance' do
5
- Transport.should be_a Calculation
10
+ @calc.should be_a Calculation
6
11
  end
7
12
  it 'should have ordered terms, with labels' do
8
- Transport.terms.labels.should eql [:fuel,:size,:distance,:co2]
13
+ @calc.terms.labels.should eql [:fuel,:size,:distance,:co2]
9
14
  end
10
15
  it 'should have amee paths for the terms' do
11
- Transport.terms.paths.should eql ['fuel','size','distance',:default]
16
+ @calc.terms.paths.should eql ['fuel','size','distance','default']
12
17
  end
13
18
  it 'should have human names for the terms' do
14
- Transport.terms.names.
19
+ @calc.terms.names.
15
20
  should eql ['Fuel Type','Vehicle Size','Distance Driven','Carbon Dioxide']
16
21
  end
17
22
  it 'should return the inputs' do
18
- Transport.inputs.labels.should eql [:fuel,:size,:distance]
23
+ @calc.inputs.labels.should eql [:fuel,:size,:distance]
19
24
  end
20
25
  it 'should return the outputs' do
21
- Transport.outputs.labels.should eql [:co2]
26
+ @calc.outputs.labels.should eql [:co2]
22
27
  end
23
28
  it 'should generate an discover URL' do
24
- Transport.discover_url.should eql 'http://discover.amee.com/categories/transport/car/generic'
29
+ @calc.discover_url.should eql 'http://discover.amee.com/categories/transport/car/generic'
25
30
  end
26
31
  it 'should redirect to discover URL' do
27
- Transport.explorer_url.should eql 'http://discover.amee.com/categories/transport/car/generic'
32
+ @calc.explorer_url.should eql 'http://discover.amee.com/categories/transport/car/generic'
28
33
  end
29
34
  it 'can return a term via []' do
30
- Transport[:co2].label.should eql :co2
35
+ @calc[:co2].label.should eql :co2
31
36
  end
32
37
  it 'when copied, should deep copy the values' do
33
- x=Transport.clone
38
+ x=@calc.clone
34
39
  x[:co2].value :somevalue
35
40
  x[:co2].value.should eql :somevalue
36
- Transport[:co2].value.should be_nil
41
+ @calc[:co2].value.should be_nil
37
42
  end
38
43
  it 'knows to get terms that come before or after others' do
39
- t=Transport.clone
44
+ t=@calc.clone
40
45
  t.before(:distance).labels.
41
46
  should eql [:fuel,:size]
42
47
  t.after(:distance).map(&:label).
43
48
  should eql [:co2]
44
49
  end
45
50
  it 'delegates selectors to terms list' do
46
- t=Transport.clone
51
+ t=@calc.clone
47
52
  t.drills.labels.should eql [:fuel,:size]
48
53
  end
49
54
  it 'can find its amee data category' do
50
- t=Transport.clone
55
+ t=@calc.clone
51
56
  mocker=AMEEMocker.new self,:path=>'transport/car/generic'
52
57
  mocker.data_category
53
58
  t.send(:amee_data_category).path.should eql '/data/transport/car/generic'
@@ -55,18 +60,18 @@ describe Calculation do
55
60
  it 'can find its amee item definition' do
56
61
  mocker=AMEEMocker.new self,:path=>'transport/car/generic'
57
62
  mocker.item_definition(:my_itemdef_name).data_category
58
- t=Transport.clone
63
+ t=@calc.clone
59
64
  t.send(:amee_item_definition).name.should eql :my_itemdef_name
60
65
  end
61
66
  it 'can give item value definition list' do
62
67
  mocker=AMEEMocker.new self,:path=>'transport/car/generic'
63
68
  mocker.item_value_definition('distance').item_value_definitions.
64
69
  item_definition.data_category
65
- t=Transport.clone
70
+ t=@calc.clone
66
71
  t.send(:amee_ivds).first.path.should eql 'distance'
67
72
  end
68
73
  it 'can memoise access to AMEE' do
69
- t=Transport.clone
74
+ t=@calc.clone
70
75
  #AMEE::Data::Category.get(connection, "/data#{path}")
71
76
  flexmock(AMEE::Data::Category).should_receive(:get).
72
77
  with(AMEE::DataAbstraction.connection,'/data/transport/car/generic').