measured 2.6.0 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +4 -3
- data/CHANGELOG.md +15 -0
- data/lib/measured/base.rb +1 -0
- data/lib/measured/cache/json.rb +11 -5
- data/lib/measured/conversion_table_builder.rb +19 -0
- data/lib/measured/cycle_detected.rb +11 -0
- data/lib/measured/parser.rb +1 -1
- data/lib/measured/version.rb +1 -1
- data/test/conversion_table_builder_test.rb +10 -0
- metadata +3 -2
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
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
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
|
+
|
1
16
|
2.6.0
|
2
17
|
-----
|
3
18
|
|
data/lib/measured/base.rb
CHANGED
data/lib/measured/cache/json.rb
CHANGED
@@ -14,7 +14,7 @@ module Measured::Cache
|
|
14
14
|
|
15
15
|
def read
|
16
16
|
return unless exist?
|
17
|
-
decode(JSON.load(File.read(@path)))
|
17
|
+
decode(JSON.load(File.read(@path), nil, freeze: true))
|
18
18
|
end
|
19
19
|
|
20
20
|
def write(table)
|
@@ -37,11 +37,17 @@ module Measured::Cache
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def decode(table)
|
40
|
-
table.
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
44
48
|
end
|
49
|
+
else
|
50
|
+
value1
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
@@ -24,6 +24,8 @@ class Measured::ConversionTableBuilder
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def generate_table
|
27
|
+
validate_no_cycles
|
28
|
+
|
27
29
|
units.map(&:name).each_with_object({}) do |to_unit, table|
|
28
30
|
to_table = {to_unit => Rational(1, 1)}
|
29
31
|
|
@@ -37,6 +39,23 @@ class Measured::ConversionTableBuilder
|
|
37
39
|
end
|
38
40
|
end
|
39
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
|
+
|
40
59
|
def find_conversion(to:, from:)
|
41
60
|
conversion = find_direct_conversion_cached(to: to, from: from) || find_tree_traversal_conversion(to: to, from: from)
|
42
61
|
|
@@ -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/parser.rb
CHANGED
data/lib/measured/version.rb
CHANGED
@@ -117,6 +117,16 @@ class Measured::ConversionTableBuilderTest < ActiveSupport::TestCase
|
|
117
117
|
end
|
118
118
|
end
|
119
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
|
+
|
120
130
|
test "#cached? returns true if there's a cache" do
|
121
131
|
builder = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)], cache: { class: AlwaysTrueCache })
|
122
132
|
assert_predicate builder, :cached?
|
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
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/measured/cache/json_writer.rb
|
127
127
|
- lib/measured/cache/null.rb
|
128
128
|
- lib/measured/conversion_table_builder.rb
|
129
|
+
- lib/measured/cycle_detected.rb
|
129
130
|
- lib/measured/measurable.rb
|
130
131
|
- lib/measured/missing_conversion_path.rb
|
131
132
|
- lib/measured/parser.rb
|