measured 2.5.2 → 2.8.0
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 +31 -0
- data/CHANGELOG.md +28 -0
- data/README.md +3 -2
- data/dev.yml +1 -1
- 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/base.rb +4 -2
- data/lib/measured/cache/json.rb +11 -5
- data/lib/measured/conversion_table_builder.rb +20 -1
- data/lib/measured/cycle_detected.rb +11 -0
- data/lib/measured/missing_conversion_path.rb +12 -0
- data/lib/measured/parser.rb +1 -1
- data/lib/measured/unit_already_added.rb +11 -0
- data/lib/measured/unit_error.rb +4 -0
- data/lib/measured/unit_system_builder.rb +1 -1
- data/lib/measured/version.rb +1 -1
- data/measured.gemspec +9 -1
- data/test/conversion_table_builder_test.rb +10 -0
- data/test/unit_system_builder_test.rb +17 -2
- metadata +14 -9
- data/.travis.yml +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 474f40f00059e15ec826a6ac74758ea6919f3b3c61a75bc42bcf181f9ac357dc
|
4
|
+
data.tar.gz: 356026fb5443a773928dbe21ac20f28f97ed72a987f6708b682cb665eeb511fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be4796ff68ae8e99abdd8361c68c79624e5b6b10f143a3314edc834e28cd79aa8c6f2c5707d49564d42c8d80fd0521a3db38023cd688a70e733a8f737991ff22
|
7
|
+
data.tar.gz: 56a1cf4956fc1eb5802bd74841dfcef1114eaf3514f1a0c3c3c9585aa7643e0c36f2ec287c8933c879ce4a5ecd2e1232ce2a4ea0e30f7cef7fc1a5cc90db69e8
|
@@ -0,0 +1,31 @@
|
|
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.6'
|
14
|
+
- '2.7'
|
15
|
+
- '3.0'
|
16
|
+
gemfile:
|
17
|
+
- Gemfile
|
18
|
+
- gemfiles/activesupport-5.2.gemfile
|
19
|
+
- gemfiles/activesupport-6.0.gemfile
|
20
|
+
- gemfiles/activesupport-6.1.gemfile
|
21
|
+
name: Ruby ${{ matrix.ruby }} ${{ matrix.gemfile }}
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v1
|
24
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
25
|
+
uses: ruby/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{ matrix.ruby }}
|
28
|
+
bundler-cache: true
|
29
|
+
- name: Run tests
|
30
|
+
run: |
|
31
|
+
bundle exec rake
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
Unreleased
|
2
|
+
-----
|
3
|
+
|
4
|
+
2.8.0
|
5
|
+
-----
|
6
|
+
|
7
|
+
* Drop support for Ruby 2.5
|
8
|
+
* Use Ruby 3.0.2 for development
|
9
|
+
|
10
|
+
2.7.1
|
11
|
+
-----
|
12
|
+
|
13
|
+
* Fix Ruby 3.0 compatibility
|
14
|
+
|
15
|
+
2.7.0
|
16
|
+
-----
|
17
|
+
|
18
|
+
* Raises an exception on cyclic conversions. (@arturopie)
|
19
|
+
* Deduplicate strings loaded from the cache.
|
20
|
+
* Deduplicate parsed units.
|
21
|
+
|
22
|
+
2.6.0
|
23
|
+
-----
|
24
|
+
|
25
|
+
* Add `Measured::MissingConversionPath` and `Measured::UnitAlreadyAdded` as subclasses of `Measured::UnitError` to handle specific error cases. (@arturopie)
|
26
|
+
* Support only ActiveSupport 5.2 and above.
|
27
|
+
|
28
|
+
|
1
29
|
2.5.2
|
2
30
|
-----
|
3
31
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Measured [![Build Status](https://
|
1
|
+
# Measured [![Build Status](https://github.com/Shopify/measured/workflows/CI/badge.svg)](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/dev.yml
CHANGED
data/lib/measured/base.rb
CHANGED
@@ -6,8 +6,6 @@ require "bigdecimal"
|
|
6
6
|
require "json"
|
7
7
|
|
8
8
|
module Measured
|
9
|
-
class UnitError < StandardError ; end
|
10
|
-
|
11
9
|
class << self
|
12
10
|
def build(&block)
|
13
11
|
builder = UnitSystemBuilder.new
|
@@ -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
@@ -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,10 +39,27 @@ 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
|
|
43
|
-
raise Measured::
|
62
|
+
raise Measured::MissingConversionPath.new(from, to) unless conversion
|
44
63
|
|
45
64
|
conversion
|
46
65
|
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/parser.rb
CHANGED
@@ -67,7 +67,7 @@ class Measured::UnitSystemBuilder
|
|
67
67
|
def check_for_duplicate_unit_names!(unit)
|
68
68
|
names = @units.flat_map(&:names)
|
69
69
|
if names.any? { |name| unit.names.include?(name) }
|
70
|
-
raise Measured::
|
70
|
+
raise Measured::UnitAlreadyAdded.new(unit.name)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
data/lib/measured/version.rb
CHANGED
data/measured.gemspec
CHANGED
@@ -13,12 +13,20 @@ 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"
|
@@ -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?
|
@@ -12,15 +12,17 @@ class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
|
|
12
12
|
end
|
13
13
|
|
14
14
|
test "#unit cannot add duplicate unit names" do
|
15
|
-
assert_raises Measured::
|
15
|
+
error = assert_raises Measured::UnitAlreadyAdded do
|
16
16
|
Measured.build do
|
17
17
|
unit :m
|
18
18
|
unit :in, aliases: [:inch], value: "0.0254 m"
|
19
19
|
unit :in, aliases: [:thing], value: "123 m"
|
20
20
|
end
|
21
21
|
end
|
22
|
+
assert_equal("in", error.unit_name)
|
23
|
+
assert_equal("Unit in has already been added.", error.message)
|
22
24
|
|
23
|
-
assert_raises Measured::
|
25
|
+
assert_raises Measured::UnitAlreadyAdded do
|
24
26
|
Measured.build do
|
25
27
|
unit :m
|
26
28
|
unit :in, aliases: [:inch], value: "0.0254 m"
|
@@ -29,6 +31,19 @@ class Measured::UnitSystemBuilderTest < ActiveSupport::TestCase
|
|
29
31
|
end
|
30
32
|
end
|
31
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
|
+
|
32
47
|
test "#unit is case sensitive" do
|
33
48
|
measurable = Measured.build do
|
34
49
|
unit :normal
|
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.8.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:
|
13
|
+
date: 2021-11-08 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
|
@@ -104,8 +104,8 @@ executables: []
|
|
104
104
|
extensions: []
|
105
105
|
extra_rdoc_files: []
|
106
106
|
files:
|
107
|
+
- ".github/workflows/ci.yml"
|
107
108
|
- ".gitignore"
|
108
|
-
- ".travis.yml"
|
109
109
|
- CHANGELOG.md
|
110
110
|
- Gemfile
|
111
111
|
- LICENSE
|
@@ -116,9 +116,9 @@ files:
|
|
116
116
|
- cache/volume.json
|
117
117
|
- cache/weight.json
|
118
118
|
- dev.yml
|
119
|
-
- gemfiles/activesupport-5.
|
120
|
-
- gemfiles/activesupport-5.1.gemfile
|
119
|
+
- gemfiles/activesupport-5.2.gemfile
|
121
120
|
- gemfiles/activesupport-6.0.gemfile
|
121
|
+
- gemfiles/activesupport-6.1.gemfile
|
122
122
|
- lib/measured.rb
|
123
123
|
- lib/measured/arithmetic.rb
|
124
124
|
- lib/measured/base.rb
|
@@ -126,9 +126,13 @@ 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
|
131
|
+
- lib/measured/missing_conversion_path.rb
|
130
132
|
- lib/measured/parser.rb
|
131
133
|
- lib/measured/unit.rb
|
134
|
+
- lib/measured/unit_already_added.rb
|
135
|
+
- lib/measured/unit_error.rb
|
132
136
|
- lib/measured/unit_system.rb
|
133
137
|
- lib/measured/unit_system_builder.rb
|
134
138
|
- lib/measured/units/length.rb
|
@@ -159,7 +163,8 @@ files:
|
|
159
163
|
homepage: https://github.com/Shopify/measured
|
160
164
|
licenses:
|
161
165
|
- MIT
|
162
|
-
metadata:
|
166
|
+
metadata:
|
167
|
+
allowed_push_host: https://rubygems.org
|
163
168
|
post_install_message:
|
164
169
|
rdoc_options: []
|
165
170
|
require_paths:
|
@@ -175,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
180
|
- !ruby/object:Gem::Version
|
176
181
|
version: '0'
|
177
182
|
requirements: []
|
178
|
-
rubygems_version: 3.
|
183
|
+
rubygems_version: 3.2.20
|
179
184
|
signing_key:
|
180
185
|
specification_version: 4
|
181
186
|
summary: Encapsulate measurements with their units in Ruby
|
data/.travis.yml
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
cache: bundler
|
4
|
-
rvm:
|
5
|
-
- 2.4.9
|
6
|
-
- 2.5.7
|
7
|
-
- 2.6.5
|
8
|
-
- 2.7.0
|
9
|
-
gemfile:
|
10
|
-
- Gemfile
|
11
|
-
- gemfiles/activesupport-5.0.gemfile
|
12
|
-
- gemfiles/activesupport-5.1.gemfile
|
13
|
-
- gemfiles/activesupport-6.0.gemfile
|
14
|
-
matrix:
|
15
|
-
exclude:
|
16
|
-
- gemfile: gemfiles/activesupport-6.0.gemfile
|
17
|
-
rvm: 2.4.9
|