general_units 0.0.4 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b587736e5f764d47a5633f38ae9e871078a9d2d
4
- data.tar.gz: 91594f7393deb805d8a15ffbdeddaeaded832e18
3
+ metadata.gz: 7bb73e1c7d4e5f31b9e497ed16286044be1bd8db
4
+ data.tar.gz: 1c48246254178ba001ca32f74470b0349aa3aef6
5
5
  SHA512:
6
- metadata.gz: 4ec7e49bc0fb3bf97304216b650fbb1f23db262ca31475db84be92448b116e2d9e033be3a3002e3ab176dc9d715d5cdf30a79cb112614c321bad755de23eb98c
7
- data.tar.gz: f75851118b3fcbbeda331e3999d2a6d205984ffd6fac174ea65512f4ce84eb552078055c9f729323c7399e6213ca6f0b9bf060085437096a2537417f98e708dc
6
+ metadata.gz: 50ed29c361c9264f312f7c258d7d2e990d670b39490abbf139b5014e4d20f9896880a2f7515792aaa370ea01095bbafdd6005a80950a57d69d090d623ef2f611
7
+ data.tar.gz: fb25b41770e84fa70242d47a65b781c6878773aebe6737fa2efb21c0d08464ed060248a4cb53c7ba4f74fd98a37aa38b2b97acb4065df666e045da1d28857c85
@@ -2,6 +2,7 @@ require "general_units/version"
2
2
 
3
3
  module GeneralUnits
4
4
  def self.load!
5
+ load_arithmetics!
5
6
  load_units!
6
7
  load_numeric!
7
8
  load_derivatives!
@@ -9,9 +10,16 @@ module GeneralUnits
9
10
  require 'general_units/railtie'
10
11
  end
11
12
 
13
+ def self.load_arithmetics!
14
+ require 'general_units/arithmetics/methods'
15
+ end
16
+
12
17
  def self.load_units!
18
+ require 'general_units/units/base/measurement'
19
+ require 'general_units/units/base/unit'
13
20
  require 'general_units/units/weight'
14
21
  require 'general_units/units/length'
22
+ require 'general_units/units/volume'
15
23
  end
16
24
 
17
25
  def self.load_numeric!
@@ -0,0 +1,28 @@
1
+ module GeneralUnits
2
+
3
+ module Arithmetics
4
+ def self.two_factors_of(number)
5
+ if (number = number.to_i) && (number > 1)
6
+ primes, powers = number.prime_division.transpose
7
+ exponents = powers.map {|i| (0..i).to_a}
8
+ divisors = exponents.shift.product(*exponents).map do |powers|
9
+ primes.zip(powers).map {|prime, power| prime ** power}.inject(:*)
10
+ end
11
+ divisors.sort.map {|div| [div, number/div]}
12
+ else
13
+ []
14
+ end
15
+ end
16
+
17
+ def self.three_factors_of(number)
18
+ export = []
19
+ two_factors_of(number).each do |factors|
20
+ two_factors_of(factors[1]).each do |next_factors|
21
+ export << [factors[0]] + next_factors
22
+ end
23
+ end
24
+ export
25
+ end
26
+ end
27
+
28
+ end
@@ -1,4 +1,5 @@
1
- require 'general_units/units/length'
1
+ require 'prime'
2
+ require 'general_units/derivatives/box/packer'
2
3
 
3
4
  module GeneralUnits
4
5
 
@@ -12,8 +13,10 @@ module GeneralUnits
12
13
  delegate :hash, :to => :attributes
13
14
 
14
15
  def initialize(length = 0, width = 0, height = 0, unit)
15
- VALUES.each {|v| instance_variable_set(:"@#{v}", validate_value(eval(v), unit))}
16
- @unit = @height.unit
16
+ if unit = valid_unit(unit)
17
+ VALUES.each {|v| instance_variable_set(:"@#{v}", validate_dimension_value(eval(v), unit))}
18
+ @unit = unit
19
+ end
17
20
  end
18
21
 
19
22
  def attributes
@@ -25,11 +28,11 @@ module GeneralUnits
25
28
  end
26
29
 
27
30
  def amount
28
- values.sum
31
+ volume.amount
29
32
  end
30
33
 
31
34
  def volume
32
- length * width * height
35
+ Volume.new(length * width * height, :"cubic_#{unit.code}")
33
36
  end
34
37
 
35
38
  def has_space?
@@ -44,33 +47,50 @@ module GeneralUnits
44
47
  values.map {|d| d.to_s(round)}.join("x")
45
48
  end
46
49
 
47
- def formatted(round = nil)
48
- "#{to_s(round)} #{unit.short}"
50
+ def formatted(round = nil, &block)
51
+ if block_given?
52
+ yield to_s(round), unit
53
+ else
54
+ "#{to_s(round)} #{unit.short}"
55
+ end
56
+ end
57
+
58
+ def to_volume
59
+ volume
49
60
  end
50
61
 
51
- def accommodates?(*boxes)
52
- boxes.map! {|box| validate_capacity(box)}
53
- boxes.sum(&:volume) < volume && includes?(Box.new(boxes.map(&:length).max, boxes.map(&:width).max, boxes.map(&:height).max, unit))
62
+ def two_max_values
63
+ sorted = values.sort.reverse
64
+ sorted.first(2)
65
+ end
66
+
67
+ def max_face
68
+ two_max_values[0] * two_max_values[1]
69
+ end
70
+
71
+ def inspect
72
+ "<#{self.class.name} length=#{length} width=#{width} height=#{height} unit=#{unit}>"
54
73
  end
