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.
- data/CHANGELOG.txt +3 -0
- data/README.txt +28 -15
- data/Rakefile +1 -16
- data/VERSION +1 -1
- data/amee-data-abstraction.gemspec +6 -5
- data/lib/amee-data-abstraction/calculation.rb +1 -1
- data/lib/amee-data-abstraction/calculation_set.rb +152 -10
- data/lib/amee-data-abstraction/drill.rb +8 -2
- data/lib/amee-data-abstraction/input.rb +24 -0
- data/lib/amee-data-abstraction/ongoing_calculation.rb +13 -9
- data/lib/amee-data-abstraction/term.rb +31 -13
- data/spec/amee-data-abstraction/calculation_set_spec.rb +244 -8
- data/spec/amee-data-abstraction/calculation_spec.rb +23 -18
- data/spec/amee-data-abstraction/drill_spec.rb +34 -7
- data/spec/amee-data-abstraction/input_spec.rb +113 -73
- data/spec/amee-data-abstraction/metadatum_spec.rb +1 -1
- data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +45 -29
- data/spec/amee-data-abstraction/profile_spec.rb +2 -2
- data/spec/amee-data-abstraction/prototype_calculation_spec.rb +12 -8
- data/spec/amee-data-abstraction/term_spec.rb +49 -5
- data/spec/amee-data-abstraction/terms_list_spec.rb +16 -12
- data/spec/config/amee_units_spec.rb +1 -2
- data/spec/core-extensions/class_spec.rb +18 -18
- data/spec/core-extensions/hash_spec.rb +1 -2
- data/spec/core-extensions/ordered_hash_spec.rb +1 -2
- data/spec/core-extensions/proc_spec.rb +1 -1
- data/spec/fixtures/{electricity.rb → config/calculations/electricity.rb} +2 -2
- data/spec/fixtures/config/calculations/electricity_and_transport.rb +53 -0
- data/spec/fixtures/{transport.rb → config/calculations/transport.rb} +2 -2
- data/spec/fixtures/config/electricity.rb +35 -0
- data/spec/spec_helper.rb +38 -6
- metadata +8 -7
- data/spec/fixtures/electricity_and_transport.rb +0 -55
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
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=
|
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
|
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
|
-
|
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
|
-
|
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
|
48
|
+
oc=@calc.begin_calculation
|
45
49
|
oc[:distance].value :somevalue
|
46
50
|
oc[:distance].value.should eql :somevalue
|
47
|
-
|
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
|
-
|
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
|
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
|
242
|
+
t=@calc.clone
|
239
243
|
t.instance_eval{
|
240
244
|
start_and_end_dates
|
241
245
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
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
|
-
|
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
|
-
|
97
|
+
@calc.terms.
|
89
98
|
select{|x|x.before?(:distance)}.map(&:label).
|
90
99
|
should eql [:fuel,:size]
|
91
|
-
|
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
|
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
|
-
|
9
|
+
@calc.terms.should be_a TermsList
|
6
10
|
end
|
7
11
|
it 'should give properties' do
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
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
|
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
|
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
|
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,25 +1,25 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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,4 +1,4 @@
|
|
1
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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=>
|
286
|
+
{:get_item=>true}.merge(existing).merge(params)).
|
255
287
|
at_least.once
|
256
288
|
return self
|
257
289
|
end
|