measured 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2957338dafe1bf300930f97c9d23678175281ec0
4
- data.tar.gz: f52c802978510562bfaf3df79b122a79c230addf
3
+ metadata.gz: b181078ed94c22d305d95b445287def5a9ee7386
4
+ data.tar.gz: 2694f75fc2085f93c93075435ad1955b5f872e36
5
5
  SHA512:
6
- metadata.gz: ad9b8f66e912f434c51d9a344a18adaa419e19b483ff4aaf7ab8df26f82cd644ca2f49aa5d3a2f5359491a36e58d62e5b0ee3a916b8f38ff1085b450ce4f1f4d
7
- data.tar.gz: 7292c1dfe2357087eee0e15f043b2ce572fe655faa40c3ea1fc228b7d8a102d472f9b9c5bd2b81d0b82401ffeef2ddba6d961c1883d789d2937d831ae4d4b40d
6
+ metadata.gz: 961ab0ba4ac6cec71002b1c4a65a179880db5e8336a722aef32849ab6bf1a1f3bb2b137687ca3a8e4fd651d90b76ab1616d6306f03e3a12d82e64c248ae377c7
7
+ data.tar.gz: 757eba32db074dfba1712fd1c9c751630d5f5bae413dbb9fea0735bc0255f63fd28ca1a8b39aea9057b308acc14d3de7330305b1711ef45f87d4bdcf4b8f879f
@@ -6,9 +6,12 @@ rvm:
6
6
  - 2.2.4
7
7
  - 2.3.1
8
8
  - 2.4.0
9
+ - 2.5.0
9
10
  gemfile:
10
11
  - Gemfile
11
12
  - gemfiles/activesupport-4.2.gemfile
13
+ before_script:
14
+ - gem update --system
12
15
  matrix:
13
16
  exclude:
14
17
  - gemfile: Gemfile
data/Rakefile CHANGED
@@ -8,9 +8,12 @@ task default: :test
8
8
 
9
9
  desc 'Run the test stuite'
10
10
  Rake::TestTask.new do |t|
11
+ files = ARGV[1..-1]
12
+ files = "test/**/*_test.rb" if !files || files.length == 0
13
+
11
14
  t.libs << "test"
12
15
  t.libs << "lib/**/*"
13
- t.test_files = FileList['test/**/*_test.rb']
16
+ t.test_files = FileList[files]
14
17
  t.verbose = true
15
18
  end
16
19
 
