amee-data-abstraction 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/CHANGELOG.txt +4 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +27 -0
- data/README.txt +188 -0
- data/Rakefile +102 -0
- data/VERSION +1 -0
- data/amee-data-abstraction.gemspec +115 -0
- data/examples/_calculator_form.erb +27 -0
- data/examples/calculation_controller.rb +16 -0
- data/init.rb +4 -0
- data/lib/amee-data-abstraction.rb +30 -0
- data/lib/amee-data-abstraction/calculation.rb +236 -0
- data/lib/amee-data-abstraction/calculation_set.rb +101 -0
- data/lib/amee-data-abstraction/drill.rb +63 -0
- data/lib/amee-data-abstraction/exceptions.rb +47 -0
- data/lib/amee-data-abstraction/input.rb +197 -0
- data/lib/amee-data-abstraction/metadatum.rb +58 -0
- data/lib/amee-data-abstraction/ongoing_calculation.rb +545 -0
- data/lib/amee-data-abstraction/output.rb +16 -0
- data/lib/amee-data-abstraction/profile.rb +108 -0
- data/lib/amee-data-abstraction/prototype_calculation.rb +350 -0
- data/lib/amee-data-abstraction/term.rb +506 -0
- data/lib/amee-data-abstraction/terms_list.rb +150 -0
- data/lib/amee-data-abstraction/usage.rb +90 -0
- data/lib/config/amee_units.rb +129 -0
- data/lib/core-extensions/class.rb +27 -0
- data/lib/core-extensions/hash.rb +43 -0
- data/lib/core-extensions/ordered_hash.rb +21 -0
- data/lib/core-extensions/proc.rb +15 -0
- data/rails/init.rb +32 -0
- data/spec/amee-data-abstraction/calculation_set_spec.rb +54 -0
- data/spec/amee-data-abstraction/calculation_spec.rb +75 -0
- data/spec/amee-data-abstraction/drill_spec.rb +38 -0
- data/spec/amee-data-abstraction/input_spec.rb +77 -0
- data/spec/amee-data-abstraction/metadatum_spec.rb +17 -0
- data/spec/amee-data-abstraction/ongoing_calculation_spec.rb +494 -0
- data/spec/amee-data-abstraction/profile_spec.rb +39 -0
- data/spec/amee-data-abstraction/prototype_calculation_spec.rb +256 -0
- data/spec/amee-data-abstraction/term_spec.rb +385 -0
- data/spec/amee-data-abstraction/terms_list_spec.rb +53 -0
- data/spec/config/amee_units_spec.rb +71 -0
- data/spec/core-extensions/class_spec.rb +25 -0
- data/spec/core-extensions/hash_spec.rb +44 -0
- data/spec/core-extensions/ordered_hash_spec.rb +12 -0
- data/spec/core-extensions/proc_spec.rb +12 -0
- data/spec/fixtures/electricity.rb +35 -0
- data/spec/fixtures/electricity_and_transport.rb +55 -0
- data/spec/fixtures/transport.rb +26 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +244 -0
- metadata +262 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Profile do
|
4
|
+
it 'defaults to be a text-box' do
|
5
|
+
i=Profile.new
|
6
|
+
i.text_box?.should be_true
|
7
|
+
end
|
8
|
+
it 'can know if it belongs to a particular usage' do
|
9
|
+
mocker=AMEEMocker.new self,:path=>'transport/car/generic'
|
10
|
+
mocker.item_value_definitions.
|
11
|
+
item_definition.data_category.
|
12
|
+
item_value_definition('distance',['someusage'],['someotherusage'])
|
13
|
+
t=Transport.clone
|
14
|
+
t[:distance].compulsory?('someusage').should eql true
|
15
|
+
t[:distance].compulsory?('someotherusage').should eql false
|
16
|
+
t[:distance].optional?('someotherusage').should eql true
|
17
|
+
t[:distance].optional?('someusage').should eql false
|
18
|
+
end
|
19
|
+
it 'can have choices' do
|
20
|
+
i=Profile.new{label :one; choices ['a','b']}
|
21
|
+
i.choices.should eql ['a','b']
|
22
|
+
i.interface.should eql :drop_down
|
23
|
+
end
|
24
|
+
it 'must have a chosen choice if it has a choice' do
|
25
|
+
i=Profile.new{label :one; choices ['a','b']}
|
26
|
+
i.choices.should eql ['a','b']
|
27
|
+
i.value 'a'
|
28
|
+
i.should be_valid
|
29
|
+
i.value 'c'
|
30
|
+
i.should_not be_valid
|
31
|
+
end
|
32
|
+
it 'doesn''t have to have choices' do
|
33
|
+
i=Profile.new{label :one}
|
34
|
+
i.choices.should be_nil
|
35
|
+
i.interface.should eql :text_box
|
36
|
+
i.value 'mark'
|
37
|
+
i.should be_valid
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
class PrototypeCalculation
|
4
|
+
def call_me
|
5
|
+
#stub, because flexmock doesn't work for new instances during constructor
|
6
|
+
@@called=true
|
7
|
+
end
|
8
|
+
cattr_accessor :called
|
9
|
+
end
|
10
|
+
describe PrototypeCalculation do
|
11
|
+
it 'can create an instance' do
|
12
|
+
Transport.should be_a PrototypeCalculation
|
13
|
+
end
|
14
|
+
it 'can be initialized with a DSL block' do
|
15
|
+
PrototypeCalculation.new {call_me}
|
16
|
+
PrototypeCalculation.called.should be_true
|
17
|
+
end
|
18
|
+
it 'can make a drill in the DSL block' do
|
19
|
+
pc=PrototypeCalculation.new {drill{label :alpha}}
|
20
|
+
pc[:alpha].should be_a Drill
|
21
|
+
end
|
22
|
+
it 'can''t make a DSL block term without a label' do
|
23
|
+
lambda{
|
24
|
+
pc=PrototypeCalculation.new {drill}
|
25
|
+
}.should raise_error Exceptions::DSL
|
26
|
+
end
|
27
|
+
it 'can make a profile item value in the DSL block' do
|
28
|
+
pc=PrototypeCalculation.new {profile{label :alpha}}
|
29
|
+
pc[:alpha].should be_a Profile
|
30
|
+
end
|
31
|
+
it 'can make an output in the DSL block' do
|
32
|
+
pc=PrototypeCalculation.new {output{label :alpha}}
|
33
|
+
pc[:alpha].should be_a Output
|
34
|
+
end
|
35
|
+
it 'can construct an ongoing calculation' do
|
36
|
+
Transport.begin_calculation.should be_a OngoingCalculation
|
37
|
+
end
|
38
|
+
it 'should make the terms of the ongoing calculation be their own instances' do
|
39
|
+
oc=Transport.begin_calculation
|
40
|
+
oc[:distance].value :somevalue
|
41
|
+
oc[:distance].value.should eql :somevalue
|
42
|
+
Transport[:distance].value.should be_nil
|
43
|
+
end
|
44
|
+
it 'should copy name, path, label when cloning ongoing' do
|
45
|
+
[:path,:name,:label].each do |property|
|
46
|
+
Transport.begin_calculation.send(property).should eql Transport.send(property)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
it 'can autogenerate drill terms for itself, based on talking to amee' do
|
50
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
51
|
+
mocker.itemdef_drills ['first','second','third']
|
52
|
+
mocker.item_value_definitions.
|
53
|
+
item_definition.data_category.
|
54
|
+
item_value_definition('first',[],[],[],nil,nil,nil,false,true).
|
55
|
+
item_value_definition('second',[],[],[],nil,nil,nil,false,true).
|
56
|
+
item_value_definition('third',[],[],[],nil,nil,nil,false,true)
|
57
|
+
pc=PrototypeCalculation.new {path '/something'; all_drills}
|
58
|
+
pc.drills.labels.should eql [:first,:second,:third]
|
59
|
+
pc.drills.names.should eql ['First','Second','Third']
|
60
|
+
end
|
61
|
+
it 'can autogenerate profile terms for itself' do
|
62
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
63
|
+
mocker.item_value_definitions.
|
64
|
+
item_definition.data_category.
|
65
|
+
item_value_definition('first',[],[],[],[],nil,nil,true,false,nil,"DOUBLE").
|
66
|
+
item_value_definition('second',[],[],[],[],:kg,nil,true,false,nil,"DOUBLE").
|
67
|
+
item_value_definition('third',[],[],[],[],nil,nil,true,false,nil,"DOUBLE")
|
68
|
+
pc=PrototypeCalculation.new {path '/something'; all_profiles}
|
69
|
+
pc.profiles.labels.should eql [:first,:second,:third]
|
70
|
+
pc.profiles.default_units.first.should be_nil
|
71
|
+
pc.profiles.default_units.compact.first.should be_a Quantify::Unit::Base
|
72
|
+
end
|
73
|
+
it 'can autogenerate profile terms for itself, based on a usage' do
|
74
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
75
|
+
mocker.item_value_definitions.
|
76
|
+
item_definition.data_category.
|
77
|
+
item_value_definition('first',['bybob'],[],[],[],nil,nil,true,false,nil,"DOUBLE").
|
78
|
+
item_value_definition('second',['bybob'],[],[],[],:MBTU,:year,true,false,nil,"DOUBLE").
|
79
|
+
item_value_definition('third',[],[],['bybob'],[],nil,nil,true,false,nil,"DOUBLE")
|
80
|
+
pc=PrototypeCalculation.new {path '/something'; profiles_from_usage('bybob')}
|
81
|
+
pc.profiles.labels.should eql [:first,:second]
|
82
|
+
pc.profiles.default_units.first.should be_nil
|
83
|
+
pc.profiles.default_units.compact.first.symbol.should eql 'MBTU'
|
84
|
+
pc.profiles.default_per_units.compact.first.symbol.should eql 'yr'
|
85
|
+
end
|
86
|
+
it 'can generate profile terms with choices' do
|
87
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
88
|
+
mocker.item_value_definitions.
|
89
|
+
item_definition.data_category.
|
90
|
+
item_value_definition('first',['bybob'],[],[],['a','b'],nil,nil,true,false,nil,"DOUBLE").
|
91
|
+
item_value_definition('second',['bybob'],[],[],[],nil,nil,true,false,nil,"DOUBLE").
|
92
|
+
item_value_definition('third',[],[],['bybob'],[],nil,nil,true,false,nil,"DOUBLE")
|
93
|
+
pc=PrototypeCalculation.new {path '/something'; profiles_from_usage('bybob')}
|
94
|
+
pc[:first].choices.should eql ['a','b']
|
95
|
+
pc[:first].interface.should eql :drop_down
|
96
|
+
pc[:second].choices.should be_empty
|
97
|
+
pc[:second].interface.should eql :text_box
|
98
|
+
end
|
99
|
+
it 'can generate profile terms with default values' do
|
100
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
101
|
+
mocker.item_value_definitions.
|
102
|
+
item_definition.data_category.
|
103
|
+
item_value_definition('first',['bybob'],[],[],['a','b'],nil,nil,true,false,"commercial","TEXT").
|
104
|
+
item_value_definition('second',['bybob'],[],[],[],nil,nil,true,false,1200,"INTEGER").
|
105
|
+
item_value_definition('third',['bybob'],[],[],[],nil,nil,true,false,nil,"DOUBLE")
|
106
|
+
pc=PrototypeCalculation.new {path '/something'; profiles_from_usage('bybob')}
|
107
|
+
pc[:first].value.should eql "commercial"
|
108
|
+
pc[:second].value.should eql 1200
|
109
|
+
pc[:third].value.should be_nil
|
110
|
+
end
|
111
|
+
it 'can generate profile terms with annotations' do
|
112
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
113
|
+
mocker.item_value_definitions.
|
114
|
+
item_definition.data_category.
|
115
|
+
item_value_definition('first',['bybob'],[],[],['a','b'],nil,nil,true,false,nil,"TEXT","Specify a number of something").
|
116
|
+
item_value_definition('second',['bybob'],[],[],[],nil,nil,true,false,nil,"TEXT","Specify a number of something else").
|
117
|
+
item_value_definition('third',['bybob'],[],[],[],nil,nil,true,false,nil,"TEXT")
|
118
|
+
pc=PrototypeCalculation.new {path '/something'; profiles_from_usage('bybob')}
|
119
|
+
pc[:first].note.should eql "Specify a number of something"
|
120
|
+
pc[:second].note.should eql "Specify a number of something else"
|
121
|
+
pc[:third].note.should be_nil
|
122
|
+
end
|
123
|
+
it 'can generate profile terms with types' do
|
124
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
125
|
+
mocker.item_value_definitions.
|
126
|
+
item_definition.data_category.
|
127
|
+
item_value_definition('first',['bybob'],[],[],['a','b'],nil,nil,true,false,nil,"DECIMAL").
|
128
|
+
item_value_definition('second',['bybob'],[],[],[],nil,nil,true,false,nil,"TEXT").
|
129
|
+
item_value_definition('third',['bybob'],[],[],[],nil,nil,true,false,'true',"BOOLEAN")
|
130
|
+
pc=PrototypeCalculation.new {path '/something'; profiles_from_usage('bybob')}
|
131
|
+
pc[:first].type.should eql "decimal"
|
132
|
+
pc[:second].type.should eql "text"
|
133
|
+
pc[:third].type.should eql "boolean"
|
134
|
+
pc[:third].value.should be_true
|
135
|
+
end
|
136
|
+
it 'can select terms by usage with a longer list' do
|
137
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
138
|
+
mocker.item_value_definitions.
|
139
|
+
item_definition.data_category.
|
140
|
+
item_value_definition('first',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
|
141
|
+
item_value_definition('second',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
|
142
|
+
item_value_definition('third',['byfrank'],[],['bybob'],[],nil,nil,true,false,nil,"TEXT")
|
143
|
+
pc=PrototypeCalculation.new {path '/something'; all_profiles ; fixed_usage 'bybob'}
|
144
|
+
pc.profiles.in_use.labels.should eql [:first,:second]
|
145
|
+
pc.profiles.out_of_use.labels.should eql [:third]
|
146
|
+
pc.fixed_usage 'byfrank'
|
147
|
+
pc.profiles.out_of_use.labels.should eql [:first,:second]
|
148
|
+
pc.profiles.in_use.labels.should eql [:third]
|
149
|
+
end
|
150
|
+
it 'can generate itself with dynamic usage dropdown' do
|
151
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
152
|
+
mocker.item_value_definitions.
|
153
|
+
item_definition.data_category.
|
154
|
+
item_value_definition('first',['bybob'],[],'byfrank',[],:kg,:mi,true,false,nil,"TEXT").
|
155
|
+
item_value_definition('second',['bybob'],[],'byfrank',[],:km,nil,true,false,nil,"TEXT").
|
156
|
+
item_value_definition('third',['byfrank'],[],['bybob'],[],:lb,:h,true,false,nil,"TEXT")
|
157
|
+
pc=PrototypeCalculation.new {path '/something'; usage{ value 'bybob'}}
|
158
|
+
pc.profiles.labels.should eql [:first,:second,:third]
|
159
|
+
pc.profiles.visible.labels.should eql [:first,:second]
|
160
|
+
pc.terms.labels.should eql [:usage,:first,:second,:third]
|
161
|
+
pc.terms.default_units.first.should be_nil
|
162
|
+
pc.terms.default_units[1].should be_a Quantify::Unit::Base
|
163
|
+
pc.terms.default_units[1].name.should eql 'kilogram'
|
164
|
+
pc.terms.default_per_units.first.should be_nil
|
165
|
+
pc.terms.default_per_units[1].should be_a Quantify::Unit::Base
|
166
|
+
pc.terms.default_per_units[1].name.should eql 'mile'
|
167
|
+
pc.terms.default_per_units[2].should be_nil
|
168
|
+
end
|
169
|
+
it 'can generate itself with outputs' do
|
170
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
171
|
+
mocker.return_value_definitions.
|
172
|
+
item_definition.data_category.
|
173
|
+
return_value_definition('first',:kg,:mi).
|
174
|
+
return_value_definition('second',:km).
|
175
|
+
return_value_definition('third')
|
176
|
+
pc=PrototypeCalculation.new {path '/something'; all_outputs}
|
177
|
+
pc.outputs.labels.should eql [:first,:second,:third]
|
178
|
+
pc.terms.default_units.first.should be_a Quantify::Unit::Base
|
179
|
+
pc.terms.default_units.first.name.should eql 'kilogram'
|
180
|
+
pc.terms.default_per_units.first.should be_a Quantify::Unit::Base
|
181
|
+
pc.terms.default_per_units.first.name.should eql 'mile'
|
182
|
+
pc.terms.default_units[2].should be_nil
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'can generate itself with everything' do
|
186
|
+
mocker=AMEEMocker.new(self,:path=>'something')
|
187
|
+
mocker.itemdef_drills ['first','second','third']
|
188
|
+
mocker.return_value_definitions.
|
189
|
+
item_value_definitions.
|
190
|
+
item_definition.data_category.
|
191
|
+
item_value_definition('first',['bybob','byfrank'],[],[],[],nil,nil,false,true,nil,"TEXT").
|
192
|
+
item_value_definition('second',['bybob','byfrank'],[],[],[],nil,nil,false,true,nil,"TEXT").
|
193
|
+
item_value_definition('third',['bybob','byfrank'],[],[],[],nil,nil,false,true,nil,"TEXT").
|
194
|
+
item_value_definition('fourth',['bybob'],[],'byfrank',[],nil,nil,true,false,nil,"TEXT").
|
195
|
+
item_value_definition('fifth',['bybob'],[],'byfrank',[],:lb,nil,true,false,nil,"TEXT").
|
196
|
+
item_value_definition('sixth',['byfrank'],[],['bybob'],[],nil,nil,true,false,nil,"TEXT").
|
197
|
+
return_value_definition('seventh').
|
198
|
+
return_value_definition('eighth').
|
199
|
+
return_value_definition('ninth',:mi,:h)
|
200
|
+
pc=PrototypeCalculation.new {
|
201
|
+
path '/something';
|
202
|
+
terms_from_amee_dynamic_usage 'bybob'}
|
203
|
+
pc.terms.labels.should eql [:usage,
|
204
|
+
:first,:second,:third,
|
205
|
+
:fourth,:fifth,:sixth,
|
206
|
+
:seventh,:eighth,:ninth
|
207
|
+
]
|
208
|
+
pc.terms.names.should eql ['Usage',
|
209
|
+
'First','Second','Third',
|
210
|
+
'Fourth','Fifth','Sixth',
|
211
|
+
'Seventh','Eighth','Ninth'
|
212
|
+
]
|
213
|
+
pc.terms.visible.labels.should eql [:usage,
|
214
|
+
:first,:second,:third,
|
215
|
+
:fourth,:fifth,
|
216
|
+
:seventh,:eighth,:ninth
|
217
|
+
]
|
218
|
+
pc.terms.default_units.compact.first.should be_a Quantify::Unit::Base
|
219
|
+
pc.terms.default_units.compact.map(&:name).should include 'pound'
|
220
|
+
pc.terms.default_units.compact.map(&:name).should include 'mile'
|
221
|
+
pc.terms.default_per_units.compact.first.should be_a Quantify::Unit::Base
|
222
|
+
pc.terms.default_per_units.compact.map(&:name).should include 'hour'
|
223
|
+
end
|
224
|
+
it 'transfers memoised amee information to constructed ongoing calculations' do
|
225
|
+
t=Transport.clone
|
226
|
+
flexmock(AMEE::Data::Category).should_receive(:get).
|
227
|
+
with(AMEE::DataAbstraction.connection,'/data/transport/car/generic').
|
228
|
+
once.and_return(true)
|
229
|
+
t.send(:amee_data_category)
|
230
|
+
t.begin_calculation.send(:amee_data_category)
|
231
|
+
end
|
232
|
+
it 'can auto-create start and end date metadata' do
|
233
|
+
t=Transport.clone
|
234
|
+
t.instance_eval{
|
235
|
+
start_and_end_dates
|
236
|
+
}
|
237
|
+
t.metadata.labels.should eql [:start_date,:end_date]
|
238
|
+
t[:start_date].interface.should eql :date
|
239
|
+
t[:start_date].date?.should be_true
|
240
|
+
t.date.labels.should eql [:start_date,:end_date]
|
241
|
+
end
|
242
|
+
it 'can correct a dodgy term' do
|
243
|
+
pc=PrototypeCalculation.new {
|
244
|
+
metadatum{ label :frank }
|
245
|
+
correcting(:frank) {name "Bob"}
|
246
|
+
}
|
247
|
+
pc[:frank].name.should eql "Bob"
|
248
|
+
end
|
249
|
+
it 'lets corrections on missing terms go' do
|
250
|
+
lambda{pc=PrototypeCalculation.new {
|
251
|
+
metadatum{ label :sid }
|
252
|
+
correcting(:frank) {name "Bob"}
|
253
|
+
}}.should_not raise_error
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
@@ -0,0 +1,385 @@
|
|
1
|
+
require File.dirname(File.dirname(__FILE__)) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
class Term
|
4
|
+
def call_me
|
5
|
+
#stub, because flexmock doesn't work for new instances during constructor
|
6
|
+
@@called=true
|
7
|
+
end
|
8
|
+
cattr_accessor :called
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Term do
|
12
|
+
it 'can be initialized via DSL block' do
|
13
|
+
Term.new {call_me}
|
14
|
+
Term.called.should be_true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has label" do
|
18
|
+
Term.new {label :hello}.label.should eql :hello
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has name" do
|
22
|
+
Term.new {name :hello}.name.should eql :hello
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has path" do
|
26
|
+
Term.new {path :hello}.path.should eql :hello
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has note" do
|
30
|
+
Term.new {note 'hello'}.note.should eql 'hello'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has parent' do
|
34
|
+
Transport[:distance].parent.should eql Transport
|
35
|
+
end
|
36
|
+
it "has name defaulting to label" do
|
37
|
+
Term.new {label :hello}.name.should eql 'Hello'
|
38
|
+
Term.new {label :hello ; name 'goodbye'}.name.should eql 'goodbye'
|
39
|
+
Term.new {name 'goodbye' ; label :hello }.name.should eql 'goodbye'
|
40
|
+
end
|
41
|
+
it 'has path defaulting to label' do
|
42
|
+
Term.new {label :hello}.path.should eql 'hello'
|
43
|
+
Term.new {label :hello ; path 'goodbye'}.path.should eql 'goodbye'
|
44
|
+
Term.new {path 'goodbye' ; label :hello }.path.should eql 'goodbye'
|
45
|
+
end
|
46
|
+
it 'has value' do
|
47
|
+
Term.new {value :hello}.value.should eql :hello
|
48
|
+
end
|
49
|
+
it 'knows if it is set' do
|
50
|
+
Term.new {value :hello}.set?.should be_true
|
51
|
+
Term.new.set?.should be_false
|
52
|
+
end
|
53
|
+
it 'has interface' do
|
54
|
+
Term.new {interface :text_box}.interface.should eql :text_box
|
55
|
+
end
|
56
|
+
it 'raises exception on invalid interface' do
|
57
|
+
lambda{Term.new {interface :bobby}}.should raise_error Exceptions::InvalidInterface
|
58
|
+
end
|
59
|
+
it 'knows what it''s interface is' do
|
60
|
+
Term.new {interface :text_box}.text_box?.should be_true
|
61
|
+
Term.new {interface :text_box}.drop_down?.should be_false
|
62
|
+
Term.new {interface :drop_down}.text_box?.should be_false
|
63
|
+
Term.new {interface :drop_down}.drop_down?.should be_true
|
64
|
+
end
|
65
|
+
it 'can be enabled or disabled' do
|
66
|
+
t=Term.new
|
67
|
+
t.enabled?.should be_true
|
68
|
+
t.disabled?.should be_false
|
69
|
+
t.disable!
|
70
|
+
t.enabled?.should be_false
|
71
|
+
t.disabled?.should be_true
|
72
|
+
t.enable!
|
73
|
+
t.enabled?.should be_true
|
74
|
+
t.disabled?.should be_false
|
75
|
+
end
|
76
|
+
it 'can be visible or invisible' do
|
77
|
+
t=Term.new
|
78
|
+
t.visible?.should be_true
|
79
|
+
t.hidden?.should be_false
|
80
|
+
t.hide!
|
81
|
+
t.visible?.should be_false
|
82
|
+
t.hidden?.should be_true
|
83
|
+
t.show!
|
84
|
+
t.visible?.should be_true
|
85
|
+
t.hidden?.should be_false
|
86
|
+
end
|
87
|
+
it 'knows which terms come before or after it' do
|
88
|
+
Transport.terms.
|
89
|
+
select{|x|x.before?(:distance)}.map(&:label).
|
90
|
+
should eql [:fuel,:size]
|
91
|
+
Transport.terms.
|
92
|
+
select{|x|x.after?(:distance)}.map(&:label).
|
93
|
+
should eql [:co2]
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should respond to unit methods" do
|
97
|
+
Term.new.methods.should include "unit","per_unit","default_unit","default_per_unit",
|
98
|
+
"alternative_units","alternative_per_units"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "has no default unit if none declared" do
|
102
|
+
Term.new {path :hello}.default_unit.should be_nil
|
103
|
+
Term.new {path :hello}.default_per_unit.should be_nil
|
104
|
+
Term.new {path :hello}.unit.should be_nil
|
105
|
+
Term.new {path :hello}.per_unit.should be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
it "has default unit if specified" do
|
109
|
+
Term.new {path :hello; default_unit :kg}.default_unit.name.should == 'kilogram'
|
110
|
+
Term.new {path :hello; default_per_unit :kWh}.default_per_unit.pluralized_name.should == 'kilowatt hours'
|
111
|
+
Term.new {path :hello; default_unit :kg}.default_unit.should be_a Quantify::Unit::SI
|
112
|
+
end
|
113
|
+
|
114
|
+
it "has current unit defaulting to default unit if none specified" do
|
115
|
+
Term.new {path :hello; default_unit :kg}.unit.name.should == 'kilogram'
|
116
|
+
Term.new {path :hello; default_per_unit :kWh}.per_unit.pluralized_name.should == 'kilowatt hours'
|
117
|
+
Term.new {path :hello; default_unit :kg}.unit.should be_a Quantify::Unit::SI
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should have no alternative units if none specified and no default" do
|
121
|
+
term = Term.new {path :hello}
|
122
|
+
term.default_unit.should be_nil
|
123
|
+
term.unit.should be_nil
|
124
|
+
term.default_per_unit.should be_nil
|
125
|
+
term.per_unit.should be_nil
|
126
|
+
term.alternative_units.should be_nil
|
127
|
+
term.alternative_per_units.should be_nil
|
128
|
+
end
|
129
|
+
|
130
|
+
it "has default unit alternatives if none specified" do
|
131
|
+
term = Term.new {path :hello; default_unit :kg; default_per_unit :kWh}
|
132
|
+
units = term.alternative_units.map(&:name)
|
133
|
+
units.should include "gigagram", "pound", "tonne"
|
134
|
+
per_units = term.alternative_per_units.map(&:name)
|
135
|
+
per_units.should include "joule", "british thermal unit", "megawatt hour"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "has limited set of alternative units if specified" do
|
139
|
+
term = Term.new {path :hello; default_unit :kg; alternative_units :t, :ton_us, :lb}
|
140
|
+
units = term.alternative_units.map(&:name)
|
141
|
+
units.should include "tonne", "pound", "short ton"
|
142
|
+
units.should_not include "gigagram", "ounce", "gram"
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should raise error when specifying incompatible alternative units" do
|
146
|
+
lambda{Term.new {path :hello; default_unit :kg; alternative_units :kWh, :km}}.should raise_error
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should make a clone" do
|
150
|
+
term = Term.new {path :hello; default_unit :kg; alternative_units :t, :ton_us, :lb}
|
151
|
+
term.path.should eql :hello
|
152
|
+
term.default_unit.should be_a Quantify::Unit::Base
|
153
|
+
original_unit_instance = term.default_unit
|
154
|
+
term.default_unit.symbol.should eql 'kg'
|
155
|
+
new_term = term.clone
|
156
|
+
new_term.path.should eql :hello
|
157
|
+
new_term.default_unit.should be_a Quantify::Unit::Base
|
158
|
+
new_term.default_unit.symbol.should eql 'kg'
|
159
|
+
new_unit_instance = new_term.default_unit
|
160
|
+
original_unit_instance.should_not eql new_unit_instance
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should represent term as string with unit symbol if no argument provided" do
|
164
|
+
Term.new {path :hello; value 12; default_unit :kg}.to_s.should == '12 kg'
|
165
|
+
Term.new {path :hello; value 12; default_unit :kg; per_unit :h}.to_s.should == '12 kg h^-1'
|
166
|
+
Term.new {path :hello; value 12; per_unit :h}.to_s.should == '12 h^-1'
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should represent term as string with unit label" do
|
170
|
+
Term.new {path :hello; value 12; default_unit :kg}.to_s(:label).should == '12 kg'
|
171
|
+
Term.new {path :hello; value 12; default_unit :kg; per_unit :h}.to_s(:label).should == '12 kg/h'
|
172
|
+
Term.new {path :hello; value 12; per_unit :h}.to_s(:label).should == '12 h^-1'
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should represent term as string with unit name" do
|
176
|
+
Term.new {path :hello; value 12; default_unit :kg}.to_s(:name).should == '12 kilogram'
|
177
|
+
Term.new {path :hello; value 12; default_unit :kg; per_unit :h}.to_s(:name).should == '12 kilogram per hour'
|
178
|
+
Term.new {path :hello; value 12; per_unit :h}.to_s(:name).should == '12 per hour'
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should represent term as string with unit pluralized name" do
|
182
|
+
Term.new {path :hello; value 12; default_unit :kg}.to_s(:pluralized_name).should == '12 kilograms'
|
183
|
+
Term.new {path :hello; value 12; default_unit :kg; per_unit :h}.to_s(:pluralized_name).should == '12 kilograms per hour'
|
184
|
+
Term.new {path :hello; value 12; per_unit :h}.to_s(:pluralized_name).should == '12 per hour'
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should be recognised as numeric" do
|
188
|
+
Term.new {path :hello; value 12; default_unit :kg}.has_numeric_value?.should be_true
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should be recognised as non numeric" do
|
192
|
+
Term.new {path :hello; value 'bob'; default_unit :kg}.has_numeric_value?.should be_false
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should convert the input to a String if the type is specified as such" do
|
196
|
+
Term.new {type :string; value 54}.value.should == "54"
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should convert the input to a Fixnum if the type is specified as such" do
|
200
|
+
Term.new {type :fixnum; value '54'}.value.should == 54
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should convert the input to a Float if the type is specified as such" do
|
204
|
+
Term.new {type :float; value '54'}.value.should == 54.0
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should convert the input to a Date if the type is specified as such" do
|
208
|
+
Term.new {type :date; value '2011-01-01'}.value.should == Date.parse("2011-01-01")
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should convert the input to a Time if the type is specified as such" do
|
212
|
+
Term.new {type :time; value "2011-01-01 10:00:00"}.value.should == Time.parse("2011-01-01 10:00:00")
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should convert the input to a DateTime if the type is specified as such" do
|
216
|
+
now = DateTime.now
|
217
|
+
Term.new {type :datetime; value now.to_s}.value.should === now
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should store the pre cast value" do
|
221
|
+
term = Term.new {type :float; value '54'}
|
222
|
+
term.value.should == 54.0
|
223
|
+
term.value_before_cast.should == "54"
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should return self if no unit or per unit attribute" do
|
227
|
+
@term = Term.new { value 20 }
|
228
|
+
@term.unit.should be_nil
|
229
|
+
@term.per_unit.should be_nil
|
230
|
+
@term.value.should eql 20
|
231
|
+
new_term = @term.convert_unit(:unit => :t)
|
232
|
+
new_term.should === @term
|
233
|
+
new_term.value.should eql 20
|
234
|
+
new_term.unit.should be_nil
|
235
|
+
new_term.per_unit.should be_nil
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should return self if not a numeric unit" do
|
239
|
+
@term = Term.new { value 'plane'; unit :kg }
|
240
|
+
@term.unit.label.should eql 'kg'
|
241
|
+
@term.value.should eql 'plane'
|
242
|
+
new_term = @term.convert_unit(:unit => :t)
|
243
|
+
new_term.should === @term
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should convert unit" do
|
247
|
+
@term = Term.new { value 20; unit :kg }
|
248
|
+
@term.unit.symbol.should eql 'kg'
|
249
|
+
@term.value.should eql 20
|
250
|
+
new_term = @term.convert_unit(:unit => :t)
|
251
|
+
new_term.unit.symbol.should eql 't'
|
252
|
+
new_term.value.should eql 0.020
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should convert per unit" do
|
256
|
+
@term = Term.new { value 20; unit :kg; per_unit :min }
|
257
|
+
@term.unit.symbol.should eql 'kg'
|
258
|
+
@term.per_unit.symbol.should eql 'min'
|
259
|
+
@term.value.should eql 20
|
260
|
+
new_term = @term.convert_unit(:per_unit => :h)
|
261
|
+
new_term.unit.symbol.should eql 'kg'
|
262
|
+
new_term.per_unit.symbol.should eql 'h'
|
263
|
+
new_term.value.should eql 1200.0
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should convert unit and per unit" do
|
267
|
+
@term = Term.new { value 20; unit :kg; per_unit :min }
|
268
|
+
@term.unit.symbol.should eql 'kg'
|
269
|
+
@term.per_unit.symbol.should eql 'min'
|
270
|
+
@term.value.should eql 20
|
271
|
+
new_term = @term.convert_unit( :unit => :t, :per_unit => :h )
|
272
|
+
new_term.unit.symbol.should eql 't'
|
273
|
+
new_term.per_unit.symbol.should eql 'h'
|
274
|
+
new_term.value.should eql 1.2000
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should convert unit if value a string" do
|
278
|
+
@term = Term.new { value "20"; unit :kg }
|
279
|
+
@term.unit.symbol.should eql 'kg'
|
280
|
+
@term.value.should eql "20"
|
281
|
+
new_term = @term.convert_unit(:unit => :t)
|
282
|
+
new_term.unit.symbol.should eql 't'
|
283
|
+
new_term.value.should eql 0.020
|
284
|
+
end
|
285
|
+
|
286
|
+
it "should convert per unit if value a string" do
|
287
|
+
@term = Term.new { value "20"; unit :kg; per_unit :min }
|
288
|
+
@term.unit.symbol.should eql 'kg'
|
289
|
+
@term.per_unit.symbol.should eql 'min'
|
290
|
+
@term.value.should eql "20"
|
291
|
+
new_term = @term.convert_unit(:per_unit => :h)
|
292
|
+
new_term.unit.symbol.should eql 'kg'
|
293
|
+
new_term.per_unit.symbol.should eql 'h'
|
294
|
+
new_term.value.should eql 1200.0
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should convert unit and per unit if value a string" do
|
298
|
+
@term = Term.new { value "20"; unit :kg; per_unit :min }
|
299
|
+
@term.unit.symbol.should eql 'kg'
|
300
|
+
@term.per_unit.symbol.should eql 'min'
|
301
|
+
@term.value.should eql "20"
|
302
|
+
new_term = @term.convert_unit( :unit => :t, :per_unit => :h )
|
303
|
+
new_term.unit.symbol.should eql 't'
|
304
|
+
new_term.per_unit.symbol.should eql 'h'
|
305
|
+
new_term.value.should eql 1.2000
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should raise error if trying to convert to non dimensionally equivalent unit" do
|
309
|
+
@term = Term.new { value 20; unit :kg; per_unit :min }
|
310
|
+
@term.unit.symbol.should eql 'kg'
|
311
|
+
@term.per_unit.symbol.should eql 'min'
|
312
|
+
@term.value.should eql 20
|
313
|
+
lambda{new_term = @term.convert_unit( :unit => :J, :per_unit => :h )}.should raise_error
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should raise error if trying to convert to non dimensionally equivalent unit" do
|
317
|
+
@term = Term.new { value 20; unit :kg; per_unit :min }
|
318
|
+
@term.unit.symbol.should eql 'kg'
|
319
|
+
@term.per_unit.symbol.should eql 'min'
|
320
|
+
@term.value.should eql 20
|
321
|
+
lambda{new_term = @term.convert_unit( :unit => :J, :per_unit => :h )}.should raise_error
|
322
|
+
end
|
323
|
+
|
324
|
+
describe "quantities" do
|
325
|
+
|
326
|
+
it "should convert term with unit to quantity object" do
|
327
|
+
@term = Term.new { value 20; unit :kg }
|
328
|
+
quantity = @term.to_quantity
|
329
|
+
quantity.unit.name.should eql "kilogram"
|
330
|
+
quantity.unit.symbol.should eql "kg"
|
331
|
+
quantity.value.should eql 20.0
|
332
|
+
quantity.to_s.should eql "20.0 kg"
|
333
|
+
end
|
334
|
+
|
335
|
+
it "should convert term with per unit to quantity object" do
|
336
|
+
@term = Term.new { value 20; per_unit :h }
|
337
|
+
quantity = @term.to_quantity
|
338
|
+
quantity.unit.name.should eql "per hour"
|
339
|
+
quantity.unit.symbol.should eql "h^-1"
|
340
|
+
quantity.value.should eql 20.0
|
341
|
+
quantity.to_s.should eql "20.0 h^-1"
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should convert term with unit and per unit to quantity object" do
|
345
|
+
@term = Term.new { value 20; unit :kg; per_unit :h }
|
346
|
+
quantity = @term.to_quantity
|
347
|
+
quantity.unit.name.should eql "kilogram per hour"
|
348
|
+
quantity.unit.symbol.should eql "kg h^-1"
|
349
|
+
quantity.value.should eql 20.0
|
350
|
+
quantity.to_s.should eql "20.0 kg h^-1"
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should return nil with no unit or per unit" do
|
354
|
+
@term = Term.new { value 20 }
|
355
|
+
quantity = @term.to_quantity
|
356
|
+
quantity.should eql 20
|
357
|
+
quantity.should be_a Integer
|
358
|
+
end
|
359
|
+
|
360
|
+
it "should return nil with non numeric term" do
|
361
|
+
@term = Term.new { value "taxi" }
|
362
|
+
quantity = @term.to_quantity
|
363
|
+
quantity.should be_nil
|
364
|
+
end
|
365
|
+
|
366
|
+
it "should convert term with unit to quantity object with alias method" do
|
367
|
+
@term = Term.new { value 20; unit :kg }
|
368
|
+
quantity = @term.to_q
|
369
|
+
quantity.unit.name.should eql "kilogram"
|
370
|
+
quantity.unit.symbol.should eql "kg"
|
371
|
+
quantity.value.should eql 20.0
|
372
|
+
quantity.to_s.should eql "20.0 kg"
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should convert term with per unit to quantity object with alias method" do
|
376
|
+
@term = Term.new { value 20; per_unit :h }
|
377
|
+
quantity = @term.to_q
|
378
|
+
quantity.unit.name.should eql "per hour"
|
379
|
+
quantity.unit.symbol.should eql "h^-1"
|
380
|
+
quantity.value.should eql 20.0
|
381
|
+
quantity.to_s.should eql "20.0 h^-1"
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|