55
74
 
56
75
  def same_size?(other_object)
57
- other_object = validate_capacity(other_object)
76
+ other_object = validate_box(other_object)
58
77
  eval VALUES.permutation.to_a.map {|values_names| "(length == other_object.#{values_names[0]} && width == other_object.#{values_names[1]} && height == other_object.#{values_names[2]})"}.join("||")
59
78
  end
60
79
 
61
80
  def includes?(other_object)
62
- other_object = validate_capacity(other_object)
81
+ other_object = validate_box(other_object)
63
82
  eval VALUES.permutation.to_a.map {|values_names| "(length >= other_object.#{values_names[0]} && width >= other_object.#{values_names[1]} && height >= other_object.#{values_names[2]})"}.join("||")
64
83
  end
65
84
 
66
85
  ### ARITHMETICS START ###
86
+ delegate :<=>, :<, :>, :<=, :>=, :positive?, :negative?, :div, :divmod, :modulo, :%, :remainder, :abs, :zero?, :nonzero?, :coerce, :to => :volume
67
87
 
68
88
  def -@
69
89
  Box.new(*values.map {|v| -v}, unit)
70
90
  end
71
91
 
72
92
  def ==(other_object)
73
- other_object = validate_capacity(other_object)
93
+ other_object = validate_box(other_object)
74
94
  length == other_object.length && width == other_object.width && height == other_object.height
75
95
  rescue
76
96
  false
@@ -79,53 +99,11 @@ module GeneralUnits
79
99
  def eql?(other_object)
80
100
  self == other_object
81
101
  end
82
-
83
- def <=>(other_object)
84
- other_object = validate_capacity_or_length(other_object)
85
- volume <=> case other_object
86
- when Length then other_object
87
- when Box then other_object.volume
88
- end
89
- end
90
-
91
- def >(other_object)
92
- other_object = validate_capacity_or_length(other_object)
93
- volume > case other_object
94
- when Length then other_object
95
- when Box then other_object.volume
96
- end
97
- end
98
-
99
- def <(other_object)
100
- other_object = validate_capacity_or_length(other_object)
101
- volume < case other_object
102
- when Length then other_object
103
- when Box then other_object.volume
104
- end
105
- end
106
-
107
- def >=(other_object)
108
- other_object = validate_capacity_or_length(other_object)
109
- volume >= case other_object
110
- when Length then other_object
111
- when Box then other_object.volume
112
- end
113
- end
114
-
115
- def <=(other_object)
116
- other_object = validate_capacity_or_length(other_object)
117
- volume <= case other_object
118
- when Length then other_object
119
- when Box then other_object.volume
120
- end
121
- end
122
-
123
- delegate :positive?, :negative?, :to => :volume
124
102
 
125
103
  def +(other_object)
126
104
  other_object = validate_capacity_or_length(other_object)
127
105
  case other_object
128
- when Length then Box.new(*values.map {|v| v + other_object/3}, unit)
106
+ when Length, Volume then Box.new(*values.map {|v| v + other_object/3}, unit)
129
107
  when Box then Box.new(*VALUES.map {|v| eval(v) + other_object.send(v)}, unit)
130
108
  end
131
109
  end
@@ -133,7 +111,7 @@ module GeneralUnits
133
111
  def -(other_object)
134
112
  other_object = validate_capacity_or_length(other_object)
135
113
  case other_object
136
- when Length then Box.new(*values.map {|v| v - other_object/3}, unit)
114
+ when Length, Volume then Box.new(*values.map {|v| v - other_object/3}, unit)
137
115
  when Box then Box.new(*VALUES.map {|v| eval(v) - other_object.send(v)}, unit)
138
116
  end
139
117
  end
@@ -141,7 +119,7 @@ module GeneralUnits
141
119
  def *(other_object)
142
120
  other_object = validate_capacity_or_length(other_object)
143
121
  case other_object
144
- when Length then Box.new(*values.map {|v| v * other_object}, unit)
122
+ when Length, Volume then Box.new(*values.map {|v| v * other_object}, unit)
145
123
  when Box then Box.new(*VALUES.map {|v| eval(v) * other_object.send(v)}, unit)
146
124
  end
147
125
  end
@@ -149,22 +127,109 @@ module GeneralUnits
149
127
  def /(other_object)
150
128
  other_object = validate_capacity_or_length(other_object)
151
129
  case other_object
152
- when Length then Box.new(*values.map {|v| v / other_object}, unit)
130
+ when Length, Volume then Box.new(*values.map {|v| v / other_object}, unit)
153
131
  when Box then Box.new(*VALUES.map {|v| eval(v) / other_object.send(v)}, unit)
154
132
  end
155
133
  end
134
+
135
+ ### ARITHMETICS END ###
156
136
 
157
- delegate :div, :divmod, :modulo, :%, :remainder, :abs, :zero?, :nonzero?, :coerce, :to => :volume
137
+ def multiplicator(sum)
138
+ GeneralUnits::Arithmetics.three_factors_of(sum).map do |v|
139
+ Box.new(v[0]*length, v[1]*width, v[2]*height, unit)
140
+ end
141
+ end
158
142
 