data/dev.yml CHANGED
@@ -14,5 +14,5 @@ commands:
14
14
  if [[ $# -eq 0 ]]; then
15
15
  bundle exec rake test
16
16
  else
17
- bundle exec ruby -Itest "$@"
17
+ bundle exec rake test "$@"
18
18
  fi
@@ -1,3 +1,4 @@
1
+ require "forwardable"
1
2
  require "measured/version"
2
3
  require "active_support/all"
3
4
  require "bigdecimal"
@@ -42,5 +43,5 @@ require "measured/parser"
42
43
  require "measured/unit"
43
44
  require "measured/unit_system"
44
45
  require "measured/unit_system_builder"
45
- require "measured/conversion_table"
46
+ require "measured/conversion_table_builder"
46
47
  require "measured/measurable"
@@ -0,0 +1,80 @@
1
+ class Measured::ConversionTableBuilder
2
+ attr_reader :units
3
+
4
+ def initialize(units)
5
+ @units = units
6
+ end
7
+
8
+ def to_h
9
+ table = {}
10
+
11
+ units.map{|u| u.name}.each do |to_unit|
12
+ to_table = {to_unit => Rational(1, 1)}
13
+
14
+ table.each do |from_unit, from_table|
15
+ conversion = find_conversion(to: from_unit, from: to_unit)
16
+ to_table[from_unit] = conversion
17
+ from_table[to_unit] = 1 / conversion
18
+ end
19
+
20
+ table[to_unit] = to_table
21
+ end
22
+
23
+ table
24
+ end
25
+
26
+ private
27
+
28
+ def find_conversion(to:, from:)
29
+ conversion = find_direct_conversion_cached(to: to, from: from) || find_tree_traversal_conversion(to: to, from: from)
30
+
31
+ raise Measured::UnitError, "Cannot find conversion path from #{ from } to #{ to }." unless conversion
32
+
33
+ conversion
34
+ end
35
+
36
+ def find_direct_conversion_cached(to:, from:)
37
+ @cache ||= {}
38
+ @cache[to] ||= {}
39
+
40
+ if @cache[to].key?(from)
41
+ @cache[to][from]
42
+ else
43
+ @cache[to][from] = find_direct_conversion(to: to, from: from)
44
+ end
45
+ end
46
+
47
+ def find_direct_conversion(to:, from:)
48
+ units.each do |unit|
49
+ return unit.conversion_amount if unit.name == from && unit.conversion_unit == to
50
+ return unit.inverse_conversion_amount if unit.name == to && unit.conversion_unit == from
51
+ end
52
+
53
+ nil
54
+ end
55
+
56
+ def find_tree_traversal_conversion(to:, from:)
57
+ traverse(from: from, to: to, units_remaining: units.map(&:name), amount: 1)
58
+ end
59
+
60
+ def traverse(from:, to:, units_remaining:, amount:)
61
+ units_remaining = units_remaining - [from]
62
+
63
+ units_remaining.each do |name|
64
+ conversion = find_direct_conversion_cached(from: from, to: name)
65
+
66
+ if conversion
67
+ new_amount = amount * conversion
68
+ if name == to
69
+ return new_amount
70
+ else
71
+ result = traverse(from: name, to: to, units_remaining: units_remaining, amount: new_amount)
72
+ return result if result
73
+ end
74
+ end
75
+ end
76
+
77
+ nil
78
+ end
79
+
80
+ end
@@ -54,7 +54,7 @@ class Measured::Unit
54
54
  end
55
55
 
56
56
  def inverse_conversion_amount
57
- @inverse_conversion_amount ||= 1 / conversion_amount
57
+ @inverse_conversion_amount ||= 1 / conversion_amount if conversion_amount
58
58
  end
59
59
 
60
60
  private
@@ -3,6 +3,7 @@ class Measured::UnitSystem
3
3
 
4
4
  def initialize(units)
5
5
  @units = units.map { |unit| unit.with_unit_system(self) }
6
+ @conversion_table_builder = Measured::ConversionTableBuilder.new(@units)
6
7
  end
7
8
 
8
9
  def unit_names_with_aliases
@@ -43,7 +44,7 @@ class Measured::UnitSystem
43
44
  protected
44
45
 
45
46
  def conversion_table
46
- @conversion_table ||= Measured::ConversionTable.build(@units)
47
+ @conversion_table ||= @conversion_table_builder.to_h
47
48
  end
48
49
 
49
50
  def unit_name_to_unit
@@ -1,3 +1,3 @@
1
1
  module Measured
2
- VERSION = "2.3.0"
2
+ VERSION = "2.4.0"
3
3
  end
@@ -6,10 +6,10 @@ require 'measured/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "measured"
8
8
  spec.version = Measured::VERSION
9
- spec.authors = ["Kevin McPhillips"]
10
- spec.email = ["github@kevinmcphillips.ca"]
9
+ spec.authors = ["Kevin McPhillips", "Jason Gedge", "Javier Honduvilla Coto"]
10
+ spec.email = ["gems@shopify.com"]
11
11
  spec.summary = %q{Encapsulate measurements with their units in Ruby}
12
- spec.description = %q{Wrapper objects which encapsulate measurments and their associated units in Ruby.}
12
+ spec.description = %q{Wrapper objects which encapsulate measurements and their associated units in Ruby.}
13
13
  spec.homepage = "https://github.com/Shopify/measured"
14
14
  spec.license = "MIT"
15
15
 
@@ -0,0 +1,118 @@
1
+ require "test_helper"
2
+
3
+ class Measured::ConversionTableBuilderTest < ActiveSupport::TestCase
4
+ test "#initialize creates a new object with the units" do
5
+ units = [Measured::Unit.new(:test)]
6
+
7
+ assert_equal units, Measured::ConversionTableBuilder.new(units).units
8
+ end
9
+
10
+ test "#to_h should return a hash for the simple case" do
11
+ conversion_table = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)]).to_h
12
+
13
+ expected = {
14
+ "test" => {"test" => Rational(1, 1)}
15
+ }
16
+
17
+ assert_equal expected, conversion_table
18
+ assert_instance_of Rational, conversion_table.values.first.values.first
19
+ end
20
+
21
+ test "#to_h returns expected nested hashes with BigDecimal conversion factors in a tiny data set" do
22
+ conversion_table = Measured::ConversionTableBuilder.new([
23
+ Measured::Unit.new(:m),
24
+ Measured::Unit.new(:cm, value: "0.01 m"),
25
+ ]).to_h
26
+
27
+ expected = {
28
+ "m" => {
29
+ "m" => Rational(1, 1),
30
+ "cm" => Rational(100, 1),
31
+ },
32
+ "cm" => {
33
+ "m" => Rational(1, 100),
34
+ "cm" => Rational(1, 1),
35
+ }
36
+ }
37
+
38
+ assert_equal expected, conversion_table
39
+
40
+ conversion_table.values.map(&:values).flatten.each do |value|
41
+ assert_instance_of Rational, value
42
+ end
43
+ end
44
+
45
+ test "#to_h returns expected nested hashes factors" do
46
+ conversion_table = Measured::ConversionTableBuilder.new([
47
+ Measured::Unit.new(:m),
48
+ Measured::Unit.new(:cm, value: "0.01 m"),
49
+ Measured::Unit.new(:mm, value: "0.001 m"),
50
+ ]).to_h
51
+
52
+ expected = {
53
+ "m" => {
54
+ "m" => Rational(1, 1),
55
+ "cm" => Rational(100, 1),
56
+ "mm" => Rational(1000, 1),
57
+ },
58
+ "cm" => {
59
+ "m" => Rational(1, 100),
60
+ "cm" => Rational(1, 1),
61
+ "mm" => Rational(10, 1),
62
+ },
63
+ "mm" => {
64
+ "m" => Rational(1, 1000),
65
+ "cm" => Rational(1, 10),
66
+ "mm" => Rational(1, 1),
67
+ }
68
+ }
69
+
70
+ assert_equal expected, conversion_table
71
+
72
+ conversion_table.values.map(&:values).flatten.each do |value|
73
+ assert_instance_of Rational, value
74
+ end
75
+ end
76
+
77
+ test "#to_h returns expected nested hashes in an indrect path" do
78
+ conversion_table = Measured::ConversionTableBuilder.new([
79
+ Measured::Unit.new(:mm),
80
+ Measured::Unit.new(:cm, value: "10 mm"),
81
+ Measured::Unit.new(:dm, value: "10 cm"),
82
+ Measured::Unit.new(:m, value: "10 dm"),
83
+ ]).to_h
84
+
85
+ expected = {
86
+ "m" => {
87
+ "m" => Rational(1, 1),
88
+ "dm" => Rational(10, 1),
89
+ "cm" => Rational(100, 1),
90
+ "mm" => Rational(1000, 1),
91
+ },
92
+ "cm" => {
93
+ "m" => Rational(1, 100),
94
+ "dm" => Rational(1, 10),
95
+ "cm" => Rational(1, 1),
96
+ "mm" => Rational(10, 1),
97
+ },
98
+ "dm" => {
99
+ "m" => Rational(1, 10),
100
+ "cm" => Rational(10, 1),
101
+ "dm" => Rational(1, 1),
102
+ "mm" => Rational(100, 1),
103
+ },
104
+ "mm" => {
105
+ "m" => Rational(1, 1000),
106
+ "dm" => Rational(1, 100),
107
+ "cm" => Rational(1, 10),
108
+ "mm" => Rational(1, 1),
109
+ }
110
+ }
111
+
112
+ assert_equal expected, conversion_table
113
+
114
+ conversion_table.values.map(&:values).flatten.each do |value|
115
+ assert_instance_of Rational, value
116
+ end
117
+ end
118
+ end
@@ -2,7 +2,7 @@ require "measured"
2
2
  require "minitest/reporters"
