m9t 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|