159
- ### ARITHMETICS END ###
143
+ def multiply_to_strong_box(sum)
144
+ multiplicator(sum).min_by {|box| box.values.max - box.values.min}
145
+ end
146
+
147
+ def multiply_to_optimal(sum)
148
+ if sum.odd?
149
+ [multiply_to_strong_box(sum-1), self]
150
+ else
151
+ [multiply_to_strong_box(sum)]
152
+ end.compact
153
+ end
154
+
155
+ def concat_with(other_box, &block)
156
+ #other_box = other_box.convert_to(unit)
157
+
158
+ length_1, width_1, height_1 = *values.sort.reverse
159
+ length_2, width_2, height_2 = *other_box.values.sort.reverse
160
+
161
+ x1 = (length_1 - length_2).abs
162
+ length = [length_1, length_2].max
163
+
164
+ x2 = (width_1 - width_2).abs
165
+ width = [width_1, width_2].max
166
+
167
+ height = height_1 + height_2
168
+
169
+ if x1 > 0
170
+ y1 = width
171
+ z1 = length_1 > length_2 ? height_2 : height_1
172
+ yield(Box.new(x1, y1, z1, unit)) if block_given?
173
+ end
174
+
175
+ if x2 > 0
176
+ y2 = ((length_1 > length_2) && (width_1 > width_2)) ? length-x1 : length
177
+ z2 = width_1 > width_2 ? height_2 : height_1
178
+ yield(Box.new(x2, y2, z2, unit)) if block_given?
179
+ end
180
+
181
+ Box.new(length, width, height, unit).convert_to(unit)
182
+ end
183
+
184
+ def estimated_spaces_with(other_box, &block)
185
+ #other_box = other_box.convert_to(unit)
186
+
187
+ if includes?(other_box)
188
+ length_1, width_1, height_1 = *values.sort.reverse
189
+ length_2, width_2, height_2 = *other_box.values.sort.reverse
190
+
191
+ estimated_spaces = []
192
+
193
+ x1 = (length_1 - length_2).abs
194
+ if x1 > 0
195
+ space1 = Box.new(x1, width_1, height_1, unit)
196
+ estimated_spaces << space1
197
+ yield(space1) if block_given?
198
+ end
199
+
200
+ x2 = (width_1 - width_2).abs
201
+ if x2 > 0
202
+ space2 = Box.new(length_1 - x1, x2, height_1, unit)
203
+ estimated_spaces << space2
204
+ yield(space2) if block_given?
205
+ end
206
+
207
+ x3 = (height_1 - height_2).abs
208
+ if x3 > 0
209
+ space3 = Box.new(length_2, width_2, x3, unit)
210
+ estimated_spaces << space3
211
+ yield(space3) if block_given?
212
+ end
213
+
214
+ estimated_spaces
215
+ end
216
+ end
160
217
 
161
218
  private
219
+
220
+ def valid_unit(unit)
221
+ unit_object = case unit
222
+ when String, Symbol then Length.units.find {|u| u.code.to_s == unit.to_s}
223
+ when Base::Unit then unit
224
+ end
225
+ unit_object || raise(TypeError, "Unprocessable unit #{unit.inspect}")
226
+ end
162
227
 
163
- def validate_value(val, unit)
228
+ def validate_dimension_value(val, unit)
164
229
  val.is_a?(Length) ? val.convert_to(unit) : Length.new(val, unit)
165
230
  end
166
231
 
167
- def validate_capacity(val)
232
+ def validate_box(val)
168
233
  case val
169
234
  when Box then val.convert_to(unit)
170
235
  else raise(TypeError, "Box required, #{val.class} passed.")
@@ -173,9 +238,10 @@ module GeneralUnits
173
238
 
174
239
  def validate_capacity_or_length(val)
175
240
  case val
176
- when Box, Length then val.convert_to(unit)
177
- when Numeric then val.to_length(unit)
178
- else raise(TypeError, "Box or Length or Numeric required, #{val.class} passed.")
241
+ when Box then val.convert_to(unit)
242
+ when Volume then val.convert_to(volume.unit.code)
243
+ when Length, Numeric then val.to_volume(volume.unit.code)
244
+ else raise(TypeError, "Box or Volume or Numeric required, #{val.class} passed.")
179
245
  end
180
246
  end
181
247
 
@@ -0,0 +1,52 @@
1
+ module GeneralUnits
2
+ class Box
3
+
4
+ class Packer
5
+ attr_reader :boxes, :gaps, :coupled, :gaps_used, :rotated
6
+
7
+ def initialize(*boxes)
8
+ @boxes = boxes.sort_by(&:max_face).reverse
9
+ @coupled = @boxes.first # first box - biggest
10
+ @gaps = []
11
+ @gaps_used = [] # for test
12
+ @rotated = 0 # if height > current length
13
+ pack!
14
+ end
15
+
16
+ def to_s
17
+ inspect
18
+ end
19
+
20
+ def inspect
21
+ "<#{self.class.name} boxes=#{boxes.size} gaps=#{gaps.size} used=#{gaps_used.size} coupled=#{coupled}>"
22
+ end
23
+
24
+ private
25
+
26
+ def pack!
27
+ boxes[1..-1].each do |box|
28
+ unless use_gap_for(box)
29
+ @coupled = coupled.concat_with(box) {|r| @gaps << r}
30
+ # if coupled.height > coupled.length
31
+ # @rotated += 1
32
+ # end
33
+ end
34
+ end
35
+ coupled
36
+ end
37
+
38
+ def use_gap_for(box)
39
+ gap = @gaps.find_all {|g| g.includes?(box)}.min_by {|g| g.volume - box.volume}
40
+ if gap && gap.estimated_spaces_with(box) {|r| @gaps << r}
41
+ @gaps.delete_if {|r| r.object_id == gap.object_id}
42
+ @gaps_used << box
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -7,11 +7,11 @@ module GeneralUnits
7
7
  end
