m9t 0.3.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,6 @@
1
+ /bin/
2
+ /coverage/
3
+ /Gemfile.lock
4
+ /pkg/
5
+ /vendor/bundle/
6
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.2.0
7
+ - 2.3.0
8
+ - 2.4.0
9
+ - jruby-19mode
10
+ branches:
11
+ only:
12
+ - master
13
+ script: "bundle exec rake spec"
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
2
 
3
- gemspec :name => 'm9t'
3
+ gemspec name: "m9t"
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
- m9t [![Build Status](https://secure.travis-ci.org/joeyates/m9t.png)][Continuous Integration]
2
- ===
1
+ [![Build Status](https://secure.travis-ci.org/joeyates/m9t.png)][Continuous Integration]
2
+ [![Source Analysis](https://codeclimate.com/github/joeyates/m9t/badges/gpa.svg)](https://codeclimate.com/github/joeyates/m9t)
3
+ [![Test Coverage](https://codeclimate.com/github/joeyates/m9t/badges/coverage.svg)](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
- height = M9t::Distance.new(1.75)
48
+ ```ruby
49
+ height = M9t::Distance.new(1.75)
50
+ ```
46
51
 
47
52
  to_f: returns the decimal value(s):
48
53
 
49
- height.to_f -> 1.75
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
- marathon = M9t::Distance.miles(26.21875)
57
- marathon.to_f -> 42194.988
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
- I18n.locale = :it
62
- puts M9t::Distance.new(3).to_s -> '3 metri'
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
- M9t::Distance.miles_to_meters(26.21875) -> 42194.988
70
-
71
- Testing
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
- require 'bundler'
2
- require 'rake/testtask'
1
+ #!/usr/bin/env rake
3
2
 
4
- $:.unshift( File.dirname(__FILE__) + '/lib' )
5
- require 'm9t'
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
6
5
 
7
- task :default => :test
6
+ task default: :spec
8
7
 
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
-
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
- locales_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'locales'))
28
- I18n.load_path += Dir.glob("#{ locales_path }/*.yml")
29
- I18n.reload!
26
+ module M9t; end
30
27
 
31
- libs = %w( base direction distance i18n pressure speed temperature version )
32
- libs.each do | library |
33
- require "m9t/#{ library }"
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
- # encoding: utf-8
2
- require 'i18n'
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 |klass|
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
- # 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])
18
24
  end
19
- return send(name, args[0]) if define_constructor(name)
20
- raise "Method '#{ name }' unknown" # TODO use standard exception
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("#{ from }_to_#{ to }") do |value|
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
- true
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( name.to_sym ) do | *args |
42
- new( args[ 0 ].to_f / self::CONVERSIONS[ name ] )
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 classes current options - see the specific class for defaults
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' default options
84
+ # Reloads the class"s default options
64
85
  def reset_options!
65
- @options = self::DEFAULT_OPTIONS.clone # 'self::' is necessary with ruby 1.8
86
+ @options = self::DEFAULT_OPTIONS.clone
66
87
  end
67
88
 
68
89
  # The name used for i18n translations
69
- # M9t::Distance => 'distance'
90
+ # M9t::Distance => "distance"
70
91
  def measurement_name
71
- name.split('::')[-1].downcase
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
- alias :to_f :value
110
+ alias_method :to_f, :value
90
111
 
91
- def initialize( value )
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
- if name.to_s =~ /^to_(\w+)$/
98
- return send(name) if define_conversion($1)
118
+ to = extract_to(name)
119
+ if to && legal_conversion?(to)
120
+ define_conversion(to)
121
+ return send(name)
99
122
  end
100
- raise "Method '#{ name }' unknown" # TODO use standard exception
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( options = {} )
106
- options = self.class.options.merge( options )
107
- units_error( options[ :units ] ) if not self.class::CONVERSIONS.has_key?( options[ :units ] )
108
- value_in_units = self.send("to_#{ options[:units] }")
109
- localized_value = I18n.localize_float(value_in_units, {:format => "%0.#{ options[:precision] }f"})
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 = 'units.' + self.class.measurement_name + '.' + options[:units].to_s
112
- options[:abbreviated] ? key += '.abbreviated' : key += '.full'
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
- "#{ localized_value }%s#{ unit }" % (options[:abbreviated] ? '' : ' ')
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
- raise M9t::UnitError.new("Unknown units '#{ units }'. Known: #{ self.class::CONVERSIONS.keys.collect{|unit| unit.to_s}.join(', ') }")
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_#{ to }") do
128
- self.class.convert(self.class.default_unit, to.to_sym, self.to_f)
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
- # encoding: utf-8
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 = {:units => :degrees, :abbreviated => false, :decimals => 5}
9
- CONVERSIONS = {
10
- :degrees => 1.0,
11
- :compass => nil,
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) => 'NE'
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 + '.sectors')[sector]
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. 'N') and returns the equivalent M9t::Direction
34
- # M9t::Direction.compass('NE') => #<M9t::Direction:0xb76cc750 @value=45.0, @options={:units=>:degrees, :decimals=>5, :abbreviated=>false}>
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 + '.sectors').find_index(compass_direction)
37
- raise "Compass direction '#{ compass_direction }' not recognised" if sector.nil?
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
- case
45
- when degrees < 0
46
- normalize(degrees + CIRCLE)
47
- when degrees >= CIRCLE
48
- normalize(degrees - CIRCLE)
41
+ remainder = degrees.remainder(CIRCLE)
42
+ if remainder < 0
43
+ remainder + CIRCLE
49
44
  else
50
- degrees
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