amee-data-abstraction 1.0.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.
- 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
|