m9t 0.3.2 → 1.0.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 +7 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/Gemfile +2 -2
- data/README.md +28 -23
- data/Rakefile +6 -28
- data/lib/m9t.rb +5 -21
- data/lib/m9t/base.rb +86 -43
- data/lib/m9t/direction.rb +15 -23
- data/lib/m9t/distance.rb +9 -12
- data/lib/m9t/errors.rb +9 -0
- data/lib/m9t/i18n.rb +19 -11
- data/lib/m9t/pressure.rb +7 -13
- data/lib/m9t/speed.rb +12 -15
- data/lib/m9t/temperature.rb +13 -19
- data/lib/m9t/version.rb +4 -7
- data/locales/de.yml +68 -0
- data/locales/en.yml +13 -0
- data/m9t.gemspec +19 -17
- data/spec/base_spec.rb +79 -0
- data/spec/direction_spec.rb +195 -0
- data/spec/distance_spec.rb +127 -0
- data/spec/i18n_spec.rb +54 -0
- data/spec/pressure_spec.rb +22 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/speed_spec.rb +102 -0
- data/spec/temperature_spec.rb +124 -0
- metadata +67 -47
- data/test/base_test.rb +0 -55
- data/test/direction_test.rb +0 -106
- data/test/distance_test.rb +0 -161
- data/test/i18n_test.rb +0 -30
- data/test/pressure_test.rb +0 -25
- data/test/speed_test.rb +0 -104
- data/test/temperature_test.rb +0 -111
- data/test/test_helper.rb +0 -17
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1e9e5be154c21f5d10e86c7c6f906f5bdd2287c56797965f8430c12fbcee09b2
|
4
|
+
data.tar.gz: c23bd6fe57b417fc46680c9c91dfe7517ea0833e1ec76f6cd959df5466a02028
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 12f3a7e3428384abfa69f90c2d7210dff7ba94494ae82e14b0be892159de160722ad60fd16502e68b699e228a1206af122246ff3c2062c24e14d942d0e90831f
|
7
|
+
data.tar.gz: 0b2fd9ec442f4dcaf8d992a3d301404d91c4ce6da76972affe6c40deb152f034cc6cc76a752ad921a7328748d3e4b05a40ee50edfe7675e687856fc5dcac58f1
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
|
-
gemspec :
|
3
|
+
gemspec name: "m9t"
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
[][Continuous Integration]
|
2
|
+
[](https://codeclimate.com/github/joeyates/m9t)
|
3
|
+
[](https://codeclimate.com/github/joeyates/m9t/coverage)
|
4
|
+
|
5
|
+
# m9t
|
3
6
|
|
4
7
|
*Measurements and coversions library for Ruby*
|
5
8
|
|
@@ -42,45 +45,41 @@ Interface
|
|
42
45
|
|
43
46
|
new: accepts the S.I. unit as a parameter:
|
44
47
|
|
45
|
-
|
48
|
+
```ruby
|
49
|
+
height = M9t::Distance.new(1.75)
|
50
|
+
```
|
46
51
|
|
47
52
|
to_f: returns the decimal value(s):
|
48
53
|
|
49
|
-
|
54
|
+
```ruby
|
55
|
+
height.to_f -> 1.75
|
56
|
+
```
|
50
57
|
|
51
58
|
other units:
|
52
59
|
there are class methods named after each known unit,
|
53
60
|
which take values in that unit
|
54
61
|
(actually, they are defined as needed):
|
55
62
|
|
56
|
-
|
57
|
-
|
63
|
+
```ruby
|
64
|
+
marathon = M9t::Distance.miles(26.21875)
|
65
|
+
marathon.to_f -> 42194.988
|
66
|
+
```
|
58
67
|
|
59
68
|
to_s: returns a localized string with units:
|
60
69
|
|
61
|
-
|
62
|
-
|
70
|
+
```ruby
|
71
|
+
I18n.locale = :it
|
72
|
+
puts M9t::Distance.new(3).to_s -> '3 metri'
|
73
|
+
```
|
63
74
|
|
64
75
|
Class methods for conversion
|
65
76
|
============================
|
66
77
|
|
67
78
|
Methods are available for conversion between any pair of units:
|
68
79
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
=======
|
73
|
-
|
74
|
-
Coverage
|
75
|
-
--------
|
76
|
-
|
77
|
-
ruby 1.8.x:
|
78
|
-
|
79
|
-
$ rake rcov
|
80
|
-
|
81
|
-
ruby 1.9.x:
|
82
|
-
|
83
|
-
$ COVERAGE=1 rake test
|
80
|
+
```ruby
|
81
|
+
M9t::Distance.miles_to_meters(26.21875) -> 42194.988
|
82
|
+
```
|
84
83
|
|
85
84
|
Alternatives
|
86
85
|
============
|
@@ -91,6 +90,12 @@ Alternatives
|
|
91
90
|
- Monkey patches a lot of core classes:
|
92
91
|
- Adds methods to e.g. Object.
|
93
92
|
|
93
|
+
Contributors
|
94
|
+
============
|
95
|
+
|
96
|
+
* [Joe Yates](https://github.com/joeyates)
|
97
|
+
* [Florian Egermann and Mathias Wollin](https://github.com/math)
|
98
|
+
|
94
99
|
License
|
95
100
|
=======
|
96
101
|
|
data/Rakefile
CHANGED
@@ -1,32 +1,10 @@
|
|
1
|
-
|
2
|
-
require 'rake/testtask'
|
1
|
+
#!/usr/bin/env rake
|
3
2
|
|
4
|
-
|
5
|
-
require
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
6
5
|
|
7
|
-
task :
|
6
|
+
task default: :spec
|
8
7
|
|
9
|
-
|
10
|
-
t.
|
11
|
-
t.test_files = FileList['test/*_test.rb']
|
12
|
-
t.verbose = true
|
8
|
+
RSpec::Core::RakeTask.new do |t|
|
9
|
+
t.pattern = "spec/**/*_spec.rb"
|
13
10
|
end
|
14
|
-
|
15
|
-
if RUBY_VERSION < '1.9'
|
16
|
-
require 'rcov/rcovtask'
|
17
|
-
Rcov::RcovTask.new do |t|
|
18
|
-
t.test_files = FileList['test/*_test.rb']
|
19
|
-
t.rcov_opts << '--exclude /gems/'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
desc "Build the gem"
|
24
|
-
task :build do
|
25
|
-
`gem build m9t.gemspec`
|
26
|
-
end
|
27
|
-
|
28
|
-
desc "Publish a new version of the gem"
|
29
|
-
task :release => :build do
|
30
|
-
`gem push m9t-#{M9t::VERSION::STRING}.gem`
|
31
|
-
end
|
32
|
-
|
data/lib/m9t.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2010 Joe Yates
|
2
|
+
# Copyright (c) 2010-2021 Joe Yates
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -22,26 +22,10 @@
|
|
22
22
|
#++
|
23
23
|
|
24
24
|
# encoding: utf-8
|
25
|
-
require 'i18n'
|
26
25
|
|
27
|
-
|
28
|
-
I18n.load_path += Dir.glob("#{ locales_path }/*.yml")
|
29
|
-
I18n.reload!
|
26
|
+
module M9t; end
|
30
27
|
|
31
|
-
libs = %w( base direction distance
|
32
|
-
libs.each do |
|
33
|
-
require "m9t/#{
|
28
|
+
libs = %w(i18n base direction distance pressure speed temperature version)
|
29
|
+
libs.each do |library|
|
30
|
+
require "m9t/#{library}"
|
34
31
|
end
|
35
|
-
|
36
|
-
module M9t
|
37
|
-
|
38
|
-
# Base class for all M9t exceptions
|
39
|
-
class M9tError < StandardError
|
40
|
-
end
|
41
|
-
|
42
|
-
# Raised when a M9t class receives an unrecogized ':units' value
|
43
|
-
class UnitError < M9tError
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
data/lib/m9t/base.rb
CHANGED
@@ -1,45 +1,65 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
require "m9t/errors"
|
2
|
+
require "m9t/i18n"
|
3
3
|
|
4
4
|
module M9t
|
5
|
-
|
6
5
|
module Base
|
7
|
-
|
8
6
|
def self.generate_conversions(klass)
|
9
|
-
klass.instance_eval do
|
7
|
+
klass.instance_eval do
|
10
8
|
def convert(from, to, value)
|
11
9
|
value / self::CONVERSIONS[from] * self::CONVERSIONS[to]
|
12
10
|
end
|
13
11
|
|
12
|
+
# Define class conversion methods as required
|
14
13
|
def method_missing(name, *args, &block)
|
15
|
-
|
16
|
-
if
|
17
|
-
|
14
|
+
from, to = extract_from_and_to(name)
|
15
|
+
if from
|
16
|
+
if legal_conversion?(from, to)
|
17
|
+
define_conversion(from, to)
|
18
|
+
return send(name, args[0])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
if legal_constructor?(name)
|
22
|
+
define_constructor(name)
|
23
|
+
return send(name, args[0])
|
18
24
|
end
|
19
|
-
|
20
|
-
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(name, _include_all = false)
|
29
|
+
from, to = extract_from_and_to(name)
|
30
|
+
return true if from && legal_conversion?(from, to)
|
31
|
+
legal_constructor?(name)
|
21
32
|
end
|
22
33
|
|
23
34
|
private
|
24
35
|
|
36
|
+
def extract_from_and_to(name)
|
37
|
+
name.to_s.scan(/^(\w+)_to_(\w+)$/)[0]
|
38
|
+
end
|
39
|
+
|
40
|
+
def legal_conversion?(from, to)
|
41
|
+
self::CONVERSIONS.include?(from.to_sym) &&
|
42
|
+
self::CONVERSIONS.include?(to.to_sym)
|
43
|
+
end
|
44
|
+
|
25
45
|
def define_conversion(from, to)
|
26
|
-
return false if not self::CONVERSIONS[from.to_sym]
|
27
|
-
return false if not self::CONVERSIONS[to.to_sym]
|
28
46
|
self.class.instance_exec do
|
29
|
-
define_method("#{
|
47
|
+
define_method("#{from}_to_#{to}") do |value|
|
30
48
|
convert(from.to_sym, to.to_sym, value)
|
31
49
|
end
|
32
50
|
end
|
33
|
-
|
51
|
+
end
|
52
|
+
|
53
|
+
def legal_constructor?(name)
|
54
|
+
self::CONVERSIONS.include?(name.to_sym)
|
34
55
|
end
|
35
56
|
|
36
57
|
# Define klass.unit(value) which converts the parameter
|
37
58
|
# from the unit and returns an instance
|
38
59
|
def define_constructor(name)
|
39
|
-
return false if not self::CONVERSIONS[name.to_sym]
|
40
60
|
self.class.instance_exec do
|
41
|
-
define_method(
|
42
|
-
new(
|
61
|
+
define_method(name.to_sym) do |*args|
|
62
|
+
new(args[0].to_f / self::CONVERSIONS[name])
|
43
63
|
end
|
44
64
|
end
|
45
65
|
end
|
@@ -55,20 +75,21 @@ module M9t
|
|
55
75
|
end
|
56
76
|
end
|
57
77
|
|
58
|
-
# Returns the
|
78
|
+
# Returns the class's current options - see the specific class for
|
79
|
+
# defaults
|
59
80
|
def options
|
60
81
|
@options
|
61
82
|
end
|
62
83
|
|
63
|
-
# Reloads the class
|
84
|
+
# Reloads the class"s default options
|
64
85
|
def reset_options!
|
65
|
-
@options = self::DEFAULT_OPTIONS.clone
|
86
|
+
@options = self::DEFAULT_OPTIONS.clone
|
66
87
|
end
|
67
88
|
|
68
89
|
# The name used for i18n translations
|
69
|
-
# M9t::Distance =>
|
90
|
+
# M9t::Distance => "distance"
|
70
91
|
def measurement_name
|
71
|
-
name.split(
|
92
|
+
name.split("::")[-1].downcase
|
72
93
|
end
|
73
94
|
|
74
95
|
def default_unit
|
@@ -86,51 +107,73 @@ module M9t
|
|
86
107
|
end
|
87
108
|
|
88
109
|
attr_reader :value, :options
|
89
|
-
|
110
|
+
alias_method :to_f, :value
|
90
111
|
|
91
|
-
def initialize(
|
112
|
+
def initialize(value)
|
92
113
|
@value = value.to_f
|
93
114
|
end
|
94
115
|
|
95
116
|
# define conversion instance methods as required
|
96
117
|
def method_missing(name, *args, &block)
|
97
|
-
|
98
|
-
|
118
|
+
to = extract_to(name)
|
119
|
+
if to && legal_conversion?(to)
|
120
|
+
define_conversion(to)
|
121
|
+
return send(name)
|
99
122
|
end
|
100
|
-
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
def respond_to?(name, _include_all = false)
|
127
|
+
to = extract_to(name)
|
128
|
+
return true if to && legal_conversion?(to)
|
129
|
+
super
|
101
130
|
end
|
102
131
|
|
103
132
|
# Returns the string representation of the measurement,
|
104
133
|
# taking into account locale, desired units and abbreviation.
|
105
|
-
def to_s(
|
106
|
-
options = self.class.options.merge(
|
107
|
-
|
108
|
-
|
109
|
-
|
134
|
+
def to_s(options = {})
|
135
|
+
options = self.class.options.merge(options)
|
136
|
+
unless self.class::CONVERSIONS.include?(options[:units])
|
137
|
+
units_error(options[:units])
|
138
|
+
end
|
139
|
+
value_in_units = send("to_#{options[:units]}")
|
140
|
+
localized_value = I18n.localize_float(
|
141
|
+
value_in_units, format: "%0.#{options[:precision]}f"
|
142
|
+
)
|
110
143
|
|
111
|
-
key =
|
112
|
-
|
113
|
-
unit = I18n.t(key, {:count => value_in_units})
|
144
|
+
key = i18n_key(options)
|
145
|
+
unit = I18n.t(key, count: value_in_units)
|
114
146
|
|
115
|
-
"#{
|
147
|
+
"#{localized_value}%s#{unit}" % (options[:abbreviated] ? "" : " ")
|
116
148
|
end
|
117
149
|
|
118
150
|
private
|
119
151
|
|
152
|
+
def i18n_key(options = {})
|
153
|
+
key = "units.#{self.class.measurement_name}.#{options[:units]}"
|
154
|
+
key += options[:abbreviated] ? ".abbreviated" : ".full"
|
155
|
+
key
|
156
|
+
end
|
157
|
+
|
120
158
|
def units_error(units)
|
121
|
-
|
159
|
+
known = self.class::CONVERSIONS.keys.collect(&:to_s).join(", ")
|
160
|
+
fail M9t::UnitError, %Q(Unknown units "#{units}". Known: #{known})
|
161
|
+
end
|
162
|
+
|
163
|
+
def extract_to(name)
|
164
|
+
name.to_s[/^to_(\w+)$/, 1]
|
165
|
+
end
|
166
|
+
|
167
|
+
def legal_conversion?(to)
|
168
|
+
self.class::CONVERSIONS.include?(to.to_sym)
|
122
169
|
end
|
123
170
|
|
124
171
|
def define_conversion(to)
|
125
|
-
return false if not self.class::CONVERSIONS[to.to_sym]
|
126
172
|
self.class.instance_exec do
|
127
|
-
define_method("to_#{
|
128
|
-
self.class.convert(self.class.default_unit, to.to_sym,
|
173
|
+
define_method("to_#{to}") do
|
174
|
+
self.class.convert(self.class.default_unit, to.to_sym, to_f)
|
129
175
|
end
|
130
176
|
end
|
131
|
-
true
|
132
177
|
end
|
133
|
-
|
134
178
|
end
|
135
|
-
|
136
179
|
end
|
data/lib/m9t/direction.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
|
2
|
-
require 'i18n'
|
1
|
+
require "m9t/base"
|
3
2
|
|
4
3
|
module M9t
|
5
|
-
|
6
4
|
# Represents a geographical direction
|
7
5
|
class Direction
|
8
|
-
DEFAULT_OPTIONS = {:
|
9
|
-
CONVERSIONS
|
10
|
-
:
|
11
|
-
:
|
6
|
+
DEFAULT_OPTIONS = {units: :degrees, abbreviated: false, decimals: 5}
|
7
|
+
CONVERSIONS = {
|
8
|
+
degrees: 1.0,
|
9
|
+
compass: nil,
|
12
10
|
}
|
13
11
|
|
14
12
|
# Conversions
|
@@ -18,39 +16,35 @@ module M9t
|
|
18
16
|
include M9t::Base
|
19
17
|
|
20
18
|
class << self
|
21
|
-
|
22
19
|
# Given a value in degrees, returns the nearest (localized) compass direction
|
23
|
-
# M9t::Directions.to_compass(42) =>
|
20
|
+
# M9t::Directions.to_compass(42) => "NE"
|
24
21
|
def degrees_to_compass(degrees)
|
25
22
|
sector = (normalize(degrees) / COMPASS_SECTOR_DEGREES).round
|
26
|
-
I18n.t(self.measurement_name +
|
23
|
+
I18n.t(self.measurement_name + ".sectors")[sector]
|
27
24
|
end
|
28
25
|
|
29
26
|
def compass_to_degrees(compass_direction)
|
30
27
|
compass(compass_direction).to_f
|
31
28
|
end
|
32
29
|
|
33
|
-
# Accepts a localized compass direction (e.g.
|
34
|
-
# M9t::Direction.compass(
|
30
|
+
# Accepts a localized compass direction (e.g. "N") and returns the equivalent M9t::Direction
|
31
|
+
# M9t::Direction.compass("NE") => #<M9t::Direction:0x000000014a438618 @value=45.0>
|
35
32
|
def compass(compass_direction)
|
36
|
-
sector = I18n.t(self.measurement_name +
|
37
|
-
raise "Compass direction '#{
|
33
|
+
sector = I18n.t(self.measurement_name + ".sectors").find_index(compass_direction)
|
34
|
+
raise "Compass direction '#{compass_direction}' not recognised" if sector.nil?
|
38
35
|
new(sector.to_f * COMPASS_SECTOR_DEGREES)
|
39
36
|
end
|
40
37
|
|
41
38
|
# Reduce directions in degrees to the range [0, 360)
|
42
39
|
# M9t::Direction.normalize(1000) => 280.0
|
43
40
|
def normalize(degrees)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
when degrees >= CIRCLE
|
48
|
-
normalize(degrees - CIRCLE)
|
41
|
+
remainder = degrees.remainder(CIRCLE)
|
42
|
+
if remainder < 0
|
43
|
+
remainder + CIRCLE
|
49
44
|
else
|
50
|
-
|
45
|
+
remainder
|
51
46
|
end
|
52
47
|
end
|
53
|
-
|
54
48
|
end
|
55
49
|
|
56
50
|
# Handles the special case where compass directions are the desired output.
|
@@ -65,7 +59,5 @@ module M9t
|
|
65
59
|
def to_compass
|
66
60
|
self.class.degrees_to_compass(@value)
|
67
61
|
end
|
68
|
-
|
69
62
|
end
|
70
|
-
|
71
63
|
end
|