measured 2.0.0 → 2.8.2

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.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +31 -0
  3. data/CHANGELOG.md +60 -0
  4. data/README.md +68 -10
  5. data/Rakefile +22 -1
  6. data/cache/.keep +0 -0
  7. data/cache/length.json +2553 -0
  8. data/cache/volume.json +4695 -0
  9. data/cache/weight.json +3195 -0
  10. data/dev.yml +2 -6
  11. data/gemfiles/{activesupport-4.2.gemfile → activesupport-5.2.gemfile} +1 -1
  12. data/gemfiles/activesupport-6.0.gemfile +5 -0
  13. data/gemfiles/activesupport-6.1.gemfile +5 -0
  14. data/lib/measured/arithmetic.rb +10 -0
  15. data/lib/measured/base.rb +13 -6
  16. data/lib/measured/cache/json.rb +55 -0
  17. data/lib/measured/cache/json_writer.rb +9 -0
  18. data/lib/measured/cache/null.rb +16 -0
  19. data/lib/measured/conversion_table_builder.rb +110 -0
  20. data/lib/measured/cycle_detected.rb +11 -0
  21. data/lib/measured/measurable.rb +28 -21
  22. data/lib/measured/missing_conversion_path.rb +12 -0
  23. data/lib/measured/parser.rb +2 -1
  24. data/lib/measured/unit.rb +17 -27
  25. data/lib/measured/unit_already_added.rb +11 -0
  26. data/lib/measured/unit_error.rb +4 -0
  27. data/lib/measured/unit_system.rb +26 -21
  28. data/lib/measured/unit_system_builder.rb +46 -2
  29. data/lib/measured/units/length.rb +6 -3
  30. data/lib/measured/units/volume.rb +20 -0
  31. data/lib/measured/units/weight.rb +10 -2
  32. data/lib/measured/version.rb +2 -1
  33. data/lib/measured.rb +2 -0
  34. data/measured.gemspec +13 -5
  35. data/test/arithmetic_test.rb +36 -0
  36. data/test/cache/json_test.rb +43 -0
  37. data/test/cache/json_writer_test.rb +23 -0
  38. data/test/cache/null_test.rb +20 -0
  39. data/test/cache_consistency_test.rb +19 -0
  40. data/test/conversion_table_builder_test.rb +147 -0
  41. data/test/measurable_test.rb +28 -13
  42. data/test/parser_test.rb +1 -0
  43. data/test/support/always_true_cache.rb +14 -0
  44. data/test/support/fake_system.rb +1 -0
  45. data/test/support/subclasses.rb +7 -0
  46. data/test/test_helper.rb +5 -3
  47. data/test/unit_error_test.rb +1 -0
  48. data/test/unit_system_builder_test.rb +120 -2
  49. data/test/unit_system_test.rb +15 -0
  50. data/test/unit_test.rb +14 -5
  51. data/test/units/length_test.rb +123 -10
  52. data/test/units/volume_test.rb +497 -0
  53. data/test/units/weight_test.rb +290 -5
  54. metadata +49 -18
  55. data/.travis.yml +0 -17
  56. data/lib/measured/conversion_table.rb +0 -65
  57. data/test/conversion_table_test.rb +0 -98
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "test_helper"
2
3
 
3
4
  class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
@@ -11,15 +12,17 @@ class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
11
12
  end
12
13
 
13
14
  test "#unit cannot add duplicate unit names" do
14
- assert_raises Measured::UnitError do
15
+ error = assert_raises Measured::UnitAlreadyAdded do
15
16
  Measured.build do
16
17
  unit :m
17
18
  unit :in, aliases: [:inch], value: "0.0254 m"
18
19
  unit :in, aliases: [:thing], value: "123 m"
19
20
  end
20
21
  end
22
+ assert_equal("in", error.unit_name)
23
+ assert_equal("Unit in has already been added.", error.message)
21
24
 
