amee-data-abstraction 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,4 +1,4 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Profile do
4
4
  it 'defaults to be a text-box' do
@@ -10,7 +10,7 @@ describe Profile do
10
10
  mocker.item_value_definitions.
11
11
  item_definition.data_category.
12
12
  item_value_definition('distance',['someusage'],['someotherusage'])
13
- t=Transport.clone
13
+ t=CalculationSet.find("transport")[:transport].clone
14
14
  t[:distance].compulsory?('someusage').should eql true
15
15
  t[:distance].compulsory?('someotherusage').should eql false
16
16
  t[:distance].optional?('someotherusage').should eql true
@@ -1,4 +1,4 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  class PrototypeCalculation
4
4
  def call_me
@@ -8,8 +8,12 @@ class PrototypeCalculation
8
8
  cattr_accessor :called
9
9
  end
10
10
  describe PrototypeCalculation do
11
+ before :all do
12
+ @calc = CalculationSet.find("transport")[:transport]
13
+ end
14
+
11
15
  it 'can create an instance' do
12
- Transport.should be_a PrototypeCalculation
16
+ @calc.should be_a PrototypeCalculation
13
17
  end
14
18
  it 'can be initialized with a DSL block' do
15
19
  PrototypeCalculation.new {call_me}
@@ -38,17 +42,17 @@ describe PrototypeCalculation do
38
42
  pc[:alpha].should be_a Output
39
43
  end
40
44
  it 'can construct an ongoing calculation' do
41
- Transport.begin_calculation.should be_a OngoingCalculation
45
+ @calc.begin_calculation.should be_a OngoingCalculation
42
46
  end
43
47
  it 'should make the terms of the ongoing calculation be their own instances' do
44
- oc=Transport.begin_calculation
48
+ oc=@calc.begin_calculation
45
49
  oc[:distance].value :somevalue
46
50
  oc[:distance].value.should eql :somevalue
47
- Transport[:distance].value.should be_nil
51
+ @calc[:distance].value.should be_nil
48
52
  end
49
53
  it 'should copy name, path, label when cloning ongoing' do
50
54
  [:path,:name,:label].each do |property|
51
- Transport.begin_calculation.send(property).should eql Transport.send(property)
55
+ @calc.begin_calculation.send(property).should eql @calc.send(property)
52
56
  end
53
57
  end
54
58
  it 'can autogenerate drill terms for itself, based on talking to amee' do
@@ -227,7 +231,7 @@ describe PrototypeCalculation do
227
231
  pc.terms.default_per_units.compact.map(&:name).should include 'hour'
228
232
  end
229
233
  it 'transfers memoised amee information to constructed ongoing calculations' do
230
- t=Transport.clone
234
+ t=@calc.clone
231
235
  flexmock(AMEE::Data::Category).should_receive(:get).
232
236
  with(AMEE::DataAbstraction.connection,'/data/transport/car/generic').
233
237
  once.and_return(true)
@@ -235,7 +239,7 @@ describe PrototypeCalculation do
235
239
  t.begin_calculation.send(:amee_data_category)
236
240
  end
237
241
  it 'can auto-create start and end date metadata' do
238
- t=Transport.clone
242
+ t=@calc.clone
239
243
  t.instance_eval{
240
244
  start_and_end_dates
241
245
  }
@@ -1,4 +1,4 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  class Term
4
4
  def call_me
@@ -9,6 +9,10 @@ class Term
9
9
  end
10
10
 
11
11
  describe Term do
12
+ before :all do
13
+ @calc = CalculationSet.find("transport")[:transport]
14
+ end
15
+
12
16
  it 'can be initialized via DSL block' do
13
17
  Term.new {call_me}
14
18
  Term.called.should be_true
@@ -30,8 +34,13 @@ describe Term do
30
34
  Term.new {note 'hello'}.note.should eql 'hello'
31
35
  end
32
36
 
37
+ it "has note with no '\"' character" do
38
+ Term.new {note 'hello "some quote"'}.note.should eql "hello 'some quote'"
39
+ end
40
+
41
+
33
42
  it 'has parent' do
