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.
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'alchemist'
2
+
3
+ module Alchemist
4
+ class Measurement
5
+ def geospatial
6
+ Alchemist::Earth.new(self).geospatial
7
+ end
8
+ end
9
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Alchemist
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -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