22
- assert_raises Measured::UnitError do
25
+ assert_raises Measured::UnitAlreadyAdded do
23
26
  Measured.build do
24
27
  unit :m
25
28
  unit :in, aliases: [:inch], value: "0.0254 m"
@@ -28,6 +31,19 @@ class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
28
31
  end
29
32
  end
30
33
 
34
+ test "#unit raises when cannot find conversion path" do
35
+ error = assert_raises Measured::MissingConversionPath do
36
+ Measured.build do
37
+ unit :m
38
+ unit :in, value: "0.0254 m"
39
+ unit :pallets, value: "5 cases"
40
+ end
41
+ end
42
+ assert_equal("pallets", error.from)
43
+ assert_equal("m", error.to)
44
+ assert_equal("Cannot find conversion path from pallets to m.", error.message)
45
+ end
46
+
31
47
  test "#unit is case sensitive" do
32
48
  measurable = Measured.build do
33
49
  unit :normal
@@ -37,4 +53,106 @@ class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
37
53
 
38
54
  assert_equal 'BOLD', measurable.unit_system.unit_for!(:BOLD).name
39
55
  end
56
+
57
+ test "#unit traverses aliases" do
58
+ measurable = Measured.build do
59
+ unit :piece, aliases: [:pieces]
60
+ unit :dozen, aliases: [:dz], value: "12 pieces"
61
+ end
62
+
63
+ assert_equal 12, measurable.unit_system.units.last.conversion_amount
64
+ assert_equal "piece", measurable.unit_system.units.last.conversion_unit
65
+ end
66
+
67
+ test "#si_unit adds 21 new units" do
68
+ measurable = Measured.build do
69
+ unit :ft
70
+ si_unit :in, value: "12 ft", aliases: [:ins]
71
+ end
72
+
73
+ assert_equal 22, measurable.unit_names.count
74
+ end
75
+
76
+ test "#si_unit creates units with si prefixes" do
77
+ measurable = Measured.build do
78
+ unit :in
79
+ si_unit :ft, value: "12 in", aliases: [:fts]
80
+ end
81
+
82
+ prefixes = Measured::UnitSystemBuilder::SI_PREFIXES.map { |short, _, _| "#{short}ft" }
83
+ prefixes += %w(in ft)
84
+ assert_equal prefixes.sort, measurable.unit_names
85
+ end
86
+
87
+ test "#si_unit cannot add duplicate unit names" do
88
+ assert_raises Measured::UnitError do
89
+ Measured.build do
90
+ unit :m
91
+ si_unit :in, aliases: [:inch], value: "0.0254 m"
92
+ si_unit :in, aliases: [:thing], value: "123 m"
93
+ end
94
+ end
95
+
96
+ assert_raises Measured::UnitError do
97
+ Measured.build do
98
+ unit :m
99
+ si_unit :in, aliases: [:inch], value: "0.0254 m"
100
+ si_unit :inch, aliases: [:thing], value: "123 m"
101
+ end
102
+ end
103
+ end
104
+
105
+ test "#si_unit is case sensitive" do
106
+ measurable = Measured.build do
107
+ unit :normal
108
+ si_unit :bold, value: "10 normal"
109
+ si_unit :BOLD, value: "100 normal"
110
+ end
111
+
112
+ assert_equal "yBOLD", measurable.unit_system.unit_for!(:yBOLD).name
113
+ assert_equal "YBOLD", measurable.unit_system.unit_for!(:YBOLD).name
114
+ assert_equal "BOLD", measurable.unit_system.unit_for!(:BOLD).name
115
+ end
116
+
117
+ test "#si_unit generates working units when given base unit" do
118
+ measurable = Measured.build do
119
+ si_unit :g, aliases: [:gram, :grams]
120
+ unit :bigg, value: "1000 g"
121
+ end
122
+
123
+ assert_equal 1000, measurable.unit_system.unit_for!(:bigg).conversion_amount
124
+ assert_equal "g", measurable.unit_system.unit_for!(:bigg).conversion_unit
125
+ assert_equal 1000, measurable.unit_system.unit_for!(:kg).conversion_amount
126
+ assert_equal "g", measurable.unit_system.unit_for!(:kg).conversion_unit
127
+ end
128
+
129
+ test "#si_unit generates working units when given non base unit" do
130
+ measurable = Measured.build do
131
+ unit :lb
132
+ si_unit :long_ton, value: "2240 lb", aliases: ["long ton", "long tons"]
133
+ end
134
+
135
+ assert_equal (2240), measurable.unit_system.unit_for!(:long_ton).conversion_amount
136
+ assert_equal "lb", measurable.unit_system.unit_for!(:long_ton).conversion_unit
137
+ end
138
+
139
+ test "#cache sets no cache by default" do
140
+ measurable = Measured.build do
141
+ unit :m
142
+ unit :km, value: "1000 m"
143
+ end
144
+
145
+ refute_predicate measurable.unit_system, :cached?
146
+ end
147
+
148
+ test "#cache sets the class and args" do
149
+ measurable = Measured.build do
150
+ unit :m
151
+ unit :km, value: "1000 m"
152
+
153
+ cache AlwaysTrueCache
154
+ end
155
+
156
+ assert_predicate measurable.unit_system, :cached?
157
+ end
40
158
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "test_helper"
2
3
 
