measured 2.5.0 → 2.7.1
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/.github/workflows/ci.yml +32 -0
- data/CHANGELOG.md +40 -0
- data/README.md +3 -2
- data/gemfiles/{activesupport-5.0.gemfile → activesupport-5.2.gemfile} +1 -1
- data/gemfiles/activesupport-6.0.gemfile +1 -1
- data/gemfiles/{activesupport-5.1.gemfile → activesupport-6.1.gemfile} +1 -1
- data/lib/measured.rb +1 -0
- data/lib/measured/arithmetic.rb +1 -0
- data/lib/measured/base.rb +7 -5
- data/lib/measured/cache/json.rb +12 -5
- data/lib/measured/cache/json_writer.rb +1 -0
- data/lib/measured/cache/null.rb +1 -0
- data/lib/measured/conversion_table_builder.rb +21 -2
- data/lib/measured/cycle_detected.rb +11 -0
- data/lib/measured/measurable.rb +18 -21
- data/lib/measured/missing_conversion_path.rb +12 -0
- data/lib/measured/parser.rb +2 -1
- data/lib/measured/unit.rb +16 -26
- data/lib/measured/unit_already_added.rb +11 -0
- data/lib/measured/unit_error.rb +4 -0
- data/lib/measured/unit_system.rb +16 -20
- data/lib/measured/unit_system_builder.rb +4 -3
- data/lib/measured/units/length.rb +1 -0
- data/lib/measured/units/volume.rb +1 -0
- data/lib/measured/units/weight.rb +1 -0
- data/lib/measured/version.rb +2 -1
- data/measured.gemspec +10 -2
- data/test/arithmetic_test.rb +1 -0
- data/test/cache/json_test.rb +1 -0
- data/test/cache/json_writer_test.rb +1 -0
- data/test/cache/null_test.rb +1 -0
- data/test/cache_consistency_test.rb +1 -0
- data/test/conversion_table_builder_test.rb +11 -0
- data/test/measurable_test.rb +1 -1
- data/test/parser_test.rb +1 -0
- data/test/support/always_true_cache.rb +1 -0
- data/test/support/fake_system.rb +1 -0
- data/test/support/subclasses.rb +1 -0
- data/test/test_helper.rb +2 -1
- data/test/unit_error_test.rb +1 -0
- data/test/unit_system_builder_test.rb +28 -2
- data/test/unit_system_test.rb +1 -0
- data/test/unit_test.rb +1 -0
- data/test/units/length_test.rb +5 -4
- data/test/units/volume_test.rb +6 -5
- data/test/units/weight_test.rb +1 -0
- metadata +19 -14
- data/.travis.yml +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2641c37b73279794a9877f2c22d4fc40ed49e3fb66ec879863d631251befec60
|
4
|
+
data.tar.gz: d065ce4bd8cc5dd3b75844aa08a2bbd931ca83bf214ea3eb5fb7db1e67c08a23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b7b34fd57b3bff6c467ba62156d21a89fadcd32e009cecb3c1ca36b609134ed4abd1950ac7513ba5ced802627a723086ba44ee563286e1294759dee1a0eeeb1
|
7
|
+
data.tar.gz: 9e2d5a86949c31b44c810c186d46b080bbc599d3f87a276676eb249b6b0841b41dc7964b4c071c98c21be3ab071f8db0085d9cf50d9134b1e377e62b545aae41
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
env:
|
9
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
10
|
+
strategy:
|
11
|
+
matrix:
|
12
|
+
ruby:
|
13
|
+
- '2.5'
|
14
|
+
- '2.6'
|
15
|
+
- '2.7'
|
16
|
+
- '3.0'
|
17
|
+
gemfile:
|
18
|
+
- Gemfile
|
19
|
+
- gemfiles/activesupport-5.2.gemfile
|
20
|
+
- gemfiles/activesupport-6.0.gemfile
|
21
|
+
- gemfiles/activesupport-6.1.gemfile
|
22
|
+
name: Ruby ${{ matrix.ruby }} ${{ matrix.gemfile }}
|
23
|
+
steps:
|
24
|
+
- uses: actions/checkout@v1
|
25
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
26
|
+
uses: ruby/setup-ruby@v1
|
27
|
+
with:
|
28
|
+
ruby-version: ${{ matrix.ruby }}
|
29
|
+
bundler-cache: true
|
30
|
+
- name: Run tests
|
31
|
+
run: |
|
32
|
+
bundle exec rake
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Unreleased
|
2
|
+
-----
|
3
|
+
|
4
|
+
2.7.1
|
5
|
+
-----
|
6
|
+
|
7
|
+
* Fix Ruby 3.0 compatibility
|
8
|
+
|
9
|
+
2.7.0
|
10
|
+
-----
|
11
|
+
|
12
|
+
* Raises an exception on cyclic conversions. (@arturopie)
|
13
|
+
* Deduplicate strings loaded from the cache.
|
14
|
+
* Deduplicate parsed units.
|
15
|
+
|
16
|
+
2.6.0
|
17
|
+
-----
|
18
|
+
|
19
|
+
* Add `Measured::MissingConversionPath` and `Measured::UnitAlreadyAdded` as subclasses of `Measured::UnitError` to handle specific error cases. (@arturopie)
|
20
|
+
* Support only ActiveSupport 5.2 and above.
|
21
|
+
|
22
|
+
|
23
|
+
2.5.2
|
24
|
+
-----
|
25
|
+
|
26
|
+
* Allow unit values to be declared in the unit system through aliases and not just the base unit name.
|
27
|
+
* Fix some deprecations in tests and CI.
|
28
|
+
|
29
|
+
2.5.1
|
30
|
+
----
|
31
|
+
|
32
|
+
* Get rid of most memoizations in favor of eager computations.
|
33
|
+
|
34
|
+
2.5.0
|
35
|
+
-----
|
36
|
+
|
37
|
+
* Add `CHANGELOG.md`.
|
38
|
+
* Fix some deprecations and warnings.
|
39
|
+
* Support Rails 6 and Ruby 2.6.
|
40
|
+
* Cache conversion table in JSON file for first load performance.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Measured [](https://github.com/Shopify/measured/actions?query=workflow%3ACI)
|
2
2
|
|
3
3
|
Encapsulates measurements with their units. Provides easy conversion between units. Built in support for weight, length, and volume.
|
4
4
|
|
@@ -273,12 +273,13 @@ Existing alternatives which were considered:
|
|
273
273
|
|
274
274
|
### Gem: [unitwise](https://github.com/joshwlewis/unitwise)
|
275
275
|
* **Pros**
|
276
|
-
* Well written
|
276
|
+
* Well written.
|
277
277
|
* Conversions done with Unified Code for Units of Measure (UCUM) so highly accurate and reliable.
|
278
278
|
* **Cons**
|
279
279
|
* Lots of code. Good code, but lots of it.
|
280
280
|
* Many modifications to core types.
|
281
281
|
* ActiveRecord adapter exists but is written and maintained by a different person/org.
|
282
|
+
* Not actively maintained.
|
282
283
|
|
283
284
|
## Contributing
|
284
285
|
|
data/lib/measured.rb
CHANGED
data/lib/measured/arithmetic.rb
CHANGED
data/lib/measured/base.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "forwardable"
|
2
3
|
require "measured/version"
|
3
4
|
require "active_support/all"
|
@@ -5,8 +6,6 @@ require "bigdecimal"
|
|
5
6
|
require "json"
|
6
7
|
|
7
8
|
module Measured
|
8
|
-
class UnitError < StandardError ; end
|
9
|
-
|
10
9
|
class << self
|
11
10
|
def build(&block)
|
12
11
|
builder = UnitSystemBuilder.new
|
@@ -23,10 +22,9 @@ module Measured
|
|
23
22
|
|
24
23
|
def method_missing(method, *args)
|
25
24
|
class_name = "Measured::#{ method }"
|
25
|
+
klass = class_name.safe_constantize
|
26
26
|
|
27
|
-
if Measurable
|
28
|
-
klass = class_name.constantize
|
29
|
-
|
27
|
+
if klass && klass < Measurable
|
30
28
|
Measured.define_singleton_method(method) do |value, unit|
|
31
29
|
klass.new(value, unit)
|
32
30
|
end
|
@@ -39,6 +37,10 @@ module Measured
|
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
40
|
+
require "measured/unit_error"
|
41
|
+
require "measured/cycle_detected"
|
42
|
+
require "measured/unit_already_added"
|
43
|
+
require "measured/missing_conversion_path"
|
42
44
|
require "measured/arithmetic"
|
43
45
|
require "measured/parser"
|
44
46
|
require "measured/unit"
|
data/lib/measured/cache/json.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Measured::Cache
|
2
3
|
class Json
|
3
4
|
attr_reader :filename, :path
|
@@ -13,7 +14,7 @@ module Measured::Cache
|
|
13
14
|
|
14
15
|
def read
|
15
16
|
return unless exist?
|
16
|
-
decode(JSON.load(File.read(@path)))
|
17
|
+
decode(JSON.load(File.read(@path), nil, freeze: true))
|
17
18
|
end
|
18
19
|
|
19
20
|
def write(table)
|
@@ -36,11 +37,17 @@ module Measured::Cache
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def decode(table)
|
39
|
-
table.
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
table.transform_values do |value1|
|
41
|
+
if value1.is_a?(Hash)
|
42
|
+
value1.transform_values do |value2|
|
43
|
+
if value2.is_a?(Hash)
|
44
|
+
Rational(value2["numerator"], value2["denominator"])
|
45
|
+
else
|
46
|
+
value2
|
47
|
+
end
|
43
48
|
end
|
49
|
+
else
|
50
|
+
value1
|
44
51
|
end
|
45
52
|
end
|
46
53
|
end
|
data/lib/measured/cache/null.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Measured::ConversionTableBuilder
|
2
3
|
attr_reader :units
|
3
4
|
|
@@ -23,6 +24,8 @@ class Measured::ConversionTableBuilder
|
|
23
24
|
private
|
24
25
|
|
25
26
|
def generate_table
|
27
|
+
validate_no_cycles
|
28
|
+
|
26
29
|
units.map(&:name).each_with_object({}) do |to_unit, table|
|
27
30
|
to_table = {to_unit => Rational(1, 1)}
|
28
31
|
|
@@ -36,10 +39,27 @@ class Measured::ConversionTableBuilder
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
42
|
+
def validate_no_cycles
|
43
|
+
graph = units.select { |unit| unit.conversion_unit.present? }.group_by { |unit| unit.name }
|
44
|
+
validate_acyclic_graph(graph, from: graph.keys[0])
|
45
|
+
end
|
46
|
+
|
47
|
+
# This uses a depth-first search algorithm: https://en.wikipedia.org/wiki/Depth-first_search
|
48
|
+
def validate_acyclic_graph(graph, from:, visited: [])
|
49
|
+
graph[from]&.each do |edge|
|
50
|
+
adjacent_node = edge.conversion_unit
|
51
|
+
if visited.include?(adjacent_node)
|
52
|
+
raise Measured::CycleDetected.new(edge)
|
53
|
+
else
|
54
|
+
validate_acyclic_graph(graph, from: adjacent_node, visited: visited + [adjacent_node])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
39
59
|
def find_conversion(to:, from:)
|
40
60
|
conversion = find_direct_conversion_cached(to: to, from: from) || find_tree_traversal_conversion(to: to, from: from)
|
41
61
|
|
42
|
-
raise Measured::
|
62
|
+
raise Measured::MissingConversionPath.new(from, to) unless conversion
|
43
63
|
|
44
64
|
conversion
|
45
65
|
end
|
@@ -87,5 +107,4 @@ class Measured::ConversionTableBuilder
|
|
87
107
|
|
88
108
|
nil
|
89
109
|
end
|
90
|
-
|
91
110
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Measured
|
3
|
+
class CycleDetected < UnitError
|
4
|
+
attr_reader :unit
|
5
|
+
|
6
|
+
def initialize(unit)
|
7
|
+
super("The following conversion introduces cycles in the unit system: #{unit}. Remove the conversion or fix the cycle.")
|
8
|
+
@unit = unit
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/measured/measurable.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Measured::Measurable < Numeric
|
2
|
-
DEFAULT_FORMAT_STRING =
|
3
|
+
DEFAULT_FORMAT_STRING = "%.2<value>f %<unit>s"
|
3
4
|
|
4
5
|
include Measured::Arithmetic
|
5
6
|
|
@@ -19,6 +20,18 @@ class Measured::Measurable < Numeric
|
|
19
20
|
else
|
20
21
|
BigDecimal(value)
|
21
22
|
end
|
23
|
+
|
24
|
+
@value_string = begin
|
25
|
+
str = case value
|
26
|
+
when Rational
|
27
|
+
value.denominator == 1 ? value.numerator.to_s : value.to_f.to_s
|
28
|
+
when BigDecimal
|
29
|
+
value.to_s("F")
|
30
|
+
else
|
31
|
+
value.to_f.to_s
|
32
|
+
end
|
33
|
+
str.gsub(/\.0*\Z/, "")
|
34
|
+
end.freeze
|
22
35
|
end
|
23
36
|
|
24
37
|
def convert_to(new_unit)
|
@@ -39,18 +52,16 @@ class Measured::Measurable < Numeric
|
|
39
52
|
end
|
40
53
|
|
41
54
|
def to_s
|
42
|
-
|
55
|
+
"#{@value_string} #{unit.name}"
|
43
56
|
end
|
44
57
|
|
45
58
|
def humanize
|
46
|
-
|
47
|
-
|
48
|
-
"#{value_string} #{unit_string}"
|
49
|
-
end
|
59
|
+
unit_string = value == 1 ? unit.name : ActiveSupport::Inflector.pluralize(unit.name)
|
60
|
+
"#{@value_string} #{unit_string}"
|
50
61
|
end
|
51
62
|
|
52
63
|
def inspect
|
53
|
-
|
64
|
+
"#<#{self.class}: #{@value_string} #{unit.inspect}>"
|
54
65
|
end
|
55
66
|
|
56
67
|
def <=>(other)
|
@@ -86,18 +97,4 @@ class Measured::Measurable < Numeric
|
|
86
97
|
def unit_from_unit_or_name!(value)
|
87
98
|
value.is_a?(Measured::Unit) ? value : self.class.unit_system.unit_for!(value)
|
88
99
|
end
|
89
|
-
|
90
|
-
def value_string
|
91
|
-
@value_string ||= begin
|
92
|
-
str = case value
|
93
|
-
when Rational
|
94
|
-
value.denominator == 1 ? value.numerator.to_s : value.to_f.to_s
|
95
|
-
when BigDecimal
|
96
|
-
value.to_s("F")
|
97
|
-
else
|
98
|
-
value.to_f.to_s
|
99
|
-
end
|
100
|
-
str.gsub(/\.0*\Z/, "")
|
101
|
-
end
|
102
|
-
end
|
103
100
|
end
|
data/lib/measured/parser.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Measured::Parser
|
2
3
|
extend self
|
3
4
|
|
@@ -32,6 +33,6 @@ module Measured::Parser
|
|
32
33
|
|
33
34
|
raise Measured::UnitError, "Cannot parse measurement from '#{string}'" unless result
|
34
35
|
|
35
|
-
[result.captures[0].to_r, result.captures[1]]
|
36
|
+
[result.captures[0].to_r, -result.captures[1]]
|
36
37
|
end
|
37
38
|
end
|
data/lib/measured/unit.rb
CHANGED
@@ -1,43 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Measured::Unit
|
2
3
|
include Comparable
|
3
4
|
|
4
|
-
attr_reader :name, :aliases, :conversion_amount, :conversion_unit, :unit_system
|
5
|
+
attr_reader :name, :names, :aliases, :conversion_amount, :conversion_unit, :unit_system, :inverse_conversion_amount
|
5
6
|
|
6
7
|
def initialize(name, aliases: [], value: nil, unit_system: nil)
|
7
8
|
@name = name.to_s.freeze
|
8
9
|
@aliases = aliases.map(&:to_s).map(&:freeze).freeze
|
10
|
+
@names = ([@name] + @aliases).sort!.freeze
|
9
11
|
@conversion_amount, @conversion_unit = parse_value(value) if value
|
12
|
+
@inverse_conversion_amount = (1 / conversion_amount if conversion_amount)
|
13
|
+
@conversion_string = ("#{conversion_amount} #{conversion_unit}" if conversion_amount || conversion_unit)
|
10
14
|
@unit_system = unit_system
|
11
15
|
end
|
12
16
|
|
13
|
-
def
|
17
|
+
def with(name: nil, unit_system: nil, aliases: nil, value: nil)
|
14
18
|
self.class.new(
|
15
|
-
name,
|
16
|
-
aliases: aliases,
|
17
|
-
value: conversion_string,
|
18
|
-
unit_system: unit_system
|
19
|
+
name || self.name,
|
20
|
+
aliases: aliases || self.aliases,
|
21
|
+
value: value || @conversion_string,
|
22
|
+
unit_system: unit_system || self.unit_system
|
19
23
|
)
|
20
24
|
end
|
21
25
|
|
22
|
-
def names
|
23
|
-
@names ||= ([name] + aliases).sort!.freeze
|
24
|
-
end
|
25
|
-
|
26
26
|
def to_s
|
27
|
-
|
28
|
-
"#{name} (#{conversion_string})".freeze
|
27
|
+
if @conversion_string
|
28
|
+
"#{name} (#{@conversion_string})".freeze
|
29
29
|
else
|
30
30
|
name
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
def inspect
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
"#<#{self.class.name}: #{pieces.join(" ")}>".freeze
|
40
|
-
end
|
35
|
+
pieces = [name]
|
36
|
+
pieces << "(#{aliases.join(", ")})" if aliases.any?
|
37
|
+
pieces << @conversion_string if @conversion_string
|
38
|
+
"#<#{self.class.name}: #{pieces.join(" ")}>".freeze
|
41
39
|
end
|
42
40
|
|
43
41
|
def <=>(other)
|
@@ -53,16 +51,8 @@ class Measured::Unit
|
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
56
|
-
def inverse_conversion_amount
|
57
|
-
@inverse_conversion_amount ||= 1 / conversion_amount if conversion_amount
|
58
|
-
end
|
59
|
-
|
60
54
|
private
|
61
55
|
|
62
|
-
def conversion_string
|
63
|
-
@conversion_string ||= ("#{conversion_amount} #{conversion_unit}" if conversion_amount || conversion_unit)
|
64
|
-
end
|
65
|
-
|
66
56
|
def parse_value(tokens)
|
67
57
|
case tokens
|
68
58
|
when String
|
data/lib/measured/unit_system.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Measured::UnitSystem
|
2
|
-
attr_reader :units
|
3
|
+
attr_reader :units, :unit_names, :unit_names_with_aliases
|
3
4
|
|
4
5
|
def initialize(units, cache: nil)
|
5
|
-
@units = units.map { |unit| unit.
|
6
|
+
@units = units.map { |unit| unit.with(unit_system: self) }
|
7
|
+
@units = @units.map do |unit|
|
8
|
+
next unit unless unit.conversion_unit
|
9
|
+
conversion_unit = @units.find { |u| u.names.include?(unit.conversion_unit) }
|
10
|
+
next unit unless conversion_unit
|
11
|
+
unit.with(value: [unit.conversion_amount, conversion_unit.name])
|
12
|
+
end
|
13
|
+
@unit_names = @units.map(&:name).sort.freeze
|
14
|
+
@unit_names_with_aliases = @units.flat_map(&:names).sort.freeze
|
15
|
+
@unit_name_to_unit = @units.each_with_object({}) do |unit, hash|
|
16
|
+
unit.names.each { |name| hash[name.to_s] = unit }
|
17
|
+
end
|
6
18
|
@conversion_table_builder = Measured::ConversionTableBuilder.new(@units, cache: cache)
|
7
|
-
|
8
|
-
|
9
|
-
def unit_names_with_aliases
|
10
|
-
@unit_names_with_aliases ||= @units.flat_map(&:names).sort
|
11
|
-
end
|
12
|
-
|
13
|
-
def unit_names
|
14
|
-
@unit_names ||= @units.map(&:name).sort
|
19
|
+
@conversion_table = @conversion_table_builder.to_h.freeze
|
15
20
|
end
|
16
21
|
|
17
22
|
def unit_or_alias?(name)
|
@@ -51,14 +56,5 @@ class Measured::UnitSystem
|
|
51
56
|
|
52
57
|
protected
|
53
58
|
|
54
|
-
|
55
|
-
@conversion_table ||= @conversion_table_builder.to_h
|
56
|
-
end
|
57
|
-
|
58
|
-
def unit_name_to_unit
|
59
|
-
@unit_name_to_unit ||= @units.inject({}) do |hash, unit|
|
60
|
-
unit.names.each { |name| hash[name.to_s] = unit }
|
61
|
-
hash
|
62
|
-
end
|
63
|
-
end
|
59
|
+
attr_reader :unit_name_to_unit, :conversion_table
|
64
60
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Measured::UnitSystemBuilder
|
2
3
|
def initialize
|
3
4
|
@units = []
|
@@ -45,8 +46,8 @@ class Measured::UnitSystemBuilder
|
|
45
46
|
["P", "peta", 15],
|
46
47
|
["E", "exa", 18],
|
47
48
|
["Z", "zetta", 21],
|
48
|
-
["Y", "yotta", 24]
|
49
|
-
]
|
49
|
+
["Y", "yotta", 24],
|
50
|
+
].map(&:freeze).freeze
|
50
51
|
|
51
52
|
def build_si_units(name, aliases: [], value: nil)
|
52
53
|
si_units = [build_unit(name, aliases: aliases, value: value)]
|
@@ -66,7 +67,7 @@ class Measured::UnitSystemBuilder
|
|
66
67
|
def check_for_duplicate_unit_names!(unit)
|
67
68
|
names = @units.flat_map(&:names)
|
68
69
|
if names.any? { |name| unit.names.include?(name) }
|
69
|
-
raise Measured::
|
70
|
+
raise Measured::UnitAlreadyAdded.new(unit.name)
|
70
71
|
end
|
71
72
|
end
|
72
73
|
end
|
data/lib/measured/version.rb
CHANGED
data/measured.gemspec
CHANGED
@@ -13,16 +13,24 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = "https://github.com/Shopify/measured"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
17
|
+
# delete this section to allow pushing this gem to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
end
|
23
|
+
|
16
24
|
spec.files = `git ls-files -z`.split("\x0")
|
17
25
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
26
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
27
|
spec.require_paths = ["lib"]
|
20
28
|
|
21
|
-
spec.add_runtime_dependency "activesupport", ">= 5.
|
29
|
+
spec.add_runtime_dependency "activesupport", ">= 5.2"
|
22
30
|
|
23
31
|
spec.add_development_dependency "rake", "> 10.0"
|
24
32
|
spec.add_development_dependency "minitest", "> 5.5.1"
|
25
33
|
spec.add_development_dependency "minitest-reporters"
|
26
|
-
spec.add_development_dependency "mocha", "
|
34
|
+
spec.add_development_dependency "mocha", ">= 1.4.0"
|
27
35
|
spec.add_development_dependency "pry"
|
28
36
|
end
|
data/test/arithmetic_test.rb
CHANGED
data/test/cache/json_test.rb
CHANGED
data/test/cache/null_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "test_helper"
|
2
3
|
|
3
4
|
class Measured::ConversionTableBuilderTest < ActiveSupport::TestCase
|
@@ -116,6 +117,16 @@ class Measured::ConversionTableBuilderTest < ActiveSupport::TestCase
|
|
116
117
|
end
|
117
118
|
end
|
118
119
|
|
120
|
+
test "#to_h raises exception when there are cycles" do
|
121
|
+
unit1 = Measured::Unit.new(:pallets, value: "1 liters")
|
122
|
+
unit2 = Measured::Unit.new(:liters, value: "0.1 cases")
|
123
|
+
unit3 = Measured::Unit.new(:cases, value: "0.1 pallets")
|
124
|
+
|
125
|
+
assert_raises(Measured::CycleDetected) do
|
126
|
+
Measured::ConversionTableBuilder.new([unit1, unit2, unit3]).to_h
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
119
130
|
test "#cached? returns true if there's a cache" do
|
120
131
|
builder = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)], cache: { class: AlwaysTrueCache })
|
121
132
|
assert_predicate builder, :cached?
|
data/test/measurable_test.rb
CHANGED
data/test/parser_test.rb
CHANGED
data/test/support/fake_system.rb
CHANGED
data/test/support/subclasses.rb
CHANGED
data/test/test_helper.rb
CHANGED
data/test/unit_error_test.rb
CHANGED
@@ -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::
|
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::
|
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
|
@@ -38,6 +54,16 @@ class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
|
|
38
54
|
assert_equal 'BOLD', measurable.unit_system.unit_for!(:BOLD).name
|
39
55
|
end
|
40
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
|
+
|
41
67
|
test "#si_unit adds 21 new units" do
|
42
68
|
measurable = Measured.build do
|
43
69
|
unit :ft
|
data/test/unit_system_test.rb
CHANGED
data/test/unit_test.rb
CHANGED
data/test/units/length_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "test_helper"
|
2
3
|
|
3
4
|
class Measured::LengthTest < ActiveSupport::TestCase
|
@@ -142,7 +143,7 @@ class Measured::LengthTest < ActiveSupport::TestCase
|
|
142
143
|
end
|
143
144
|
|
144
145
|
test ".convert_to from km to mi" do
|
145
|
-
assert_conversion Measured::Length, "2000 km", "0.1242742384475E4 mi"
|
146
|
+
assert_conversion Measured::Length, "2000 km", "0.1242742384475E4 mi"
|
146
147
|
end
|
147
148
|
|
148
149
|
test ".convert_to from km to mm" do
|
@@ -150,7 +151,7 @@ class Measured::LengthTest < ActiveSupport::TestCase
|
|
150
151
|
end
|
151
152
|
|
152
153
|
test ".convert_to from km to yd" do
|
153
|
-
assert_conversion Measured::Length, "2000 km", "0.218722659667542E7 yd"
|
154
|
+
assert_conversion Measured::Length, "2000 km", "0.218722659667542E7 yd"
|
154
155
|
end
|
155
156
|
|
156
157
|
test ".convert_to from m to cm" do
|
@@ -206,7 +207,7 @@ class Measured::LengthTest < ActiveSupport::TestCase
|
|
206
207
|
end
|
207
208
|
|
208
209
|
test ".convert_to from mi to mi" do
|
209
|
-
assert_exact_conversion Measured::Length, "2000 mi", "2000 mi"
|
210
|
+
assert_exact_conversion Measured::Length, "2000 mi", "2000 mi"
|
210
211
|
end
|
211
212
|
|
212
213
|
test ".convert_to from mi to mm" do
|
@@ -214,7 +215,7 @@ class Measured::LengthTest < ActiveSupport::TestCase
|
|
214
215
|
end
|
215
216
|
|
216
217
|
test ".convert_to from mi to yd" do
|
217
|
-
assert_exact_conversion Measured::Length, "2000 mi", "3520000 yd"
|
218
|
+
assert_exact_conversion Measured::Length, "2000 mi", "3520000 yd"
|
218
219
|
end
|
219
220
|
|
220
221
|
test ".convert_to from mm to cm" do
|
data/test/units/volume_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "test_helper"
|
2
3
|
|
3
4
|
class Measured::VolumeTest < ActiveSupport::TestCase
|
@@ -68,7 +69,7 @@ class Measured::VolumeTest < ActiveSupport::TestCase
|
|
68
69
|
test ".unit_names should be the list of base unit names" do
|
69
70
|
expected_units = %w(l m3 ft3 in3 gal us_gal qt us_qt pt us_pt oz us_oz)
|
70
71
|
expected_units += Measured::UnitSystemBuilder::SI_PREFIXES.map { |short, _, _| "#{short}l" }
|
71
|
-
assert_equal expected_units.sort, Measured::Volume.unit_names
|
72
|
+
assert_equal expected_units.sort, Measured::Volume.unit_names
|
72
73
|
end
|
73
74
|
|
74
75
|
test ".name" do
|
@@ -95,7 +96,7 @@ class Measured::VolumeTest < ActiveSupport::TestCase
|
|
95
96
|
test ".convert_to from gal to gal" do
|
96
97
|
assert_conversion Measured::Volume, "2000 gal", "2000 gal"
|
97
98
|
end
|
98
|
-
|
99
|
+
|
99
100
|
test ".convert_to from us_gal to us_gal" do
|
100
101
|
assert_conversion Measured::Volume, "2000 us_gal", "2000 us_gal"
|
101
102
|
end
|
@@ -123,7 +124,7 @@ class Measured::VolumeTest < ActiveSupport::TestCase
|
|
123
124
|
test ".convert_to from us_oz to us_oz" do
|
124
125
|
assert_conversion Measured::Volume, "2000 us_oz", "2000 us_oz"
|
125
126
|
end
|
126
|
-
|
127
|
+
|
127
128
|
test ".convert_to from ml to m3" do
|
128
129
|
assert_conversion Measured::Volume, "2000 ml", "0.002 m3"
|
129
130
|
end
|
@@ -303,7 +304,7 @@ class Measured::VolumeTest < ActiveSupport::TestCase
|
|
303
304
|
test ".convert_to from gal to us_oz" do
|
304
305
|
assert_conversion Measured::Volume, "2 gal", "307.4431809292 us_oz"
|
305
306
|
end
|
306
|
-
|
307
|
+
|
307
308
|
test ".convert_to from us_gal to qt" do
|
308
309
|
assert_conversion Measured::Volume, "2 us_gal", "6.661393477 qt"
|
309
310
|
end
|
@@ -387,4 +388,4 @@ class Measured::VolumeTest < ActiveSupport::TestCase
|
|
387
388
|
test ".convert_to from oz to us_oz" do
|
388
389
|
assert_conversion Measured::Volume, "2 oz", "1.9215207864 us_oz"
|
389
390
|
end
|
390
|
-
end
|
391
|
+
end
|
data/test/units/weight_test.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: measured
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin McPhillips
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2021-05-07 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '5.
|
21
|
+
version: '5.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: '5.
|
28
|
+
version: '5.2'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: rake
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,16 +72,16 @@ dependencies:
|
|
72
72
|
name: mocha
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
|
-
- - "
|
75
|
+
- - ">="
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 1.
|
77
|
+
version: 1.4.0
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- - "
|
82
|
+
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
version: 1.
|
84
|
+
version: 1.4.0
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: pry
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,8 +104,9 @@ executables: []
|
|
104
104
|
extensions: []
|
105
105
|
extra_rdoc_files: []
|
106
106
|
files:
|
107
|
+
- ".github/workflows/ci.yml"
|
107
108
|
- ".gitignore"
|
108
|
-
-
|
109
|
+
- CHANGELOG.md
|
109
110
|
- Gemfile
|
110
111
|
- LICENSE
|
111
112
|
- README.md
|
@@ -115,9 +116,9 @@ files:
|
|
115
116
|
- cache/volume.json
|
116
117
|
- cache/weight.json
|
117
118
|
- dev.yml
|
118
|
-
- gemfiles/activesupport-5.
|
119
|
-
- gemfiles/activesupport-5.1.gemfile
|
119
|
+
- gemfiles/activesupport-5.2.gemfile
|
120
120
|
- gemfiles/activesupport-6.0.gemfile
|
121
|
+
- gemfiles/activesupport-6.1.gemfile
|
121
122
|
- lib/measured.rb
|
122
123
|
- lib/measured/arithmetic.rb
|
123
124
|
- lib/measured/base.rb
|
@@ -125,9 +126,13 @@ files:
|
|
125
126
|
- lib/measured/cache/json_writer.rb
|
126
127
|
- lib/measured/cache/null.rb
|
127
128
|
- lib/measured/conversion_table_builder.rb
|
129
|
+
- lib/measured/cycle_detected.rb
|
128
130
|
- lib/measured/measurable.rb
|
131
|
+
- lib/measured/missing_conversion_path.rb
|
129
132
|
- lib/measured/parser.rb
|
130
133
|
- lib/measured/unit.rb
|
134
|
+
- lib/measured/unit_already_added.rb
|
135
|
+
- lib/measured/unit_error.rb
|
131
136
|
- lib/measured/unit_system.rb
|
132
137
|
- lib/measured/unit_system_builder.rb
|
133
138
|
- lib/measured/units/length.rb
|
@@ -158,7 +163,8 @@ files:
|
|
158
163
|
homepage: https://github.com/Shopify/measured
|
159
164
|
licenses:
|
160
165
|
- MIT
|
161
|
-
metadata:
|
166
|
+
metadata:
|
167
|
+
allowed_push_host: https://rubygems.org
|
162
168
|
post_install_message:
|
163
169
|
rdoc_options: []
|
164
170
|
require_paths:
|
@@ -174,8 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
174
180
|
- !ruby/object:Gem::Version
|
175
181
|
version: '0'
|
176
182
|
requirements: []
|
177
|
-
|
178
|
-
rubygems_version: 2.7.6
|
183
|
+
rubygems_version: 3.0.3
|
179
184
|
signing_key:
|
180
185
|
specification_version: 4
|
181
186
|
summary: Encapsulate measurements with their units in Ruby
|
data/.travis.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
cache: bundler
|
4
|
-
rvm:
|
5
|
-
- 2.3.8
|
6
|
-
- 2.4.5
|
7
|
-
- 2.5.5
|
8
|
-
- 2.6.3
|
9
|
-
gemfile:
|
10
|
-
- Gemfile
|
11
|
-
- gemfiles/activesupport-5.0.gemfile
|
12
|
-
- gemfiles/activesupport-5.1.gemfile
|
13
|
-
- gemfiles/activesupport-6.0.gemfile
|
14
|
-
before_script:
|
15
|
-
- gem update --system
|
16
|
-
matrix:
|
17
|
-
exclude:
|
18
|
-
- gemfile: gemfiles/activesupport-6.0.gemfile
|
19
|
-
rvm: 2.3.8
|
20
|
-
- gemfile: gemfiles/activesupport-6.0.gemfile
|
21
|
-
rvm: 2.4.5
|