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,506 @@
|
|
1
|
+
# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
|
2
|
+
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
|
+
|
4
|
+
# :title: Class: AMEE::DataAbstraction::Term
|
5
|
+
require 'bigdecimal'
|
6
|
+
|
7
|
+
module AMEE
|
8
|
+
module DataAbstraction
|
9
|
+
|
10
|
+
# Base class for representing quantities which are inputs to, outputs of, or
|
11
|
+
# metadata associated with, calculations. Typically several instances of the
|
12
|
+
# <i>Term</i> class (or subclasses) will be associated with instances of the
|
13
|
+
# <i>Calculation</i> class or its subclasses (<i>PrototypeCalculation</i>,
|
14
|
+
# <i>OngoingCalculation</i>).
|
15
|
+
#
|
16
|
+
# Instances of <i>Term</i> are represented by several primary attributes:
|
17
|
+
#
|
18
|
+
# label:: Symbol representing the unique, machine-readable name for the
|
19
|
+
# term (<b>required</b>)
|
20
|
+
#
|
21
|
+
# value:: In principle, any object which represent the desired value
|
22
|
+
# which the term represents
|
23
|
+
#
|
24
|
+
# name:: String representing a human-readable name for the term
|
25
|
+
#
|
26
|
+
# path:: String representing the AMEE platform path to the AMEE item
|
27
|
+
# value definition which is associated with <tt>self</tt>.
|
28
|
+
# This attribute is required only if the term represents an
|
29
|
+
# item value definition in the AMEE platform
|
30
|
+
#
|
31
|
+
# Other available attribute-like methods include <tt>type</tt>,
|
32
|
+
# <tt>interface</tt>, <tt>note</tt>, <tt>unit</tt>, <tt>per_unit</tt>,
|
33
|
+
# <tt>default_unit</tt>, <tt>default_per_unit</tt> and <tt>parent</tt>.
|
34
|
+
#
|
35
|
+
# Subclasses of the <i>Term</i> correspond to:
|
36
|
+
# * <i>Input</i>
|
37
|
+
# * * <i>Profile</i> -- corresponds to an AMEE profile item value
|
38
|
+
# * * <i>Drill</i> -- corresponds to an AMEE drill down choice
|
39
|
+
# * * <i>Usage</i> -- corresponds to a (runtime adjustable) AMEE usage choice
|
40
|
+
# * * <i>Metadatum</i> -- represents other arbitrary inputs
|
41
|
+
# * <i>Output</i> -- corresponds to an AMEE return value
|
42
|
+
#
|
43
|
+
class Term
|
44
|
+
|
45
|
+
public
|
46
|
+
|
47
|
+
# Symbol representing the unique (within the parent calculation), machine-
|
48
|
+
# readable name for <tt>self</tt>. Set a value by passing an argument.
|
49
|
+
# Retrieve a value by calling without an argument, e.g.,
|
50
|
+
#
|
51
|
+
# my_term.label :distance
|
52
|
+
#
|
53
|
+
# my_term.label #=> :distance
|
54
|
+
#
|
55
|
+
attr_property :label
|
56
|
+
|
57
|
+
# String representing a human-readable name for <tt>self</tt>. Set a value
|
58
|
+
# by passing an argument. Retrieve a value by calling without an argument,
|
59
|
+
# e.g.,
|
60
|
+
#
|
61
|
+
# my_term.name 'Distance driven'
|
62
|
+
#
|
63
|
+
# my_term.name #=> 'Distance driven'
|
64
|
+
#
|
65
|
+
attr_property :name
|
66
|
+
|
67
|
+
# Symbol representing the class the value should be parsed to. If
|
68
|
+
# omitted a string is assumed, e.g.:
|
69
|
+
#
|
70
|
+
# my_term.type :integer
|
71
|
+
# my_term.value "12"
|
72
|
+
# my_term.value # => 12
|
73
|
+
# my_term.value_before_cast # => "12"
|
74
|
+
#
|
75
|
+
attr_property :type
|
76
|
+
|
77
|
+
# String representing a the AMEE platform path for <tt>self</tt>. Set a
|
78
|
+
# value by passing an argument. Retrieve a value by calling without an
|
79
|
+
# argument, e.g.,
|
80
|
+
#
|
81
|
+
# my_term.path 'mass'
|
82
|
+
#
|
83
|
+
# my_term.path #=> 'mass'
|
84
|
+
#
|
85
|
+
attr_property :path
|
86
|
+
|
87
|
+
# String representing an annotation for <tt>self</tt>. Set a value by
|
88
|
+
# passing an argument. Retrieve a value by calling without an argument,
|
89
|
+
# e.g.,
|
90
|
+
#
|
91
|
+
# my_term.note 'Enter the mass of cement produced in the reporting period'
|
92
|
+
#
|
93
|
+
# my_term.note #=> 'Enter the mass of cement ...'
|
94
|
+
#
|
95
|
+
attr_property :note
|
96
|
+
|
97
|
+
# Symbol representing the owning parent calculation of <tt>self</tt>. Set
|
98
|
+
# the owning calculation object by passing as an argument. Retrieve it by
|
99
|
+
# calling without an argument, e.g.,
|
100
|
+
#
|
101
|
+
# my_calculation = <AMEE::DataAbstraction::OngoingCalculation ... >
|
102
|
+
#
|
103
|
+
# my_term.parent my_calculation
|
104
|
+
#
|
105
|
+
# my_term.parent #=> <AMEE::DataAbstraction::OngoingCalculation ... >
|
106
|
+
#
|
107
|
+
attr_accessor :parent
|
108
|
+
|
109
|
+
# Stores pre-cast value
|
110
|
+
attr_accessor :value_before_cast
|
111
|
+
|
112
|
+
# Initialize a new instance of <i>Term</i>.
|
113
|
+
#
|
114
|
+
# The term can be configured in place by passing a block (evaluated in the
|
115
|
+
# context of the new instance) which defines the term properties using the
|
116
|
+
# macro-style instance helper methods.
|
117
|
+
#
|
118
|
+
# my_term = Term.new {
|
119
|
+
#
|
120
|
+
# label :size
|
121
|
+
# path "vehicleSize"
|
122
|
+
# hide!
|
123
|
+
# ...
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# The parent calculation object associated with <tt>self</tt> can be assigned
|
127
|
+
# using the :parent hash key passed as an argument.
|
128
|
+
#
|
129
|
+
# Unless otherwise configured within the passed block, several attributes
|
130
|
+
# attempt to take default configurations if possible using rules of thumb:
|
131
|
+
#
|
132
|
+
# * value => <tt>nil</tt>
|
133
|
+
# * enabled => <tt>true</tt>
|
134
|
+
# * visible => <tt>true</tt>
|
135
|
+
# * label => underscored, symbolized version of <tt>path</tt>
|
136
|
+
# * path => stringified version of <tt>label</tt>
|
137
|
+
# * name => stringified and humanised version of <tt>label</tt>
|
138
|
+
# * unit => <tt>default_unit</tt>
|
139
|
+
# * per_unit => <tt>default_per_unit</tt>
|
140
|
+
#
|
141
|
+
def initialize(options={},&block)
|
142
|
+
@parent=options[:parent]
|
143
|
+
@value=nil
|
144
|
+
@type=nil
|
145
|
+
@enabled=true
|
146
|
+
@visible=true
|
147
|
+
instance_eval(&block) if block
|
148
|
+
label path.to_s.underscore.to_sym unless path.blank?||label
|
149
|
+
path label.to_s unless path
|
150
|
+
name label.to_s.humanize unless name
|
151
|
+
unit default_unit unless unit
|
152
|
+
per_unit default_per_unit unless per_unit
|
153
|
+
end
|
154
|
+
|
155
|
+
# Valid choices for suggested interfaces for a term.
|
156
|
+
# Dynamic boolean methods (such as <tt>text_box?</tt>) are generated for
|
157
|
+
# checking which value is set.
|
158
|
+
#
|
159
|
+
# my_term.drop_down? #=> true
|
160
|
+
#
|
161
|
+
Interfaces=[:text_box,:drop_down,:date]
|
162
|
+
|
163
|
+
Interfaces.each do |inf|
|
164
|
+
define_method("#{inf.to_s}?") {
|
165
|
+
interface==inf
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
# Symbolized attribute representing the expected interface type for
|
170
|
+
# <tt>self</tt>. Set a value by passing an argument. Retrieve a value by
|
171
|
+
# calling without an argument, e.g.,
|
172
|
+
#
|
173
|
+
# my_term.interface :drop_down
|
174
|
+
#
|
175
|
+
# my_term.interface #=> :drop_down
|
176
|
+
#
|
177
|
+
# Must represent one of the valid choices defined in the
|
178
|
+
# <i>Term::Interfaces</i> constant
|
179
|
+
#
|
180
|
+
# If the provided interface is not valid (as defined in <i>Term::Interfaces</i>)
|
181
|
+
# an <i>InvalidInterface</i> exception is raised
|
182
|
+
#
|
183
|
+
def interface(inf=nil)
|
184
|
+
if inf
|
185
|
+
raise Exceptions::InvalidInterface unless Interfaces.include? inf
|
186
|
+
@interface=inf
|
187
|
+
end
|
188
|
+
return @interface
|
189
|
+
end
|
190
|
+
|
191
|
+
# Object representing the value which <tt>self</tt> is considered to
|
192
|
+
# represent (e.g. the quantity or name of something). Set a value by
|
193
|
+
# passing an argument. Retrieve a value by calling without an argument,
|
194
|
+
# e.g.,
|
195
|
+
#
|
196
|
+
# my_term.value 12
|
197
|
+
# my_term.value #=> 12
|
198
|
+
#
|
199
|
+
#
|
200
|
+
# my_term.value 'Ford Escort'
|
201
|
+
# my_term.value #=> 'Ford Escort'
|
202
|
+
#
|
203
|
+
#
|
204
|
+
# my_term.value DateTime.civil(2010,12,31)
|
205
|
+
# my_term.value #=> <Date: 4911123/2,0,2299161>
|
206
|
+
#
|
207
|
+
def value(*args)
|
208
|
+
unless args.empty?
|
209
|
+
@value_before_cast = args.first
|
210
|
+
@value = @type ? self.class.convert_value_to_type(args.first, @type) : args.first
|
211
|
+
end
|
212
|
+
@value
|
213
|
+
end
|
214
|
+
|
215
|
+
# Symbols representing the attributes of <tt>self</tt> which are concerned
|
216
|
+
# with quantity units.
|
217
|
+
#
|
218
|
+
# Each symbol also represents <b>dynamically defined method<b> name for
|
219
|
+
# setting and retrieving the default and current units and per units. Units
|
220
|
+
# are initialized as instances of <i>Quantify::Unit::Base</tt> is required.
|
221
|
+
#
|
222
|
+
# Set a unit attribute by passing an argument. Retrieve a value by calling
|
223
|
+
# without an argument. Unit attributes can be defined by any form which is
|
224
|
+
# accepted by the <i>Quantify::Unit#for</i> method (either an instance of
|
225
|
+
# <i>Quantify::Unit::Base</i> (or subclass) or a symbolized or string
|
226
|
+
# representation of the a unit symbol, name or label). E.g.,
|
227
|
+
#
|
228
|
+
# my_term.unit :mi
|
229
|
+
# my_term.unit #=> <Quantify::Unit::NonSI:0xb71cac48 @label="mi" ... >
|
230
|
+
#
|
231
|
+
# my_term.default_unit 'feet'
|
232
|
+
# my_term.default_unit #=> <Quantify::Unit::NonSI:0xb71cac48 @label="ft" ... >
|
233
|
+
#
|
234
|
+
#
|
235
|
+
# my_time_unit = Unit.hour #=> <Quantify::Unit::NonSI:0xb71cac48 @label="h" ... >
|
236
|
+
# my_term.default_per_unit my_time_unit
|
237
|
+
# my_term.default_per_unit #=> <Quantify::Unit::NonSI:0xb71cac48 @label="h" ... >
|
238
|
+
#
|
239
|
+
#
|
240
|
+
# Dynamically defined methods are also available for setting and retrieving
|
241
|
+
# alternative units for the <tt>unit</tt> and <tt>per_unit</tt> attributes.
|
242
|
+
# If no alternative units are explicitly defined, they are instantiated by
|
243
|
+
# default to represent all dimensionally equivalent units available in the
|
244
|
+
# system of units defined by <i>Quantify</i>. E.g.
|
245
|
+
#
|
246
|
+
# my_term.unit :kg
|
247
|
+
# my_term.alternative_units #=> [ <Quantify::Unit::NonSI:0xb71cac48 @label="mi" ... >,
|
248
|
+
# <Quantify::Unit::SI:0xb71cac48 @label="km" ... >,
|
249
|
+
# <Quantify::Unit::NonSI:0xb71cac48 @label="ft" ... >,
|
250
|
+
# ... ]
|
251
|
+
#
|
252
|
+
# my_term.unit 'litre'
|
253
|
+
# my_term.alternative_units :bbl, :gal
|
254
|
+
# my_term.alternative_units #=> [ <Quantify::Unit::NonSI:0xb71cac48 @label="bbl" ... >,
|
255
|
+
# <Quantify::Unit::NonSI:0xb71cac48 @label="gal" ... > ]
|
256
|
+
#
|
257
|
+
UnitFields = [:unit,:per_unit,:default_unit,:default_per_unit]
|
258
|
+
|
259
|
+
UnitFields.each do |field|
|
260
|
+
define_method(field) do |*unit|
|
261
|
+
instance_variable_set("@#{field}",Unit.for(unit.first)) unless unit.empty?
|
262
|
+
instance_variable_get("@#{field}")
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
[:unit,:per_unit].each do |field|
|
267
|
+
define_method("alternative_#{field}s") do |*args|
|
268
|
+
ivar = "@alternative_#{field}s"
|
269
|
+
default = send("default_#{field}".to_sym)
|
270
|
+
unless args.empty?
|
271
|
+
args << default if default
|
272
|
+
units = args.map {|arg| Unit.for(arg) }
|
273
|
+
Term.validate_dimensional_equivalence?(*units)
|
274
|
+
instance_variable_set(ivar, units)
|
275
|
+
else
|
276
|
+
return instance_variable_get(ivar) if instance_variable_get(ivar)
|
277
|
+
return instance_variable_set(ivar, (default.alternatives << default)) if default
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Returns <tt>true</tt> if <tt>self</tt> has a populated value attribute.
|
283
|
+
# Otherwise, returns <tt>false</tt>.
|
284
|
+
#
|
285
|
+
def set?
|
286
|
+
!value_before_cast.nil?
|
287
|
+
end
|
288
|
+
|
289
|
+
# Returns <tt>true</tt> if <tt>self</tt> does not have a populated value
|
290
|
+
# attribute. Otherwise, returns <tt>false</tt>.
|
291
|
+
#
|
292
|
+
def unset?
|
293
|
+
value_before_cast.nil?
|
294
|
+
end
|
295
|
+
|
296
|
+
# Declare that the term's UI element should be disabled
|
297
|
+
def disable!
|
298
|
+
@disabled=true
|
299
|
+
end
|
300
|
+
|
301
|
+
# Declare that the term's UI element should be enabled
|
302
|
+
def enable!
|
303
|
+
@disabled=false
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns <tt>true</tt> if the UI element of <tt>self</tt> is disabled.
|
307
|
+
# Otherwise, returns <tt>false</tt>.
|
308
|
+
#
|
309
|
+
def disabled?
|
310
|
+
@disabled
|
311
|
+
end
|
312
|
+
|
313
|
+
# Returns <tt>true</tt> if the UI element of <tt>self</tt> is enabled.
|
314
|
+
# Otherwise, returns <tt>false</tt>.
|
315
|
+
#
|
316
|
+
def enabled?
|
317
|
+
!disabled?
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns <tt>true</tt> if <tt>self</tt> is configured as visible.
|
321
|
+
# Otherwise, returns <tt>false</tt>.
|
322
|
+
#
|
323
|
+
def visible?
|
324
|
+
@visible
|
325
|
+
end
|
326
|
+
|
327
|
+
# Returns <tt>true</tt> if <tt>self</tt> is configured as hidden.
|
328
|
+
# Otherwise, returns <tt>false</tt>.
|
329
|
+
#
|
330
|
+
def hidden?
|
331
|
+
!visible?
|
332
|
+
end
|
333
|
+
|
334
|
+
# Declare that the term's UI element should not be shown in generated UIs.
|
335
|
+
def hide!
|
336
|
+
@visible=false
|
337
|
+
end
|
338
|
+
|
339
|
+
# Declare that the term's UI element should be shown in generated UIs.
|
340
|
+
def show!
|
341
|
+
@visible=true
|
342
|
+
end
|
343
|
+
|
344
|
+
# Returns <tt>true</tt> if <tt>self</tt> has a numeric value. That is, can
|
345
|
+
# it have statistics applied? This method permits handling of term summing,
|
346
|
+
# averaging, etc. Otherwise, returns <tt>false</tt>.
|
347
|
+
#
|
348
|
+
def has_numeric_value?
|
349
|
+
set? and Float(value) rescue false
|
350
|
+
end
|
351
|
+
|
352
|
+
# Returns a pretty print string representation of <tt>self</tt>
|
353
|
+
def inspect
|
354
|
+
elements = {:label => label, :value => value, :unit => unit,
|
355
|
+
:per_unit => per_unit, :type => type,
|
356
|
+
:disabled => disabled?, :visible => visible?}
|
357
|
+
attr_list = elements.map {|k,v| "#{k}: #{v.inspect}" } * ', '
|
358
|
+
"<#{self.class.name} #{attr_list}>"
|
359
|
+
end
|
360
|
+
|
361
|
+
# Returns <tt>true</tt> if <tt>self</tt> occurs before the term with a label
|
362
|
+
# matching <tt>lab</tt> in the terms list of the parent calculation. Otherwise,
|
363
|
+
# returns <tt>false</tt>.
|
364
|
+
#
|
365
|
+
def before?(lab)
|
366
|
+
parent.terms.labels.index(lab)>parent.terms.labels.index(label)
|
367
|
+
end
|
368
|
+
|
369
|
+
# Returns <tt>true</tt> if <tt>self</tt> occurs after the term with a label
|
370
|
+
# matching <tt>lab</tt> in the terms list of the parent calculation. Otherwise,
|
371
|
+
# returns <tt>false</tt>.
|
372
|
+
#
|
373
|
+
def after?(lab)
|
374
|
+
parent.terms.labels.index(lab)<parent.terms.labels.index(label)
|
375
|
+
end
|
376
|
+
|
377
|
+
def initialize_copy(source)
|
378
|
+
super
|
379
|
+
UnitFields.each do |property|
|
380
|
+
prop = send(property)
|
381
|
+
self.send(property, prop.clone) unless prop.nil?
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# Return a new instance of <i>Term</i>, based on <tt>self</tt> but with
|
386
|
+
# a change of units, according to the <tt>options</tt> hash provided, and
|
387
|
+
# the value attribute updated to reflect the new units.
|
388
|
+
#
|
389
|
+
# To specify a new unit, pass the required unit via the <tt>:unit</tt> key.
|
390
|
+
# To specify a new per_unit, pass the required per unit via the
|
391
|
+
# <tt>:per_unit</tt> key. E.g.,
|
392
|
+
#
|
393
|
+
# my_term.convert_unit(:unit => :kg)
|
394
|
+
#
|
395
|
+
# my_term.convert_unit(:unit => :kg, :per_unit => :h)
|
396
|
+
#
|
397
|
+
# my_term.convert_unit(:unit => 'kilogram')
|
398
|
+
#
|
399
|
+
# my_term.convert_unit(:per_unit => Quantify::Unit.h)
|
400
|
+
#
|
401
|
+
# my_term.convert_unit(:unit => <Quantify::Unit::SI ... >)
|
402
|
+
#
|
403
|
+
# If <tt>self</tt> does not hold a numeric value or either a unit or per
|
404
|
+
# unit attribute, <tt.self</tt> is returned.
|
405
|
+
#
|
406
|
+
def convert_unit(options={})
|
407
|
+
return self unless has_numeric_value? and (unit or per_unit)
|
408
|
+
new = clone
|
409
|
+
if options[:unit] and unit
|
410
|
+
new_unit = Unit.for(options[:unit])
|
411
|
+
Term.validate_dimensional_equivalence?(unit,new_unit)
|
412
|
+
new.value Quantity.new(new.value,new.unit).to(new_unit).value
|
413
|
+
new.unit options[:unit]
|
414
|
+
end
|
415
|
+
if options[:per_unit] and per_unit
|
416
|
+
new_per_unit = Unit.for(options[:per_unit])
|
417
|
+
Term.validate_dimensional_equivalence?(per_unit,new_per_unit)
|
418
|
+
new.value Quantity.new(new.value,(1/new.per_unit)).to(Unit.for(new_per_unit)).value
|
419
|
+
new.per_unit options[:per_unit]
|
420
|
+
end
|
421
|
+
return new
|
422
|
+
end
|
423
|
+
|
424
|
+
# Return an instance of Quantify::Quantity describing the quantity represented
|
425
|
+
# by <tt>self</tt>.
|
426
|
+
#
|
427
|
+
# If <tt>self</tt> does not contain a numeric value, <tt>nil</tt> is returned.
|
428
|
+
#
|
429
|
+
# If <tt>self</tt> contains a numeric value, but no unit or per unit, just
|
430
|
+
# the numeric value is returned
|
431
|
+
#
|
432
|
+
def to_quantity
|
433
|
+
return nil unless has_numeric_value?
|
434
|
+
if (unit.is_a? Quantify::Unit::Base) && (per_unit.is_a? Quantify::Unit::Base)
|
435
|
+
quantity_unit = unit/per_unit
|
436
|
+
elsif unit.is_a? Quantify::Unit::Base
|
437
|
+
quantity_unit = unit
|
438
|
+
elsif per_unit.is_a? Quantify::Unit::Base
|
439
|
+
quantity_unit = 1/per_unit
|
440
|
+
else
|
441
|
+
return value
|
442
|
+
end
|
443
|
+
Quantity.new(value,quantity_unit)
|
444
|
+
end
|
445
|
+
alias :to_q :to_quantity
|
446
|
+
|
447
|
+
# Returns a string representation of term based on the term value and any
|
448
|
+
# units which are defined. The format of the unit representation follows
|
449
|
+
# that defined by <tt>format</tt>, which should represent any of the formats
|
450
|
+
# supported by the <i>Quantify::Unit::Base</tt> class (i.e. :name,
|
451
|
+
# :pluralized_name, :symbol and :label). Default behaviour uses the unit
|
452
|
+
# symbol atribute, i.e. if no format explcitly specified:
|
453
|
+
#
|
454
|
+
# my_term.to_s #=> "12345 ton"
|
455
|
+
#
|
456
|
+
# my_term.to_s :symbol #=> "12345 ton"
|
457
|
+
#
|
458
|
+
# my_term.to_s :name #=> "12345 short ton"
|
459
|
+
#
|
460
|
+
# my_term.to_s :pluralized_name #=> "12345 tons"
|
461
|
+
#
|
462
|
+
# my_term.to_s :label #=> "12345 ton_us"
|
463
|
+
#
|
464
|
+
def to_s(format=:symbol)
|
465
|
+
string = "#{value}"
|
466
|
+
if unit and per_unit
|
467
|
+
string += " #{(unit/per_unit).send(format)}"
|
468
|
+
elsif unit
|
469
|
+
string += " #{unit.send(format)}"
|
470
|
+
elsif per_unit
|
471
|
+
string += " #{(1/per_unit).send(format)}"
|
472
|
+
end
|
473
|
+
return string
|
474
|
+
end
|
475
|
+
|
476
|
+
# Checks that the units included in <tt>units</tt> are dimensionally
|
477
|
+
# equivalent, that is, that they represent the same physucal quantity
|
478
|
+
#
|
479
|
+
def self.validate_dimensional_equivalence?(*units)
|
480
|
+
unless [units].flatten.all? {|unit| unit.dimensions == units[0].dimensions }
|
481
|
+
raise AMEE::DataAbstraction::Exceptions::InvalidUnits,
|
482
|
+
"The specified term units are not of equivalent dimensions: #{units.map(&:label).join(",")}"
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def self.convert_value_to_type(value, type)
|
487
|
+
return nil if value.blank?
|
488
|
+
type = type.downcase.to_sym if type.is_a?(String)
|
489
|
+
|
490
|
+
case type
|
491
|
+
when :string then value.to_s
|
492
|
+
when :text then value.to_s
|
493
|
+
when :integer then value.to_i rescue 0
|
494
|
+
when :fixnum then value.to_i rescue 0
|
495
|
+
when :float then value.to_f rescue 0
|
496
|
+
when :decimal then value.to_s.to_d rescue 0
|
497
|
+
when :double then value.to_s.to_d rescue 0
|
498
|
+
when :datetime then DateTime.parse(value.to_s) rescue nil
|
499
|
+
when :time then Time.parse(value.to_s) rescue nil
|
500
|
+
when :date then Date.parse(value.to_s) rescue nil
|
501
|
+
else value
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|