34
- Transport[:distance].parent.should eql Transport
43
+ @calc[:distance].parent.should eql @calc
35
44
  end
36
45
  it "has name defaulting to label" do
37
46
  Term.new {label :hello}.name.should eql 'Hello'
@@ -85,17 +94,18 @@ describe Term do
85
94
  t.hidden?.should be_false
86
95
  end
87
96
  it 'knows which terms come before or after it' do
88
- Transport.terms.
97
+ @calc.terms.
89
98
  select{|x|x.before?(:distance)}.map(&:label).
90
99
  should eql [:fuel,:size]
91
- Transport.terms.
100
+ @calc.terms.
92
101
  select{|x|x.after?(:distance)}.map(&:label).
93
102
  should eql [:co2]
94
103
  end
95
104
 
96
105
  it "should respond to unit methods" do
97
106
  Term.new.methods.should include "unit","per_unit","default_unit","default_per_unit",
98
- "alternative_units","alternative_per_units"
107
+ "alternative_units","alternative_per_units", "unit_choices",
108
+ "per_unit_choices"
99
109
  end
100
110
 
101
111
  it "has no default unit if none declared" do
@@ -131,14 +141,48 @@ describe Term do
131
141
  term = Term.new {path :hello; default_unit :kg; default_per_unit :kWh}
132
142
  units = term.alternative_units.map(&:name)
133
143
  units.should include "gigagram", "pound", "tonne"
144
+ units.should_not include "kilogram", "kelvin"
134
145
  per_units = term.alternative_per_units.map(&:name)
135
146
  per_units.should include "joule", "british thermal unit", "megawatt hour"
147
+ per_units.should_not include "kilowatt hour"
148
+ end
149
+
150
+ it "has unit choices which include default and alternative" do
151
+ term = Term.new {path :hello; default_unit :kg; default_per_unit :kWh}
152
+ units = term.alternative_units.map(&:name)
153
+ units.should include "gigagram", "pound", "tonne"
154
+ units.should_not include "kilogram", "kelvin"
155
+
156
+ units = term.unit_choices.map(&:name)
157
+ units.first.should eql "kilogram"
158
+ units.should include "kilogram", "gigagram", "pound", "tonne"
159
+ units.should_not include "kelvin"
160
+
161
+ per_units = term.alternative_per_units.map(&:name)
162
+ per_units.should include "joule", "british thermal unit", "megawatt hour"
163
+ per_units.should_not include "kilowatt hour"
164
+
165
+ per_units = term.per_unit_choices.map(&:name)
166
+ per_units.first.should eql "kilowatt hour"
167
+ per_units.should include "kilowatt hour", "joule", "british thermal unit", "megawatt hour"
136
168
  end
137
169
 
138
170
  it "has limited set of alternative units if specified" do
139
171
  term = Term.new {path :hello; default_unit :kg; alternative_units :t, :ton_us, :lb}
140
172
  units = term.alternative_units.map(&:name)
141
173
  units.should include "tonne", "pound", "short ton"
174
+ units.should_not include "kilogram", "gigagram", "ounce", "gram"
175
+ end
176
+
177
+ it "has unit choices which include default and alternative with limited set of alternative units" do
178
+ term = Term.new {path :hello; default_unit :kg; alternative_units :t, :ton_us, :lb}
179
+ units = term.alternative_units.map(&:name)
180
+ units.should include "tonne", "pound", "short ton"
181
+ units.should_not include "kilogram", "gigagram", "ounce", "gram"
182
+
183
+ units = term.unit_choices.map(&:name)
184
+ units.first.should eql "kilogram"
185
+ units.should include "kilogram", "tonne", "pound", "short ton"
142
186
  units.should_not include "gigagram", "ounce", "gram"
143
187
  end
144
188
 
@@ -1,27 +1,31 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  describe TermsList do
4
+ before :all do
5
+ @calc = CalculationSet.find("transport")[:transport]
6
+ end
7
+
4
8
  it 'should be returned from calculations' do