3
4
  class Measured::UnitSystemTest < ActiveSupport::TestCase
@@ -99,4 +100,18 @@ class Measured::UnitSystemTest < ActiveSupport::TestCase
99
100
  test "#convert handles the same unit" do
100
101
  assert_equal 2, @conversion.convert(2, from: @unit_in, to: @unit_in)
101
102
  end
103
+
104
+ test "#update_cache calls the builder" do
105
+ Measured::ConversionTableBuilder.any_instance.expects(:update_cache)
106
+ @conversion.update_cache
107
+ end
108
+
109
+ test "#cached? calls the builder and is false" do
110
+ refute_predicate @conversion, :cached?
111
+ end
112
+
113
+ test "#cached? calls the builder and is true" do
114
+ conversion = Measured::UnitSystem.new([@unit_m, @unit_in, @unit_ft], cache: { class: AlwaysTrueCache })
115
+ assert_predicate conversion, :cached?
116
+ end
102
117
  end
data/test/unit_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "test_helper"
2
3
 
3
4
  class Measured::UnitTest < ActiveSupport::TestCase
@@ -42,6 +43,10 @@ class Measured::UnitTest < ActiveSupport::TestCase
42
43
  assert_equal "pie (1/2 sweet)", Measured::Unit.new(:pie, aliases: ["cake"], value: "0.5 sweet").to_s
43
44
  end
44
45
 
46
+ test "#to_s can drop the conversion amount" do
47
+ assert_equal "pie", Measured::Unit.new(:pie).to_s(with_conversion_string: false)
48
+ end
49
+
45
50
  test "#inspect returns an expected string" do
46
51
  assert_equal "#<Measured::Unit: pie>", Measured::Unit.new(:pie).inspect
47
52
  assert_equal "#<Measured::Unit: pie (cake, semi-sweet)>", Measured::Unit.new(:pie, aliases: ["cake", "semi-sweet"]).inspect
@@ -55,7 +60,7 @@ class Measured::UnitTest < ActiveSupport::TestCase
55
60
 
56
61
  test "#<=> compares non-Unit classes against name" do
57
62
  assert_equal 1, @unit <=> "Pap"
58
- assert_equal -1, @unit <=> "Pop"
63
+ assert_equal (-1), @unit <=> "Pop"
59
64
  end
60
65
 
61
66
  test "#<=> is 0 for Unit instances that should be equivalent" do
@@ -65,12 +70,12 @@ class Measured::UnitTest < ActiveSupport::TestCase
65
70
  end
66
71
 
67
72
  test "#<=> is -1 for units with names that come after Pie lexicographically" do