3
3
  require "minitest/autorun"
4
4
  require "mocha/setup"
5
- require "pry"
5
+ require "pry" unless ENV["CI"]
6
6
 
7
7
  ActiveSupport.test_order = :random
8
8
 
@@ -78,4 +78,8 @@ class Measured::UnitTest < ActiveSupport::TestCase
78
78
  test "#inverse_conversion_amount returns 1/amount" do
79
79
  assert_equal Rational(1, 10), @unit.inverse_conversion_amount
80
80
  end
81
+
82
+ test "#inverse_conversion_amount handles nil for base unit" do
83
+ assert_nil Measured::Unit.new(:pie).inverse_conversion_amount
84
+ end
81
85
  end
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: measured
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin McPhillips
8
+ - Jason Gedge
9
+ - Javier Honduvilla Coto
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
- date: 2018-01-17 00:00:00.000000000 Z
13
+ date: 2018-01-24 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: activesupport
@@ -94,10 +96,10 @@ dependencies:
94
96
  - - ">="
95
97
  - !ruby/object:Gem::Version
96
98
  version: '0'
97
- description: Wrapper objects which encapsulate measurments and their associated units
99
+ description: Wrapper objects which encapsulate measurements and their associated units
98
100
  in Ruby.
99
101
  email:
100
- - github@kevinmcphillips.ca
102
+ - gems@shopify.com
101
103
  executables: []