5
- Transport.terms.should be_a TermsList
9
+ @calc.terms.should be_a TermsList
6
10
  end
7
11
  it 'should give properties' do
8
- Transport.terms.labels.should eql [:fuel,:size,:distance,:co2]
9
- Transport.terms.paths.should eql ['fuel','size','distance',:default]
10
- Transport.terms.names.should eql ['Fuel Type','Vehicle Size','Distance Driven','Carbon Dioxide']
12
+ @calc.terms.labels.should eql [:fuel,:size,:distance,:co2]
13
+ @calc.terms.paths.should eql ['fuel','size','distance','default']
14
+ @calc.terms.names.should eql ['Fuel Type','Vehicle Size','Distance Driven','Carbon Dioxide']
11
15
  end
12
16
  it 'should select by class' do
13
- Transport.terms.drills.labels.should eql [:fuel,:size]
14
- Transport.terms.profiles.labels.should eql [:distance]
15
- Transport.terms.outputs.labels.should eql [:co2]
17
+ @calc.terms.drills.labels.should eql [:fuel,:size]
18
+ @calc.terms.profiles.labels.should eql [:distance]
19
+ @calc.terms.outputs.labels.should eql [:co2]
16
20
  end
17
21
  it 'should select by property' do
18
- t=Transport.begin_calculation
22
+ t=@calc.begin_calculation
19
23
  t[:distance].value 5
20
24
  t.terms.set.labels.should eql [:distance]
21
25
  t.terms.unset.labels.should eql [:fuel,:size,:co2]
22
26
  end
23
27
  it 'should select by chain' do
24
- t=Transport.begin_calculation
28
+ t=@calc.begin_calculation
25
29
  t[:fuel].value 'diesel'
26
30
  t.terms.drills.set.labels.should eql [:fuel]
27
31
  t.terms.drills.unset.labels.should eql [:size]
@@ -29,7 +33,7 @@ describe TermsList do
29
33
  t.terms.unset.drills.labels.should eql [:size]
30
34
  end
31
35
  it 'should select by order' do
32
- t=Transport.clone
36
+ t=@calc.clone
33
37
  t.terms.before(:distance).labels.
34
38
  should eql [:fuel,:size]
35
39
  t.terms.after(:distance).labels.
@@ -40,7 +44,7 @@ describe TermsList do
40
44
  mocker.item_value_definitions.
41
45
  item_definition.data_category.
42
46
  item_value_definition('distance',['usage1'],['usage2'],['usage3'])
43
- t=Transport.clone
47
+ t=@calc.clone
44
48
  t.profiles.compulsory('usage1').labels.should eql [:distance]
45
49
  t.profiles.optional('usage2').labels.should eql [:distance]
46
50
  t.profiles.compulsory('usage2').labels.should be_empty
@@ -1,5 +1,4 @@
1
-
2
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
3
2
 
4
3
  describe "AMEE units" do
5
4
 
@@ -1,25 +1,25 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
- class Myclass
4
- attr_property :prop
5
- end
3
+ class Myclass
4
+ attr_property :prop
5
+ end
6
6
 
7
7
  describe Class do
8
8
  before :all do
9
9
  @it=Myclass.new
10
10
  end
11
- it 'can have a property defined' do
12
- @it.prop 5
13
- @it.prop.should eql 5
14
- end
15
- it 'can have a prop unset' do
16
- @it.prop 5
17
- @it.prop nil
18
- @it.prop.should be_nil
19
- end
20
- it 'does not unset by query' do
21
- @it.prop 5
22
- @it.prop
23
- @it.prop.should_not be_nil
24
- end
11
+ it 'can have a property defined' do
12
+ @it.prop 5
13
+ @it.prop.should eql 5
14
+ end
15
+ it 'can have a prop unset' do
16
+ @it.prop 5
17
+ @it.prop nil
18
+ @it.prop.should be_nil
19
+ end
20
+ it 'does not unset by query' do
21
+ @it.prop 5
22
+ @it.prop
23
+ @it.prop.should_not be_nil
24
+ end
25
25
  end
