measured 2.3.0 → 2.6.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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +31 -0
- data/CHANGELOG.md +25 -0
- data/README.md +12 -10
- data/Rakefile +22 -1
- data/cache/.keep +0 -0
- data/cache/length.json +2553 -0
- data/cache/volume.json +4163 -0
- data/cache/weight.json +3195 -0
- data/dev.yml +2 -2
- data/gemfiles/{activesupport-4.2.gemfile → activesupport-5.2.gemfile} +1 -1
- data/gemfiles/activesupport-6.0.gemfile +5 -0
- data/gemfiles/activesupport-6.1.gemfile +5 -0
- data/lib/measured.rb +1 -0
- data/lib/measured/arithmetic.rb +1 -0
- data/lib/measured/base.rb +12 -6
- data/lib/measured/cache/json.rb +49 -0
- data/lib/measured/cache/json_writer.rb +9 -0
- data/lib/measured/cache/null.rb +16 -0
- data/lib/measured/conversion_table_builder.rb +91 -0
- data/lib/measured/measurable.rb +18 -21
- data/lib/measured/missing_conversion_path.rb +12 -0
- data/lib/measured/parser.rb +1 -0
- 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 +26 -21
- data/lib/measured/unit_system_builder.rb +11 -4
- data/lib/measured/units/length.rb +3 -0
- data/lib/measured/units/volume.rb +4 -1
- data/lib/measured/units/weight.rb +3 -0
- data/lib/measured/version.rb +2 -1
- data/measured.gemspec +13 -5
- data/test/arithmetic_test.rb +1 -0
- data/test/cache/json_test.rb +43 -0
- data/test/cache/json_writer_test.rb +23 -0
- data/test/cache/null_test.rb +20 -0
- data/test/cache_consistency_test.rb +19 -0
- data/test/conversion_table_builder_test.rb +137 -0
- data/test/measurable_test.rb +5 -5
- data/test/parser_test.rb +1 -0
- data/test/support/always_true_cache.rb +14 -0
- data/test/support/fake_system.rb +1 -0
- data/test/support/subclasses.rb +7 -0
- data/test/test_helper.rb +5 -3
- data/test/unit_error_test.rb +1 -0
- data/test/unit_system_builder_test.rb +49 -3
- data/test/unit_system_test.rb +15 -0
- data/test/unit_test.rb +5 -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 +45 -18
- data/.travis.yml +0 -17
- data/lib/measured/conversion_table.rb +0 -65
- data/test/conversion_table_test.rb +0 -98
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Measured::UnitSystemBuilder
|
2
3
|
def initialize
|
3
4
|
@units = []
|
5
|
+
@cache = nil
|
4
6
|
end
|
5
7
|
|
6
8
|
def unit(unit_name, aliases: [], value: nil)
|
@@ -13,8 +15,13 @@ class Measured::UnitSystemBuilder
|
|
13
15
|
nil
|
14
16
|
end
|
15
17
|
|
18
|
+
def cache(cache_class, *args)
|
19
|
+
@cache = {class: cache_class, args: args}
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
16
23
|
def build
|
17
|
-
Measured::UnitSystem.new(@units)
|
24
|
+
Measured::UnitSystem.new(@units, cache: @cache)
|
18
25
|
end
|
19
26
|
|
20
27
|
private
|
@@ -39,8 +46,8 @@ class Measured::UnitSystemBuilder
|
|
39
46
|
["P", "peta", 15],
|
40
47
|
["E", "exa", 18],
|
41
48
|
["Z", "zetta", 21],
|
42
|
-
["Y", "yotta", 24]
|
43
|
-
]
|
49
|
+
["Y", "yotta", 24],
|
50
|
+
].map(&:freeze).freeze
|
44
51
|
|
45
52
|
def build_si_units(name, aliases: [], value: nil)
|
46
53
|
si_units = [build_unit(name, aliases: aliases, value: value)]
|
@@ -60,7 +67,7 @@ class Measured::UnitSystemBuilder
|
|
60
67
|
def check_for_duplicate_unit_names!(unit)
|
61
68
|
names = @units.flat_map(&:names)
|
62
69
|
if names.any? { |name| unit.names.include?(name) }
|
63
|
-
raise Measured::
|
70
|
+
raise Measured::UnitAlreadyAdded.new(unit.name)
|
64
71
|
end
|
65
72
|
end
|
66
73
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
Measured::Length = Measured.build do
|
2
3
|
si_unit :m, aliases: [:meter, :metre, :meters, :metres]
|
3
4
|
|
@@ -5,4 +6,6 @@ Measured::Length = Measured.build do
|
|
5
6
|
unit :ft, value: "12 in", aliases: [:foot, :feet]
|
6
7
|
unit :yd, value: "3 ft", aliases: [:yard, :yards]
|
7
8
|
unit :mi, value: "5280 ft", aliases: [:mile, :miles]
|
9
|
+
|
10
|
+
cache Measured::Cache::Json, "length.json"
|
8
11
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
Measured::Volume = Measured.build do
|
2
3
|
si_unit :l, aliases: [:liter, :litre, :liters, :litres]
|
3
4
|
|
@@ -12,4 +13,6 @@ Measured::Volume = Measured.build do
|
|
12
13
|
unit :us_pt, value: "0.125 us_gal", aliases: [:us_pint, :us_pints]
|
13
14
|
unit :oz, value: "0.00625 gal", aliases: [:fl_oz, :imp_fl_oz, :imperial_fluid_ounce, :imperial_fluid_ounces]
|
14
15
|
unit :us_oz, value: "0.0078125 us_gal", aliases: [:us_fl_oz, :us_fluid_ounce, :us_fluid_ounces]
|
15
|
-
|
16
|
+
|
17
|
+
cache Measured::Cache::Json, "volume.json"
|
18
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
Measured::Weight = Measured.build do
|
2
3
|
si_unit :g, aliases: [:gram, :grams]
|
3
4
|
|
@@ -8,4 +9,6 @@ Measured::Weight = Measured.build do
|
|
8
9
|
unit :short_ton, value: "2000 lb", aliases: [:short_tons]
|
9
10
|
unit :lb, value: "0.45359237 kg", aliases: [:lbs, :pound, :pounds]
|
10
11
|
unit :oz, value: "1/16 lb", aliases: [:ounce, :ounces]
|
12
|
+
|
13
|
+
cache Measured::Cache::Json, "weight.json"
|
11
14
|
end
|
data/lib/measured/version.rb
CHANGED
data/measured.gemspec
CHANGED
@@ -6,23 +6,31 @@ 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 = ["
|
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
|
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
|
|
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", ">=
|
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
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class Measured::Cache::JsonTest < ActiveSupport::TestCase
|
5
|
+
setup do
|
6
|
+
@cache = Measured::Cache::Json.new("test.json")
|
7
|
+
@table_json = { "a" => { "b" => { "numerator" => 2, "denominator" => 3 } } }.to_json
|
8
|
+
@table_hash = { "a" => { "b" => Rational(2, 3) } }
|
9
|
+
end
|
10
|
+
|
11
|
+
test "#initialize sets the filename and path" do
|
12
|
+
assert_equal "test.json", @cache.filename
|
13
|
+
assert_match(/.+\/cache\/test\.json$/, @cache.path.to_s)
|
14
|
+
refute_match "../", @cache.path.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
test "#exist? returns false if the file does not exist" do
|
18
|
+
File.expects(:exist?).with(@cache.path).returns(false)
|
19
|
+
refute_predicate @cache, :exist?
|
20
|
+
end
|
21
|
+
|
22
|
+
test "#exist? returns true if the file exists" do
|
23
|
+
File.expects(:exist?).with(@cache.path).returns(true)
|
24
|
+
assert_predicate @cache, :exist?
|
25
|
+
end
|
26
|
+
|
27
|
+
test "#read returns nil if the file does not exist" do
|
28
|
+
File.expects(:exist?).with(@cache.path).returns(false)
|
29
|
+
assert_nil @cache.read
|
30
|
+
end
|
31
|
+
|
32
|
+
test "#read loads the file if it exists" do
|
33
|
+
File.expects(:exist?).with(@cache.path).returns(true)
|
34
|
+
File.expects(:read).with(@cache.path).returns(@table_json)
|
35
|
+
assert_equal @table_hash, @cache.read
|
36
|
+
end
|
37
|
+
|
38
|
+
test "#write raises not implemented" do
|
39
|
+
assert_raises(ArgumentError) do
|
40
|
+
@cache.write({})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class Measured::Cache::JsonWriterTest < ActiveSupport::TestCase
|
5
|
+
class JsonTestWithWriter < Measured::Cache::Json
|
6
|
+
prepend Measured::Cache::JsonWriter
|
7
|
+
end
|
8
|
+
|
9
|
+
setup do
|
10
|
+
@cache = JsonTestWithWriter.new("test.json")
|
11
|
+
@table_json = JSON.pretty_generate({ "a" => { "b" => { "numerator" => 2, "denominator" => 3 } } })
|
12
|
+
@table_hash = { "a" => { "b" => Rational(2, 3) } }
|
13
|
+
end
|
14
|
+
|
15
|
+
test "#write writes the file" do
|
16
|
+
f = stub
|
17
|
+
f.expects(:write).with("// Do not modify this file directly. Regenerate it with 'rake cache:write'.\n")
|
18
|
+
f.expects(:write).with(@table_json)
|
19
|
+
|
20
|
+
File.expects(:open).with(@cache.path, "w").returns(123).yields(f)
|
21
|
+
assert_equal 123, @cache.write(@table_hash)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class Measured::Cache::NullTest < ActiveSupport::TestCase
|
5
|
+
setup do
|
6
|
+
@cache = Measured::Cache::Null.new
|
7
|
+
end
|
8
|
+
|
9
|
+
test "#exist? false" do
|
10
|
+
assert_equal false, @cache.exist?
|
11
|
+
end
|
12
|
+
|
13
|
+
test "#read returns nil" do
|
14
|
+
assert_nil @cache.read
|
15
|
+
end
|
16
|
+
|
17
|
+
test "#write returns nil" do
|
18
|
+
assert_nil @cache.write({})
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
# In general we do not want to expose the inner workings of the caching and unit systems as part of the API.
|
5
|
+
# But we do want to be able to test that there is no conflict between what's in the cache file and what is defined
|
6
|
+
# in the unit systems. So we compromise by reaching into implementation to force compare them.
|
7
|
+
class Measured::CacheConsistencyTest < ActiveSupport::TestCase
|
8
|
+
measurable_subclasses.select { |m| m.unit_system.cached? }.each do |measurable|
|
9
|
+
test "cached measurable #{ measurable } is not out of sync with the definition" do
|
10
|
+
builder = measurable.unit_system.instance_variable_get("@conversion_table_builder")
|
11
|
+
cache = builder.instance_variable_get("@cache")
|
12
|
+
|
13
|
+
expected = builder.send(:generate_table)
|
14
|
+
actual = cache.read
|
15
|
+
|
16
|
+
assert expected == actual, "The contents of the file cache for `#{ measurable }` does not match what the unit system generated.\nTry running `rake cache:write` to update the caches."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class Measured::ConversionTableBuilderTest < ActiveSupport::TestCase
|
5
|
+
test "#initialize creates a new object with the units" do
|
6
|
+
units = [Measured::Unit.new(:test)]
|
7
|
+
|
8
|
+
assert_equal units, Measured::ConversionTableBuilder.new(units).units
|
9
|
+
end
|
10
|
+
|
11
|
+
test "#to_h should return a hash for the simple case" do
|
12
|
+
conversion_table = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)]).to_h
|
13
|
+
|
14
|
+
expected = {
|
15
|
+
"test" => {"test" => Rational(1, 1)}
|
16
|
+
}
|
17
|
+
|
18
|
+
assert_equal expected, conversion_table
|
19
|
+
assert_instance_of Rational, conversion_table.values.first.values.first
|
20
|
+
end
|
21
|
+
|
22
|
+
test "#to_h returns expected nested hashes with BigDecimal conversion factors in a tiny data set" do
|
23
|
+
conversion_table = Measured::ConversionTableBuilder.new([
|
24
|
+
Measured::Unit.new(:m),
|
25
|
+
Measured::Unit.new(:cm, value: "0.01 m"),
|
26
|
+
]).to_h
|
27
|
+
|
28
|
+
expected = {
|
29
|
+
"m" => {
|
30
|
+
"m" => Rational(1, 1),
|
31
|
+
"cm" => Rational(100, 1),
|
32
|
+
},
|
33
|
+
"cm" => {
|
34
|
+
"m" => Rational(1, 100),
|
35
|
+
"cm" => Rational(1, 1),
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
assert_equal expected, conversion_table
|
40
|
+
|
41
|
+
conversion_table.values.map(&:values).flatten.each do |value|
|
42
|
+
assert_instance_of Rational, value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
test "#to_h returns expected nested hashes factors" do
|
47
|
+
conversion_table = Measured::ConversionTableBuilder.new([
|
48
|
+
Measured::Unit.new(:m),
|
49
|
+
Measured::Unit.new(:cm, value: "0.01 m"),
|
50
|
+
Measured::Unit.new(:mm, value: "0.001 m"),
|
51
|
+
]).to_h
|
52
|
+
|
53
|
+
expected = {
|
54
|
+
"m" => {
|
55
|
+
"m" => Rational(1, 1),
|
56
|
+
"cm" => Rational(100, 1),
|
57
|
+
"mm" => Rational(1000, 1),
|
58
|
+
},
|
59
|
+
"cm" => {
|
60
|
+
"m" => Rational(1, 100),
|
61
|
+
"cm" => Rational(1, 1),
|
62
|
+
"mm" => Rational(10, 1),
|
63
|
+
},
|
64
|
+
"mm" => {
|
65
|
+
"m" => Rational(1, 1000),
|
66
|
+
"cm" => Rational(1, 10),
|
67
|
+
"mm" => Rational(1, 1),
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
assert_equal expected, conversion_table
|
72
|
+
|
73
|
+
conversion_table.values.map(&:values).flatten.each do |value|
|
74
|
+
assert_instance_of Rational, value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
test "#to_h returns expected nested hashes in an indrect path" do
|
79
|
+
conversion_table = Measured::ConversionTableBuilder.new([
|
80
|
+
Measured::Unit.new(:mm),
|
81
|
+
Measured::Unit.new(:cm, value: "10 mm"),
|
82
|
+
Measured::Unit.new(:dm, value: "10 cm"),
|
83
|
+
Measured::Unit.new(:m, value: "10 dm"),
|
84
|
+
]).to_h
|
85
|
+
|
86
|
+
expected = {
|
87
|
+
"m" => {
|
88
|
+
"m" => Rational(1, 1),
|
89
|
+
"dm" => Rational(10, 1),
|
90
|
+
"cm" => Rational(100, 1),
|
91
|
+
"mm" => Rational(1000, 1),
|
92
|
+
},
|
93
|
+
"cm" => {
|
94
|
+
"m" => Rational(1, 100),
|
95
|
+
"dm" => Rational(1, 10),
|
96
|
+
"cm" => Rational(1, 1),
|
97
|
+
"mm" => Rational(10, 1),
|
98
|
+
},
|
99
|
+
"dm" => {
|
100
|
+
"m" => Rational(1, 10),
|
101
|
+
"cm" => Rational(10, 1),
|
102
|
+
"dm" => Rational(1, 1),
|
103
|
+
"mm" => Rational(100, 1),
|
104
|
+
},
|
105
|
+
"mm" => {
|
106
|
+
"m" => Rational(1, 1000),
|
107
|
+
"dm" => Rational(1, 100),
|
108
|
+
"cm" => Rational(1, 10),
|
109
|
+
"mm" => Rational(1, 1),
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
assert_equal expected, conversion_table
|
114
|
+
|
115
|
+
conversion_table.values.map(&:values).flatten.each do |value|
|
116
|
+
assert_instance_of Rational, value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
test "#cached? returns true if there's a cache" do
|
121
|
+
builder = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)], cache: { class: AlwaysTrueCache })
|
122
|
+
assert_predicate builder, :cached?
|
123
|
+
end
|
124
|
+
|
125
|
+
test "#cached? returns false if there is not a cache" do
|
126
|
+
builder = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)])
|
127
|
+
refute_predicate builder, :cached?
|
128
|
+
end
|
129
|
+
|
130
|
+
test "#write_cache pushes the generated table into the cache and writes it" do
|
131
|
+
builder = Measured::ConversionTableBuilder.new([Measured::Unit.new(:test)], cache: { class: AlwaysTrueCache })
|
132
|
+
AlwaysTrueCache.any_instance.expects(:exist?).returns(false)
|
133
|
+
table = builder.to_h
|
134
|
+
AlwaysTrueCache.any_instance.expects(:write).with(table).returns(123)
|
135
|
+
assert_equal 123, builder.update_cache
|
136
|
+
end
|
137
|
+
end
|
data/test/measurable_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "test_helper"
|
2
3
|
|
3
4
|
class Measured::MeasurableTest < ActiveSupport::TestCase
|
4
|
-
|
5
5
|
setup do
|
6
6
|
@arcane = Magic.unit_system.unit_for!(:arcane)
|
7
7
|
@fireball = Magic.unit_system.unit_for!(:fireball)
|
@@ -261,7 +261,7 @@ class Measured::MeasurableTest < ActiveSupport::TestCase
|
|
261
261
|
|
262
262
|
test "#<=> doesn't compare against zero" do
|
263
263
|
assert_nil @magic <=> 0
|
264
|
-
assert_nil @magic <=> BigDecimal
|
264
|
+
assert_nil @magic <=> BigDecimal(0)
|
265
265
|
assert_nil @magic <=> 0.00
|
266
266
|
end
|
267
267
|
|
@@ -279,7 +279,7 @@ class Measured::MeasurableTest < ActiveSupport::TestCase
|
|
279
279
|
test "#== doesn't compare against zero" do
|
280
280
|
arcane_zero = Magic.new(0, :arcane)
|
281
281
|
refute_equal arcane_zero, 0
|
282
|
-
refute_equal arcane_zero, BigDecimal
|
282
|
+
refute_equal arcane_zero, BigDecimal(0)
|
283
283
|
refute_equal arcane_zero, 0.0
|
284
284
|
end
|
285
285
|
|
@@ -295,10 +295,10 @@ class Measured::MeasurableTest < ActiveSupport::TestCase
|
|
295
295
|
|
296
296
|
test "#> and #< should not compare against zero" do
|
297
297
|
assert_raises(ArgumentError) { @magic > 0 }
|
298
|
-
assert_raises(ArgumentError) { @magic > BigDecimal
|
298
|
+
assert_raises(ArgumentError) { @magic > BigDecimal(0) }
|
299
299
|
assert_raises(ArgumentError) { @magic > 0.00 }
|
300
300
|
assert_raises(ArgumentError) { @magic < 0 }
|
301
|
-
assert_raises(ArgumentError) { @magic < BigDecimal
|
301
|
+
assert_raises(ArgumentError) { @magic < BigDecimal(0) }
|
302
302
|
assert_raises(ArgumentError) { @magic < 0.00 }
|
303
303
|
end
|
304
304
|
end
|
data/test/parser_test.rb
CHANGED
data/test/support/fake_system.rb
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Extract the subclasses that exist early on because other classes will get added by tests
|
3
|
+
# later on in execution and in an unpredictable order.
|
4
|
+
class ActiveSupport::TestCase
|
5
|
+
cattr_accessor :measurable_subclasses
|
6
|
+
self.measurable_subclasses = Measured::Measurable.subclasses
|
7
|
+
end
|