8
8
 
9
9
  def weight_units_for_select
10
- Weight::UNITS.map {|u| [u.name, u.code]}
10
+ Weight.units.map {|u| [u.name, u.code]}
11
11
  end
12
12
 
13
13
  def length_units_for_select
14
- Length::UNITS.map {|u| [u.name, u.code]}
14
+ Length.units.map {|u| [u.name, u.code]}
15
15
  end
16
16
 
17
17
  end
@@ -1,7 +1,7 @@
1
1
  module GeneralUnits
2
2
 
3
3
  module ActiveRecordExtension
4
- extend ActiveSupport::Concern
4
+ extend ::ActiveSupport::Concern
5
5
 
6
6
  module ClassMethods
7
7
 
@@ -17,12 +17,12 @@ module GeneralUnits
17
17
  self._has_weight[prefix][:default_unit] = default_unit = options[:default_unit]||:kilogram
18
18
  self._has_weight[prefix][:default_unit_method] = default_unit_method = options[:default_unit_method]||:"deafult_#{prefix}_unit"
19
19
 
20
- validates_inclusion_of unit_field, :in => Weight::UNITS.map {|u| u.code.to_s}, :if => Proc.new {|o| o.send(amount_field).present?}
20
+ validates_inclusion_of unit_field, :in => Weight.units.map {|u| u.code.to_s}, :if => Proc.new {|o| o.send(amount_field).present?}
21
21
 
22
22
  class_eval <<-EOV
23
23
 
24
24
  def #{unit_field}=(value)
25
- if value.to_sym.in?(GeneralUnits::Weight::UNITS.map(&:code))
25
+ if value.to_sym.in?(GeneralUnits::Weight.units.map(&:code))
26
26
  super(value.to_s)
27
27
  else
28
28
  raise ArgumentError, "Unprocessable unit: \#{value.inspect\}"
@@ -77,12 +77,12 @@ module GeneralUnits
77
77
  self._has_length[prefix][:default_unit] = default_unit = options[:default_unit]||:centimeter
78
78
  self._has_length[prefix][:default_unit_method] = default_unit_method = options[:default_unit_method]||:"deafult_#{prefix}_unit"
79
79
 
80
- validates_inclusion_of unit_field, :in => Length::UNITS.map {|u| u.code.to_s}, :if => Proc.new {|o| o.send(amount_field).present?}
80
+ validates_inclusion_of unit_field, :in => Length.units.map {|u| u.code.to_s}, :if => Proc.new {|o| o.send(amount_field).present?}
81
81
 
82
82
  class_eval <<-EOV
83
83
 
84
84
  def #{unit_field}=(value)
85
- if value.to_sym.in?(GeneralUnits::Length::UNITS.map(&:code))
85
+ if value.to_sym.in?(GeneralUnits::Length.units.map(&:code))
86
86
  super(value.to_s)
87
87
  else
88
88
  raise ArgumentError, "Unprocessable unit: \#{value.inspect\}"
@@ -140,12 +140,12 @@ module GeneralUnits
140
140
  self._has_box[prefix][:default_unit] = default_unit = options[:default_unit]||:centimeter
141
141
  self._has_box[prefix][:default_unit_method] = default_unit_method = options[:default_unit_method]||:"deafult_#{prefix}_unit"
142
142
 
143
- validates_inclusion_of unit_field, :in => Length::UNITS.map {|u| u.code.to_s}, :if => Proc.new {|o| o.send(length_field).present? || o.send(width_field).present? || o.send(height_field).present?}
143
+ validates_inclusion_of unit_field, :in => Length.units.map {|u| u.code.to_s}, :if => Proc.new {|o| o.send(length_field).present? || o.send(width_field).present? || o.send(height_field).present?}
144
144
 
145
145
  class_eval <<-EOV
146
146
 
147
147
  def #{unit_field}=(value)
148
- if value.to_sym.in?(GeneralUnits::Length::UNITS.map(&:code))
148
+ if value.to_sym.in?(GeneralUnits::Length.units.map(&:code))
149
149
  super(value.to_s)
150
150
  else
151
151
  raise ArgumentError, "Unprocessable unit: \#{value.inspect\}"
@@ -7,5 +7,9 @@ class Numeric
7
7
  def to_length(unit = :millimeter)
8
8
  GeneralUnits::Length.new(self, unit)
9
9
  end
10
+
11
+ def to_volume(unit = :cubic_millimeter)
12
+ GeneralUnits::Volume.new(self, unit)
13
+ end
10
14
 
