m9t 0.3.2 → 1.0.0

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