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 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
+