11
15
  end
@@ -0,0 +1,185 @@
1
+ module GeneralUnits
2
+ module Base
3
+ class Measurement
4
+ class_attribute :units unless defined?(units)
5
+
6
+ attr_reader :amount, :unit
7
+ delegate :hash, :to => :attributes
8
+
9
+ def initialize(amount = 0, unit)
10
+ if unit = valid_unit?(unit)
11
+ @amount = amount.try(:to_d)||0.to_d
12
+ @unit = unit
13
+ end
14
+ end
15
+
16
+ def attributes
17
+ {:amount => amount, :unit => unit}
18
+ end
19
+
20
+ def to_s(round = nil)
21
+ "#{to_f.divmod(1).last == 0 ? to_f.round(0) : to_f.round(round||2)}"
22
+ end
23
+
24
+ def inspect
25
+ "<#{self.class.name} amount=#{amount} unit=#{unit}>"
26
+ end
27
+
28
+ def formatted(round = nil, &block)
29
+ if block_given?
30
+ yield to_s(round), unit
31
+ else
32
+ "#{to_s(round)} #{unit.short}"
33
+ end
34
+ end
35
+
36
+ ### ARITHMETICS START ###
37
+ delegate :to_f, :to_d, :to_i, :round, :to => :amount
38
+
39
+ def measurement
40
+ self.class.name.split("::").last.underscore
41
+ end
42
+
43
+ def -@
44
+ self.class.new(-amount, unit)
45
+ end
46
+
47
+ def ==(other_object)
48
+ amount == valid_amount(other_object)
49
+ rescue NoMethodError
50
+ false
51
+ end
52
+
53
+ def eql?(other_object)
54
+ self == other_object
55
+ end
56
+
57
+ def <=>(other_object)
58
+ amount <=> valid_amount(other_object)
59
+ rescue NoMethodError
60
+ raise ArgumentError, "Comparison of #{self.class} with #{val.inspect} failed"
61
+ end
62
+
63
+ def >(other_object)
64
+ amount > valid_amount(other_object)
65
+ end
66
+
67
+ def <(other_object)
68
+ amount < valid_amount(other_object)
69
+ end
70
+
71
+ def >=(other_object)
72
+ amount >= valid_amount(other_object)
73
+ end
74
+
75
+ def <=(other_object)
76
+ amount <= valid_amount(other_object)
77
+ end
78
+
79
+ def positive?
80
+ self > 0
81
+ end
82
+
83
+ def negative?
84
+ self < 0
85
+ end
86
+
87
+ def +(other_object)
88
+ self.class.new(amount + valid_amount(other_object), unit)
89
+ end
90
+
91
+ def -(other_object)
92
+ self.class.new(amount - valid_amount(other_object), unit)
93
+ end
94
+
95
+ def *(other_object)
96
+ self.class.new(amount * valid_amount(other_object), unit)
97
+ end
98
+
99
+ def /(other_object)
100
+ self.class.new(amount / valid_amount(other_object), unit)
101
+ end
102
+
103
+ def div(value)
104
+ amount.div(value)
105
+ end
106
+
107
+ def divmod(val)
108
+ if val.is_a?(self.class)
109
+ divmod_object(val)
110
+ else
111
+ divmod_other(val)
112
+ end
113
+ end
114
+
115
+ def divmod_object(val)
116
+ amount = val.convert_to(unit).amount
117
+ quotient, remainder = amount.divmod(amount)
118
+ [quotient, self.class.new(remainder, unit)]
119
+ end
120
+ private :divmod_object
121
+
122
+ def divmod_other(val)
123
+ quotient, remainder = amount.divmod(valid_amount(other_object))
124
+ [self.class.new(quotient, unit), self.class.new(remainder, unit)]
125
+ end
126
+ private :divmod_other
127
+
128
+ def modulo(val)
129
+ divmod(val)[1]
130
+ end
131
+
132
+ def %(val)
133
+ modulo(val)
134
+ end
135
+
136
+ def remainder(val)
137
+ if val.is_a?(self.class) && unit != val.unit
138
+ val = val.convert_to(unit)
139
+ end
140
+
141
+ if (amount < 0 && val < 0) || (amount > 0 && val > 0)
142
+ self.modulo(val)
143
+ else
144
+ self.modulo(val) - (val.is_a?(self.class) ? val : self.class.new(val, unit))
145
+ end
146
+ end
147
+
148
+ def abs
149
+ self.class.new(amount.abs, unit)
150
+ end
151
+
152
+ def zero?
153
+ amount == 0
154
+ end
155
+
156
+ def nonzero?
157
+ amount != 0 ? self : nil
158
+ end
159
+
160
+ def coerce(other)
161
+ [self, other]
162
+ end
163
+ ### ARITHMETICS END ###
164
+
165
+ private
166
+
167
+ def valid_amount(other_object)
168
+ case other_object
169
+ when self.class then other_object.convert_to(unit).amount
170
+ when Numeric then other_object
171
+ else other_object.send(:"to_#{measurement}").convert_to(unit).amount
172
+ end
173
+ end
174
+
175
+ def valid_unit?(unit)
176
+ unit_object = case unit
177
+ when String, Symbol then units.find {|u| u.code.to_s == unit.to_s}
178
+ when Unit then unit
179
+ end
180
+ unit_object || raise(TypeError, "Unprocessable unit #{unit.inspect}")
181
+ end
182
+
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,36 @@
1
+ module GeneralUnits
2
+ module Base
3
+ class Unit
4
+ METRIC_SYSTEMS = [:metric, :english, :american]
5
+
6
+ attr_reader :code, :name, :short, :fractional, :system
7
+
8
+ def initialize(code, name, short, fractional, system = :metric)
9
+ @code = code
10
+ @name = name.to_s
11
+ @short = short.to_s
12
+ @fractional = fractional.to_d
13
+ @system = system.to_sym
14
+ METRIC_SYSTEMS.each do |s|
15
+ class_eval do
16
+ define_method "#{s}?" do
17
+ self.system == s
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def to_s
24
+ name
25
+ end
26
+
27
+ def inspect
28
+ code
29
+ end
30
+
31
+ def system
32
+ METRIC_SYSTEMS.find {|s| s == @system}
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,52 +1,19 @@
1
- require 'general_units/units/arithmetics/methods'
2
-
3
1
  module GeneralUnits
