uom 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +102 -0
- data/doc/units.txt +53 -0
- data/lib/active_support/README.txt +2 -0
- data/lib/active_support/core_ext/string.rb +7 -0
- data/lib/active_support/core_ext/string/inflections.rb +167 -0
- data/lib/active_support/inflections.rb +55 -0
- data/lib/active_support/inflector.rb +396 -0
- data/lib/uom.rb +7 -0
- data/lib/uom/composite_unit.rb +105 -0
- data/lib/uom/composite_unit_key_canonicalizer.rb +53 -0
- data/lib/uom/dimension.rb +39 -0
- data/lib/uom/dimensions.rb +12 -0
- data/lib/uom/error.rb +3 -0
- data/lib/uom/factor.rb +55 -0
- data/lib/uom/factors.rb +32 -0
- data/lib/uom/measurement.rb +127 -0
- data/lib/uom/unit.rb +213 -0
- data/lib/uom/unit_factory.rb +145 -0
- data/lib/uom/units.rb +115 -0
- metadata +101 -0
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
require 'uom/error'
|
4
|
+
require 'uom/units'
|
5
|
+
|
6
|
+
module UOM
|
7
|
+
# A UnitFactory creates Unit instances.
|
8
|
+
class UnitFactory
|
9
|
+
LABEL_EXPONENT_HASH = {:square=>2, :sq=>2, :cubic=>3, :cu=>3}
|
10
|
+
|
11
|
+
# Returns the unit with the given label. Creates a new unit if necessary.
|
12
|
+
#
|
13
|
+
# Raises MeasurementError if the unit could not be created.
|
14
|
+
def create(label)
|
15
|
+
create_atomic(label.to_s) or create_composite(label.to_s) or raise MeasurementError.new("Unit '#{label}' not found")
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def unit_for(label)
|
21
|
+
# the label => Unit hash of known units
|
22
|
+
@label_unit_hash ||= Unit.extent.association
|
23
|
+
# test whether there is an association before accessing it by label, since the extent hash
|
24
|
+
# has a hash factory that creates a Unit on demand, which is not desirable here
|
25
|
+
@label_unit_hash[label.to_sym] if @label_unit_hash.has_key?(label.to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_atomic(label)
|
29
|
+
scalar, axis = partition_atomic_label(label)
|
30
|
+
return if axis.nil?
|
31
|
+
label = "#{scalar}#{axis}".to_sym
|
32
|
+
unit_for(label) or Unit.new(label, scalar, axis)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_composite(label)
|
36
|
+
create_division(label) or create_product(label)
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_division(str)
|
40
|
+
labels = str.split('_per_')
|
41
|
+
return if labels.size < 2
|
42
|
+
units = labels.map { |label| create(label) or raise MeasurementError.new("Unit '#{label}' not found in #{str}") }
|
43
|
+
std_label = units.join('_per_')
|
44
|
+
unit_for(std_label) or units.inject { |quotient, divisor| quotient / divisor }
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_product(str)
|
48
|
+
std_label, units = collect_product_units(str)
|
49
|
+
unit_for(std_label) or units.inject { |product, multiplier| product * multiplier } unless units.size < 2
|
50
|
+
end
|
51
|
+
|
52
|
+
def collect_product_units(str)
|
53
|
+
return nil, [] if str.nil? or str.empty?
|
54
|
+
prefix = str[/[[:alnum:]]+/]
|
55
|
+
exponent = LABEL_EXPONENT_HASH[prefix.to_sym]
|
56
|
+
if exponent.nil? then
|
57
|
+
label, unit = slice_atomic_unit(str)
|
58
|
+
rest_label, rest_units = collect_product_units(str[label.length + 1..-1])
|
59
|
+
return "#{unit.label}_#{rest_label}".to_sym, [unit].concat(rest_units)
|
60
|
+
else
|
61
|
+
label, unit = slice_atomic_unit(str[prefix.length + 1..-1])
|
62
|
+
return "#{prefix}_#{unit.label}".to_sym, Array.new(exponent, unit)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the Unit which matches the str prefix.
|
67
|
+
#
|
68
|
+
# Raises MeasurementError if the unit could not be determined.
|
69
|
+
def slice_atomic_unit(str)
|
70
|
+
label = ''
|
71
|
+
str.scan(/_?[^_]+/) do |chunk|
|
72
|
+
label << chunk
|
73
|
+
unit = create_atomic(label)
|
74
|
+
return label, unit if unit
|
75
|
+
end
|
76
|
+
raise MeasurementError.new("Unit '#{str}' not found")
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the +[scale, axis]+ pair parsed from the label.
|
80
|
+
def partition_atomic_label(label)
|
81
|
+
# singularize the label if it contains more than two letters and ends in s but not ss. this condition
|
82
|
+
# avoids incorrect singularization of, e.g., ms (millisecond) and gauss.
|
83
|
+
label = label.singularize if label =~ /[[:alnum:]][^s]s$/
|
84
|
+
scalar, axis = parse_atomic_label(label) { |label| match_axis_label_suffix(label) }
|
85
|
+
return scalar, axis unless scalar.nil? and axis.nil?
|
86
|
+
parse_atomic_label(label) { |label| match_axis_abbreviation_suffix(label) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_atomic_label(label)
|
90
|
+
# match the axis from the label suffix
|
91
|
+
suffix, axis = yield label
|
92
|
+
return nil, axis if axis.nil? or suffix == label
|
93
|
+
prefix = suffix.nil? ? label : label[0...-suffix.to_s.length]
|
94
|
+
# remove trailing separator from prefix if necessary,
|
95
|
+
# e.g. the "milli_meter" prefix "milli_" becomes "milli"
|
96
|
+
prefix = prefix[0, prefix.length - 1] if prefix[prefix.length - 1] == '_'
|
97
|
+
# match the remaining prefix part of the label to a scalar
|
98
|
+
scalar = match_scalar(prefix)
|
99
|
+
return nil, nil if scalar.nil?
|
100
|
+
return scalar, axis
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns the axis label and Unit which match on the label suffix substring, or nil
|
104
|
+
# if no match. For example,
|
105
|
+
# match_axis_label_suffix(:millimeter)
|
106
|
+
# returns "meter" and the meter Unit.
|
107
|
+
def match_axis_label_suffix(str)
|
108
|
+
best = [nil, nil]
|
109
|
+
Unit.each do |unit|
|
110
|
+
label_s = unit.label.to_s
|
111
|
+
best = [label_s, unit] if suffix?(label_s, str) and (best.first.nil? or label_s.length > best.first.length)
|
112
|
+
end
|
113
|
+
best
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the axis abbreviation and Unit which match on the label suffix substring, or nil
|
117
|
+
# if no match. For example,
|
118
|
+
# match_axis_abbreviation_suffix(:mm)
|
119
|
+
# returns "meter" and the meter Unit.
|
120
|
+
def match_axis_abbreviation_suffix(str)
|
121
|
+
best = [nil, nil]
|
122
|
+
Unit.each do |unit|
|
123
|
+
unit.abbreviations.each do |abbrev|
|
124
|
+
abbrev_s = abbrev.to_s
|
125
|
+
best = abbrev_s, unit if suffix?(abbrev_s, str) and (best.first.nil? or abbrev_s.length > best.first.length)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
best
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the Factor which matches the label, or nil if no match.
|
132
|
+
def match_scalar(label)
|
133
|
+
label = label.to_sym
|
134
|
+
Factor.detect do |factor|
|
135
|
+
return factor if factor.label == label or factor.abbreviation == label
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns whether text ends in suffix.
|
140
|
+
def suffix?(suffix, text)
|
141
|
+
len = suffix.length
|
142
|
+
len <= text.length and suffix == text[-len, len]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/uom/units.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'uom/dimensions'
|
2
|
+
require 'uom/factors'
|
3
|
+
require 'uom/unit'
|
4
|
+
require 'uom/composite_unit'
|
5
|
+
|
6
|
+
module UOM
|
7
|
+
## STANDARD UNITS ##
|
8
|
+
|
9
|
+
# the standard units for each dimension
|
10
|
+
METER = Unit.new(:meter, :m, LENGTH, *METRIC_FACTORS)
|
11
|
+
GRAM = Unit.new(:gram, :gm, :g, MASS, *METRIC_FACTORS)
|
12
|
+
SECOND = Unit.new(:second, :sec, :s, TIME, MILLI, MICRO, NANO, PICO, FEMTO)
|
13
|
+
KELVIN = Unit.new(:kelvin, :K, TEMPERATURE)
|
14
|
+
CANDELA = Unit.new(:candela, :cd, INTENSITY)
|
15
|
+
AMPERE = Unit.new(:ampere, :A, CURRENT, *ELECTRONIC_FACTORS)
|
16
|
+
MOLE = Unit.new(:mole, :mol, MASS)
|
17
|
+
BYTE = Unit.new(:byte, :Byte, :B, :b, INFORMATION) # Scaled byte units, e.g KByte, are defined below
|
18
|
+
|
19
|
+
## SCALED UNITS ##
|
20
|
+
# Just a few common metric units; units are typically referred to by label rather than a constant,
|
21
|
+
# e.g. <tt>Unit.for(:millimeter)</tt>
|
22
|
+
MILLIMETER = Unit.for(:millimeter)
|
23
|
+
CENTIMETER = Unit.for(:centimeter)
|
24
|
+
DECIMETER = Unit.for(:decimeter)
|
25
|
+
MICROGRAM = Unit.for(:microgram)
|
26
|
+
MILLIGRAM = Unit.for(:milligram)
|
27
|
+
KILOGRAM = Unit.for(:kilogram)
|
28
|
+
|
29
|
+
# The BYTE scale is a base two axis multiplier rather than a metric scalar multipler.
|
30
|
+
# TODO - express these with base-2 factors?
|
31
|
+
KILOBYTE = Unit.new(:kilobyte, :KByte, :KB, BYTE, 1024)
|
32
|
+
MEGABYTE = Unit.new(:megabyte, :MByte, :MB, KILOBYTE, 1024)
|
33
|
+
GIGABYTE = Unit.new(:gigabyte, :GByte, :GB, MEGABYTE, 1024)
|
34
|
+
TERABYTE = Unit.new(:terabyte, :TByte, :TB, GIGABYTE, 1024)
|
35
|
+
PETABYTE = Unit.new(:petabyte, :PByte, :PB, TERABYTE, 1024)
|
36
|
+
|
37
|
+
## DERIVED UNITS ##
|
38
|
+
|
39
|
+
# astronomical distances
|
40
|
+
ANGSTROM = Unit.new(:angstrom, :a, METER, 10000000000)
|
41
|
+
AU = Unit.new(:astronomical_unit, :AU, METER, 149597870691)
|
42
|
+
LIGHT_YEAR = Unit.new(:light_year, :ly, METER, 9460730472580800)
|
43
|
+
|
44
|
+
# temperature with inverse converters
|
45
|
+
CELSIUS = Unit.new(:celsius, :C, KELVIN) { |celsius| celsius - 273.15 }
|
46
|
+
KELVIN.add_converter(CELSIUS) { |kelvin| kelvin + 273.15 }
|
47
|
+
FARENHEIT = Unit.new(:farenheit, :F, CELSIUS) { |farenheit| (farenheit - 32) * (5 / 9) }
|
48
|
+
CELSIUS.add_converter(FARENHEIT) { |celsius| (celsius + 32) * (9 / 5) }
|
49
|
+
# mark temperatures as uncountable for the parser
|
50
|
+
ActiveSupport::Inflector.inflections { |inflect| inflect.uncountable('celsius', 'kelvin', 'farenheit') }
|
51
|
+
|
52
|
+
# time
|
53
|
+
MINUTE = Unit.new(:minute, :min, SECOND, 60)
|
54
|
+
HOUR = Unit.new(:hour, :hr, MINUTE, 60)
|
55
|
+
DAY = Unit.new(:day, HOUR, 24)
|
56
|
+
|
57
|
+
# information
|
58
|
+
BIT = Unit.new(:bit, BYTE, 1.0 / 8)
|
59
|
+
|
60
|
+
# US Customary length units
|
61
|
+
INCH = Unit.new(:inch, :in, METER, 0.0254)
|
62
|
+
FOOT = Unit.new(:foot, :ft, INCH, 12)
|
63
|
+
YARD = Unit.new(:yard, :yd, FOOT, 3)
|
64
|
+
MILE = Unit.new(:mile, :mi, FOOT, 5280)
|
65
|
+
|
66
|
+
# US Customary weight units
|
67
|
+
OUNCE = Unit.new(:ounce, :oz, GRAM, 28.34952)
|
68
|
+
POUND = Unit.new(:pound, :lb, OUNCE, 16)
|
69
|
+
TON = Unit.new(:ton, POUND, 2000)
|
70
|
+
GRAIN = Unit.new(:grain, :gr, POUND, 1.0 / 7000)
|
71
|
+
DRAM = Unit.new(:dram, :dr, OUNCE, 1.0 / 16)
|
72
|
+
|
73
|
+
# Metric composite units
|
74
|
+
LITER = Unit.new(:liter, :l, :L, DECIMETER * DECIMETER * DECIMETER, *METRIC_FACTORS)
|
75
|
+
MILLILITER = Unit.for(:milliliter)
|
76
|
+
JOULE = Unit.new(:joule, :J, (KILOGRAM * (METER * METER)) / (SECOND * SECOND), *ELECTRONIC_FACTORS)
|
77
|
+
ERG = Unit.new(:erg, (GRAM * (CENTIMETER * CENTIMETER)) / (SECOND * SECOND), *ELECTRONIC_FACTORS)
|
78
|
+
DYNE = Unit.new(:dyne, :dyn, (GRAM * CENTIMETER) / (SECOND * SECOND), *ELECTRONIC_FACTORS)
|
79
|
+
BARYE = Unit.new(:barye, :Ba, GRAM / (CENTIMETER * (SECOND * SECOND)), *ELECTRONIC_FACTORS)
|
80
|
+
POISE = Unit.new(:poise, :P, GRAM / (CENTIMETER * SECOND), KILO, MILLI, MICRO, NANO, PICO)
|
81
|
+
COULOMB = Unit.new(:coulomb, AMPERE / SECOND, *ELECTRONIC_FACTORS) # C abbreviation is taken by CENTIGRADE
|
82
|
+
VOLT = Unit.new(:volt, :V, JOULE / COULOMB, *ELECTRONIC_FACTORS)
|
83
|
+
FARAD = Unit.new(:farad, COULOMB / VOLT, *ELECTRONIC_FACTORS) # F abbreviation is taken by FARENHEIT
|
84
|
+
WEBER = Unit.new(:weber, :Wb, VOLT * SECOND, *ELECTRONIC_FACTORS)
|
85
|
+
HENRY = Unit.new(:henry, :H, WEBER / AMPERE, METRIC_FACTORS)
|
86
|
+
MAXWELL = Unit.new(:maxwell, :Mx, WEBER, 1.0 / 10 ** 8, METRIC_FACTORS)
|
87
|
+
GAUSS = Unit.new(:gauss, :G, MAXWELL / (CENTIMETER * CENTIMETER), METRIC_FACTORS)
|
88
|
+
# mark gauss as irregular for the parser
|
89
|
+
ActiveSupport::Inflector.inflections { |inflect| inflect.irregular('gauss', 'gausses') }
|
90
|
+
TESLA = Unit.new(:tesla, :T, WEBER / (METER * METER), DECI, CENTI, MILLI, MICRO, NANO, PICO)
|
91
|
+
|
92
|
+
# The speed of light in a vacuum in cm/sec.
|
93
|
+
C = 29979245800
|
94
|
+
|
95
|
+
OHM = Unit.new(:ohm, SECOND / CENTIMETER, 10 ** 9 / C)
|
96
|
+
|
97
|
+
# US Customary liquid volume units
|
98
|
+
FLUID_OUNCE = Unit.new(:fluid_ounce, :fl_oz, MILLILITER, 29.57353)
|
99
|
+
TBSP = Unit.new(:tablespoon, :tbsp, FLUID_OUNCE, 2)
|
100
|
+
TSP = Unit.new(:teaspoon, :tsp, TBSP, 1.0 / 3)
|
101
|
+
CUP = Unit.new(:cup, :cp, FLUID_OUNCE, 8)
|
102
|
+
PINT = Unit.new(:pint, :pt, CUP, 2)
|
103
|
+
QUART = Unit.new(:quart, :qt, PINT, 2)
|
104
|
+
GALLON = Unit.new(:gallon, :gal, QUART, 4)
|
105
|
+
|
106
|
+
# US Customary dry volume units
|
107
|
+
DRY_PINT = Unit.new(:dry_pint, :dry_pt, LITER) { |liter| liter / 0.5506105 }
|
108
|
+
DRY_QUART = Unit.new(:dry_quart, :dry_qt, DRY_PINT, 2)
|
109
|
+
DRY_GALLON = Unit.new(:dry_gallon, :dry_gal, DRY_QUART, 4)
|
110
|
+
PECK = Unit.new(:peck, :pk, DRY_GALLON, 2)
|
111
|
+
BUSHEL = Unit.new(:bushel, :bu, PECK, 4)
|
112
|
+
|
113
|
+
# Pressure
|
114
|
+
PSI = Unit.for(:pounds_per_square_inch).add_abbreviation(:psi)
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 2
|
9
|
+
- 1
|
10
|
+
version: 1.2.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- OHSU
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-04 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: extensional
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: " UOM implements Units of Measurement based on the International System of Units (SI).\n The base SI units, metric scalar factors and all possible combinations of these units\n are supported out of the box.\n"
|
36
|
+
email: caruby.org@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- doc/units.txt
|
45
|
+
- lib/active_support/core_ext/string/inflections.rb
|
46
|
+
- lib/active_support/core_ext/string.rb
|
47
|
+
- lib/active_support/inflections.rb
|
48
|
+
- lib/active_support/inflector.rb
|
49
|
+
- lib/active_support/README.txt
|
50
|
+
- lib/uom/composite_unit.rb
|
51
|
+
- lib/uom/composite_unit_key_canonicalizer.rb
|
52
|
+
- lib/uom/dimension.rb
|
53
|
+
- lib/uom/dimensions.rb
|
54
|
+
- lib/uom/error.rb
|
55
|
+
- lib/uom/factor.rb
|
56
|
+
- lib/uom/factors.rb
|
57
|
+
- lib/uom/measurement.rb
|
58
|
+
- lib/uom/unit.rb
|
59
|
+
- lib/uom/unit_factory.rb
|
60
|
+
- lib/uom/units.rb
|
61
|
+
- lib/uom.rb
|
62
|
+
- History.txt
|
63
|
+
- LEGAL
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
has_rdoc: uom
|
67
|
+
homepage: http://github.com/caruby/uom/
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project: caruby
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Ruby Unit of Measurement library.
|
100
|
+
test_files: []
|
101
|
+
|