68
- assert_equal -1, @unit <=> Measured::Unit.new(:Pigs, value: "10 bacon")
69
- assert_equal -1, @unit <=> Measured::Unit.new("Pig", aliases: %w(Pigs), value: "10 bacon")
73
+ assert_equal (-1), @unit <=> Measured::Unit.new(:Pigs, value: "10 bacon")
74
+ assert_equal (-1), @unit <=> Measured::Unit.new("Pig", aliases: %w(Pigs), value: "10 bacon")
70
75
  end
71
-
76
+
72
77
  test "#<=> compares #conversion_amount when unit names the same" do
73
- assert_equal -1, @unit <=> Measured::Unit.new(:Pie, value: [11, :pancake])
78
+ assert_equal (-1), @unit <=> Measured::Unit.new(:Pie, value: [11, :pancake])
74
79
  assert_equal 0, @unit <=> Measured::Unit.new(:Pie, value: [10, :foo])
75
80
  assert_equal 1, @unit <=> Measured::Unit.new(:Pie, value: [9, :pancake])
76
81
  end
@@ -78,4 +83,8 @@ class Measured::UnitTest < ActiveSupport::TestCase
78
83
  test "#inverse_conversion_amount returns 1/amount" do
79
84
  assert_equal Rational(1, 10), @unit.inverse_conversion_amount
80
85
  end
86
+
87
+ test "#inverse_conversion_amount handles nil for base unit" do
88
+ assert_nil Measured::Unit.new(:pie).inverse_conversion_amount
89
+ end
81
90
  end
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
1
2
  require "test_helper"
2
3
 
3
4
  class Measured::LengthTest < ActiveSupport::TestCase
4
5
  test ".unit_names_with_aliases should be the expected list of valid units" do
5
- assert_equal(
6
- %w(
7
- centimeter centimeters centimetre centimetres cm feet foot ft
8
- in inch inches m meter meters metre metres millimeter
9
- millimeters millimetre millimetres mm yard yards yd
10
- ),
11
- Measured::Length.unit_names_with_aliases
12
- )
6
+ expected_units = %w(m meter metre meters metres feet foot ft in inch inches mi mile miles yard yards yd)
7
+ expected_units += Measured::UnitSystemBuilder::SI_PREFIXES.flat_map do |short, long, _|
8
+ ["#{short}m", "#{long}meter", "#{long}metre", "#{long}meters", "#{long}metres"]
9
+ end
10
+
11
+ assert_equal expected_units.sort, Measured::Length.unit_names_with_aliases
13
12
  end
14
13
 
15
14
  test ".unit_names should be the list of base unit names" do
16
- assert_equal %w(cm ft in m mm yd), Measured::Length.unit_names
15
+ expected_units = %w(m ft in mi yd)
16
+ expected_units += Measured::UnitSystemBuilder::SI_PREFIXES.map { |short, _, _| "#{short}m" }
17
+ assert_equal expected_units.sort, Measured::Length.unit_names
17
18
  end
18
19
 
19
20
  test ".name" do
@@ -37,10 +38,18 @@ class Measured::LengthTest < ActiveSupport::TestCase
37
38
  assert_conversion Measured::Length, "2000 cm", "0.7874015748E3 in"
38
39
  end
39
40
 
41
+ test ".convert_to from cm to km" do
42
+ assert_exact_conversion Measured::Length, "2000 cm", "0.02 km"
43
+ end
44
+
40
45
  test ".convert_to from cm to m" do
41
46
  assert_exact_conversion Measured::Length, "2000 cm", "20 m"
42
47
  end
43
48
 
49
+ test ".convert_to from cm to mi" do
50
+ assert_conversion Measured::Length, "2000 cm", "0.1242742E-1 mi"
51
+ end
52
+
44
53
  test ".convert_to from cm to mm" do
45
54
  assert_exact_conversion Measured::Length, "2000 cm", "20000 mm"
46
55
  end