102
104
  extensions: []
103
105
  extra_rdoc_files: []
@@ -113,7 +115,7 @@ files:
113
115
  - lib/measured.rb
114
116
  - lib/measured/arithmetic.rb
115
117
  - lib/measured/base.rb
116
- - lib/measured/conversion_table.rb
118
+ - lib/measured/conversion_table_builder.rb
117
119
  - lib/measured/measurable.rb
118
120
  - lib/measured/parser.rb
119
121
  - lib/measured/unit.rb
@@ -126,7 +128,7 @@ files:
126
128
  - measured.gemspec
127
129
  - shipit.rubygems.yml
128
130
  - test/arithmetic_test.rb
129
- - test/conversion_table_test.rb
131
+ - test/conversion_table_builder_test.rb
130
132
  - test/measurable_test.rb
131
133
  - test/parser_test.rb
132
134
  - test/support/fake_system.rb
@@ -164,7 +166,7 @@ specification_version: 4
164
166
  summary: Encapsulate measurements with their units in Ruby
165
167
  test_files:
166
168
  - test/arithmetic_test.rb
167
- - test/conversion_table_test.rb
169
+ - test/conversion_table_builder_test.rb
168
170
  - test/measurable_test.rb
169
171
  - test/parser_test.rb
170
172
  - test/support/fake_system.rb