@@ -1,5 +1,4 @@
1
-
2
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
3
2
 
4
3
  describe Hash do
5
4
  before(:each) do
@@ -1,5 +1,4 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
2
-
1
+ require 'spec_helper'
3
2
 
4
3
  describe ActiveSupport::OrderedHash do
5
4
  it 'Can insert at start' do
@@ -1,4 +1,4 @@
1
- require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Proc do
4
4
  before :all do
@@ -1,4 +1,4 @@
1
- Electricity=AMEE::DataAbstraction::PrototypeCalculation.new { # The application has support for an electricity calculation. :electricity is the internal label used to refer to it
1
+ calculation { # The application has support for an electricity calculation. :electricity is the internal label used to refer to it
2
2
  label :electricity
3
3
  name "Electricity Consumption"
4
4
  path '/business/energy/electricity/grid'
@@ -28,7 +28,7 @@ Electricity=AMEE::DataAbstraction::PrototypeCalculation.new { # The application
28
28
 
29
29
  output { #A marv output value
30
30
  label :co2
31
- path :default #It's not a marv, use the default output
31
+ path 'default' #It's not a marv, use the default output
32
32
  name "Carbon Dioxide"
33
33
  }
34
34
  }
@@ -0,0 +1,53 @@
1
+ all_calculations {
2
+ metadatum {
3
+ label :department
4
+ choices %w{stuff things more_stuff meta_things}
5
+ }
6
+ }
7
+
8
+ calculation{
9
+ name 'electricity'
10
+ label :electricity
11
+ path '/business/energy/electricity/grid'
12
+ profile {
13
+ label :usage
14
+ name 'Electricity Used'
15
+ path 'energyPerTime'
16
+ }
17
+ drill {
18
+ label :country
19
+ path 'country'
20
+ fixed 'Argentina'
21
+ }
22
+ output {
23
+ label :co2
24
+ path 'default'
25
+ }
26
+ }
27
+
28
+ calculation {
29
+ name 'transport'
30
+ label :transport
31
+ path '/transport/car/generic'
32
+
33
+ drill {
34
+ path 'fuel'
35
+ label :fuel
36
+ name 'Fuel Type'
37
+ }
38
+ drill {
39
+ path 'size'
40
+ label :size
41
+ name 'Vehicle Size'
42
+ }
43
+ profile {
44
+ path 'distance'
45
+ label :distance
46
+ name 'Distance Driven'
47
+ }
48
+ output {
49
+ label :co2
50
+ path 'default'
51
+ name 'Carbon Dioxide'
52
+ }
53
+ }
@@ -1,4 +1,4 @@
1
- Transport=AMEE::DataAbstraction::PrototypeCalculation.new{
1
+ calculation {
2
2
  name 'transport'
3
3
  label :transport
4
4
  path '/transport/car/generic'
@@ -20,7 +20,7 @@ Transport=AMEE::DataAbstraction::PrototypeCalculation.new{
20
20
  }
21
21
  output {
22
22
  label :co2
23
- path :default
23
+ path 'default'
24
24
  name 'Carbon Dioxide'
25
25
  }
26
26
  }
@@ -0,0 +1,35 @@
1
+ calculation { # The application has support for an electricity calculation. :electricity is the internal label used to refer to it
2
+ label :electricity
3
+ name "Electricity Consumption"
4
+ path '/business/energy/electricity/grid'
5
+
6
+ drill {
7
+ fixed 'argentina' #Not to be unset, value pre-given
8
+ label :country #Name will default to label.humanize if not given
9
+ path 'country' #Some of the fields on the form are drill-downs, but the application doesn't need to display these differently
10
+ #type :autocompleting_text_box #default for a drill with entries is probably a dropdown
11
+ }
12
+
13
+ profile {
14
+ label :energy_used
15
+ # Symbol provided here is used in generating html ids for elements etc
16
+ path 'energyPerTime' #The amee profile item value corresponding to the field
17
+ name "Energy Used" #The display name used on the form
18
+ unit "kWh" #Default unit choice
19
+ interface :text_box #Probably not needed, as likely to be the default for profile item value unsets
20
+ validation lambda{|x|x.is_a? Float} #Probably not needed, as default can be deduced from PIV TYPE in API. Here as illustrative.
21
+ alternative_units :MWh, :MBTU, :BTU # If these are explcitly specified then the alternatives are limited to only these units. Otherwise all dimensionally equivalent units are available as alternatives by default
22
+ }
23
+
24
+ # Alternatively, the drill might be fixed
25
+ #permanent :country {
26
+ # drill_path 'country'
27
+ # value 'Argentina'
28
+
29
+ output { #A marv output value
30
+ label :co2
31
+ path 'default' #It's not a marv, use the default output
32
+ name "Carbon Dioxide"
33
+ }
34
+ }
35
+
data/spec/spec_helper.rb CHANGED
@@ -2,20 +2,37 @@ require 'rubygems'
2
2
  require 'spec'
3
3
  require 'rspec_spinner'
4
4
  require 'flexmock'
5
- $:.unshift(File.dirname(__FILE__) + '/../lib')
6
5
  require 'amee'
6
+
7
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
7
8
  require 'amee-data-abstraction'
8
9
 
10
+ # Fake up Rails.root to be fixtures directory
11
+ class Rails
12
+ def self.root
13
+ File.dirname(__FILE__) + '/fixtures'
14
+ end
15
+ def self.logger
16
+ nil
17
+ end
18
+ end
19
+
9
20
  Spec::Runner.configure do |config|
10
21
  config.mock_with :flexmock
22
+ config.after(:each) do
23
+ delete_lock_files
24
+ end
11
25
  end
12
26
 
13
- AMEE::DataAbstraction.connection=FlexMock.new('connection') #Global connection mock, shouldn't receive anything, as we mock the individual amee-ruby calls in the tests
14
-
15
- Dir.glob(File.dirname(__FILE__) + '/fixtures/*') do |filename|
16
- require filename
27
+ def delete_lock_files
28
+ config_dir = Dir.new("#{Rails.root}/config/calculations")
29
+ config_dir.each do |file|
30
+ File.delete("#{config_dir.path}/#{file}") if file =~ /lock/
31
+ end
17
32
  end
18
33
 
34
+ AMEE::DataAbstraction.connection=FlexMock.new('connection') #Global connection mock, shouldn't receive anything, as we mock the individual amee-ruby calls in the tests
35
+
19
36
  include AMEE::DataAbstraction
20
37
 
21
38
  class AMEEMocker
@@ -81,6 +98,21 @@ class AMEEMocker
81
98
  return self
82
99
  end
83
100
 
101
+ # This represents the skipping of drill choices which occur on an AMEE drill
102
+ # down when only one choice exists for a given drill - it skips to the next,
103
+ # offering the next set of choices or a uid. In these cases, the skipped drill
104
+ # is set as an automatic selection
105
+ def drill_with_skip(skipped_selections=[])
106
+ test.flexmock(AMEE::Data::DrillDown).
107
+ should_receive(:get).
108
+ with(connection,
109
+ "/data/#{path}/drill?#{selections.map{|k,v|"#{k}=#{v}"}.join('&')}").
110
+ at_least.once.
111
+ and_return(test.flexmock(:choices=>choices,:selections=>Hash[selections].merge(skipped_selections),
112
+ :data_item_uid=>dataitemuid))
113
+ return self
114
+ end
115
+
84
116
  def profile_list
85
117
  test.flexmock(AMEE::Profile::ProfileList).should_receive(:new).
86
118
  with(connection).at_least.once.
@@ -251,7 +283,7 @@ class AMEEMocker
251
283
  def update
252
284
  test.flexmock(AMEE::Profile::Item).should_receive(:update).
253
285
  with(connection,pipath,
254
- {:get_item=>false}.merge(existing).merge(params)).
286
+ {:get_item=>true}.merge(existing).merge(params)).
255
287
  at_least.once
256
288
  return self
257
289
  end