ruby-measurement 0.0.1 → 1.0.0

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.
@@ -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: []