@@ -61,10 +70,18 @@ class Measured::LengthTest < ActiveSupport::TestCase
61
70
  assert_exact_conversion Measured::Length, "2000 ft", "24000 in"
62
71
  end
63
72
 
73
+ test ".convert_to from ft to km" do
74
+ assert_conversion Measured::Length, "2000 ft", "0.6096 km"
75
+ end
76
+
64
77
  test ".convert_to from ft to m" do
65
78
  assert_exact_conversion Measured::Length, "2000 ft", "609.6 m"
66
79
  end
67
80
 
81
+ test ".convert_to from ft to mi" do
82
+ assert_conversion Measured::Length, "2000 ft", "0.3787879 mi"
83
+ end
84
+
68
85
  test ".convert_to from ft to mm" do
69
86
  assert_conversion Measured::Length, "2000 ft", "609600 mm"
70
87
  end
@@ -85,10 +102,18 @@ class Measured::LengthTest < ActiveSupport::TestCase
85
102
  assert_exact_conversion Measured::Length, "2000 in", "2000 in"
86
103
  end
87
104
 
105
+ test ".convert_to from in to km" do
106
+ assert_conversion Measured::Length, "2000 in", "0.508E-1 km"
107
+ end
108
+
88
109
  test ".convert_to from in to m" do
89
110
  assert_conversion Measured::Length, "2000 in", "50.8 m"
90
111
  end
91
112
 
113
+ test ".convert_to from in to mi" do
114
+ assert_conversion Measured::Length, "2000 in", "0.31565658E-1 mi"
115
+ end
116
+
92
117
  test ".convert_to from in to mm" do
93
118
  assert_conversion Measured::Length, "2000 in", "50800 mm"
94
119
  end
@@ -97,8 +122,40 @@ class Measured::LengthTest < ActiveSupport::TestCase
97
122
  assert_conversion Measured::Length, "2000 in", "0.555555555384E2 yd"
98
123
  end
99
124
 
125
+ test ".convert_to from km to cm" do
126
+ assert_exact_conversion Measured::Length, "2000 km", "2.0E8 cm"
127
+ end
128
+
129
+ test ".convert_to from km to ft" do
130
+ assert_conversion Measured::Length, "2000 km", "0.6561679790026247E7 ft"
131
+ end
132
+
133
+ test ".convert_to from km to in" do
134
+ assert_conversion Measured::Length, "2000 km", "0.78740157480315E8 in"
135
+ end
136
+
137
+ test ".convert_to from km to km" do
138
+ assert_exact_conversion Measured::Length, "2000 km", "2000 km"
139
+ end
140
+
141
+ test ".convert_to from km to m" do
142
+ assert_exact_conversion Measured::Length, "2000 km", "2.0E6 m"
143
+ end
144
+
145
+ test ".convert_to from km to mi" do
146
+ assert_conversion Measured::Length, "2000 km", "0.1242742384475E4 mi"
147
+ end
148
+
149
+ test ".convert_to from km to mm" do
150
+ assert_exact_conversion Measured::Length, "2000 km", "2.0E9 mm"
151
+ end
152
+
153
+ test ".convert_to from km to yd" do
154
+ assert_conversion Measured::Length, "2000 km", "0.218722659667542E7 yd"
155
+ end
156
+
100
157
  test ".convert_to from m to cm" do
101
- assert_conversion Measured::Length, "2000 m", "200000 cm"
158
+ assert_exact_conversion Measured::Length, "2000 m", "200000 cm"
102
159
  end
103
160
 
104
161
  test ".convert_to from m to ft" do
@@ -109,10 +166,18 @@ class Measured::LengthTest < ActiveSupport::TestCase
109
166
  assert_conversion Measured::Length, "2000 m", "0.7874015748E5 in"
110
167
  end
111
168
 
169
+ test ".convert_to from m to km" do
170
+ assert_exact_conversion Measured::Length, "2000 m", "2 km"
171
+ end
172
+
112
173
  test ".convert_to from m to m" do
