general_units 0.0.4 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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