4
- class Length
5
- ::GeneralUnits::Arithmetics.extend_class(self)
2
+ class Length < ::GeneralUnits::Base::Measurement
6
3
 
7
- class Unit
8
- attr_reader :code, :name, :short, :millimeters
9
-
10
- def initialize(code, name, short, millimeters)
11
- @code = code
12
- @name = name.to_s
13
- @short = short.to_s
14
- @millimeters = millimeters.to_d
15
- end
16
-
17
- def to_s
18
- name
19
- end
20
-
21
- def inspect
22
- code
23
- end
4
+ class Unit < ::GeneralUnits::Base::Unit
5
+ alias :millimeters :fractional
24
6
  end
25
7
 
26
- UNITS = [Unit.new(:mile_nautical, "Mile (nautical)", "mln.", 1852000.0),
27
- Unit.new(:mile, "Mile", "ml.", 1609344.0),
28
- Unit.new(:yard, "Yard", "yrd.", 914.4),
29
- Unit.new(:foot, "Foot", "ft.", 304.8),
30
- Unit.new(:inch, "Inch", "in.", 25.4),
31
- Unit.new(:kilometer, "Kilometer", "km.", 1000000.0),
32
- Unit.new(:meter, "Meter", "m.", 1000.0),
33
- Unit.new(:centimeter, "Centimeter", "cm.", 10.0),
34
- Unit.new(:millimeter, "Millimeter", "mm.", 1.0)]
35
-
36
- attr_reader :amount, :unit
37
- delegate :to_f, :to => :amount
38
- delegate :hash, :to => :attributes
39
-
40
- def initialize(amount = 0, unit)
41
- if unit = valid_unit?(unit)
42
- @amount = amount.try(:to_d)||0.to_d
43
- @unit = unit
44
- end
45
- end
46
-
47
- def attributes
48
- {:amount => amount, :unit => unit}
49
- end
8
+ self.units = [Unit.new(:mile_nautical, "Mile (nautical)", "mln", 1852000.0, :english),
9
+ Unit.new(:mile, "Mile", "ml", 1609344.0, :english),
10
+ Unit.new(:yard, "Yard", "yrd", 914.4, :english),
11
+ Unit.new(:foot, "Foot", "ft", 304.8, :english),
12
+ Unit.new(:inch, "Inch", "in", 25.4, :english),
13
+ Unit.new(:kilometer, "Kilometer", "km", 1000000.0),
14
+ Unit.new(:meter, "Meter", "m", 1000.0),
15
+ Unit.new(:centimeter, "Centimeter", "cm", 10.0),
16
+ Unit.new(:millimeter, "Millimeter", "mm", 1.0)]
50
17
 
51
18
  def convert_to(unit)
52
19
  if convert_unit = valid_unit?(unit)
@@ -54,27 +21,10 @@ module GeneralUnits
54
21
  Length.new(convert_amount, unit)
55
22
  end
56
23
  end
57
-
58
- def to_s(round = nil)
59
- "#{to_f.divmod(1).last == 0 ? to_f.round(0) : to_f.round(round||2)}"
60
- end
61
24
 
62
- def formatted(round = nil)
63
- "#{to_s(round)} #{unit.short}"
64
- end
65
-
66
- def to_length
67
- self
68
- end
69
-
70
- private
71
-
72
- def valid_unit?(unit)
73
- unit_object = case unit
74
- when String, Symbol then UNITS.find {|u| u.code.to_s == unit.to_s}
75
- when Unit then unit
76
- end
77
- unit_object || raise(TypeError, "Unprocessable unit #{unit.inspect}")
25
+ def to_volume(unit_code)
26
+ volume = GeneralUnits::Volume.new(amount, :"cubic_#{unit.code}")
27
+ unit_code ? volume.convert_to(unit_code) : volume
78
28
  end
79
29
 
80
30
  end
