rails-units 1.3.1
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/CHANGELOG.txt +212 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +20 -0
- data/Manifest.txt +19 -0
- data/README.md +161 -0
- data/RakeFile +83 -0
- data/TODO +2 -0
- data/VERSION +1 -0
- data/lib/ruby-units.rb +14 -0
- data/lib/ruby_units.rb +14 -0
- data/lib/ruby_units/array.rb +9 -0
- data/lib/ruby_units/cache.rb +20 -0
- data/lib/ruby_units/date.rb +53 -0
- data/lib/ruby_units/fixnum.rb +20 -0
- data/lib/ruby_units/math.rb +101 -0
- data/lib/ruby_units/numeric.rb +8 -0
- data/lib/ruby_units/object.rb +8 -0
- data/lib/ruby_units/string.rb +122 -0
- data/lib/ruby_units/time.rb +71 -0
- data/lib/ruby_units/unit.rb +1272 -0
- data/lib/ruby_units/unit_definitions.rb +248 -0
- data/lib/ruby_units/version.rb +5 -0
- data/rails-units.gemspec +83 -0
- data/spec/ruby-units/array_spec.rb +14 -0
- data/spec/ruby-units/complex_spec.rb +37 -0
- data/spec/ruby-units/date_spec.rb +38 -0
- data/spec/ruby-units/math_spec.rb +63 -0
- data/spec/ruby-units/numeric_spec.rb +12 -0
- data/spec/ruby-units/object_spec.rb +7 -0
- data/spec/ruby-units/string_spec.rb +61 -0
- data/spec/ruby-units/time_spec.rb +28 -0
- data/spec/ruby-units/unit_spec.rb +888 -0
- data/spec/spec_helper.rb +4 -0
- data/test/test_cache.rb +26 -0
- data/test/test_ruby-units.rb +1011 -0
- metadata +129 -0
data/TODO
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.3.1
|
data/lib/ruby-units.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
require "ruby_units/version"
|
3
|
+
require "ruby_units/cache"
|
4
|
+
require 'ruby_units/array'
|
5
|
+
require 'ruby_units/date'
|
6
|
+
require 'ruby_units/time'
|
7
|
+
require 'ruby_units/math'
|
8
|
+
require 'ruby_units/numeric'
|
9
|
+
require 'ruby_units/object'
|
10
|
+
require 'ruby_units/string'
|
11
|
+
require 'ruby_units/unit_definitions'
|
12
|
+
require 'ruby_units/unit'
|
13
|
+
require 'ruby_units/fixnum'
|
14
|
+
|
data/lib/ruby_units.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
require "ruby_units/version"
|
3
|
+
require "ruby_units/cache"
|
4
|
+
require 'ruby_units/array'
|
5
|
+
require 'ruby_units/date'
|
6
|
+
require 'ruby_units/time'
|
7
|
+
require 'ruby_units/math'
|
8
|
+
require 'ruby_units/numeric'
|
9
|
+
require 'ruby_units/object'
|
10
|
+
require 'ruby_units/string'
|
11
|
+
require 'ruby_units/unit_definitions'
|
12
|
+
require 'ruby_units/unit'
|
13
|
+
require 'ruby_units/fixnum'
|
14
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Unit < Numeric
|
2
|
+
@@cached_units = {}
|
3
|
+
|
4
|
+
class Cache
|
5
|
+
def self.get(key = nil)
|
6
|
+
key.nil? ? @@cached_units : @@cached_units[key]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.set(key, value)
|
10
|
+
@@cached_units[key] = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.clear
|
14
|
+
@@cached_units = {}
|
15
|
+
@@base_unit_cache = {}
|
16
|
+
Unit.new(1)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Allow date objects to do offsets by a time unit
|
2
|
+
# Date.today + U"1 week" => gives today+1 week
|
3
|
+
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
class Date
|
7
|
+
alias :unit_date_add :+
|
8
|
+
def +(unit)
|
9
|
+
case unit
|
10
|
+
when Unit
|
11
|
+
unit = unit.to('d').round if ['y', 'decade', 'century'].include? unit.units
|
12
|
+
unit_date_add(unit.to('day').scalar)
|
13
|
+
else
|
14
|
+
unit_date_add(unit)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
alias :unit_date_sub :-
|
20
|
+
def -(unit)
|
21
|
+
case unit
|
22
|
+
when Unit
|
23
|
+
unit = unit.to('d').round if ['y', 'decade', 'century'].include? unit.units
|
24
|
+
unit_date_sub(unit.to('day').scalar)
|
25
|
+
else
|
26
|
+
unit_date_sub(unit)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_unit(other = nil)
|
31
|
+
other ? Unit.new(self).to(other) : Unit.new(self)
|
32
|
+
end
|
33
|
+
alias :unit :to_unit
|
34
|
+
|
35
|
+
unless Date.instance_methods.include?(:to_time)
|
36
|
+
def to_time
|
37
|
+
Time.local(*ParseDate.parsedate(self.to_s))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
alias :units_datetime_inspect :inspect
|
42
|
+
def inspect(raw = false)
|
43
|
+
return self.units_datetime_inspect if raw
|
44
|
+
self.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
unless Date.instance_methods.include?(:to_date)
|
48
|
+
def to_date
|
49
|
+
Date.civil(self.year, self.month, self.day)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# this patch is necessary for ruby 1.8 because cases where
|
2
|
+
# Integers are divided by Units don't work quite right
|
3
|
+
|
4
|
+
if RUBY_VERSION < "1.9"
|
5
|
+
class Fixnum
|
6
|
+
alias quo_without_units quo
|
7
|
+
|
8
|
+
def quo_with_units(other)
|
9
|
+
case other
|
10
|
+
when Unit
|
11
|
+
self * other.inverse
|
12
|
+
else
|
13
|
+
quo_without_units(other)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
alias quo quo_with_units
|
18
|
+
alias / quo_with_units
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Math will convert unit objects to radians and then attempt to use the value for
|
2
|
+
# trigonometric functions.
|
3
|
+
require 'mathn'
|
4
|
+
|
5
|
+
module Math
|
6
|
+
|
7
|
+
alias :unit_sqrt :sqrt
|
8
|
+
def sqrt(n)
|
9
|
+
if Unit === n
|
10
|
+
(n**(Rational(1,2))).to_unit
|
11
|
+
else
|
12
|
+
unit_sqrt(n)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
module_function :unit_sqrt
|
16
|
+
module_function :sqrt
|
17
|
+
|
18
|
+
#:nocov:
|
19
|
+
if self.respond_to?(:cbrt)
|
20
|
+
alias :unit_cbrt :cbrt
|
21
|
+
def cbrt(n)
|
22
|
+
if Unit === n
|
23
|
+
(n**(Rational(1,3))).to_unit
|
24
|
+
else
|
25
|
+
unit_cbrt(n)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
module_function :unit_cbrt
|
29
|
+
module_function :cbrt
|
30
|
+
end
|
31
|
+
#:nocov:
|
32
|
+
|
33
|
+
alias :unit_sin :sin
|
34
|
+
def sin(n)
|
35
|
+
Unit === n ? unit_sin(n.to('radian').scalar) : unit_sin(n)
|
36
|
+
end
|
37
|
+
module_function :unit_sin
|
38
|
+
module_function :sin
|
39
|
+
|
40
|
+
alias :unit_cos :cos
|
41
|
+
def cos(n)
|
42
|
+
Unit === n ? unit_cos(n.to('radian').scalar) : unit_cos(n)
|
43
|
+
end
|
44
|
+
module_function :unit_cos
|
45
|
+
module_function :cos
|
46
|
+
|
47
|
+
alias :unit_sinh :sinh
|
48
|
+
def sinh(n)
|
49
|
+
Unit === n ? unit_sinh(n.to('radian').scalar) : unit_sinh(n)
|
50
|
+
end
|
51
|
+
module_function :unit_sinh
|
52
|
+
module_function :sinh
|
53
|
+
|
54
|
+
alias :unit_cosh :cosh
|
55
|
+
def cosh(n)
|
56
|
+
Unit === n ? unit_cosh(n.to('radian').scalar) : unit_cosh(n)
|
57
|
+
end
|
58
|
+
module_function :unit_cosh
|
59
|
+
module_function :cosh
|
60
|
+
|
61
|
+
alias :unit_tan :tan
|
62
|
+
def tan(n)
|
63
|
+
Unit === n ? unit_tan(n.to('radian').scalar) : unit_tan(n)
|
64
|
+
end
|
65
|
+
module_function :tan
|
66
|
+
module_function :unit_tan
|
67
|
+
|
68
|
+
alias :unit_tanh :tanh
|
69
|
+
def tanh(n)
|
70
|
+
Unit === n ? unit_tanh(n.to('radian').scalar) : unit_tanh(n)
|
71
|
+
end
|
72
|
+
module_function :unit_tanh
|
73
|
+
module_function :tanh
|
74
|
+
|
75
|
+
alias :unit_hypot :hypot
|
76
|
+
# Convert parameters to consistent units and perform the function
|
77
|
+
def hypot(x,y)
|
78
|
+
if Unit === x && Unit === y
|
79
|
+
(x**2 + y**2)**(1/2)
|
80
|
+
else
|
81
|
+
unit_hypot(x,y)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
module_function :unit_hypot
|
85
|
+
module_function :hypot
|
86
|
+
|
87
|
+
alias :unit_atan2 :atan2
|
88
|
+
def atan2(x,y)
|
89
|
+
case
|
90
|
+
when (x.is_a?(Unit) && y.is_a?(Unit)) && (x !~ y)
|
91
|
+
raise ArgumentError, "Incompatible Units"
|
92
|
+
when (x.is_a?(Unit) && y.is_a?(Unit)) && (x =~ y)
|
93
|
+
Math::unit_atan2(x.base_scalar, y.base_scalar)
|
94
|
+
else
|
95
|
+
Math::unit_atan2(x,y)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
module_function :unit_atan2
|
99
|
+
module_function :atan2
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'time'
|
2
|
+
# make a string into a unit
|
3
|
+
class String
|
4
|
+
def to_unit(other = nil)
|
5
|
+
other ? Unit.new(self).to(other) : Unit.new(self)
|
6
|
+
end
|
7
|
+
alias :unit :to_unit
|
8
|
+
alias :u :to_unit
|
9
|
+
alias :unit_format :%
|
10
|
+
|
11
|
+
# format unit output using formating codes '%0.2f' % '1 mm'.unit => '1.00 mm'
|
12
|
+
def %(*args)
|
13
|
+
return "" if self.empty?
|
14
|
+
case
|
15
|
+
when args.first.is_a?(Unit)
|
16
|
+
args.first.to_s(self)
|
17
|
+
when (!defined?(Uncertain).nil? && args.first.is_a?(Uncertain))
|
18
|
+
args.first.to_s(self)
|
19
|
+
when args.first.is_a?(Complex)
|
20
|
+
args.first.to_s
|
21
|
+
else
|
22
|
+
unit_format(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#needed for compatibility with Rails, which defines a String.from method
|
27
|
+
if self.public_instance_methods.include? 'from'
|
28
|
+
alias :old_from :from
|
29
|
+
end
|
30
|
+
|
31
|
+
# "5 min".from("now")
|
32
|
+
def from(time_point = ::Time.now)
|
33
|
+
return old_from(time_point) if self.respond_to?(:old_from) && time_point.instance_of?(Integer)
|
34
|
+
self.unit.from(time_point)
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :after :from
|
38
|
+
|
39
|
+
def from_now
|
40
|
+
self.from('now')
|
41
|
+
end
|
42
|
+
|
43
|
+
# "5 min".ago
|
44
|
+
def ago
|
45
|
+
self.unit.ago
|
46
|
+
end
|
47
|
+
|
48
|
+
def before(time_point = ::Time.now)
|
49
|
+
self.unit.before(time_point)
|
50
|
+
end
|
51
|
+
|
52
|
+
def before_now
|
53
|
+
self.before('now')
|
54
|
+
end
|
55
|
+
|
56
|
+
def since(time_point = ::Time.now)
|
57
|
+
self.unit.since(time_point)
|
58
|
+
end
|
59
|
+
|
60
|
+
def until(time_point = ::Time.now)
|
61
|
+
self.unit.until(time_point)
|
62
|
+
end
|
63
|
+
|
64
|
+
def time(options = {})
|
65
|
+
self.to_time(options) rescue self.to_datetime(options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_time(options = {})
|
69
|
+
begin
|
70
|
+
#raises exception when Chronic not defined or when it returns a nil (i.e., can't parse the input)
|
71
|
+
r = Chronic.parse(self,options)
|
72
|
+
raise(ArgumentError, 'Invalid Time String') unless r
|
73
|
+
return r
|
74
|
+
rescue
|
75
|
+
case
|
76
|
+
when self == "now"
|
77
|
+
Time.now
|
78
|
+
when Time.respond_to?(:parse)
|
79
|
+
Time.parse(self)
|
80
|
+
else
|
81
|
+
Time.local(*ParseDate.parsedate(self))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_datetime(options = {})
|
87
|
+
begin
|
88
|
+
# raises an exception if Chronic.parse = nil or if Chronic not defined
|
89
|
+
r = Chronic.parse(self,options).send(:to_datetime)
|
90
|
+
rescue Exception => e
|
91
|
+
r = case
|
92
|
+
when self.to_s == "now"
|
93
|
+
DateTime.now
|
94
|
+
else
|
95
|
+
DateTime.parse(self)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
raise RuntimeError, "Invalid Time String (#{self.to_s})" if r == DateTime.new
|
99
|
+
return r
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_date(options={})
|
103
|
+
begin
|
104
|
+
r = Chronic.parse(self,options).to_date
|
105
|
+
rescue
|
106
|
+
r = case
|
107
|
+
when self == "today"
|
108
|
+
Date.today
|
109
|
+
when RUBY_VERSION < "1.9"
|
110
|
+
Date.civil(*ParseDate.parsedate(self)[0..5].compact)
|
111
|
+
else
|
112
|
+
Date.parse(self)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
raise RuntimeError, 'Invalid Date String' if r == Date.new
|
116
|
+
return r
|
117
|
+
end
|
118
|
+
|
119
|
+
def datetime(options = {})
|
120
|
+
self.to_datetime(options) rescue self.to_time(options)
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# Time math is handled slightly differently. The difference is considered to be an exact duration if
|
3
|
+
# the subtracted value is in hours, minutes, or seconds. It is rounded to the nearest day if the offset
|
4
|
+
# is in years, decades, or centuries. This leads to less precise values, but ones that match the
|
5
|
+
# calendar better.
|
6
|
+
class Time
|
7
|
+
|
8
|
+
class << self
|
9
|
+
alias unit_time_at at
|
10
|
+
end
|
11
|
+
|
12
|
+
# Convert a duration to a Time value by considering the duration to be the number of seconds since the
|
13
|
+
# epoch
|
14
|
+
def self.at(arg)
|
15
|
+
case arg
|
16
|
+
when Unit
|
17
|
+
unit_time_at(arg.to("s").scalar)
|
18
|
+
else
|
19
|
+
unit_time_at(arg)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_unit(other = nil)
|
24
|
+
other ? Unit.new(self).to(other) : Unit.new(self)
|
25
|
+
end
|
26
|
+
alias :unit :to_unit
|
27
|
+
alias :u :to_unit
|
28
|
+
alias :unit_add :+
|
29
|
+
|
30
|
+
unless Time.instance_methods.include?(:to_date)
|
31
|
+
def to_date
|
32
|
+
x=(Date.civil(1970,1,1)+((self.to_f+self.gmt_offset)/86400.0)-0.5)
|
33
|
+
Date.civil(x.year, x.month, x.day)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def +(other)
|
38
|
+
case other
|
39
|
+
when Unit
|
40
|
+
other = other.to('d').round.to('s') if ['y', 'decade', 'century'].include? other.units
|
41
|
+
begin
|
42
|
+
unit_add(other.to('s').scalar)
|
43
|
+
rescue RangeError
|
44
|
+
self.to_datetime + other
|
45
|
+
end
|
46
|
+
else
|
47
|
+
unit_add(other)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# usage: Time.in '5 min'
|
52
|
+
def self.in(duration)
|
53
|
+
Time.now + duration.to_unit
|
54
|
+
end
|
55
|
+
|
56
|
+
alias :unit_sub :-
|
57
|
+
|
58
|
+
def -(other)
|
59
|
+
case other
|
60
|
+
when Unit
|
61
|
+
other = other.to('d').round.to('s') if ['y', 'decade', 'century'].include? other.units
|
62
|
+
begin
|
63
|
+
unit_sub(other.to('s').scalar)
|
64
|
+
rescue RangeError
|
65
|
+
self.send(:to_datetime) - other
|
66
|
+
end
|
67
|
+
else
|
68
|
+
unit_sub(other)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|