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 CHANGED
@@ -1,3 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec :name => 'm9t'
4
+
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
- height = M9t::Distance.new(1.75)
45
+ ```ruby
46
+ height = M9t::Distance.new(1.75)
47
+ ```
46
48
 
47
49
  to_f: returns the decimal value(s):
48
50
 
49
- height.to_f -> 1.75
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
- marathon = M9t::Distance.miles(26.21875)
57
- marathon.to_f -> 42194.988
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
- I18n.locale = :it
62
- puts M9t::Distance.new(3).to_s -> '3 metri'
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
- M9t::Distance.miles_to_meters(26.21875) -> 42194.988
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
- $ rake rcov
89
+ ```shell
90
+ $ rake rcov
91
+ ```
80
92
 
81
93
  ruby 1.9.x:
82
94
 
83
- $ COVERAGE=1 rake test
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
- require 'bundler'
2
- require 'rake/testtask'
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
- $:.unshift( File.dirname(__FILE__) + '/lib' )
5
- require 'm9t'
6
+ task :default => :spec
6
7
 
7
- task :default => :test
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
- require 'rcov/rcovtask'
17
- Rcov::RcovTask.new do |t|
18
- t.test_files = FileList['test/*_test.rb']
19
- t.rcov_opts << '--exclude /gems/'
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
-
@@ -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
- # Define class conversion methods as required
16
- if name.to_s =~ /^(\w+)_to_(\w+)$/
17
- return send(name, args[0]) if define_conversion($1, $2)
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 send(name, args[0]) if define_constructor(name)
20
- raise "Method '#{ name }' unknown" # TODO use standard exception
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
- true
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
- if name.to_s =~ /^to_(\w+)$/
98
- return send(name) if define_conversion($1)
123
+ to = extract_to(name)
124
+ if to and legal_conversion?(to)
125
+ define_conversion(to)
126
+ return send(name)
99
127
  end
100
- raise "Method '#{ name }' unknown" # TODO use standard exception
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_#{ to }") do
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
+
@@ -3,7 +3,7 @@ module M9t
3
3
  module VERSION #:nodoc:
4
4
  MAJOR = 0
5
5
  MINOR = 3
6
- TINY = 3
6
+ TINY = 4
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  end
@@ -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
@@ -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 'rcov' if RUBY_VERSION < '1.9'
27
- s.add_development_dependency 'simplecov' if RUBY_VERSION > '1.9'
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
+
@@ -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
+