unit_measurements 0.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/Gemfile.lock +1 -1
- data/README.md +426 -0
- data/lib/unit_measurements/base.rb +34 -0
- data/lib/unit_measurements/errors/parse_error.rb +14 -0
- data/lib/unit_measurements/errors/unit_already_defined_error.rb +14 -0
- data/lib/unit_measurements/errors/unit_error.rb +14 -0
- data/lib/unit_measurements/measurement.rb +102 -0
- data/lib/unit_measurements/normalizer.rb +73 -0
- data/lib/unit_measurements/parser.rb +58 -0
- data/lib/unit_measurements/unit.rb +79 -0
- data/lib/unit_measurements/unit_group.rb +51 -0
- data/lib/unit_measurements/unit_group_builder.rb +56 -0
- data/lib/unit_measurements/unit_groups/all.rb +6 -0
- data/lib/unit_measurements/unit_groups/length.rb +12 -0
- data/lib/unit_measurements/unit_groups/weight.rb +10 -0
- data/lib/unit_measurements/version.rb +1 -1
- data/lib/unit_measurements.rb +4 -0
- data/units.md +32 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 286c6438c249edf7fe0872a2ad8956f42cb5150819885e8bebe353a7955c97c9
|
4
|
+
data.tar.gz: 1e82f6cf7ec7dc4f51670be13980cc30ccfe56583fb2b94622f12614d9a0c91e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5353470b9101426b12d5fd34474d41fe04de2e4ef8286dd42c78605f01f35116f62341626299708f5ea0cc53613977437876b906d7fd79d91888ed9f1221a6e9
|
7
|
+
data.tar.gz: 7bda4b4f0598d53e2b3cf933da8d8485d8052ca6172d772bbaf55aa4400542782c62b99d583d0109ef5af2a7ed3504eb4991eb06b5a5f9cbcdcc4f87575ac919
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
|
+
## [1.0.0](https://github.com/shivam091/unit_measurements/compare/v0.1.0...v1.0.0) - 2023-09-14
|
2
|
+
|
3
|
+
### What's new
|
4
|
+
|
5
|
+
- Added support to build unit groups.
|
6
|
+
- Added unit group for `length` units.
|
7
|
+
- Added unit group for `weight` units.
|
8
|
+
- Added support to build `si` units.
|
9
|
+
- Added support to parse `Complex`, `Rational`, `Scientific` numbers, and `ratios`.
|
10
|
+
- Added support to convert quantity between two units using `#convert_to`, `#convert_to!`, and `#parse` methods.
|
11
|
+
|
12
|
+
----------
|
13
|
+
|
1
14
|
## [0.1.0] - 2023-09-13
|
2
15
|
|
3
|
-
|
16
|
+
### Initial release
|
4
17
|
|
5
18
|
-----------
|
6
19
|
|
data/Gemfile.lock
CHANGED
data/README.md
ADDED
@@ -0,0 +1,426 @@
|
|
1
|
+
# Unit Measurements
|
2
|
+
|
3
|
+
A library that encapsulate measurements and their units in Ruby.
|
4
|
+
|
5
|
+
[](https://github.com/shivam091/unit_measurements/actions/workflows/main.yml)
|
6
|
+
[](https://badge.fury.io/rb/unit_measurements)
|
7
|
+
[](http://rubygems.org/gems/unit_measurements)
|
8
|
+
[](https://codeclimate.com/github/shivam091/unit_measurements/maintainability)
|
9
|
+
[](https://codeclimate.com/github/shivam091/unit_measurements/test_coverage)
|
10
|
+
[](https://github.com/shivam091/unit_measurements/blob/main/LICENSE)
|
11
|
+
|
12
|
+
**Harshal V. Ladhe, M.Sc. Computer Science.**
|
13
|
+
|
14
|
+
## Introduction
|
15
|
+
|
16
|
+
Many technical applications need use of specialized calculations at some point of time.
|
17
|
+
Frequently, these calculations require unit conversions to ensure accurate
|
18
|
+
results. Needless to say, this is a pain to properly keep track of, and is prone
|
19
|
+
to numerous errors.
|
20
|
+
|
21
|
+
## Solution
|
22
|
+
|
23
|
+
The `unit_measurements` gem is designed to simplify the handling of units for scientific calculations.
|
24
|
+
|
25
|
+
## Advantages
|
26
|
+
|
27
|
+
1. Provides easy conversion between units.
|
28
|
+
2. Built in support for various [unit groups](units.md).
|
29
|
+
3. Lightweight and easily extensible to include other units and conversions.
|
30
|
+
4. Can convert `complex`, `rational`, `fractions`, `exponents`, `scientific notations`, and `ratios`.
|
31
|
+
|
32
|
+
## Disclaimer
|
33
|
+
|
34
|
+
_The unit conversions presented in `unit_measurements` are provided for reference and general informational purposes.
|
35
|
+
While we aim to offer accurate conversions, we cannot guarantee their precision in all scenarios.
|
36
|
+
Users are advised to cross-verify conversions as needed for their specific use cases._
|
37
|
+
|
38
|
+
## Minimum Requirements
|
39
|
+
|
40
|
+
* Ruby 3.2.2+ (https://www.ruby-lang.org/en/downloads/branches/)
|
41
|
+
|
42
|
+
## Installation
|
43
|
+
|
44
|
+
If using bundler, first add this line to your application's Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem "unit_measurements"
|
48
|
+
```
|
49
|
+
|
50
|
+
And then execute:
|
51
|
+
|
52
|
+
`$ bundle install`
|
53
|
+
|
54
|
+
Or otherwise simply install it yourself as:
|
55
|
+
|
56
|
+
`$ gem install unit_measurements`
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
|
60
|
+
The **`UnitMeasurements::Measurement`** class is responsible for conversion of quantity to various compatble units.
|
61
|
+
|
62
|
+
Measurements can't be initialized or converted to other units directly with the `UnitMeasurements::Measurement` class,
|
63
|
+
but rather with the unit group classes viz., `UnitMeasurements::Weight`, `UnitMeasurements::Length`, etc.
|
64
|
+
|
65
|
+
**Initialize a measurement:**
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
UnitMeasurements::Weight.new(1, :kg)
|
69
|
+
#=> 1 kg
|
70
|
+
```
|
71
|
+
|
72
|
+
**Converting to other units:**
|
73
|
+
|
74
|
+
This gem allows you to convert among units of same unit group.
|
75
|
+
You can convert measurement to other unit using `#convert_to` (aliased as `#to`)
|
76
|
+
or `#convert_to!` (aliased as `#to!`) methods.
|
77
|
+
|
78
|
+
You can use `#convert_to` as:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
UnitMeasurements::Weight.new(1, :kg).convert_to(:g)
|
82
|
+
#=> 1000 g
|
83
|
+
```
|
84
|
+
|
85
|
+
If you want to modify measurement object itself, you can use `#convert_to!` method as:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
UnitMeasurements::Weight.new(1, :kg).convert_to!(:g)
|
89
|
+
#=> 1000 g
|
90
|
+
```
|
91
|
+
|
92
|
+
You can also chain call of `#convert_to` and `#convert_to!` methods as:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
UnitMeasurements::Weight.new(1, :kg).convert_to(:g).convert_to(:t).convert_to!(:q)
|
96
|
+
#=> 0.01 q
|
97
|
+
```
|
98
|
+
|
99
|
+
**Parse string without having to split out the quantity and source unit:**
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
UnitMeasurements::Weight.parse("1 kg")
|
103
|
+
#=> 1 kg
|
104
|
+
```
|
105
|
+
|
106
|
+
**Parse string that mentions quantity, source unit, and target unit:**
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
UnitMeasurements::Weight.parse("1 kg to g")
|
110
|
+
#=> 1000 g
|
111
|
+
UnitMeasurements::Weight.parse("1 kg as g")
|
112
|
+
#=> 1000 g
|
113
|
+
UnitMeasurements::Weight.parse("1 kg in g")
|
114
|
+
#=> 1000 g
|
115
|
+
```
|
116
|
+
|
117
|
+
**Parse rational numbers, source unit, and (or) target unit:**
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
UnitMeasurements::Weight.new(Rational(2, 3), :kg).convert_to(:g)
|
121
|
+
#=> 666.666666666667 g
|
122
|
+
UnitMeasurements::Weight.new("2/3", :kg).convert_to(:g)
|
123
|
+
#=> 666.666666666667 g
|
124
|
+
UnitMeasurements::Weight.parse("2/3 kg").convert_to(:g)
|
125
|
+
#=> 666.666666666667 g
|
126
|
+
UnitMeasurements::Weight.parse("2/3 kg to g")
|
127
|
+
#=> 666.666666666667 g
|
128
|
+
```
|
129
|
+
|
130
|
+
**Parse complex numbers, source unit, and (or) target unit:**
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
UnitMeasurements::Weight.new(Complex(2, 3), :kg).convert_to(:g)
|
134
|
+
#=> 2000.0+3000.0i g
|
135
|
+
UnitMeasurements::Weight.new("2+3i", :kg).convert_to(:g)
|
136
|
+
#=> 2000.0+3000.0i g
|
137
|
+
UnitMeasurements::Weight.parse("2+3i kg").convert_to(:g)
|
138
|
+
#=> 2000.0+3000.0i g
|
139
|
+
UnitMeasurements::Weight.parse("2+3i kg to g")
|
140
|
+
#=> 2000.0+3000.0i g
|
141
|
+
```
|
142
|
+
|
143
|
+
**Parse scientific numbers, source unit, and (or) target unit:**
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
UnitMeasurements::Weight.new(BigDecimal(2), :kg).convert_to(:g)
|
147
|
+
#=> 2000 g
|
148
|
+
UnitMeasurements::Weight.new(0.2e1, :kg).convert_to(:g)
|
149
|
+
#=> 2000 g
|
150
|
+
UnitMeasurements::Weight.parse("0.2e1 kg").convert_to(:g)
|
151
|
+
#=> 2000 g
|
152
|
+
UnitMeasurements::Weight.parse("0.2e1 kg to g")
|
153
|
+
#=> 2000 g
|
154
|
+
```
|
155
|
+
|
156
|
+
**Parse ratios, source unit, and (or) target unit:**
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
UnitMeasurements::Weight.new("1:2", :kg).convert_to(:g)
|
160
|
+
#=> 500 g
|
161
|
+
UnitMeasurements::Weight.parse("1:2 kg").convert_to(:g)
|
162
|
+
#=> 500 g
|
163
|
+
UnitMeasurements::Weight.parse("1:2 kg to g")
|
164
|
+
#=> 500 g
|
165
|
+
```
|
166
|
+
|
167
|
+
**Parse fractional notations, source unit, and (or) target unit:**
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
UnitMeasurements::Weight.new("1/2", :kg).convert_to(:g)
|
171
|
+
#=> 500 g
|
172
|
+
UnitMeasurements::Weight.parse("1/2 kg").convert_to(:g)
|
173
|
+
#=> 500 g
|
174
|
+
UnitMeasurements::Weight.parse("1/2 kg to g")
|
175
|
+
#=> 500 g
|
176
|
+
UnitMeasurements::Weight.new("½", :kg).convert_to(:g)
|
177
|
+
#=> 500 g
|
178
|
+
UnitMeasurements::Weight.parse("½ kg").convert_to(:g)
|
179
|
+
#=> 500 g
|
180
|
+
UnitMeasurements::Weight.parse("½ kg to g")
|
181
|
+
#=> 500 g
|
182
|
+
```
|
183
|
+
|
184
|
+
**Parse mixed fractional notations, source unit, and (or) target unit:**
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
UnitMeasurements::Weight.new("2 1/2", :kg).convert_to(:g)
|
188
|
+
#=> 2500 g
|
189
|
+
UnitMeasurements::Weight.parse("2 1/2 kg").convert_to(:g)
|
190
|
+
#=> 2500 g
|
191
|
+
UnitMeasurements::Weight.parse("2 1/2 kg to g")
|
192
|
+
#=> 2500 g
|
193
|
+
UnitMeasurements::Weight.new("2 ½", :kg).convert_to(:g)
|
194
|
+
#=> 2500 g
|
195
|
+
UnitMeasurements::Weight.parse("2 ½ kg").convert_to(:g)
|
196
|
+
#=> 2500 g
|
197
|
+
UnitMeasurements::Weight.parse("2 ½ kg to g")
|
198
|
+
#=> 2500 g
|
199
|
+
```
|
200
|
+
|
201
|
+
Supported special characters for fractional notations are `¼`, `½`, `¾`, `⅓`, `⅔`, `⅕`, `⅖`, `⅗`, `⅘`, `⅙`, `⅚`, `⅐`, `⅛`, `⅜`, `⅝`, `⅞`, `⅑`, `⅒`, `↉`, `⁄`.
|
202
|
+
|
203
|
+
**Parse exponents, source unit, and (or) target unit:**
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
UnitMeasurements::Weight.new("2e+2", :kg).convert_to(:g)
|
207
|
+
#=> 200000 g
|
208
|
+
UnitMeasurements::Weight.parse("2e² kg").convert_to(:g)
|
209
|
+
#=> 200000 g
|
210
|
+
UnitMeasurements::Weight.parse("2e+2 kg to g")
|
211
|
+
#=> 200000 g
|
212
|
+
UnitMeasurements::Weight.new("2e⁺²", :kg).convert_to(:g)
|
213
|
+
#=> 200000 g
|
214
|
+
UnitMeasurements::Weight.parse("2e⁺2 kg").convert_to(:g)
|
215
|
+
#=> 200000 g
|
216
|
+
UnitMeasurements::Weight.parse("2e⁻² kg to g")
|
217
|
+
#=> 20 g
|
218
|
+
```
|
219
|
+
|
220
|
+
Supported special characters for exponents are `⁰`, `¹`, `²`, `³`, `⁴`, `⁵`, `⁶`, `⁷`, `⁸`, `⁹`, `⁺`, `⁻`.
|
221
|
+
|
222
|
+
**Extract the unit and the quantity from measurement:**
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
weight = UnitMeasurements::Weight.new(1.0, :kg)
|
226
|
+
weight.quantity
|
227
|
+
#=> 0.1e1
|
228
|
+
weight.unit
|
229
|
+
#=> #<UnitMeasurements::Unit: kg (kilogram, kilogramme, kilogrammes, kilograms)>
|
230
|
+
```
|
231
|
+
|
232
|
+
**See all units of the unit group:**
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
UnitMeasurements::Weight.units
|
236
|
+
#=> [#<UnitMeasurements::Unit: g (gram, gramme, grammes, grams)>, ..., ...]
|
237
|
+
```
|
238
|
+
|
239
|
+
**See names of all valid units of the unit group:**
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
UnitMeasurements::Weight.unit_names
|
243
|
+
#=> ["g", "kg", "lb", "oz", ...]
|
244
|
+
```
|
245
|
+
|
246
|
+
**See all valid units of the unit group along with their aliases:**
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
UnitMeasurements::Weight.unit_names_with_aliases
|
250
|
+
#=> ["g", "gram", "gramme", "grammes", "grams", "kg", "kilogram", "kilogramme", "kilogrammes", "kilograms", "lb", "ounce", "ounces", "oz", "pound", "pounds", ...]
|
251
|
+
```
|
252
|
+
|
253
|
+
**Finding units within the unit group:**
|
254
|
+
|
255
|
+
You can use `#unit_for` or `#unit_for!` (aliased as `#[]`) to find units within
|
256
|
+
the unit group. `#unit_for!` method returns error if a unit is not present in the
|
257
|
+
unit group.
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
UnitMeasurements::Weight.unit_for(:g)
|
261
|
+
#=> #<UnitMeasurements::Unit: g (gram, gramme, grammes, grams)>
|
262
|
+
UnitMeasurements::Weight.unit_for(:z)
|
263
|
+
#=> nil
|
264
|
+
UnitMeasurements::Weight.unit_for!(:g)
|
265
|
+
#=> #<UnitMeasurements::Unit: g (gram, gramme, grammes, grams)>
|
266
|
+
UnitMeasurements::Weight.unit_for!(:z)
|
267
|
+
#=> Invalid unit: 'z'. (UnitMeasurements::UnitError)
|
268
|
+
```
|
269
|
+
|
270
|
+
**Finding whether the unit is defined within the unit group:**
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
UnitMeasurements::Weight.defined?(:g)
|
274
|
+
#=> true
|
275
|
+
UnitMeasurements::Weight.defined?(:kg)
|
276
|
+
#=> true
|
277
|
+
UnitMeasurements::Weight.defined?(:gramme)
|
278
|
+
#=> false
|
279
|
+
```
|
280
|
+
|
281
|
+
**Check if the unit is a valid unit or alias within the unit group:**
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
UnitMeasurements::Weight.unit_or_alias?(:g)
|
285
|
+
#=> true
|
286
|
+
UnitMeasurements::Weight.unit_or_alias?(:kg)
|
287
|
+
#=> true
|
288
|
+
UnitMeasurements::Weight.unit_or_alias?(:gramme)
|
289
|
+
#=> true
|
290
|
+
```
|
291
|
+
|
292
|
+
## Units
|
293
|
+
|
294
|
+
The **`UnitMeasurements::Unit`** class is used to represent the units for a measurement.
|
295
|
+
|
296
|
+
### SI units support
|
297
|
+
|
298
|
+
There is support for SI units through the use of `si_unit` method.
|
299
|
+
Units declared through it will have automatic support for all SI prefixes:
|
300
|
+
|
301
|
+
| Multiplying Factor | SI Prefix | Scientific Notation |
|
302
|
+
| ----------------------------------------- | ---------- | ------------------- |
|
303
|
+
| 1 000 000 000 000 000 000 000 000 000 000 | quetta (Q) | 10^30 |
|
304
|
+
| 1 000 000 000 000 000 000 000 000 000 | ronna (R) | 10^27 |
|
305
|
+
| 1 000 000 000 000 000 000 000 000 | yotta (Y) | 10^24 |
|
306
|
+
| 1 000 000 000 000 000 000 000 | zetta (Z) | 10^21 |
|
307
|
+
| 1 000 000 000 000 000 000 | exa (E) | 10^18 |
|
308
|
+
| 1 000 000 000 000 000 | peta (P) | 10^15 |
|
309
|
+
| 1 000 000 000 000 | tera (T) | 10^12 |
|
310
|
+
| 1 000 000 000 | giga (G) | 10^9 |
|
311
|
+
| 1 000 000 | mega (M) | 10^6 |
|
312
|
+
| 1 000 | kilo (k) | 10^3 |
|
313
|
+
| 1 00 | hecto (h) | 10^2 |
|
314
|
+
| 1 0 | deca (da) | 10^1 |
|
315
|
+
| 0.1 | deci (d) | 10^-1 |
|
316
|
+
| 0.01 | centi (c) | 10^-2 |
|
317
|
+
| 0.001 | milli (m) | 10^-3 |
|
318
|
+
| 0.000 001 | micro (µ) | 10^-6 |
|
319
|
+
| 0.000 000 001 | nano (n) | 10^-9 |
|
320
|
+
| 0.000 000 000 001 | pico (p) | 10^-12 |
|
321
|
+
| 0.000 000 000 000 001 | femto (f) | 10^-15 |
|
322
|
+
| 0.000 000 000 000 000 001 | atto (a) | 10^-18 |
|
323
|
+
| 0.000 000 000 000 000 000 001 | zepto (z) | 10^-21 |
|
324
|
+
| 0.000 000 000 000 000 000 000 001 | yocto (y) | 10^-24 |
|
325
|
+
| 0.000 000 000 000 000 000 000 000 001 | ronto (r) | 10^-27 |
|
326
|
+
| 0.000 000 000 000 000 000 000 000 000 001 | quecto (q) | 10^-30 |
|
327
|
+
|
328
|
+
### Bundled units
|
329
|
+
|
330
|
+
There are tons of units that are bundled in `unit_measurements`. You can check them out [here](units.md).
|
331
|
+
|
332
|
+
### Specifing units
|
333
|
+
|
334
|
+
By default, `unit_measurements` ships with all the unit groups and this happens automatically
|
335
|
+
when requiring the gem in the following manner.
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
require "unit_measurements"
|
339
|
+
```
|
340
|
+
|
341
|
+
**You can skip these unit groups and only [build your own unit groups](#building-new-unit-groups) by doing:**
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
require "unit_measurements/base"
|
345
|
+
```
|
346
|
+
|
347
|
+
or simply
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
gem "unit_measurements", require: "unit_measurements/base"
|
351
|
+
```
|
352
|
+
|
353
|
+
**You can also use unit groups in your application as per your need as:**
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
require "unit_measurements/base"
|
357
|
+
|
358
|
+
require "unit_measurements/unit_groups/length"
|
359
|
+
require "unit_measurements/unit_groups/weight"
|
360
|
+
require "unit_measurements/unit_groups/volume"
|
361
|
+
```
|
362
|
+
|
363
|
+
or
|
364
|
+
|
365
|
+
```ruby
|
366
|
+
gem "unit_measurements", require: ["unit_measurements/base", "unit_measurements/unit_groups/length"]
|
367
|
+
```
|
368
|
+
|
369
|
+
### Building new unit groups
|
370
|
+
|
371
|
+
This library provides simpler way to build your own unit groups. To build new unit group,
|
372
|
+
use `UnitMeasurements.build` in order to define base units and conversion units within it:
|
373
|
+
|
374
|
+
If you build unit using `base` method, base unit automatically gets set for the unit group.
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
UnitMeasurements::Time = UnitMeasurements.build do
|
378
|
+
# Add a base unit to the group.
|
379
|
+
base :s, aliases: [:second, :seconds]
|
380
|
+
|
381
|
+
# Add other units to the group, along with their conversion multipliers against
|
382
|
+
# base unit.
|
383
|
+
unit :min, value: 60.0, aliases: [:minute, :minutes]
|
384
|
+
|
385
|
+
# You can also specify conversion string if it's converted against a unit other
|
386
|
+
# than the unit group's base unit.
|
387
|
+
unit :h, value: "60 min", aliases: [:hour, :hours]
|
388
|
+
end
|
389
|
+
```
|
390
|
+
|
391
|
+
If the unit is supporting [si prefixes](#si-units-support), you can use `si_unit` method to build it.
|
392
|
+
If you build unit using `si_unit`, Base unit is automatically added to the group along with all SI prefixes for it.
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
UnitMeasurements::Time = UnitMeasurements.build do
|
396
|
+
# Add a SI unit to the unit group
|
397
|
+
si_unit :s, aliases: [:second, :seconds]
|
398
|
+
|
399
|
+
unit :min, value: "60 s", aliases: [:minute, :minutes]
|
400
|
+
end
|
401
|
+
```
|
402
|
+
|
403
|
+
All units allow aliases, as long as they are unique. Unit symbol can be used to
|
404
|
+
define the unit as long as it is unique. All unit names are case sensitive.
|
405
|
+
|
406
|
+
### Namespaces
|
407
|
+
|
408
|
+
All unit groups and their definition classes are namespaced by default, but can be aliased in your application.
|
409
|
+
|
410
|
+
```ruby
|
411
|
+
Weight = UnitMeasurements::Weight
|
412
|
+
Length = UnitMeasurements::Length
|
413
|
+
Volume = UnitMeasurements::Volume
|
414
|
+
```
|
415
|
+
|
416
|
+
## Contributing
|
417
|
+
|
418
|
+
1. Fork it
|
419
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
420
|
+
3. Commit your changes (`git commit -am "Add some feature"`)
|
421
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
422
|
+
5. Create new Pull Request
|
423
|
+
|
424
|
+
## License
|
425
|
+
|
426
|
+
Copyright 2023 [Harshal V. LADHE](https://github.com/shivam091), Released under the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
require "active_support/all"
|
6
|
+
require "unit_measurements/version"
|
7
|
+
|
8
|
+
module UnitMeasurements
|
9
|
+
class << self
|
10
|
+
def build(&block)
|
11
|
+
builder = UnitGroupBuilder.new
|
12
|
+
builder.instance_eval(&block)
|
13
|
+
|
14
|
+
Class.new(Measurement) do
|
15
|
+
class << self
|
16
|
+
attr_reader :unit_group
|
17
|
+
end
|
18
|
+
|
19
|
+
@unit_group = builder.build
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require "unit_measurements/unit_group_builder"
|
26
|
+
require "unit_measurements/unit"
|
27
|
+
require "unit_measurements/unit_group"
|
28
|
+
require "unit_measurements/normalizer"
|
29
|
+
require "unit_measurements/parser"
|
30
|
+
require "unit_measurements/measurement"
|
31
|
+
|
32
|
+
require "unit_measurements/errors/unit_error"
|
33
|
+
require "unit_measurements/errors/unit_already_defined_error"
|
34
|
+
require "unit_measurements/errors/parse_error"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class ParseError < StandardError
|
7
|
+
attr_reader :string
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
@string = string
|
11
|
+
super("Unable to parse: '#{string}'.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class UnitAlreadyDefinedError < StandardError
|
7
|
+
attr_reader :unit
|
8
|
+
|
9
|
+
def initialize(unit)
|
10
|
+
@unit = unit
|
11
|
+
super("Unit already defined: '#{unit}'.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class UnitError < StandardError
|
7
|
+
attr_reader :unit
|
8
|
+
|
9
|
+
def initialize(unit)
|
10
|
+
@unit = unit
|
11
|
+
super("Invalid unit: '#{unit}'.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class Measurement
|
7
|
+
CONVERSION_STRING_REGEXP = /(.+?)\s?(?:\s+(?:in|to|as)\s+(.+)|\z)/i.freeze
|
8
|
+
|
9
|
+
attr_reader :quantity, :unit
|
10
|
+
|
11
|
+
def initialize(quantity, unit)
|
12
|
+
raise ArgumentError, "Quantity cannot be blank." if quantity.blank?
|
13
|
+
raise ArgumentError, "Unit cannot be blank." if unit.blank?
|
14
|
+
|
15
|
+
@quantity = convert_quantity(quantity)
|
16
|
+
@unit = unit_from_unit_or_name!(unit)
|
17
|
+
end
|
18
|
+
|
19
|
+
def convert_to(target_unit)
|
20
|
+
target_unit = unit_from_unit_or_name!(target_unit)
|
21
|
+
|
22
|
+
return self if target_unit == unit
|
23
|
+
|
24
|
+
conversion_factor = (unit.conversion_factor / target_unit.conversion_factor)
|
25
|
+
self.class.new((quantity * conversion_factor), target_unit)
|
26
|
+
end
|
27
|
+
alias_method :to, :convert_to
|
28
|
+
|
29
|
+
def convert_to!(target_unit)
|
30
|
+
measurement = convert_to(target_unit)
|
31
|
+
@quantity, @unit = measurement.quantity, measurement.unit
|
32
|
+
self
|
33
|
+
end
|
34
|
+
alias_method :to!, :convert_to!
|
35
|
+
|
36
|
+
def inspect(dump: false)
|
37
|
+
return super() if dump
|
38
|
+
|
39
|
+
to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"#{humanized_quantity} #{unit.to_s}"
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
extend Forwardable
|
48
|
+
|
49
|
+
def unit_group
|
50
|
+
raise "`Measurement` does not have a `unit_group` object. You cannot directly use `Measurement`. Instead, build a new unit group by calling `UnitMeasurements.build`."
|
51
|
+
end
|
52
|
+
|
53
|
+
def_delegators :unit_group, :units, :unit_names, :unit_with_name_and_aliases,
|
54
|
+
:unit_names_with_aliases, :unit_for, :unit_for!, :defined?,
|
55
|
+
:unit_or_alias?, :[]
|
56
|
+
|
57
|
+
def parse(input)
|
58
|
+
input = Normalizer.normalize(input)
|
59
|
+
source, target = input.match(CONVERSION_STRING_REGEXP)&.captures
|
60
|
+
target ? _parse(source).convert_to(target) : _parse(source)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def _parse(string)
|
66
|
+
quantity, unit = Parser.parse(string)
|
67
|
+
new(quantity, unit)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def convert_quantity(quantity)
|
74
|
+
case quantity
|
75
|
+
when Float
|
76
|
+
BigDecimal(quantity, Float::DIG)
|
77
|
+
when Integer
|
78
|
+
Rational(quantity)
|
79
|
+
when String
|
80
|
+
quantity = Normalizer.normalize(quantity)
|
81
|
+
quantity, _ = Parser.parse(quantity)
|
82
|
+
quantity
|
83
|
+
else
|
84
|
+
quantity
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def unit_from_unit_or_name!(value)
|
89
|
+
value.is_a?(Unit) ? value : self.class.unit_group.unit_for!(value)
|
90
|
+
end
|
91
|
+
|
92
|
+
def humanized_quantity
|
93
|
+
case quantity
|
94
|
+
when Complex
|
95
|
+
quantity
|
96
|
+
when Numeric
|
97
|
+
num = quantity.to_r
|
98
|
+
num.denominator == 1 ? num.numerator.to_s : num.to_f.to_s
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_stringing_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class Normalizer
|
7
|
+
FRACTIONS_SYMBOLS = {
|
8
|
+
"¼" => "1/4",
|
9
|
+
"½" => "1/2",
|
10
|
+
"¾" => "3/4",
|
11
|
+
"⅓" => "1/3",
|
12
|
+
"⅔" => "2/3",
|
13
|
+
"⅕" => "1/5",
|
14
|
+
"⅖" => "2/5",
|
15
|
+
"⅗" => "3/5",
|
16
|
+
"⅘" => "4/5",
|
17
|
+
"⅙" => "1/6",
|
18
|
+
"⅚" => "5/6",
|
19
|
+
"⅐" => "1/7",
|
20
|
+
"⅛" => "1/8",
|
21
|
+
"⅜" => "3/8",
|
22
|
+
"⅝" => "5/8",
|
23
|
+
"⅞" => "7/8",
|
24
|
+
"⅑" => "1/9",
|
25
|
+
"⅒" => "1/10",
|
26
|
+
"↉" => "0/3",
|
27
|
+
"⁄" => "/"
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
EXPONENTS_SYMBOLS = {
|
31
|
+
"⁰" => "0",
|
32
|
+
"¹" => "1",
|
33
|
+
"²" => "2",
|
34
|
+
"³" => "3",
|
35
|
+
"⁴" => "4",
|
36
|
+
"⁵" => "5",
|
37
|
+
"⁶" => "6",
|
38
|
+
"⁷" => "7",
|
39
|
+
"⁸" => "8",
|
40
|
+
"⁹" => "9",
|
41
|
+
"⁺" => "+",
|
42
|
+
"⁻" => "-",
|
43
|
+
}
|
44
|
+
|
45
|
+
FRACTION_REGEX = /(#{FRACTIONS_SYMBOLS.keys.join("|")})/
|
46
|
+
EXPONENT_REGEX = /([\d]+[Ee]?[+-]?)(#{EXPONENTS_SYMBOLS.keys.join("|")})/
|
47
|
+
RATIO_REGEX = /([\d]+):([\d]+)/
|
48
|
+
|
49
|
+
class << self
|
50
|
+
def normalize(string)
|
51
|
+
string.dup.tap do |str|
|
52
|
+
if str =~ Regexp.new(FRACTION_REGEX)
|
53
|
+
FRACTIONS_SYMBOLS.each do |search, replace|
|
54
|
+
str.gsub!(search) { " #{replace}" }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if str =~ Regexp.new(EXPONENT_REGEX)
|
59
|
+
EXPONENTS_SYMBOLS.each do |search, replace|
|
60
|
+
str.gsub!(search) { "#{replace}" }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if str =~ Regexp.new(RATIO_REGEX)
|
65
|
+
str.gsub!(RATIO_REGEX) { "#{$1.to_i}/#{$2.to_i}" }
|
66
|
+
end
|
67
|
+
|
68
|
+
str.strip!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_stringing_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class Parser
|
7
|
+
UNIT_REGEX = /([^\d\s\/].*)/.freeze
|
8
|
+
|
9
|
+
SCIENTIFIC_NUMBER = /([+-]?\d*\.?\d+(?:[Ee][+-]?)?\d*)/.freeze
|
10
|
+
RATIONAL_NUMBER = /([+-]?\d+\s+)?((\d+)\/(\d+))/.freeze
|
11
|
+
COMPLEX_NUMBER = /#{SCIENTIFIC_NUMBER}#{SCIENTIFIC_NUMBER}i/.freeze
|
12
|
+
|
13
|
+
SCIENTIFIC_REGEX = /\A#{SCIENTIFIC_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
|
14
|
+
RATIONAL_REGEX = /\A#{RATIONAL_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
|
15
|
+
COMPLEX_REGEX = /\A#{COMPLEX_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def parse(string)
|
19
|
+
case string
|
20
|
+
when COMPLEX_REGEX then parse_complex(string)
|
21
|
+
when SCIENTIFIC_REGEX then parse_scientific(string)
|
22
|
+
when RATIONAL_REGEX then parse_rational(string)
|
23
|
+
else raise ParseError, string
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def parse_complex(string)
|
30
|
+
real, imaginary, unit = string.match(COMPLEX_REGEX)&.captures
|
31
|
+
quantity = Complex(real.to_f, imaginary.to_f)
|
32
|
+
|
33
|
+
[quantity, unit]
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_scientific(string)
|
37
|
+
whole, unit = string.match(SCIENTIFIC_REGEX)&.captures
|
38
|
+
quantity = whole.to_f
|
39
|
+
|
40
|
+
[quantity, unit]
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_rational(string)
|
44
|
+
whole, _, numerator, denominator, unit = string.match(RATIONAL_REGEX)&.captures
|
45
|
+
|
46
|
+
if numerator && denominator
|
47
|
+
numerator = numerator.to_f + (denominator.to_f * whole.to_f)
|
48
|
+
denominator = denominator.to_f
|
49
|
+
quantity = Rational(numerator, denominator).to_f
|
50
|
+
else
|
51
|
+
quantity = whole.to_f
|
52
|
+
end
|
53
|
+
|
54
|
+
[quantity, unit]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
require "set"
|
6
|
+
|
7
|
+
module UnitMeasurements
|
8
|
+
class Unit
|
9
|
+
attr_reader :name, :value, :aliases, :unit_group
|
10
|
+
|
11
|
+
def initialize(name, value:, aliases:, unit_group: nil)
|
12
|
+
@name = name.to_s.freeze
|
13
|
+
@value = value
|
14
|
+
@aliases = Set.new(aliases.sort.map(&:to_s).map(&:freeze)).freeze
|
15
|
+
@unit_group = unit_group
|
16
|
+
end
|
17
|
+
|
18
|
+
def with(name: nil, value: nil, aliases: nil, unit_group: nil)
|
19
|
+
self.class.new(
|
20
|
+
(name || self.name),
|
21
|
+
value: (value || self.value),
|
22
|
+
aliases: (aliases || self.aliases),
|
23
|
+
unit_group: (unit_group || self.unit_group)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def names
|
28
|
+
(aliases + [name]).sort.freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
name
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
aliases = "(#{@aliases.join(", ")})" if @aliases.any?
|
37
|
+
"#<#{self.class.name}: #{name} #{aliases}>"
|
38
|
+
end
|
39
|
+
|
40
|
+
def conversion_factor
|
41
|
+
if value.is_a?(String)
|
42
|
+
measurement_value, measurement_unit = Parser.parse(value)
|
43
|
+
conversion_factor = unit_group.unit_for!(measurement_unit).conversion_factor
|
44
|
+
conversion_factor * measurement_value
|
45
|
+
else
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
SI_PREFIXES = [
|
53
|
+
["q", %w(quecto), 1e-30],
|
54
|
+
["r", %w(ronto), 1e-27],
|
55
|
+
["y", %w(yocto), 1e-24],
|
56
|
+
["z", %w(zepto), 1e-21],
|
57
|
+
["a", %w(atto), 1e-18],
|
58
|
+
["f", %w(femto), 1e-15],
|
59
|
+
["p", %w(pico), 1e-12],
|
60
|
+
["n", %w(nano), 1e-9],
|
61
|
+
["μ", %w(micro), 1e-6],
|
62
|
+
["m", %w(milli), 1e-3],
|
63
|
+
["c", %w(centi), 1e-2],
|
64
|
+
["d", %w(deci), 1e-1],
|
65
|
+
["da", %w(deca deka), 1e+1],
|
66
|
+
["h", %w(hecto), 1e+2],
|
67
|
+
["k", %w(kilo), 1e+3],
|
68
|
+
["M", %w(mega), 1e+6],
|
69
|
+
["G", %w(giga), 1e+9],
|
70
|
+
["T", %w(tera), 1e+12],
|
71
|
+
["P", %w(peta), 1e+15],
|
72
|
+
["E", %w(exa), 1e+18],
|
73
|
+
["Z", %w(zetta), 1e+21],
|
74
|
+
["Y", %w(yotta), 1e+24],
|
75
|
+
["R", %w(ronna), 1e+27],
|
76
|
+
["Q", %w(quetta), 1e+30]
|
77
|
+
].map(&:freeze).freeze
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class UnitGroup
|
7
|
+
attr_reader :units
|
8
|
+
|
9
|
+
def initialize(units)
|
10
|
+
@units = units.map { |unit| unit.with(unit_group: self) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def unit_for(name)
|
14
|
+
unit_name_to_unit(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def unit_for!(name)
|
18
|
+
unit = unit_for(name)
|
19
|
+
raise UnitError, name unless unit
|
20
|
+
unit
|
21
|
+
end
|
22
|
+
alias_method :[], :unit_for!
|
23
|
+
|
24
|
+
def unit_with_name_and_aliases
|
25
|
+
units.each_with_object({}) do |unit, hash|
|
26
|
+
unit.names.each { |name| hash[name.to_s] = unit }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def unit_names
|
31
|
+
units.map(&:name).sort
|
32
|
+
end
|
33
|
+
|
34
|
+
def unit_names_with_aliases
|
35
|
+
units.flat_map(&:names).sort
|
36
|
+
end
|
37
|
+
|
38
|
+
def unit_name_to_unit(name)
|
39
|
+
unit_with_name_and_aliases[name.to_s]
|
40
|
+
end
|
41
|
+
|
42
|
+
def defined?(name)
|
43
|
+
unit = unit_for(name)
|
44
|
+
unit ? unit.name == name.to_s : false
|
45
|
+
end
|
46
|
+
|
47
|
+
def unit_or_alias?(name)
|
48
|
+
!!unit_for(name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
module UnitMeasurements
|
6
|
+
class UnitGroupBuilder
|
7
|
+
attr_reader :units
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@units = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def base(name, aliases: [])
|
14
|
+
@units << build_unit(name, value: 1.0, aliases: aliases)
|
15
|
+
end
|
16
|
+
|
17
|
+
def unit(name, value: 1.0, aliases: [])
|
18
|
+
@units << build_unit(name, value: value, aliases: aliases)
|
19
|
+
end
|
20
|
+
|
21
|
+
def si_unit(name, value: 1.0, aliases: [])
|
22
|
+
@units += build_si_units(name, value: value, aliases: aliases)
|
23
|
+
end
|
24
|
+
|
25
|
+
def build
|
26
|
+
UnitGroup.new(@units)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def build_si_units(name, value:, aliases:)
|
32
|
+
si_units = [build_unit(name, value: value, aliases: aliases)]
|
33
|
+
|
34
|
+
Unit::SI_PREFIXES.each do |short_prefix, long_prefix, multiplier|
|
35
|
+
si_aliases = long_prefix.product(aliases.to_a).flat_map do |prefix, unit|
|
36
|
+
aliases.map { |alias_unit| prefix + alias_unit.to_s }
|
37
|
+
end
|
38
|
+
si_units << build_unit("#{short_prefix}#{name}", value: "#{multiplier} #{name}", aliases: si_aliases)
|
39
|
+
end
|
40
|
+
si_units
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_unit(name, value:, aliases:)
|
44
|
+
unit = Unit.new(name, value: value, aliases: aliases)
|
45
|
+
check_for_duplicate_unit_names!(unit)
|
46
|
+
unit
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_for_duplicate_unit_names!(unit)
|
50
|
+
names = @units.flat_map(&:names)
|
51
|
+
if names.any? { |name| unit.names.include?(name) }
|
52
|
+
raise UnitAlreadyDefinedError.new(unit.name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
UnitMeasurements::Length = UnitMeasurements.build do
|
6
|
+
si_unit :m, aliases: %i[meter metre meters metres]
|
7
|
+
|
8
|
+
unit :in, value: "25.4 mm", aliases: %i[" inch inches]
|
9
|
+
unit :ft, value: "12 in", aliases: %i[' foot feet]
|
10
|
+
unit :yd, value: "3 ft", aliases: %i[yard yards]
|
11
|
+
unit :mi, value: "1760 yd", aliases: %i[mile miles]
|
12
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- frozen_string_literal: true -*-
|
3
|
+
# -*- warn_indent: true -*-
|
4
|
+
|
5
|
+
UnitMeasurements::Weight = UnitMeasurements.build do
|
6
|
+
si_unit :g, aliases: %i[gram grams gramme grammes]
|
7
|
+
|
8
|
+
unit :q, value: "100 kg", aliases: %i[quintal quintals]
|
9
|
+
unit :t, value: "10 q", aliases: %i[tonne tonnes metric\ tonne metric\ tonnes]
|
10
|
+
end
|
data/lib/unit_measurements.rb
CHANGED
data/units.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Bundled unit groups and units
|
2
|
+
|
3
|
+
As there are lots of units bundled with `unit_measurements`, we recommend you to check below list of
|
4
|
+
bundled units before converting your measurements.
|
5
|
+
|
6
|
+
**Notes:**
|
7
|
+
1. Base unit for each unit group is highlighted.
|
8
|
+
2. Unit numbers suffixed with `*` support all [SI prefixes](README.md#si-units-support).
|
9
|
+
|
10
|
+
Below are the units which are bundled in the unit_measurements.
|
11
|
+
|
12
|
+
## 1. Length/Distance
|
13
|
+
|
14
|
+
These units are defined in `UnitMeasurements::Length`.
|
15
|
+
|
16
|
+
| # | Symbol | Aliases |
|
17
|
+
|:--|:--|:--|
|
18
|
+
| **1\*** | **m** | **meter, metre, meters, metres** |
|
19
|
+
| 2 | in | ", inch, inches |
|
20
|
+
| 3 | ft | ', foot, feet |
|
21
|
+
| 4 | yd | yard, yards |
|
22
|
+
| 5 | mi | mile, miles |
|
23
|
+
|
24
|
+
## 2. Weight/Mass
|
25
|
+
|
26
|
+
These units are defined in `UnitMeasurements::Weight`.
|
27
|
+
|
28
|
+
| # | Symbol | Aliases |
|
29
|
+
|--|--|--|
|
30
|
+
| **1\*** | **g** | **gram, grams, gramme, grammes** |
|
31
|
+
| 2 | q | quintal, quintals |
|
32
|
+
| 3 | t | tonne, tonnes, metric tonne, metric tonnes |
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unit_measurements
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harshal LADHE
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -100,10 +100,25 @@ files:
|
|
100
100
|
- Gemfile
|
101
101
|
- Gemfile.lock
|
102
102
|
- LICENSE
|
103
|
+
- README.md
|
103
104
|
- Rakefile
|
104
105
|
- lib/unit_measurements.rb
|
106
|
+
- lib/unit_measurements/base.rb
|
107
|
+
- lib/unit_measurements/errors/parse_error.rb
|
108
|
+
- lib/unit_measurements/errors/unit_already_defined_error.rb
|
109
|
+
- lib/unit_measurements/errors/unit_error.rb
|
110
|
+
- lib/unit_measurements/measurement.rb
|
111
|
+
- lib/unit_measurements/normalizer.rb
|
112
|
+
- lib/unit_measurements/parser.rb
|
113
|
+
- lib/unit_measurements/unit.rb
|
114
|
+
- lib/unit_measurements/unit_group.rb
|
115
|
+
- lib/unit_measurements/unit_group_builder.rb
|
116
|
+
- lib/unit_measurements/unit_groups/all.rb
|
117
|
+
- lib/unit_measurements/unit_groups/length.rb
|
118
|
+
- lib/unit_measurements/unit_groups/weight.rb
|
105
119
|
- lib/unit_measurements/version.rb
|
106
120
|
- unit_measurements.gemspec
|
121
|
+
- units.md
|
107
122
|
homepage: https://github.com/shivam091/unit_measurements
|
108
123
|
licenses:
|
109
124
|
- MIT
|