alchemist 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +11 -9
- data/README.md +56 -84
- data/Rakefile +3 -12
- data/alchemist.gemspec +2 -0
- data/lib/alchemist.rb +68 -31
- data/lib/alchemist/{compound_numeric_conversion.rb → compound_measurement.rb} +3 -4
- data/lib/alchemist/conversion_table.rb +69 -0
- data/lib/alchemist/data/binary_prefixes.yml +33 -0
- data/lib/alchemist/data/si_units.yml +76 -0
- data/lib/alchemist/data/unit_prefixes.yml +58 -0
- data/lib/alchemist/{units.yml → data/units.yml} +0 -0
- data/lib/alchemist/geospatial.rb +9 -0
- data/lib/alchemist/measurement.rb +128 -0
- data/lib/alchemist/measurement_convertor.rb +35 -0
- data/lib/alchemist/module_builder.rb +41 -0
- data/lib/alchemist/objects/planets/earth.rb +34 -0
- data/lib/alchemist/version.rb +1 -1
- data/spec/alchemist_spec.rb +51 -0
- data/spec/compound_measurement_spec.rb +13 -0
- data/spec/conversion_table_spec.rb +39 -0
- data/spec/fixtures/bad_test.yml +11 -0
- data/spec/fixtures/good_test.yml +11 -0
- data/spec/measurement_spec.rb +69 -0
- data/spec/spec_helper.rb +6 -0
- metadata +44 -19
- data/LICENSE.txt +0 -22
- data/lib/alchemist/binary_prefixes.rb +0 -25
- data/lib/alchemist/compound.rb +0 -7
- data/lib/alchemist/numeric_conversion.rb +0 -146
- data/lib/alchemist/numeric_ext.rb +0 -13
- data/lib/alchemist/si_units.rb +0 -13
- data/lib/alchemist/unit_prefixes.rb +0 -38
- data/lib/alchemist/units.rb +0 -76
- data/test/alchemist_compound_test.rb +0 -12
- data/test/alchemist_measurement_test.rb +0 -13
- data/test/alchemist_test.rb +0 -98
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
:yotta: 1.2089258196146292e+24
|
3
|
+
:Y: 1.2089258196146292e+24
|
4
|
+
:zetta: 1.1805916207174113e+21
|
5
|
+
:Z: 1.1805916207174113e+21
|
6
|
+
:exa: 1.152921504606847e+18
|
7
|
+
:E: 1.152921504606847e+18
|
8
|
+
:peta: 1.125899906842624e+15
|
9
|
+
:P: 1.125899906842624e+15
|
10
|
+
:tera: 1099511627776.0
|
11
|
+
:T: 1099511627776.0
|
12
|
+
:giga: 1073741824.0
|
13
|
+
:G: 1073741824.0
|
14
|
+
:mega: 1048576.0
|
15
|
+
:M: 1048576.0
|
16
|
+
:kilo: 1024.0
|
17
|
+
:k: 1024.0
|
18
|
+
:kibi: 1024.0
|
19
|
+
:Ki: 1024.0
|
20
|
+
:mebi: 1048576.0
|
21
|
+
:Mi: 1048576.0
|
22
|
+
:gibi: 1073741824.0
|
23
|
+
:Gi: 1073741824.0
|
24
|
+
:tebi: 1099511627776.0
|
25
|
+
:Ti: 1099511627776.0
|
26
|
+
:pebi: 1.125899906842624e+15
|
27
|
+
:Pi: 1.125899906842624e+15
|
28
|
+
:exbi: 1.152921504606847e+18
|
29
|
+
:Ei: 1.152921504606847e+18
|
30
|
+
:zebi: 1.1805916207174113e+21
|
31
|
+
:Zi: 1.1805916207174113e+21
|
32
|
+
:yobi: 1.2089258196146292e+24
|
33
|
+
:Yi: 1.2089258196146292e+24
|
@@ -0,0 +1,76 @@
|
|
1
|
+
---
|
2
|
+
- m
|
3
|
+
- meter
|
4
|
+
- metre
|
5
|
+
- meters
|
6
|
+
- metres
|
7
|
+
- liter
|
8
|
+
- litre
|
9
|
+
- litres
|
10
|
+
- liters
|
11
|
+
- l
|
12
|
+
- L
|
13
|
+
- farad
|
14
|
+
- farads
|
15
|
+
- F
|
16
|
+
- coulombs
|
17
|
+
- C
|
18
|
+
- gray
|
19
|
+
- grays
|
20
|
+
- Gy
|
21
|
+
- siemen
|
22
|
+
- siemens
|
23
|
+
- S
|
24
|
+
- mhos
|
25
|
+
- mho
|
26
|
+
- ohm
|
27
|
+
- ohms
|
28
|
+
- volt
|
29
|
+
- volts
|
30
|
+
- V
|
31
|
+
- joule
|
32
|
+
- joules
|
33
|
+
- J
|
34
|
+
- newton
|
35
|
+
- newtons
|
36
|
+
- N
|
37
|
+
- lux
|
38
|
+
- lx
|
39
|
+
- henry
|
40
|
+
- henrys
|
41
|
+
- H
|
42
|
+
- b
|
43
|
+
- B
|
44
|
+
- bits
|
45
|
+
- bytes
|
46
|
+
- bit
|
47
|
+
- byte
|
48
|
+
- lumen
|
49
|
+
- lumens
|
50
|
+
- lm
|
51
|
+
- candela
|
52
|
+
- candelas
|
53
|
+
- cd
|
54
|
+
- tesla
|
55
|
+
- teslas
|
56
|
+
- T
|
57
|
+
- gauss
|
58
|
+
- Gs
|
59
|
+
- G
|
60
|
+
- gram
|
61
|
+
- gramme
|
62
|
+
- grams
|
63
|
+
- grammes
|
64
|
+
- g
|
65
|
+
- watt
|
66
|
+
- watts
|
67
|
+
- W
|
68
|
+
- pascal
|
69
|
+
- pascals
|
70
|
+
- Pa
|
71
|
+
- becquerel
|
72
|
+
- becquerels
|
73
|
+
- Bq
|
74
|
+
- curie
|
75
|
+
- curies
|
76
|
+
- Ci
|
@@ -0,0 +1,58 @@
|
|
1
|
+
---
|
2
|
+
:googol: 1.0e+100
|
3
|
+
:yotta: 1.0e+24
|
4
|
+
:Y: 1.0e+24
|
5
|
+
:zetta: 1.0e+21
|
6
|
+
:Z: 1.0e+21
|
7
|
+
:exa: 1.0e+18
|
8
|
+
:E: 1.0e+18
|
9
|
+
:peta: 1.0e+15
|
10
|
+
:P: 1.0e+15
|
11
|
+
:tera: 1000000000000.0
|
12
|
+
:T: 1000000000000.0
|
13
|
+
:giga: 1000000000.0
|
14
|
+
:G: 1000000000.0
|
15
|
+
:mega: 1000000.0
|
16
|
+
:M: 1000000.0
|
17
|
+
:kilo: 1000.0
|
18
|
+
:k: 1000.0
|
19
|
+
:hecto: 100.0
|
20
|
+
:h: 100.0
|
21
|
+
:deca: 10
|
22
|
+
:da: 10
|
23
|
+
:deci: 0.1
|
24
|
+
:d: 0.1
|
25
|
+
:centi: 0.01
|
26
|
+
:c: 0.01
|
27
|
+
:milli: 0.001
|
28
|
+
:m: 0.001
|
29
|
+
:micro: 1.0e-06
|
30
|
+
:u: 1.0e-06
|
31
|
+
:nano: 1.0e-09
|
32
|
+
:n: 1.0e-09
|
33
|
+
:pico: 1.0e-12
|
34
|
+
:p: 1.0e-12
|
35
|
+
:femto: 1.0e-15
|
36
|
+
:f: 1.0e-15
|
37
|
+
:atto: 1.0e-18
|
38
|
+
:a: 1.0e-18
|
39
|
+
:zepto: 1.0e-21
|
40
|
+
:z: 1.0e-21
|
41
|
+
:yocto: 1.0e-24
|
42
|
+
:y: 1.0e-24
|
43
|
+
:kibi: 1024.0
|
44
|
+
:Ki: 1024.0
|
45
|
+
:mebi: 1048576.0
|
46
|
+
:Mi: 1048576.0
|
47
|
+
:gibi: 1073741824.0
|
48
|
+
:Gi: 1073741824.0
|
49
|
+
:tebi: 1099511627776.0
|
50
|
+
:Ti: 1099511627776.0
|
51
|
+
:pebi: 1.125899906842624e+15
|
52
|
+
:Pi: 1.125899906842624e+15
|
53
|
+
:exbi: 1.152921504606847e+18
|
54
|
+
:Ei: 1.152921504606847e+18
|
55
|
+
:zebi: 1.1805916207174113e+21
|
56
|
+
:Zi: 1.1805916207174113e+21
|
57
|
+
:yobi: 1.2089258196146292e+24
|
58
|
+
:Yi: 1.2089258196146292e+24
|
File without changes
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "alchemist/measurement_convertor"
|
2
|
+
|
3
|
+
module Alchemist
|
4
|
+
class Measurement
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
attr_reader :unit_name, :exponent, :value
|
8
|
+
|
9
|
+
def initialize value, unit_name, exponent = 1.0
|
10
|
+
@value = value.to_f
|
11
|
+
@unit_name = unit_name
|
12
|
+
@exponent = exponent
|
13
|
+
end
|
14
|
+
|
15
|
+
def per
|
16
|
+
CompoundMeasurement.new self
|
17
|
+
end
|
18
|
+
|
19
|
+
def to type = nil
|
20
|
+
if type
|
21
|
+
convertor.send(type)
|
22
|
+
else
|
23
|
+
convertor
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def + measurement
|
28
|
+
ensure_shared_type!(measurement)
|
29
|
+
converted = measurement.to(unit_name)
|
30
|
+
Measurement.new(value + converted.value, unit_name, exponent)
|
31
|
+
end
|
32
|
+
|
33
|
+
def - measurement
|
34
|
+
ensure_shared_type!(measurement)
|
35
|
+
converted = measurement.to(unit_name)
|
36
|
+
Measurement.new(value - converted.value, unit_name, exponent)
|
37
|
+
end
|
38
|
+
|
39
|
+
def / measurement
|
40
|
+
ensure_shared_type!(measurement)
|
41
|
+
dividend = measurement.is_a?(Measurement) ? measurement.to(unit_name).to_f / exponent : measurement
|
42
|
+
Measurement.new(value / dividend, unit_name, exponent).value
|
43
|
+
end
|
44
|
+
|
45
|
+
def * multiplicand
|
46
|
+
if multiplicand.is_a?(Numeric)
|
47
|
+
Measurement.new(value * multiplicand, unit_name, exponent)
|
48
|
+
else
|
49
|
+
try_raising_dimension(multiplicand)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def base unit_type
|
54
|
+
conversion_base = conversion_base_for(unit_type)
|
55
|
+
convert_to_base conversion_base
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
value.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_i
|
63
|
+
value.to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_f
|
67
|
+
@value
|
68
|
+
end
|
69
|
+
|
70
|
+
def <=>(other)
|
71
|
+
(self.to_f * exponent).to_f <=> other.to(unit_name).to_f
|
72
|
+
end
|
73
|
+
|
74
|
+
def types
|
75
|
+
Alchemist.measurement_for(unit_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def shared_types other_unit_name
|
79
|
+
types & Alchemist.measurement_for(other_unit_name)
|
80
|
+
end
|
81
|
+
|
82
|
+
def coerce(number)
|
83
|
+
[self, number]
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def ensure_shared_type! measurement
|
89
|
+
if !has_shared_types?(measurement.unit_name)
|
90
|
+
incompatible_types
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def convert_to_base conversion_base
|
95
|
+
if conversion_base.is_a?(Array)
|
96
|
+
exponent * conversion_base.first.call(value)
|
97
|
+
else
|
98
|
+
exponent * value * conversion_base
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def conversion_base_for unit_type
|
103
|
+
Alchemist.conversion_table[unit_type][unit_name]
|
104
|
+
end
|
105
|
+
|
106
|
+
def has_shared_types? other_unit_name
|
107
|
+
shared_types(other_unit_name).length > 0
|
108
|
+
end
|
109
|
+
|
110
|
+
def try_raising_dimension(measurement)
|
111
|
+
valid_types = shared_types(measurement.unit_name)
|
112
|
+
Alchemist.operator_actions[:*].each do |s1, s2, new_type|
|
113
|
+
if (valid_types & [s1, s2]).any?
|
114
|
+
return Alchemist.measure(value * measurement.to_f, new_type)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
incompatible_types
|
118
|
+
end
|
119
|
+
|
120
|
+
def incompatible_types
|
121
|
+
raise Exception, "Incompatible Types"
|
122
|
+
end
|
123
|
+
|
124
|
+
def convertor
|
125
|
+
MeasurementConvertor.new(self)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module Alchemist
|
4
|
+
class MeasurementConvertor
|
5
|
+
def initialize from
|
6
|
+
@from = from
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing method, *args, &block
|
10
|
+
exponent, unit_name = Alchemist.parse_prefix(method)
|
11
|
+
convert(from.shared_types(unit_name), unit_name, exponent)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
attr_reader :from
|
16
|
+
|
17
|
+
def convert types, unit_name, exponent
|
18
|
+
if type = types[0]
|
19
|
+
conversion_base = BigDecimal.new(from.base(type).to_s)
|
20
|
+
conversion_factor = Alchemist.conversion_table[type][unit_name]
|
21
|
+
if proc_based?(conversion_factor)
|
22
|
+
Measurement.new(conversion_factor[1].call(conversion_base), unit_name, exponent)
|
23
|
+
else
|
24
|
+
Measurement.new(conversion_base / BigDecimal.new(conversion_factor.to_s), unit_name, exponent)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
raise Exception, "Incompatible Types"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def proc_based? conversion_factor
|
32
|
+
conversion_factor.is_a? Array
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Alchemist
|
2
|
+
class ModuleBuilder
|
3
|
+
def initialize category
|
4
|
+
@category = category
|
5
|
+
end
|
6
|
+
|
7
|
+
def build
|
8
|
+
Module.new.tap do |category_module|
|
9
|
+
category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end)
|
10
|
+
category_module.class_eval category_methods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
attr_reader :category
|
16
|
+
|
17
|
+
def category_methods
|
18
|
+
unit_names.map do |name|
|
19
|
+
%(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name)
|
20
|
+
end.join("\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def unit_names
|
24
|
+
Alchemist.conversion_table[category.to_sym].map { |values| values[0] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def prefixes_with_value(name)
|
28
|
+
if Alchemist.si_units.include?(name.to_s)
|
29
|
+
Alchemist.unit_prefixes
|
30
|
+
else
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def prefixed_methods(name)
|
36
|
+
prefixes_with_value(name).map do |prefix, value|
|
37
|
+
%(define_method("#{prefix}#{name}") { Alchemist.measure self, :#{name}, #{value} })
|
38
|
+
end.join("\n")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Alchemist
|
2
|
+
class Earth
|
3
|
+
RADIUS = Alchemist.measure(6378100, :meters)
|
4
|
+
|
5
|
+
def initialize measurement
|
6
|
+
@measurement = measurement
|
7
|
+
end
|
8
|
+
|
9
|
+
def geospatial
|
10
|
+
if types.include?(:angles)
|
11
|
+
geospatial_angle_to_arc
|
12
|
+
elsif types.include?(:distance)
|
13
|
+
geospatial_arc_to_angle
|
14
|
+
else
|
15
|
+
raise Exception, "geospatial must either be angles or distance"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
attr_reader :measurement, :base
|
21
|
+
|
22
|
+
def types
|
23
|
+
measurement.types
|
24
|
+
end
|
25
|
+
|
26
|
+
def geospatial_angle_to_arc
|
27
|
+
measurement.to(:radians).to_f * RADIUS
|
28
|
+
end
|
29
|
+
|
30
|
+
def geospatial_arc_to_angle
|
31
|
+
Alchemist.measure(measurement.to(:meters) / RADIUS, :radians)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/alchemist/version.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alchemist do
|
4
|
+
|
5
|
+
it "sets up Numeric" do
|
6
|
+
category_module = double()
|
7
|
+
module_builder = double()
|
8
|
+
module_builder.should_receive(:build).and_return(category_module)
|
9
|
+
fake_module = double()
|
10
|
+
fake_module.should_receive(:include).with(category_module)
|
11
|
+
Alchemist::ModuleBuilder.should_receive(:new).with('distance').and_return(module_builder)
|
12
|
+
stub_const("Numeric", fake_module)
|
13
|
+
Alchemist.setup('distance')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "creates a measurement" do
|
17
|
+
unit = Alchemist.measure(1, :meter)
|
18
|
+
expect(unit).to eq(1.meter)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "knows if it has a measurement" do
|
22
|
+
expect(Alchemist.has_measurement?(:meter)).to be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "knows if it doesn't have a measurement" do
|
26
|
+
expect(Alchemist.has_measurement?(:wombat)).to be_false
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can register units" do
|
30
|
+
Alchemist.register :quux, :qaat, 1.0
|
31
|
+
Alchemist.register :quux, :quut, 3.0
|
32
|
+
expect(Alchemist.conversion_table[:quux]).to eq({:qaat=>1.0, :quut=>3.0})
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can register units with plural names" do
|
36
|
+
Alchemist.register(:beards, [:beard_second, :beard_seconds], 1.0)
|
37
|
+
expect(Alchemist.conversion_table[:beards]).to eq({:beard_second=>1.0, :beard_seconds=>1.0})
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can register units with formulas" do
|
41
|
+
to = lambda { |t| t + 1 }
|
42
|
+
from = lambda { |t| t - 1 }
|
43
|
+
Alchemist.register(:yetis, :yeti, [to, from])
|
44
|
+
expect(Alchemist.conversion_table[:yetis]).to eq({:yeti => [to, from]})
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can parse a prefix" do
|
48
|
+
parsed = Alchemist.parse_prefix(:kilometer)
|
49
|
+
expect(parsed).to eq([1000.0, :meter])
|
50
|
+
end
|
51
|
+
end
|