@@ -0,0 +1,20 @@
1
+ module GeneralUnits
2
+ class Volume < ::GeneralUnits::Base::Measurement
3
+
4
+ class Unit < ::GeneralUnits::Base::Unit
5
+ alias :cubic_millimeters :fractional
6
+ end
7
+
8
+ self.units = Length.units.map do |unit|
9
+ Unit.new("cubic_#{unit.code}", "Cubic #{unit.name}", "#{unit.short}", unit.fractional**3, unit.system)
10
+ end
11
+
12
+ def convert_to(unit)
13
+ if convert_unit = valid_unit?(unit)
14
+ convert_amount = amount * @unit.cubic_millimeters/convert_unit.cubic_millimeters
15
+ Volume.new(convert_amount, unit)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -1,51 +1,18 @@
1
- require 'general_units/units/arithmetics/methods'
2
-
3
1
  module GeneralUnits
4
- class Weight
5
- ::GeneralUnits::Arithmetics.extend_class(self)
2
+ class Weight < ::GeneralUnits::Base::Measurement
6
3
 
7
- class Unit
8
- attr_reader :code, :name, :short, :grams
9
-
10
- def initialize(code, name, short, grams)
11
- @code = code
12
- @name = name.to_s
13
- @short = short.to_s
14
- @grams = grams.to_d
15
- end
16
-
17
- def to_s
18
- name
19
- end
20
-
21
- def inspect
22
- code
23
- end
24
- end
25
-
26
- UNITS = [Unit.new(:short_ton_us, "Short ton (US)", "Sht.", 907184.74),
27
- Unit.new(:pound_us, "Pound (US)", "Pnd.", 453.59237),
28
- Unit.new(:ounce_us, "Ounce (US)", "Ounce", 28.349523),
29
- Unit.new(:stone, "Stone", "Stn", 6350.2932),
30
- Unit.new(:long_ton_uk, "Long Ton (UK)", "Lngt.", 1016046.9),
31
- Unit.new(:metric_ton, "Metric Ton", "Ton", 1000000.0),
32
- Unit.new(:kilogram, "Kilogram", "Kg.", 1000.0),
33
- Unit.new(:gram, "Gram", "g.", 1.0)]
34
-
35
- attr_reader :amount, :unit
36
- delegate :to_f, :to => :amount
37
- delegate :hash, :to => :attributes
38
-
39
- def initialize(amount = 0, unit)
40
- if unit = valid_unit?(unit)
41
- @amount = amount.try(:to_d)||0.to_d
42
- @unit = unit
43
- end
4
+ class Unit < ::GeneralUnits::Base::Unit
5
+ alias :grams :fractional
44
6
  end
45
7
 
46
- def attributes
47
- {:amount => amount, :unit => unit}
48
- end
8
+ self.units = [Unit.new(:short_ton_us, "Short ton (US)", "Sht", 907184.74, :american),
9
+ Unit.new(:pound_us, "Pound (US)", "Pnd", 453.59237, :american),
10
+ Unit.new(:ounce_us, "Ounce (US)", "Ounce", 28.349523, :american),
11
+ Unit.new(:stone, "Stone", "Stn", 6350.2932, :english),
12
+ Unit.new(:long_ton_uk, "Long Ton (UK)", "Lngt", 1016046.9, :english),
13
+ Unit.new(:metric_ton, "Metric Ton", "Ton", 1000000.0),
14
+ Unit.new(:kilogram, "Kilogram", "Kg", 1000.0),
15
+ Unit.new(:gram, "Gram", "g.", 1.0)]
49
16
 
50
17
  def convert_to(unit)
51
18
  if convert_unit = valid_unit?(unit)
@@ -53,28 +20,6 @@ module GeneralUnits
53
20
  Weight.new(convert_amount, unit)
54
21
  end
55
22
  end
56
-
57
- def to_s(round = nil)
58
- "#{to_f.divmod(1).last == 0 ? to_f.round(0) : to_f.round(round||2)}"
59
- end
60
23
 
61
- def formatted(round = nil)
62
- "#{to_s(round)} #{unit.short}"
63
- end
64
-
65
- def to_weight
66
- self
67
- end
68
-
69
- private
70
-
71
- def valid_unit?(unit)
72
- unit_object = case unit
73
- when String, Symbol then UNITS.find {|u| u.code.to_s == unit.to_s}
74
- when Unit then unit
75
- end
76
- unit_object || raise(TypeError, "Unprocessable unit #{unit.inspect}")
77
- end
78
-
79
24
  end
80
25
  end
@@ -1,3 +1,3 @@
1
1
  module GeneralUnits
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: general_units
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valery Kvon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-26 00:00:00.000000000 Z
11
+ date: 2014-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -38,14 +38,18 @@ files:
38
38
  - Rakefile
39
39
  - general_units.gemspec
40
40
  - lib/general_units.rb
41
+ - lib/general_units/arithmetics/methods.rb
41
42
  - lib/general_units/derivatives/box.rb
43
+ - lib/general_units/derivatives/box/packer.rb
42
44
  - lib/general_units/engine.rb
43
45
  - lib/general_units/helpers/action_view_extension.rb
44
46
  - lib/general_units/models/active_record_extension.rb
45
47
  - lib/general_units/numeric.rb
46
48
  - lib/general_units/railtie.rb
47
- - lib/general_units/units/arithmetics/methods.rb
49
+ - lib/general_units/units/base/measurement.rb
50
+ - lib/general_units/units/base/unit.rb
48
51
  - lib/general_units/units/length.rb
