ruby-measurement 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,17 +2,26 @@ require 'ruby-measurement/unit'
2
2
  require 'ruby-measurement/version'
3
3
 
4
4
  class Measurement
5
- SCIENTIFIC_NUMBER = %r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}.freeze
6
- RATIONAL_NUMBER = /\A\(?([+-]?\d+)\/(\d+)\)?\z/.freeze
7
- COMPLEX_NUMBER = /\A#{SCIENTIFIC_NUMBER}?#{SCIENTIFIC_NUMBER}i\b\z/.freeze
5
+ UNIT_REGEX = /([a-zA-Z].*)/.freeze
6
+ SCIENTIFIC_NUMBER = /([+-]?\d*\.?\d+(?:[Ee][+-]?)?\d*)/.freeze
7
+ SCIENTIFIC_REGEX = /\A#{SCIENTIFIC_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
8
+ RATIONAL_REGEX = /\A([+-]?\d+\s+)?((\d+)\/(\d+))?\s*#{UNIT_REGEX}?\z/.freeze
9
+ COMPLEX_REGEX = /\A#{SCIENTIFIC_NUMBER}?#{SCIENTIFIC_NUMBER}i\s*#{UNIT_REGEX}?\z/.freeze
8
10
 
9
11
  attr_reader :quantity, :unit
10
12
 
11
13
  class << self; attr_accessor :units end
12
14
  @units = {}
13
15
 
14
- def initialize(str)
15
- parse(str)
16
+ def initialize(quantity, unit_name = :count)
17
+ unit = unit_name
18
+ unit = self.class.units[unit_name.to_s] if unit_name.kind_of?(Symbol) || unit_name.kind_of?(String)
19
+
20
+ raise ArgumentError, "Invalid quantity: #{quantity}" unless quantity.kind_of?(Numeric)
21
+ raise ArgumentError, "Invalid unit: #{unit_name}" unless unit.kind_of?(Unit)
22
+
23
+ @quantity = quantity
24
+ @unit = unit
16
25
  end
17
26
 
18
27
  def inspect
@@ -26,10 +35,10 @@ class Measurement
26
35
  def +(obj)
27
36
  case obj
28
37
  when Numeric
29
- self.class.new("#{quantity + obj.to_f} #{unit}")
38
+ self.class.new(quantity + obj.to_f, unit)
30
39
  when self.class
31
40
  if obj.unit == unit
32
- self.class.new("#{quantity + obj.quantity} #{unit}")
41
+ self.class.new(quantity + obj.quantity, unit)
33
42
  else
34
43
  self + obj.convert_to(unit)
35
44
  end
@@ -41,10 +50,10 @@ class Measurement
41
50
  def -(obj)
42
51
  case obj
43
52
  when Numeric
44
- self.class.new("#{quantity - obj.to_f} #{unit}")
53
+ self.class.new(quantity - obj.to_f, unit)
45
54
  when self.class
46
55
  if obj.unit == unit
47
- self.class.new("#{quantity - obj.quantity} #{unit}")
56
+ self.class.new(quantity - obj.quantity, unit)
48
57
  else
49
58
  self - obj.convert_to(unit)
50
59
  end
@@ -56,10 +65,10 @@ class Measurement
56
65
  def *(obj)
57
66
  case obj
58
67
  when Numeric
59
- self.class.new("#{quantity * obj.to_f} #{unit}")
68
+ self.class.new(quantity * obj.to_f, unit)
60
69
  when self.class
61
70
  if obj.unit == unit
62
- self.class.new("#{quantity * obj.quantity} #{unit}")
71
+ self.class.new(quantity * obj.quantity, unit)
63
72
  else
64
73
  self * obj.convert_to(unit)
65
74
  end
@@ -71,10 +80,10 @@ class Measurement
71
80
  def /(obj)
72
81
  case obj
73
82
  when Numeric
74
- self.class.new("#{quantity / obj.to_f} #{unit}")
83
+ self.class.new(quantity / obj.to_f, unit)
75
84
  when self.class
76
85
  if obj.unit == unit
77
- self.class.new("#{quantity / obj.quantity} #{unit}")
86
+ self.class.new(quantity / obj.quantity, unit)
78
87
  else
79
88
  self / obj.convert_to(unit)
80
89
  end
@@ -86,7 +95,7 @@ class Measurement
86
95
  def **(obj)
87
96
  case obj
88
97
  when Numeric
89
- self.class.new("#{quantity ** obj.to_f} #{unit}")
98
+ self.class.new(quantity ** obj.to_f, unit)
90
99
  else
