m9t 0.3.3 → 0.3.4
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.
- data/Gemfile +2 -1
- data/README.md +25 -10
- data/Rakefile +11 -24
- data/lib/m9t/base.rb +62 -19
- data/lib/m9t/version.rb +1 -1
- data/locales/en.yml +13 -0
- data/m9t.gemspec +9 -2
- data/spec/spec_helper.rb +23 -0
- data/spec/unit/base_spec.rb +53 -0
- data/spec/unit/direction_spec.rb +185 -0
- data/spec/unit/distance_spec.rb +129 -0
- data/spec/unit/i18n_spec.rb +56 -0
- data/spec/unit/pressure_spec.rb +24 -0
- data/spec/unit/speed_spec.rb +104 -0
- data/spec/unit/temperature_spec.rb +111 -0
- metadata +29 -20
- 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 -42
- 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
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -42,31 +42,41 @@ Interface
|
|
42
42
|
|
43
43
|
new: accepts the S.I. unit as a parameter:
|
44
44
|
|
45
|
-
|
45
|
+
```ruby
|
46
|
+
height = M9t::Distance.new(1.75)
|
47
|
+
```
|
46
48
|
|
47
49
|
to_f: returns the decimal value(s):
|
48
50
|
|
49
|
-
|
51
|
+
```ruby
|
52
|
+
height.to_f -> 1.75
|
53
|
+
```
|
50
54
|
|
51
55
|
other units:
|
52
56
|
there are class methods named after each known unit,
|
53
57
|
which take values in that unit
|
54
58
|
(actually, they are defined as needed):
|
55
59
|
|
56
|
-
|
57
|
-
|
60
|
+
```ruby
|
61
|
+
marathon = M9t::Distance.miles(26.21875)
|
62
|
+
marathon.to_f -> 42194.988
|
63
|
+
```
|
58
64
|
|
59
65
|
to_s: returns a localized string with units:
|
60
66
|
|
61
|
-
|
62
|
-
|
67
|
+
```ruby
|
68
|
+
I18n.locale = :it
|
69
|
+
puts M9t::Distance.new(3).to_s -> '3 metri'
|
70
|
+
```
|
63
71
|
|
64
72
|
Class methods for conversion
|
65
73
|
============================
|
66
74
|
|
67
75
|
Methods are available for conversion between any pair of units:
|
68
76
|
|
69
|
-
|
77
|
+
```ruby
|
78
|
+
M9t::Distance.miles_to_meters(26.21875) -> 42194.988
|
79
|
+
```
|
70
80
|
|
71
81
|
Testing
|
72
82
|
=======
|
@@ -74,13 +84,17 @@ Testing
|
|
74
84
|
Coverage
|
75
85
|
--------
|
76
86
|
|
77
|
-
ruby 1.8.x:
|
87
|
+
ruby 1.8.x (not jruby):
|
78
88
|
|
79
|
-
|
89
|
+
```shell
|
90
|
+
$ rake rcov
|
91
|
+
```
|
80
92
|
|
81
93
|
ruby 1.9.x:
|
82
94
|
|
83
|
-
|
95
|
+
```shell
|
96
|
+
$ COVERAGE=1 rake test
|
97
|
+
```
|
84
98
|
|
85
99
|
Alternatives
|
86
100
|
============
|
@@ -104,3 +118,4 @@ Dual license:
|
|
104
118
|
|
105
119
|
- MIT License: see MIT-LICENSE.txt,
|
106
120
|
- GPL version 3: see GPL-LICENSE.txt
|
121
|
+
|
data/Rakefile
CHANGED
@@ -1,32 +1,19 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rcov/rcovtask' if RUBY_VERSION < '1.9'
|
4
|
+
require 'rspec/core/rake_task'
|
3
5
|
|
4
|
-
|
5
|
-
require 'm9t'
|
6
|
+
task :default => :spec
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
Rake::TestTask.new do |t|
|
10
|
-
t.libs << 'test'
|
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
11
|
|
15
12
|
if RUBY_VERSION < '1.9'
|
16
|
-
|
17
|
-
|
18
|
-
t.
|
19
|
-
t.rcov_opts
|
13
|
+
RSpec::Core::RakeTask.new('spec:rcov') do |t|
|
14
|
+
t.pattern = 'spec/**/*_spec.rb'
|
15
|
+
t.rcov = true
|
16
|
+
t.rcov_opts = ['--exclude', 'spec/,/gems/']
|
20
17
|
end
|
21
18
|
end
|
22
19
|
|
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/base.rb
CHANGED
@@ -2,41 +2,67 @@
|
|
2
2
|
require 'i18n'
|
3
3
|
|
4
4
|
module M9t
|
5
|
-
|
6
5
|
module Base
|
7
|
-
|
8
6
|
def self.generate_conversions(klass)
|
9
7
|
klass.instance_eval do |klass|
|
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])
|
24
|
+
end
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(name, include_all = false)
|
29
|
+
from, to = extract_from_and_to(name)
|
30
|
+
if from
|
31
|
+
return true if legal_conversion?(from, to)
|
18
32
|
end
|
19
|
-
return
|
20
|
-
|
33
|
+
return legal_constructor?(name)
|
34
|
+
super
|
21
35
|
end
|
22
36
|
|
23
37
|
private
|
24
38
|
|
39
|
+
def extract_from_and_to(name)
|
40
|
+
if name.to_s =~ /^(\w+)_to_(\w+)$/
|
41
|
+
[$1, $2]
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def legal_conversion?(from, to)
|
48
|
+
self::CONVERSIONS.include?(from.to_sym) and self::CONVERSIONS.include?(to.to_sym)
|
49
|
+
end
|
50
|
+
|
25
51
|
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
52
|
self.class.instance_exec do
|
29
53
|
define_method("#{ from }_to_#{ to }") do |value|
|
30
54
|
convert(from.to_sym, to.to_sym, value)
|
31
55
|
end
|
32
56
|
end
|
33
|
-
|
57
|
+
end
|
58
|
+
|
59
|
+
def legal_constructor?(name)
|
60
|
+
self::CONVERSIONS.include?(name.to_sym)
|
34
61
|
end
|
35
62
|
|
36
63
|
# Define klass.unit(value) which converts the parameter
|
37
64
|
# from the unit and returns an instance
|
38
65
|
def define_constructor(name)
|
39
|
-
return false if not self::CONVERSIONS[name.to_sym]
|
40
66
|
self.class.instance_exec do
|
41
67
|
define_method( name.to_sym ) do | *args |
|
42
68
|
new( args[ 0 ].to_f / self::CONVERSIONS[ name ] )
|
@@ -94,10 +120,18 @@ module M9t
|
|
94
120
|
|
95
121
|
# define conversion instance methods as required
|
96
122
|
def method_missing(name, *args, &block)
|
97
|
-
|
98
|
-
|
123
|
+
to = extract_to(name)
|
124
|
+
if to and legal_conversion?(to)
|
125
|
+
define_conversion(to)
|
126
|
+
return send(name)
|
99
127
|
end
|
100
|
-
|
128
|
+
super
|
129
|
+
end
|
130
|
+
|
131
|
+
def respond_to?(name, include_all = false)
|
132
|
+
to = extract_to(name)
|
133
|
+
return true if to and legal_conversion?(to)
|
134
|
+
super
|
101
135
|
end
|
102
136
|
|
103
137
|
# Returns the string representation of the measurement,
|
@@ -121,16 +155,25 @@ module M9t
|
|
121
155
|
raise M9t::UnitError.new("Unknown units '#{ units }'. Known: #{ self.class::CONVERSIONS.keys.collect{|unit| unit.to_s}.join(', ') }")
|
122
156
|
end
|
123
157
|
|
158
|
+
def extract_to(name)
|
159
|
+
if name.to_s =~ /^to_(\w+)$/
|
160
|
+
$1
|
161
|
+
else
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def legal_conversion?(to)
|
167
|
+
self.class::CONVERSIONS.include?(to.to_sym)
|
168
|
+
end
|
169
|
+
|
124
170
|
def define_conversion(to)
|
125
|
-
return false if not self.class::CONVERSIONS[to.to_sym]
|
126
171
|
self.class.instance_exec do
|
127
|
-
define_method("to_#{
|
172
|
+
define_method("to_#{to}") do
|
128
173
|
self.class.convert(self.class.default_unit, to.to_sym, self.to_f)
|
129
174
|
end
|
130
175
|
end
|
131
|
-
true
|
132
176
|
end
|
133
|
-
|
134
177
|
end
|
135
|
-
|
136
178
|
end
|
179
|
+
|
data/lib/m9t/version.rb
CHANGED
data/locales/en.yml
CHANGED
@@ -22,6 +22,7 @@ en:
|
|
22
22
|
zero: miles
|
23
23
|
one: mile
|
24
24
|
other: miles
|
25
|
+
abbreviated: mi
|
25
26
|
feet:
|
26
27
|
full:
|
27
28
|
one: foot
|
@@ -35,6 +36,12 @@ en:
|
|
35
36
|
other: degrees
|
36
37
|
abbreviated: °
|
37
38
|
speed:
|
39
|
+
miles_per_hour:
|
40
|
+
full:
|
41
|
+
zero: miles per hour
|
42
|
+
one: mile per hour
|
43
|
+
other: miles per hour
|
44
|
+
abbreviated: mph
|
38
45
|
meters_per_second:
|
39
46
|
full:
|
40
47
|
zero: meters per second
|
@@ -52,6 +59,12 @@ en:
|
|
52
59
|
one: knot
|
53
60
|
other: knots
|
54
61
|
temperature:
|
62
|
+
fahrenheit:
|
63
|
+
full:
|
64
|
+
zero: degrees
|
65
|
+
one: degree
|
66
|
+
other: degrees
|
67
|
+
abbreviated: °F
|
55
68
|
degrees:
|
56
69
|
full:
|
57
70
|
zero: degrees
|
data/m9t.gemspec
CHANGED
@@ -23,6 +23,13 @@ gemspec = Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency 'rake'
|
24
24
|
s.add_dependency 'i18n', '>= 0.3.5'
|
25
25
|
|
26
|
-
s.add_development_dependency '
|
27
|
-
|
26
|
+
s.add_development_dependency 'rspec'
|
27
|
+
if RUBY_VERSION < '1.9'
|
28
|
+
if RUBY_PLATFORM != 'java'
|
29
|
+
s.add_development_dependency 'rcov'
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_development_dependency 'simplecov'
|
33
|
+
end
|
28
34
|
end
|
35
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
2
|
+
require 'rspec'
|
3
|
+
require 'rspec/autorun'
|
4
|
+
|
5
|
+
if RUBY_VERSION > '1.9'
|
6
|
+
require 'simplecov'
|
7
|
+
if ENV['COVERAGE']
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter '/spec/'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'm9t'
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
18
|
+
config.run_all_when_everything_filtered = true
|
19
|
+
config.filter_run :focus
|
20
|
+
|
21
|
+
config.order = 'random'
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
class SomeMeasurement
|
5
|
+
DEFAULT_OPTIONS = {
|
6
|
+
:units => :foos,
|
7
|
+
:abbreviated => false,
|
8
|
+
:decimals => 1
|
9
|
+
}
|
10
|
+
CONVERSIONS = {
|
11
|
+
:foos => 1.0,
|
12
|
+
:bars => 1 / 42.0
|
13
|
+
}
|
14
|
+
|
15
|
+
include M9t::Base
|
16
|
+
end
|
17
|
+
|
18
|
+
class SomeDerivedMeasurement < SomeMeasurement; end
|
19
|
+
|
20
|
+
describe M9t::Base do
|
21
|
+
describe '.method_missing' do
|
22
|
+
it 'handles conversion between known units' do
|
23
|
+
expect(SomeMeasurement.foos_to_bars(3.0)).to be_a(Float)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'fails for unknown units' do
|
27
|
+
expect {
|
28
|
+
SomeMeasurement.bazs_to_bars(3.0)
|
29
|
+
}.to raise_error(NoMethodError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#method_missing' do
|
34
|
+
subject { SomeMeasurement.new(3.0) }
|
35
|
+
|
36
|
+
it 'handles conversion to known units' do
|
37
|
+
expect(subject.to_bars).to be_a(Float)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'fails for unknown units' do
|
41
|
+
expect {
|
42
|
+
subject.to_bazs(3.0)
|
43
|
+
}.to raise_error(NoMethodError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'derived class' do
|
48
|
+
it 'inherits options' do
|
49
|
+
expect(SomeDerivedMeasurement.options).to eq(SomeMeasurement.options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe M9t::Direction do
|
5
|
+
before do
|
6
|
+
I18n.locale = :en
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.measurement_name' do
|
10
|
+
it "is 'direction'" do
|
11
|
+
expect(M9t::Direction.measurement_name).to eq('direction')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.options' do
|
16
|
+
it 'is set' do
|
17
|
+
expect(M9t::Direction.options).not_to be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'abbreviated' do
|
21
|
+
it 'is false' do
|
22
|
+
expect(M9t::Direction.options[:abbreviated]).to be_false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'units' do
|
27
|
+
it 'is degrees' do
|
28
|
+
expect(M9t::Direction.options[:units]).to eq(:degrees)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.normalize' do
|
34
|
+
it 'handles values > 360' do
|
35
|
+
expect(M9t::Direction.normalize(725)).to eq(5)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'handles values < 0' do
|
39
|
+
expect(M9t::Direction.normalize(-355)).to eq(5)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'conversion class methods' do
|
44
|
+
describe '.degrees_to_degrees' do
|
45
|
+
it 'returns the identity' do
|
46
|
+
expect(M9t::Direction.degrees_to_degrees(45)).to eq(45)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '.degrees_to_compass' do
|
51
|
+
before do
|
52
|
+
I18n.locale = :en
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'exact' do
|
56
|
+
[
|
57
|
+
'N', 'NNE', 'NE', 'ENE',
|
58
|
+
'E', 'ESE', 'SE', 'SSE',
|
59
|
+
'S', 'SSW', 'SW', 'WSW',
|
60
|
+
'W', 'WNW', 'NW', 'NNW'
|
61
|
+
].each.with_index do |result, i|
|
62
|
+
it "recognizes #{result}" do
|
63
|
+
expect(M9t::Direction.degrees_to_compass(i * 22.5)).to eq(result)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'rounding' do
|
69
|
+
specify 'up' do
|
70
|
+
expect(M9t::Direction.degrees_to_compass(42)).to eq('NE')
|
71
|
+
end
|
72
|
+
|
73
|
+
specify 'down' do
|
74
|
+
expect(M9t::Direction.degrees_to_compass(93)).to eq('E')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'i18n' do
|
79
|
+
before do
|
80
|
+
I18n.locale = :it
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'translates' do
|
84
|
+
expect(M9t::Direction.degrees_to_compass(270)).to eq('O')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'compass_to_degrees' do
|
90
|
+
it 'converts correctly' do
|
91
|
+
expect(M9t::Direction.compass_to_degrees('WSW')).to eq(247.5)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '.new' do
|
97
|
+
context 'strings' do
|
98
|
+
it 'works' do
|
99
|
+
expect(M9t::Direction.new('35').value).to eq(35)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'handles leading zeroes' do
|
103
|
+
expect(M9t::Direction.new('010').value).to eq(10)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '.compass' do
|
109
|
+
it 'converts cardinals' do
|
110
|
+
expect(M9t::Direction.compass('N').value).to eq(0)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'handles 16ths' do
|
114
|
+
expect(M9t::Direction.compass('WSW').value).to eq(247.5)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#value' do
|
119
|
+
let(:degrees) { M9t::Direction.new(45) }
|
120
|
+
|
121
|
+
it 'returns the supplied value' do
|
122
|
+
expect(degrees.value).to eq(45)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#to_s' do
|
127
|
+
context 'not abbreviated' do
|
128
|
+
context 'singular' do
|
129
|
+
subject { M9t::Direction.new(1) }
|
130
|
+
|
131
|
+
it 'returns the full unit name' do
|
132
|
+
expect(subject.to_s).to eq('1 degree')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'translates' do
|
136
|
+
I18n.locale = :it
|
137
|
+
expect(subject.to_s).to eq('1 grado')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'plural' do
|
142
|
+
subject { M9t::Direction.new(135) }
|
143
|
+
|
144
|
+
it 'returns the full unit name' do
|
145
|
+
expect(subject.to_s).to eq('135 degrees')
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'translates' do
|
149
|
+
I18n.locale = :it
|
150
|
+
expect(subject.to_s).to eq('135 gradi')
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'abbreviated' do
|
156
|
+
subject { M9t::Direction.new(135) }
|
157
|
+
|
158
|
+
it 'uses the symbol' do
|
159
|
+
expect(subject.to_s(:abbreviated => true)).to eq('135°')
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'compass units' do
|
164
|
+
subject { M9t::Direction.new(225) }
|
165
|
+
|
166
|
+
it 'works' do
|
167
|
+
expect(subject.to_s(:units => :compass)).to eq('SW')
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'translates' do
|
171
|
+
I18n.locale = :it
|
172
|
+
expect(subject.to_s(:units => :compass)).to eq('SO')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#to_compass' do
|
178
|
+
subject { M9t::Direction.new(0) }
|
179
|
+
|
180
|
+
it 'is correct' do
|
181
|
+
expect(subject.to_compass).to eq('N')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|