@@ -1,65 +0,0 @@
1
- module Measured::ConversionTable
2
- extend self
3
-
4
- def build(units)
5
- table = {}
6
-
7
- units.map{|u| u.name}.each do |to_unit|
8
- to_table = {to_unit => BigDecimal("1")}
9
-
10
- table.each do |from_unit, from_table|
11
- to_table[from_unit] = find_conversion(units, to: from_unit, from: to_unit)
12
- from_table[to_unit] = find_conversion(units, to: to_unit, from: from_unit)
13
- end
14
-
15
- table[to_unit] = to_table
16
- end
17
-
18
- table
19
- end
20
-
21
- private
22
-
23
- def find_conversion(units, to:, from:)
24
- conversion = find_direct_conversion(units, to: to, from: from) || find_tree_traversal_conversion(units, to: to, from: from)
25
-
26
- raise Measured::UnitError, "Cannot find conversion path from #{ from } to #{ to }." unless conversion
27
-
28
- conversion
29
- end
30
-
31
- def find_direct_conversion(units, to:, from:)
32
- units.each do |unit|
33
- return unit.conversion_amount if unit.name == from && unit.conversion_unit == to
34
- end
35
-
36
- units.each do |unit|
37
- return unit.inverse_conversion_amount if unit.name == to && unit.conversion_unit == from
38
- end
39
-
40
- nil
41
- end
42
-
43
- def find_tree_traversal_conversion(units, to:, from:)
44
- traverse(units, from: from, to: to, unit_names: units.map(&:name), amount: 1)
45
- end
46
-
47
- def traverse(units, from:, to:, unit_names:, amount:)
48
- unit_names = unit_names - [from]
49
-
50
- unit_names.each do |name|
51
- if conversion = find_direct_conversion(units, from: from, to: name)
52
- new_amount = amount * conversion
53
- if name == to
54
- return new_amount
55
- else
56
- result = traverse(units, from: name, to: to, unit_names: unit_names, amount: new_amount)
57
- return result if result
58
- end
59
- end
60
- end
61
-
62
- nil
63
- end
64
-
65
- end
@@ -1,98 +0,0 @@
1
- require "test_helper"
2
-
3
- class Measured::ConversionTableTest < ActiveSupport::TestCase
4
- test ".build should return a hash for the simple case" do
5
- expected = {
6
- "test" => {"test" => BigDecimal("1")}
7
- }
8
-
9
- assert_equal expected, Measured::ConversionTable.build([Measured::Unit.new(:test)])
10
- end
11
-
12
- test ".build returns expected nested hashes with BigDecimal conversion factors in a tiny data set" do
13
- conversion_table = Measured::ConversionTable.build([
14
- Measured::Unit.new(:m),
15
- Measured::Unit.new(:cm, value: "0.01 m"),
16
- ])
17
-
18
- expected = {
19
- "m" => {
20
- "m" => BigDecimal("1"),
21
- "cm" => BigDecimal("100"),
22
- },
23
- "cm" => {
24
- "m" => BigDecimal("0.01"),
25
- "cm" => BigDecimal("1"),
26
- }
27
- }
28
-
29
- assert_equal expected, conversion_table
30
- end
31
-
32
- test ".build returns expected nested hashes with BigDecimal conversion factors" do
33
- conversion_table = Measured::ConversionTable.build([
34
- Measured::Unit.new(:m),
35
- Measured::Unit.new(:cm, value: "0.01 m"),
36
- Measured::Unit.new(:mm, value: "0.001 m"),
37
- ])
38
-
39
- expected = {
40
- "m" => {
41
- "m" => BigDecimal("1"),
42
- "cm" => BigDecimal("100"),
43
- "mm" => BigDecimal("1000"),
44
- },
45
- "cm" => {
46
- "m" => BigDecimal("0.01"),
47
- "cm" => BigDecimal("1"),
48
- "mm" => BigDecimal("10"),
49
- },
50
- "mm" => {
51
- "m" => BigDecimal("0.001"),
52
- "cm" => BigDecimal("0.1"),
53
- "mm" => BigDecimal("1"),
54
- }
55
- }
56
-
57
- assert_equal expected, conversion_table
58
- end
59
-
60
- test ".build returns expected nested hashes with BigDecimal conversion factors in an indrect path" do
61
- conversion_table = Measured::ConversionTable.build([
62
- Measured::Unit.new(:mm),
63
- Measured::Unit.new(:cm, value: "10 mm"),
64
- Measured::Unit.new(:dm, value: "10 cm"),
65
- Measured::Unit.new(:m, value: "10 dm"),
66
- ])
67
-
68
- expected = {
69
- "m" => {
70
- "m" => BigDecimal("1"),
71
- "dm" => BigDecimal("10"),
72
- "cm" => BigDecimal("100"),
73
- "mm" => BigDecimal("1000"),
74
- },
75
- "cm" => {
76
- "m" => BigDecimal("0.01"),
77
- "dm" => BigDecimal("0.1"),
78
- "cm" => BigDecimal("1"),
79
- "mm" => BigDecimal("10"),
80
- },
81
- "dm" => {
82
- "m" => BigDecimal("0.1"),
83
- "cm" => BigDecimal("10"),
84
- "dm" => BigDecimal("1"),
85
- "mm" => BigDecimal("100"),
86
- },
87
- "mm" => {
88
- "m" => BigDecimal("0.001"),
89
- "dm" => BigDecimal("0.01"),
90
- "cm" => BigDecimal("0.1"),
91
- "mm" => BigDecimal("1"),
92
- }
93
- }
94
-
95
- assert_equal expected, conversion_table
96
- end
97
-
98
- end