91
100
  raise ArgumentError, "Invalid arithmetic: #{self} ** #{obj}"
92
101
  end
@@ -105,7 +114,7 @@ class Measurement
105
114
  conversion = @unit.conversion(unit.name)
106
115
  raise ArgumentError, "Invalid conversion: '#@unit' to '#{unit.name}'" unless conversion
107
116
 
108
- self.class.new("#{conversion.call(@quantity)} #{unit.name}")
117
+ self.class.new(conversion.call(@quantity), unit.name)
109
118
  end
110
119
 
111
120
  def convert_to!(unit_name)
@@ -120,7 +129,33 @@ class Measurement
120
129
  end
121
130
 
122
131
  def self.parse(str = '0')
123
- new(str)
132
+ str = str.strip
133
+
134
+ if str =~ COMPLEX_REGEX
135
+ real, imaginary, unit_name = str.scan(COMPLEX_REGEX).first
136
+ quantity = Complex(real.to_f, imaginary.to_f).to_f
137
+ elsif str =~ SCIENTIFIC_REGEX
138
+ whole, unit_name = str.scan(SCIENTIFIC_REGEX).first
139
+ quantity = whole.to_f
140
+ elsif str =~ RATIONAL_REGEX
141
+ whole, _, numerator, denominator, unit_name = str.scan(RATIONAL_REGEX).first
142
+
143
+ if numerator && denominator
144
+ numerator = numerator.to_f + (denominator.to_f * whole.to_f)
145
+ denominator = denominator.to_f
146
+ quantity = Rational(numerator, denominator).to_f
147
+ else
148
+ quantity = whole.to_f
149
+ end
150
+ else
151
+ raise ArgumentError, "Unable to parse: '#{str}'"
152
+ end
153
+
154
+ unit_name ||= 'count'
155
+ unit = units[unit_name.strip.downcase]
156
+ raise ArgumentError, "Invalid unit: '#{unit_name}'" unless unit
157
+
158
+ new(quantity, unit)
124
159
  end
125
160
 
126
161
  def self.define(unit_name, &block)
@@ -128,29 +163,6 @@ class Measurement
128
163
  unit.aliases.each { |a| @units[a.downcase] = unit }
129
164
  end
130
165
 
131
- private
132
-
133
- def parse(str)
134
- value = str.strip
135
-
136
- quantity, unit_name = value.split(/\s+/, 2)
137
- unit_name ||= 'count'
138
- raise ArgumentError, "Missing quantity: '#{str}'" if !quantity || quantity.empty?
139
-
140
- @unit = self.class.units[unit_name.downcase]
141
- raise ArgumentError, "Invalid unit: '#{unit_name}'" unless @unit
142
-
143
- if quantity =~ COMPLEX_NUMBER
144
- real, imaginary = quantity.scan(COMPLEX_NUMBER).first
145
- @quantity = Complex(real.to_f, imaginary.to_f).to_f
146
- elsif quantity =~ RATIONAL_NUMBER
147
- numerator, denominator = quantity.scan(RATIONAL_NUMBER).first
148
- @quantity = Rational(numerator.to_i, denominator.to_i).to_f
149
- else
150
- @quantity = quantity.to_f
151
- end
152
- end
153
-
154
166
  define(:count) do |unit|
155
167
  unit.convert_to(:dozen) { |value| value / 12.0 }
156
168
  end
@@ -1,3 +1,3 @@
1
1
  class Measurement
2
- VERSION = '0.0.1'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.email = 'matt.huggins@gmail.com'
11
11
  gem.description = 'Simple gem for calculating and converting measurements'
12
12
  gem.summary = 'Simple gem for calculating and converting measurements'
13
- gem.homepage = 'https://github.com/mhuggins/measurement'
13
+ gem.homepage = 'https://github.com/mhuggins/ruby-measurement'
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-measurement
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2013-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &2160164820 !ruby/object:Gem::Requirement
16
+ requirement: &2156250560 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2160164820
24
+ version_requirements: *2156250560
25
25
  description: Simple gem for calculating and converting measurements
26
26
  email: matt.huggins@gmail.com
27
27
  executables: []
@@ -51,7 +51,7 @@ files:
51
51
  - lib/ruby-measurement/version.rb
52
52
  - ruby-measurement.gemspec
53
53
  - tasks/debug.rake
54
- homepage: https://github.com/mhuggins/measurement
54
+ homepage: https://github.com/mhuggins/ruby-measurement
55
55
  licenses: []
56
56
  post_install_message:
57
57
  rdoc_options: []