eymiha_units 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,229 @@
1
+ require 'eymiha'
2
+
3
+ require 'forward_referencing'
4
+
5
+ require 'units/units_measure'
6
+ require 'units/units_exception'
7
+ require 'units/numeric'
8
+ require 'units/numeric_with_units'
9
+ require 'units/units_hash'
10
+ require 'units/object'
11
+
12
+ # The Units framework
13
+ #
14
+ # Units is the top level that you'll typically never have to deal with
15
+ # directly. While it provides a few chunks of general functionality such
16
+ # as getting defined units and ranking, most of its guts are devoted to
17
+ # defining new UnitsMeasures - unless you're setting up your own sets of
18
+ # specialized units, you will hardly know it's here.
19
+ class Units
20
+
21
+ @@debug = false
22
+
23
+ def self.debug=(value)
24
+ @@debug = value
25
+ end
26
+
27
+ extend ForwardReferencing
28
+ start_forward_referencing
29
+
30
+ @@measures = {}
31
+ @@units = {}
32
+ @@defining = nil
33
+ @@holding = nil
34
+
35
+ # Returns an Array of the defined UnitsMeasures.
36
+ def Units.units_measures
37
+ @@measures.keys
38
+ end
39
+
40
+ # Creates or extends a UnitsMeasure.
41
+ def Units.create(name, &block)
42
+ measure = (@@measures[name.to_s] ||= UnitsMeasure.new)
43
+ block.call measure if block_given?
44
+ measure
45
+ end
46
+
47
+ # Creates or extends a UnitsMeasure derived from other UnitsMeasures.
48
+ def Units.derive(name, target, &block)
49
+ measure = ( @@measures[name.to_s] =
50
+ if target.kind_of? UnitsMeasure
51
+ if target.derived && (derived = find_by_derivation target.derived)
52
+ derived
53
+ else
54
+ target
55
+ end
56
+ else
57
+ Units.create(target.to_s)
58
+ end )
59
+ block.call measure if block_given?
60
+ measure
61
+ end
62
+
63
+ # Returns the UnitsMeasure with the given derivation.
64
+ def Units.find_by_derivation(derivation)
65
+ matches = @@measures.values.uniq.select { |measure|
66
+ if measure.derived
67
+ measure == derivation
68
+ elsif derivation.size == 1
69
+ a = derivation.to_a[0]
70
+ a[0] == measure and a[1] == 1
71
+ else
72
+ false
73
+ end
74
+ }
75
+ case matches.size
76
+ when 0 then nil
77
+ when 1 then matches[0]
78
+ else raise UnitsException.new(
79
+ "Multiple UnitsMeasures with same derivation found")
80
+ end
81
+ end
82
+
83
+ # Removes the named UnitsMeasure from the Units framework.
84
+ def Units.delete(name)
85
+ @@measures.delete name.to_s
86
+ end
87
+
88
+ # Clears the Units framework of all defined elements.
89
+ def Units.clear
90
+ @@measures.clear
91
+ @@units.clear
92
+ forward_references_clear
93
+ self
94
+ end
95
+
96
+ # Returns the number of defined UnitsMeasures.
97
+ def Units.size
98
+ @@measures.size
99
+ end
100
+
101
+ # Returns the named UnitsMeasure.
102
+ def Units.[](name)
103
+ @@measures[name.to_s]
104
+ end
105
+
106
+ def Units.method_missing(method,*args) # :nodoc:
107
+ measure = self[method]
108
+ raise UnitsException.new("UnitsMeasure '#{method}' undefined") if !measure
109
+ measure
110
+ end
111
+
112
+ # Returns an Array containing the names of a given UnitsMeasure.
113
+ def Units.names_of(units_measure)
114
+ @@measures.keys.select { |name| @@measures[name].equal? units_measure }
115
+ end
116
+
117
+ def Units.add_unit(unit,unit_identifier=nil) # :nodoc:
118
+ if unit_identifier
119
+ if (element = @@units[unit_identifier])
120
+ @@units[unit_identifier] += [ unit ] unless element.index(unit)
121
+ else
122
+ @@units[unit_identifier] = [ unit ]
123
+ end
124
+ else
125
+ add_unit unit, unit.name
126
+ add_unit unit, unit.plural
127
+ unit.abbrevs.each { |abbrev| add_unit unit, abbrev }
128
+ end
129
+ end
130
+
131
+ # Returns an Array of UnitsUnit associated with a singular, plural or
132
+ # abbreviated name.
133
+ def Units.lookup(unit_identifier)
134
+ @@units[unit_identifier.to_s] || [ ]
135
+ end
136
+
137
+ # Answers the question of whether a UnitsMeasure is being defined with the
138
+ # instance if true, or nil if false.
139
+ def Units.defining?
140
+ @@defining
141
+ end
142
+
143
+ def Units.defining(measure) # :nodoc:
144
+ @@defining = measure
145
+ end
146
+
147
+ def Units.convert(numeric,unit_identifier) # :nodoc:
148
+ puts "Units:convert #{numeric} #{unit_identifier}" if @@debug
149
+ if (candidates = lookup(unit_identifier)).size == 0
150
+ puts @@units.keys.sort.join(" ") if @@debug
151
+ puts " no candidates!" if @@debug
152
+ raise MissingUnitsException.new(unit_identifier.to_s)
153
+ elsif !defining?
154
+ if candidates.size > 1
155
+ raise AmbiguousUnitsException.new(unit_identifier.to_s)
156
+ else
157
+ unit = candidates[0]
158
+ NumericWithUnits.new(numeric,unit)
159
+ end
160
+ else
161
+ if candidates.size == 1
162
+ units = candidates
163
+ else
164
+ units = candidates.select { |candidate|
165
+ @@defining == candidate.units_system.units_measure }
166
+ units = candidates.select { |candidate|
167
+ @@defining.derived[candidate.units_measure] } if units.size == 0
168
+ end
169
+ case units.size
170
+ when 0 then
171
+ raise MissingUnitsException.new(unit_identifier.to_s)
172
+ when 1 then
173
+ unit = units[0]
174
+ if unit.equals.kind_of? Array
175
+ element = unit.equals[0]
176
+ value = NumericWithUnits.
177
+ new(numeric*element.numeric,element.unit)
178
+ else
179
+ value = NumericWithUnits.
180
+ new(numeric*unit.equals.numeric,unit.equals.unit)
181
+ end
182
+ value.original = numeric.unite(unit)
183
+ value
184
+ else
185
+ raise AmbiguousUnitsException.new(unit_identifier.to_s)
186
+ end
187
+ end
188
+ end
189
+
190
+ def Units.make_forward_reference(method,context) # :nodoc:
191
+ @@holding ? nil : create_forward_reference(method,context)
192
+ end
193
+
194
+ def Units.release_forward_reference(reference = nil) # :nodoc:
195
+ remove_forward_reference(reference) if reference != nil
196
+ end
197
+
198
+ def Units.hold_forward_reference(hold = true) # :nodoc:
199
+ @@holding = hold
200
+ end
201
+
202
+ def Units.holding_forward_reference? # :nodoc:
203
+ @@holding
204
+ end
205
+
206
+ def Units.establish_forward_reference_context(context) # :nodoc:
207
+ defining context
208
+ end
209
+
210
+ # Given a Hash of units to raking weights, a set of samples, and
211
+ # optionally a block that returns rankings, returns an Array of units
212
+ # ordered by best weighted fit over the samples. Like golf, low scores
213
+ # rank best. In absence of a block, ranking is based on the number of
214
+ # unit values whose magnitudes are between 1 and 10.
215
+ def Units.rank(unit_choices = {}, samples = [], &numeric_ranker) # :yields: n
216
+ if block_given?
217
+ scores = {}
218
+ samples.each {|s|
219
+ unit_choices.each {|uc,w|
220
+ scores[uc] = (scores[uc] || 0) + w*(yield s.convert(uc).numeric) } }
221
+ scores.sort {|e1,e2| -(e1[1] <=> e2[1])}.collect {|s| s[0]}
222
+ else
223
+ rank(unit_choices,samples) { |n|
224
+ e = ((((n.abs)-5.5).abs-4.5).at_least(0))
225
+ (e == 0) ? 0 : (e == 1)? 0 : -(e + 1/(1-e)) }
226
+ end
227
+ end
228
+
229
+ end
@@ -0,0 +1,14 @@
1
+ # Raised when a general problem has occurred in the Units framework or some
2
+ # other error has occurred during its use.
3
+ class UnitsException < Exception
4
+ end
5
+
6
+
7
+ # Raised when a unit was expected but was not found.
8
+ class MissingUnitsException < UnitsException
9
+ end
10
+
11
+
12
+ # Raised when a unit was expected but more than one was found.
13
+ class AmbiguousUnitsException < UnitsException
14
+ end
@@ -0,0 +1,112 @@
1
+ require "units/units"
2
+
3
+ # The unit part of a NumericWithUnits is a UnitsHash - a Hash from UnitsUnit
4
+ # instances to powers.
5
+ class UnitsHash < Hash
6
+
7
+ @@debug = false
8
+
9
+ def self.debug=(value)
10
+ @@debug = value
11
+ end
12
+
13
+ def initialize(unit = nil,power = 1) # :nodoc
14
+ if unit
15
+ if unit.kind_of? UnitsUnit
16
+ self[unit] = power
17
+ elsif unit.kind_of? UnitsHash
18
+ unit.each { |k,v| self[k] = v*power }
19
+ else
20
+ raise UnitsException.new("invalid unit: #{unit}")
21
+ end
22
+ end
23
+ end
24
+
25
+ # Returns a String reprentation of the instance. If there is only one item in
26
+ # the UnitsHash, whole names are rendered; otherwise abbreviations are used.
27
+ def to_s(numeric = 1,use_abbrevs = false)
28
+ if size == 1 &&
29
+ (su = select {|k,v| v == 1}).size == 1 &&
30
+ !use_abbrevs
31
+ su = su.to_a[0][0]
32
+ ((numeric == 1)? su.name : su.plural).gsub(/_/,' ')
33
+ else
34
+ abbrevs_to_s
35
+ end
36
+ end
37
+
38
+ def abbrevs_to_s # :nodoc:
39
+ p = select {|k,v| v > 0}.sort{|e1,e2| e1[1] <=> e2[1]}.collect {|e|
40
+ "#{e[0].abbrevs[0] || e[0].name}#{(e[1] == 1) ? "" : "^#{e[1]}"}"}
41
+ n = select {|k,v| v < 0}.sort{|e1,e2| e1[1] <=> e2[1]}.collect {|e|
42
+ "#{e[0].abbrevs[0] || e[0].name}#{(e[1] == -1) ? "" : "^#{-e[1]}"}"}
43
+ numerator = (p.size > 0)? p.join(" ") : (n.size > 0)? "1" : ""
44
+ denominator = (n.size > 0)? ((p.size > 0)? " / " : "/")+n.join(" ") : ""
45
+ "#{numerator}#{denominator}"
46
+ end
47
+
48
+ # Raises and returns the instance after each of its exponents has been raised
49
+ # by the given power.
50
+ def power!(power)
51
+ self.each { |k,v| self[k] = v*power }
52
+ end
53
+
54
+ # Returns a new UnitsHash whose value is the instance raised to the given
55
+ # power.
56
+ def **(power)
57
+ clone.power!(power)
58
+ end
59
+
60
+ # Returns a new UnitsHash whose value is the instance's units and powers have
61
+ # been merged with the given value raised to the power.
62
+ def merge(value,power=1)
63
+ clone.merge!(value,power)
64
+ end
65
+
66
+ # Merges and returns the instance after the value raised to the power has
67
+ # been merged into it.
68
+ def merge!(value,power=1)
69
+ value.unit.each { |k,v|
70
+ self[k] = (self[k] || 0) + v*power
71
+ delete k if self[k] == 0
72
+ }
73
+ self
74
+ end
75
+
76
+ alias :* :merge
77
+
78
+ # Returns the UnitsMeasure of the instance if defined.
79
+ def measure
80
+ measure = UnitsMeasure.new
81
+ each { |unit,power|
82
+ measure.merge_derivation unit.units_system.units_measure => power }
83
+ Units.find_by_derivation(measure.derived)
84
+ end
85
+
86
+ # Returns true is any of the instance's components are derived.
87
+ def derived?
88
+ keys.select{|unit| unit.units_system.units_measure.derived != nil}.size > 0
89
+ end
90
+
91
+ # Return a NumericWithUnits that represents the instance when all derived
92
+ # units have been replaced with the units from which they derive.
93
+ def reduce
94
+ puts "UnitsHash:reduce #{to_s}" if @@debug
95
+ factor = 1.0
96
+ new_unit = UnitsHash.new
97
+ each do |unit,power|
98
+ puts " #{unit} #{power}" if @@debug
99
+ factor *= unit.equals.numeric**power
100
+ puts " #{factor}" if @@debug
101
+ new_unit.merge!(unit.equals,power)
102
+ end
103
+ factor.unite new_unit
104
+ end
105
+
106
+ # Return true if the instance is not equal to 1, ie. has at least one non-zero
107
+ # exponent.
108
+ def has_units?
109
+ (self.select {|k,v| v != 0.0}).size > 0
110
+ end
111
+
112
+ end
@@ -0,0 +1,91 @@
1
+ require 'units/units_system'
2
+ require 'methodic_hash'
3
+
4
+ # A UnitsMeasure groups sets of units that measure the same quality.
5
+ class UnitsMeasure < MethodicHash
6
+
7
+ # A Hash of UnitsMeasures to powers that represent the bases for this
8
+ # UnitsMeasure if it is derived.
9
+ attr_reader :derived
10
+ attr_writer :derived # :nodoc:
11
+
12
+ # A Methodic Hash mapping names of formats to procs that implement them.
13
+ attr_reader :formats
14
+
15
+ def initialize # :nodoc:
16
+ @formats = MethodicHash.new
17
+ end
18
+
19
+ # Defines or extends a UnitsSystem. During definition, if forward
20
+ # references between entities may occur and when encountered, are stored.
21
+ # Upon completion of the definition, if any existing unresolved still
22
+ # exist, an attempt is made to resolve them.
23
+ def system(name,&block)
24
+ Units.defining self
25
+ system = (self[name] ||= UnitsSystem.new(self,name))
26
+ block.call system if block_given?
27
+ Units.resolve_forward_references
28
+ Units.defining nil
29
+ system
30
+ end
31
+
32
+ # Returns the names by which this UnitsMeasure is known.
33
+ def names
34
+ Units.names_of self
35
+ end
36
+
37
+ # Returns a new UnitsMeasure equal to the instance raised to the given
38
+ # power. This is typically used to derive UnitsMeasures - for example,
39
+ # if length is a UnitsMeasure, then length**3 could be used as the target
40
+ # to derive volume.
41
+ def **(exponent)
42
+ UnitsMeasure.new.merge_derivation(self => exponent)
43
+ end
44
+
45
+ # Returns a new UnitsMeasure equal to the instance divided by another
46
+ # UnitsMeasure. This is typically used to derive UnitsMeasures - for
47
+ # example, if mass and volume are UnitsMeasures, then mass/volume could be
48
+ # used as the target to derive density.
49
+ def /(divisor)
50
+ UnitsMeasure.new.merge_derivation(self).merge_derivation(divisor,-1)
51
+ end
52
+
53
+ # Returns a new UnitsMeasure equal to the instance multiplied by another
54
+ # UnitsMeasure. This is typically used to derive UnitsMeasures - for
55
+ # example, if length is a UnitsMeasure, then length*length could be
56
+ # used as the target to derive area.
57
+ def *(factor)
58
+ UnitsMeasure.new.merge_derivation(self).merge_derivation(factor)
59
+ end
60
+
61
+ def merge_derivation derivation, multiplier=1 # :nodoc:
62
+ current = (self.derived ||= {})
63
+ if derivation.kind_of? UnitsMeasure
64
+ if derivation.derived
65
+ derivation.derived.each {|key,value|
66
+ current[key] = (current[key] || 0) + multiplier*value }
67
+ else
68
+ current[derivation] = (current[derivation] || 0) + multiplier
69
+ end
70
+ elsif derivation.kind_of? Hash
71
+ derivation.each {|key,value|
72
+ current[key] = ((current[key] || 0 ) + multiplier*value) }
73
+ else
74
+ raise UnitsException.new(
75
+ "Cannot add #{derivation.self_name} to derivation")
76
+ end
77
+ self
78
+ end
79
+
80
+ # Returns a String containing the names of this UnitsMeasure and the
81
+ # namse of the UnitsSystems defined within it.
82
+ def to_s
83
+ "#{self_name} #{names} #{keys}"
84
+ end
85
+
86
+ # Associates the name of a format with an output formatter.
87
+ def format(options)
88
+ @formats[options[:name]] = options[:format]
89
+ end
90
+
91
+ end
@@ -0,0 +1,85 @@
1
+ require 'units/units_unit'
2
+ require 'methodic_hash'
3
+
4
+ # A UnitsSystem groups units that measure the same quality and are
5
+ # conceptually organized together within a UnitsMeasure. Its a MethodicHash
6
+ # of unit names to UnitsUnit instances.
7
+ class UnitsSystem < MethodicHash
8
+
9
+ # The UnitsMeasure to which this instance is associated.
10
+ attr_reader :units_measure
11
+ # The name of this UnitsSystem
12
+ attr_reader :name
13
+
14
+ def initialize(units_measure,name) # :nodoc:
15
+ @units_measure = units_measure
16
+ @name = name
17
+ end
18
+
19
+ # Defines a new UnitsUnit. If ten-based or two-based greeking is defined,
20
+ # it is applied.
21
+ def unit(attributes)
22
+ unit = nil
23
+ if !Units.holding_forward_reference?
24
+ unit = UnitsUnit.new self, attributes
25
+ self[unit.name] = unit
26
+ Units.add_unit unit
27
+ case unit.greek
28
+ when :ten then greek_ten unit
29
+ when :two then greek_two unit
30
+ end
31
+ else
32
+ Units.hold_forward_reference nil
33
+ end
34
+ Units.continue_forward_reference_resolution
35
+ unit
36
+ end
37
+
38
+ # Associates the name of a format with an output formatter in the instance's
39
+ # UnitsMeasure.
40
+ def format(options)
41
+ @units_measure.format(options)
42
+ end
43
+
44
+ def greek_ten(base) # :nodoc:
45
+ greek_unit base, "yocto", "y", 0.000000000000000000000001
46
+ greek_unit base, "zepto", "z", 0.000000000000000000001
47
+ greek_unit base, "atto", "a", 0.000000000000000001
48
+ greek_unit base, "femto", "f", 0.000000000000001
49
+ greek_unit base, "pico", "p", 0.000000000001
50
+ greek_unit base, "nano", "n", 0.000000001
51
+ greek_unit base, "micro", "u", 0.000001
52
+ greek_unit base, "milli", "m", 0.001
53
+ greek_unit base, "centi", "c", 0.01
54
+ greek_unit base, "deci", "d", 0.1
55
+ greek_unit base, "deca", "da", 10.0
56
+ greek_unit base, "hecto", "h", 100.0
57
+ greek_unit base, "kilo", "k", 1000.0
58
+ greek_unit base, "mega", "M", 1000000.0
59
+ greek_unit base, "giga", "G", 1000000000.0
60
+ greek_unit base, "tera", "T", 1000000000000.0
61
+ greek_unit base, "peta", "P", 1000000000000000.0
62
+ greek_unit base, "exa", "E", 1000000000000000000.0
63
+ greek_unit base, "zetta", "Z", 1000000000000000000000.0
64
+ greek_unit base, "yotta", "Y", 1000000000000000000000000.0
65
+ end
66
+
67
+ def greek_two(base) # :nodoc:
68
+ greek_unit base, "kilo", "k", 2**10
69
+ greek_unit base, "mega", "m", 2**20
70
+ greek_unit base, "giga", "g", 2**30
71
+ greek_unit base, "tera", "t", 2**40
72
+ greek_unit base, "peta", "p", 2**50
73
+ greek_unit base, "exa", "e", 2**60
74
+ greek_unit base, "zetta", "z", 2**70
75
+ greek_unit base, "yotta", "y", 2**80
76
+ end
77
+
78
+ def greek_unit(base,prefix,abbrev_prefix,scalar) # :nodoc:
79
+ unit :name => prefix+base.name,
80
+ :plural => prefix+base.plural,
81
+ :abbrevs => base.abbrevs.collect { |abbrev| abbrev_prefix+abbrev },
82
+ :equals => base.equals * scalar
83
+ end
84
+
85
+ end
@@ -0,0 +1,60 @@
1
+ require 'methodic_hash'
2
+
3
+ require 'units/numeric_with_units'
4
+
5
+ # A unit in the context of a particular UnitsSystem. It's a MethodicHash of
6
+ # its named qualities.
7
+ class UnitsUnit < MethodicHash
8
+
9
+ # The UnitsSystem to which this instance is associated.
10
+ attr_reader :units_system
11
+
12
+ def initialize(units_system,attributes) # :nodoc:
13
+ @units_system = units_system
14
+ merge! attributes
15
+ normalize
16
+ end
17
+
18
+ def ==(unit) # :nodoc:
19
+ self.equal? unit
20
+ end
21
+
22
+ def normalize # :nodoc:
23
+ raise UnitsException.new("UnitUnits must have a name attribute") unless
24
+ self.name
25
+ self.name = self.name.to_s
26
+ add_plural
27
+ add_abbrevs
28
+ add_equals
29
+ equals.each { |n| n.promote_original } if equals.kind_of? Array
30
+ self
31
+ end
32
+
33
+ def add_plural # :nodoc:
34
+ self.plural =
35
+ (self.no_plural == true) ? self.name :
36
+ (plural = self.plural) ? plural.to_s : self.name+'s'
37
+ end
38
+
39
+ def add_abbrevs(attribute = nil) # :nodoc:
40
+ if attribute == nil
41
+ self.abbrevs = add_abbrevs(:abbrev)+add_abbrevs(:abbrevs)
42
+ elsif (value = self[attribute])
43
+ self.delete(attribute)
44
+ (value.kind_of? Array) ? value.collect {|e| e.to_s } : [ value.to_s ]
45
+ else
46
+ [ ]
47
+ end
48
+ end
49
+
50
+ def add_equals # :nodoc:
51
+ self.equals = NumericWithUnits.new(1,self) unless self.equals
52
+ end
53
+
54
+ # returns the UnitsMeasure containing the UnitsSystem to which this instance
55
+ # belongs.
56
+ def units_measure
57
+ units_system.units_measure
58
+ end
59
+
60
+ end
data/lib/units.rb ADDED
@@ -0,0 +1,4 @@
1
+ # Adds the Units framework and a set of basic definitions.
2
+ require 'units/units'
3
+ require 'units/definitions/measures'
4
+
data/rakefile.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'gem_package'
2
+ require 'gem_raker'
data/test/framework.rb ADDED
@@ -0,0 +1,7 @@
1
+ module UnitsTest
2
+
3
+ def setup
4
+ Units.clear
5
+ end
6
+
7
+ end
@@ -0,0 +1,49 @@
1
+ require 'test/unit'
2
+
3
+ require 'units'
4
+
5
+ class TC_definitions < Test::Unit::TestCase
6
+
7
+ def test_definitions
8
+
9
+ assert(Units.forward_references.size == 0) # all the definitions loaded
10
+
11
+ t = 5.years
12
+ assert(t.to_s == "5 years")
13
+ assert(t.in_months.to_s == "60 months")
14
+ assert(t.in_months.in_days.to_s == "1800 days")
15
+ assert(t.in_days.to_s == "1825 days")
16
+ assert(t.in_days.in_months.to_s == "60.8333333333333 months")
17
+ assert(t.in_hours.to_s == "43800.0 hours")
18
+ assert(t.in_weeks.to_s == "260 weeks")
19
+ assert(t.in_months.in_weeks.to_s == "240 weeks")
20
+ assert(t.in_days.in_weeks.to_s == "260.714285714286 weeks")
21
+ assert(t.in_days == 260.weeks+5.days)
22
+
23
+ assert(days_in_january.to_s == "31.0")
24
+ assert(hours_in_january.to_s == "744.0")
25
+ assert(hours_per_january == 744)
26
+ assert(1.january.hours == 744.hours)
27
+ assert(january.hours == 744.hours)
28
+ assert(february.hours == 672.hours)
29
+ assert(july.weeks.to_s == "4.42857142857143 weeks")
30
+ assert(july.format(:weeks_and_days).to_s == "4 weeks 3 days")
31
+ assert(july.to_s(:weeks_and_days) == "4 weeks 3 days")
32
+
33
+ assert(speed_of_light.in_mi_s.to_s == "186282.024486427 mi / s")
34
+ assert(4.5.ly.in_mi.to_s == "26435654658917.8 miles")
35
+ assert(4.5.ly.AU.to_s == "284389.813364457 astronomical units")
36
+ assert(speed_of_light.miles.to_s == "186282.024486427 mi / s")
37
+ assert(speed_of_light.feet_nanosecond.to_s == "0.983569089288333 ft / ns")
38
+ assert(4.5.unite(["light_years"]).AU.to_s ==
39
+ "284389.813364457 astronomical units")
40
+ assert(4.5.light_years.AU.to_s ==
41
+ "284389.813364457 astronomical units")
42
+ assert(4.5.light_years.astronomical_units.to_s ==
43
+ "284389.813364457 astronomical units")
44
+ assert(1.speed_of_light.to_s == "1 speed of light")
45
+ assert(4.5.speed_of_light.to_s == "4.5 speed of light")
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/unit'
2
+ require 'test/framework'
3
+
4
+ require 'units'
5
+
6
+ class TC_formatting < Test::Unit::TestCase
7
+
8
+ understands UnitsTest
9
+
10
+ def test_fractions
11
+
12
+ Units.create :length do |m|
13
+ m.system :english do |s|
14
+ s.unit :name => :inch, :plural => :inches, :abbrev => :in
15
+ s.unit :name => :foot, :plural => :feet, :abbrev => :ft,
16
+ :equals => 12.inches
17
+ s.unit :name => :yard, :abbrevs => :yd, :equals => 3.feet
18
+ s.unit :name => :mile, :abbrevs => :mi, :equals => 1760.yards
19
+ s.format :name => :feet_inches_and_32s,
20
+ :format => lambda { |u|
21
+ ru = u.inch.round_to_nearest 32
22
+ feet = ru.feet.floor
23
+ inches = (ru-feet).inches
24
+ "#{feet} #{inches.with_fraction 32}" }
25
+ end
26
+ end
27
+
28
+ l = 13.28090635.ft
29
+ assert(l.measure == Units.length)
30
+ assert(3.46875.with_fraction(32) == "3-15/32")
31
+ assert(l.format(:feet_inches_and_32s) == "13 feet 3-12/32 inches")
32
+
33
+ almost = 1.999.ft
34
+ assert(almost.inch.round_to_nearest(32) == 24.0.in)
35
+
36
+ almost = 1.996.ft
37
+ assert(almost.inch.round_to_nearest(32) == 23.9375.in)
38
+
39
+ end
40
+
41
+ end