113
174
  assert_exact_conversion Measured::Length, "2000 m", "2000 m"
114
175
  end
115
176
 
177
+ test ".convert_to from m to mi" do
178
+ assert_conversion Measured::Length, "2000 m", "0.1242742E1 mi"
179
+ end
180
+
116
181
  test ".convert_to from m to mm" do
117
182
  assert_exact_conversion Measured::Length, "2000 m", "2000000 mm"
118
183
  end
@@ -121,6 +186,38 @@ class Measured::LengthTest < ActiveSupport::TestCase
121
186
  assert_conversion Measured::Length, "2000 m", "0.2187226596E4 yd"
122
187
  end
123
188
 
189
+ test ".convert_to from mi to cm" do
190
+ assert_conversion Measured::Length, "2000 mi", "0.3218688E9 cm"
191
+ end
192
+
193
+ test ".convert_to from mi to ft" do
194
+ assert_exact_conversion Measured::Length, "2000 mi", "0.1056E8 ft"
195
+ end
196
+
197
+ test ".convert_to from mi to in" do
198
+ assert_exact_conversion Measured::Length, "2000 mi", "0.12672E9 in"
199
+ end
200
+
201
+ test ".convert_to from mi to km" do
202
+ assert_conversion Measured::Length, "2000 mi", "0.3218688E4 km"
203
+ end
204
+
205
+ test ".convert_to from mi to m" do
206
+ assert_conversion Measured::Length, "2000 mi", "0.3218688E7 m"
207
+ end
208
+
209
+ test ".convert_to from mi to mi" do
210
+ assert_exact_conversion Measured::Length, "2000 mi", "2000 mi"
211
+ end
212
+
213
+ test ".convert_to from mi to mm" do
214
+ assert_conversion Measured::Length, "2000 mi", "0.3218688E10 mm"
215
+ end
216
+
217
+ test ".convert_to from mi to yd" do
218
+ assert_exact_conversion Measured::Length, "2000 mi", "3520000 yd"
219
+ end
220
+
124
221
  test ".convert_to from mm to cm" do
125
222
  assert_exact_conversion Measured::Length, "2000 mm", "200 cm"
126
223
  end
@@ -133,10 +230,18 @@ class Measured::LengthTest < ActiveSupport::TestCase
133
230
  assert_conversion Measured::Length, "2000 mm", "0.7874015748E2 in"
134
231
  end
135
232
 
233
+ test ".convert_to from mm to km" do
234
+ assert_exact_conversion Measured::Length, "2000 mm", "0.002 km"
235
+ end
236
+
136
237
  test ".convert_to from mm to m" do
137
238
  assert_exact_conversion Measured::Length, "2000 mm", "2 m"
138
239
  end
139
240
 
241
+ test ".convert_to from mm to mi" do
242
+ assert_conversion Measured::Length, "2000 mm", "0.1242742E-2 mi"
243
+ end
244
+
140
245
  test ".convert_to from mm to mm" do
141
246
  assert_exact_conversion Measured::Length, "2000 mm", "2000 mm"
142
247
  end
@@ -157,10 +262,18 @@ class Measured::LengthTest < ActiveSupport::TestCase
157
262
  assert_exact_conversion Measured::Length, "2000 yd", "72000 in"
158
263
  end
159
264
 
265
+ test ".convert_to from yd to km" do
266
+ assert_conversion Measured::Length, "2000 yd", "0.18288E1 km"
267
+ end
268
+
160
269
  test ".convert_to from yd to m" do
161
270
  assert_conversion Measured::Length, "2000 yd", "1828.8 m"
162
271
  end
163
272
 
273
+ test ".convert_to from yd to mi" do
274
+ assert_conversion Measured::Length, "2000 yd", "1.136364 mi"
275
+ end
276
+
164
277
  test ".convert_to from yd to mm" do
165
278
  assert_conversion Measured::Length, "2000 yd", "1828800 mm"
166
279
  end