measured 2.6.0 → 2.7.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.

Potentially problematic release.


This version of measured might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2de9a5e120aaab53f71956ad225722ed2d5cdd01d018104b214af063eb22b149
4
- data.tar.gz: f65f75ed06c7118f76da05d638782c8b0049f928e6ce0ce96f93e27673d3dd48
3
+ metadata.gz: 82ffeb317f95fc3c354f43453eabc91b138a5e4f1b50eec651775425a27316e5
4
+ data.tar.gz: c664577f19d2ab99b95f7c533fb46323b60390c1b34da1bc3602b27979cddff6
5
5
  SHA512:
6
- metadata.gz: c445792e2670c86ab8526ba7021880c6bef7db3850be768aae8a54ff024b4e56816b24cd4df88f6748971b7e176bb5eceefa48234511d9fbb25f67a065966733
7
- data.tar.gz: 3eae614b358b735a68a6d8a17088f252d7f9db11db3f31c8cbc2a3e4e54d1ac0441d0af2d2f69a44bcbe2654c8839356ac00bc6b7c929d6c3e857f7afbc91d6e
6
+ metadata.gz: 4ae4538c2b2e9b00403b6363d639a6f0959704c4702b65cca3d1cd33d714f2eaf14c4fa4aa446b6247e1f9b7cd7977bc80581115965b21abbd4972b10b2e3eea
7
+ data.tar.gz: ee33ab98e63f586bcf1ce299909e4cd6ecc0c6b434e3f2e48340d5dc9f2641a8c34bcc2dd695f9c75f162f413b6533456f6c0ab22d29b3c93c21c186e647b671
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ 2.7.0
2
+ -----
3
+
4
+ * Raises an exception on cyclic conversions. (@arturopie)
5
+ * Deduplicate strings loaded from the cache.
6
+ * Deduplicate parsed units.
7
+
1
8
  2.6.0
2
9
  -----
3
10
 
data/lib/measured/base.rb CHANGED
@@ -38,6 +38,7 @@ module Measured
38
38
  end
39
39
 
40
40
  require "measured/unit_error"
41
+ require "measured/cycle_detected"
41
42
  require "measured/unit_already_added"
42
43
  require "measured/missing_conversion_path"
43
44
  require "measured/arithmetic"
@@ -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)
@@ -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
@@ -33,6 +33,6 @@ module Measured::Parser
33
33
 
34
34
  raise Measured::UnitError, "Cannot parse measurement from '#{string}'" unless result
35
35
 
36
- [result.captures[0].to_r, result.captures[1]]
36
+ [result.captures[0].to_r, -result.captures[1]]
37
37
  end
38
38
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Measured
3
- VERSION = "2.6.0"
3
+ VERSION = "2.7.0"
4
4
  end
@@ -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.6.0
4
+ version: 2.7.0
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: 2020-12-10 00:00:00.000000000 Z
13
+ date: 2021-05-06 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