52
+ - lib/general_units/units/volume.rb
49
53
  - lib/general_units/units/weight.rb
50
54
  - lib/general_units/version.rb
51
55
  homepage: http://vkvon.ru/projects/general_units
@@ -1,152 +0,0 @@
1
- module GeneralUnits
2
-
3
- module Arithmetics
4
- def self.extend_class(klass)
5
- unit_name = klass.name.split("::").last.underscore
6
- klass.class_eval <<-EOV
7
- delegate :to_f, :to_d, :to_i, :to => :amount
8
-
9
- def -@
10
- #{klass.name}.new(-amount, unit)
11
- end
12
-
13
- def ==(other_object)
14
- other_object = other_object.to_#{unit_name} unless other_object.is_a?(#{klass.name})
15
- amount == other_object.convert_to(unit).amount
16
- rescue NoMethodError
17
- false
18
- end
19
-
20
- def eql?(other_object)
21
- self == other_object
22
- end
23
-
24
- def <=>(val)
25
- val = val.to_#{unit_name}
26
- unless amount == 0 || val.amount == 0 || unit == val.unit
27
- val = val.convert_to(unit)
28
- end
29
- amount <=> val.amount
30
- rescue NoMethodError
31
- raise ArgumentError, "Comparison of #{self.class} with \#{val.inspect} failed"
32
- end
33
-
34
- def >(other_object)
35
- other_object = other_object.is_a?(#{klass.name}) ? other_object.convert_to(unit) : other_object.to_#{unit_name}
36
- amount > other_object.amount
37
- end
38
-
39
- def <(other_object)
40
- other_object = other_object.is_a?(#{klass.name}) ? other_object.convert_to(unit) : other_object.to_#{unit_name}
41
- amount < other_object.amount
42
- end
43
-
44
- def >=(other_object)
45
- other_object = other_object.is_a?(#{klass.name}) ? other_object.convert_to(unit) : other_object.to_#{unit_name}
46
- amount >= other_object.amount
47
- end
48
-
49
- def <=(other_object)
50
- other_object = other_object.is_a?(#{klass.name}) ? other_object.convert_to(unit) : other_object.to_#{unit_name}
51
- amount <= other_object.amount
52
- end
53
-
54
- def positive?
55
- amount > 0
56
- end
57
-
58
- def negative?
59
- amount < 0
60
- end
61
-
62
- def +(other_object)
63
- other_object = other_object.is_a?(#{klass.name}) ? other_object.convert_to(unit) : other_object.to_#{unit_name}
64
- #{klass.name}.new(amount + other_object.amount, unit)
65
- end
66
-
67
- def -(other_object)
68
- other_object = other_object.is_a?(#{klass.name}) ? other_object.convert_to(unit) : other_object.to_#{unit_name}
69
- #{klass.name}.new(amount - other_object.amount, unit)
70
- end
71
-
72
- def *(value)
73
- case value
74
- when Numeric then #{klass.name}.new(amount * value, unit)
75
- when #{klass.name} then #{klass.name}.new(amount * value.convert_to(unit).amount, unit)
76
- else raise ArgumentError, "Can't multiply a #{klass.name} by a \#{value.class.name}'s value"
77
- end
78
- end
79
-
80
- def /(value)
81
- case value
82
- when Numeric then #{klass.name}.new(amount / value, unit)
83
- when #{klass.name} then #{klass.name}.new(amount / value.convert_to(unit).amount, unit)
84
- else raise ArgumentError, "Can't divide a #{klass.name} by a \#{value.class.name}'s value"
85
- end
86
- end
87
-
88
- def div(value)
89
- amount.div(value)
90
- end
91
-
92
- def divmod(val)
93
- if val.is_a?(#{klass.name})
94
- divmod_object(val)
95
- else
96
- divmod_other(val)
97
- end
98
- end
99
-
100
- def divmod_object(val)
101
- amount = val.convert_to(unit).amount
102
- quotient, remainder = amount.divmod(amount)
103
- [quotient, #{klass.name}.new(remainder, unit)]
104
- end
105
- private :divmod_object
106
-
107
- def divmod_other(val)
108
- quotient, remainder = amount.divmod(val.to_#{unit_name}(unit).amount)
109
- [#{klass.name}.new(quotient, unit), #{klass.name}.new(remainder, unit)]
110
- end
111
- private :divmod_other
112
-
113
- def modulo(val)
114
- divmod(val)[1]
115
- end
116
-
117
- def %(val)
118
- modulo(val)
119
- end
120
-
121
- def remainder(val)
122
- if val.is_a?(#{klass.name}) && unit != val.unit
123
- val = val.convert_to(unit)
124
- end
125
-
126
- if (amount < 0 && val < 0) || (amount > 0 && val > 0)
127
- self.modulo(val)
128
- else
129
- self.modulo(val) - (val.is_a?(#{klass.name}) ? val : #{klass.name}.new(val, unit))
130
- end
131
- end
132
-
133
- def abs
134
- #{klass.name}.new(amount.abs, unit)
135
- end
136
-
137
- def zero?
138
- amount == 0
139
- end
140
-
141
- def nonzero?
142
- amount != 0 ? self : nil
143
- end
144
-
145
- def coerce(other)
146
- [self, other]
147
- end
148
- EOV
149
- end
150
- end
151
-
152
- end