seamusabshere-conversions 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ module Conversions
2
+ # Implements new accessor classmethods to define conversion accessors on active record classes.
3
+ module ActiveRecordAccessors
4
+ # Adds conversion methods to the model for a certain attribute.
5
+ #
6
+ # Options:
7
+ #
8
+ # * <tt>:internal</tt>: The unit of the internal value
9
+ # * <tt>:external</tt>: The unit of desired external value
10
+ # * <tt>:scale</tt>: If a scale is given, the external value is automatically rounded on the specified scale.
11
+ #
12
+ #
13
+ # Examples:
14
+ #
15
+ # require 'conversions'
16
+ #
17
+ # class Flight
18
+ # extend Conversions::ActiveRecordAccessors
19
+ #
20
+ # attr_accessor :distance
21
+ # conversion_accessor :distance, :internal => :kilometers, :external => :miles, :scale => 2
22
+ #
23
+ # def initialize(distance)
24
+ # self.distance = distance
25
+ # end
26
+ # end
27
+ #
28
+ # flight = Flight.new(1200)
29
+ # flight.distance_in_miles #=> 745.65, rounded down to two decimals as specified in :scale
30
+ #
31
+ # When used as a plugin for Rails, the ActiveRecord::Base is automatically extended.
32
+ #
33
+ # class Car < ActiveRecord::Base
34
+ # conversion_accessor :length, :internal => :kilometers, :external => :miles
35
+ # end
36
+ def conversion_accessor(attribute, options={})
37
+ if options[:internal].nil? or options[:external].nil?
38
+ raise ArgumentError, "Please specify both :external and :internal metrics."
39
+ end
40
+ define_method "#{attribute}_in_#{options[:external]}" do
41
+ value = send(attribute)
42
+ value ? value.convert(options[:internal], options[:external], options.except(:internal, :external)) : nil
43
+ end
44
+ define_method "#{attribute}_in_#{options[:external]}=" do |v|
45
+ send("#{attribute}=", v.to_f.convert(options[:external], options[:internal], options.except(:internal, :external)))
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ {
2
+ :miles => {
3
+ :kilometres => 1.609344
4
+ },
5
+ :kilograms => {
6
+ :grams => 1000.0,
7
+ :pounds => 2.20462262,
8
+ :short_tons => 0.00110231131,
9
+ :tons => 0.00110231131
10
+ },
11
+ :tons => {
12
+ :pounds => 2000.0
13
+ },
14
+ :gallons => {
15
+ :litres => 3.7854118
16
+ },
17
+ :cubic_feet => {
18
+ :cubic_meters => 0.0283168466
19
+ },
20
+ :miles_per_gallon => {
21
+ :kilometres_per_litre => 0.425143707
22
+ }
23
+ }.each do |from_unit, to_units|
24
+ to_units.each do |to_unit, rate|
25
+ Conversions.register(from_unit, to_unit, rate)
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ module Conversions
2
+ # Proxy class to contain the unit as well as reference the base value
3
+ class Unit
4
+ # Create a new Unit instance.
5
+ #
6
+ # * _value_: The value to convert from (ie. 4.92)
7
+ # * _from_: The unit to convert from (ie. :miles)
8
+ def initialize(value, from)
9
+ @value = value
10
+ @from = from
11
+ end
12
+
13
+ # Convert to a certain other unit.
14
+ #
15
+ # * _to_: The unit to convert to (ie. :kilometers)
16
+ # * _options_:
17
+ # * :scale: The number of digits behind the decimal point to you want to keep
18
+ def to(to, options={})
19
+ case options
20
+ when Integer
21
+ scale = options
22
+ when Hash
23
+ scale = options[:scale]
24
+ end
25
+
26
+ value = @value * self.class.exchange_rate(@from, to)
27
+ scale.nil? ? value : (value * (10 ** scale)).round / (10 ** scale).to_f
28
+ end
29
+
30
+ def self.exchange_rate(from_unit, to_unit) #:nodoc:
31
+ return 1 if from_unit == to_unit
32
+ from = Conversions.conversions[from_unit]
33
+ raise ArgumentError, "Can't convert from `#{from}', unknown unit" if from.nil?
34
+ to = from[to_unit]
35
+ raise ArgumentError, "Can't convert from `#{from_unit}' to `#{to_unit}', unknown unit" if to.nil?
36
+ to
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,59 @@
1
+ # Conversions makes it easy to convert between units.
2
+ module Conversions
3
+ mattr_accessor :conversions
4
+
5
+ # Clear all previously registered conversions
6
+ def self.clear
7
+ self.conversions = {}
8
+ end
9
+ clear
10
+
11
+ # Load all the default conversions shipped with the code
12
+ def self.load_defaults
13
+ load File.expand_path('../conversions/defaults.rb', __FILE__)
14
+ end
15
+
16
+ # Register a new conversion. This automatically also registers the inverse conversion.
17
+ #
18
+ # * _from_: The unit to convert from (ie. :miles, :stones, or :pints)
19
+ # * _to_: The unit to convert to
20
+ # * _rate_: The conversion rate from _from_ to _to_. (_from_ * _rate_ = _to_)
21
+ def self.register(from, to, rate)
22
+ conversions[from] ||= {}
23
+ conversions[from][to] = rate
24
+ conversions[to] ||= {}
25
+ conversions[to][from] = 1.0 / rate
26
+ Conversions.define_shortcut(from)
27
+ Conversions.define_shortcut(to)
28
+ end
29
+
30
+ def self.define_shortcut(unit)
31
+ Numeric.class_eval do
32
+ define_method unit do
33
+ Conversions::Unit.new(self, unit)
34
+ end unless respond_to? unit
35
+ end
36
+ end
37
+
38
+ module Ext
39
+ # Convert from one unit to another.
40
+ #
41
+ # * _from_: The unit to convert from (ie. :miles, :stones, or :pints)
42
+ # * _to_: The unit to convert to
43
+ # * _options_:
44
+ # * :scale: The number of digits you want after the dot.
45
+ def convert(from, to, options={})
46
+ Conversions::Unit.new(self, from).to(to, options)
47
+ end
48
+ end
49
+ end
50
+
51
+ require 'conversions/unit'
52
+
53
+ Conversions.load_defaults
54
+ Numeric.send(:include, Conversions::Ext)
55
+
56
+ if defined?(ActiveRecord)
57
+ require 'conversions/active_record_accessors'
58
+ ActiveRecord::Base.send(:extend, Conversions::ActiveRecordAccessors)
59
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class Flight
4
+ extend Conversions::ActiveRecordAccessors
5
+ attr_accessor :distance, :fuel_consumption
6
+ conversion_accessor :distance, :internal => :kilometres, :external => :miles
7
+ conversion_accessor :fuel_consumption, :internal => :litres, :external => :gallons, :scale => 2
8
+
9
+ Conversions.register(:kilometres, :leagues, 0.179985601)
10
+ conversion_accessor :distance, :internal => :kilometres, :external => :leagues, :scale => 2
11
+ end
12
+
13
+ class AccessorTest < Test::Unit::TestCase
14
+ def setup
15
+ reset_defaults
16
+ end
17
+
18
+ def setup
19
+ @flight = Flight.new
20
+ end
21
+
22
+ def test_include
23
+ assert @flight.respond_to?(:distance)
24
+ assert @flight.respond_to?(:distance_in_miles)
25
+ end
26
+
27
+ def test_conversion
28
+ @flight.distance = 1200
29
+ assert_in_delta 745.645430684801, @flight.distance_in_miles, DELTA
30
+ end
31
+
32
+ def test_conversion_with_scale
33
+ @flight.fuel_consumption = 3400
34
+ assert_equal 898.18, @flight.fuel_consumption_in_gallons, DELTA
35
+ end
36
+
37
+ def test_register
38
+ @flight.distance = 1200
39
+ assert_in_delta 215.98, @flight.distance_in_leagues, DELTA
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ConversionsTest < Test::Unit::TestCase
4
+ def setup
5
+ Conversions.clear
6
+ end
7
+
8
+ def test_register
9
+ Conversions.register(:records, :cds, 0.47)
10
+ assert 2, Conversions.conversions.length
11
+
12
+ assert_nothing_raised do
13
+ 1.convert(:records, :cds)
14
+ 1.convert(:cds, :records)
15
+ 1.records.to(:cds)
16
+ 1.cds.to(:records)
17
+ end
18
+ end
19
+
20
+ def test_clear
21
+ Conversions.register(:records, :cds, 0.47)
22
+ assert 2, Conversions.conversions.length
23
+ Conversions.clear
24
+ assert 0, Conversions.conversions.length
25
+ end
26
+ end
data/test/ext_test.rb ADDED
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ExtTest < Test::Unit::TestCase
4
+ def setup
5
+ reset_defaults
6
+ end
7
+
8
+ def test_conversions
9
+ assert_in_delta 1.609344, 1.convert(:miles, :kilometres), DELTA
10
+ assert_in_delta 1.609344, 1.0.convert(:miles, :kilometres), DELTA
11
+ assert_in_delta 0.45359237, 1.convert(:pounds, :kilograms), DELTA
12
+ assert_in_delta 0.00110231131092439, 1.convert(:kilograms, :tons), DELTA
13
+ assert_in_delta 2.20462262184878, 1.convert(:kilograms, :pounds), DELTA
14
+ assert_in_delta 1, ( 1.convert(:kilograms, :pounds) * 1.convert(:pounds, :kilograms) ), DELTA
15
+ assert_in_delta 1.609344, 1.miles.to(:kilometres), DELTA
16
+ assert_in_delta 1.609344, 1.0.miles.to(:kilometres), DELTA
17
+ assert_in_delta 0.45359237, 1.pounds.to(:kilograms), DELTA
18
+ assert_in_delta 0.00110231131092439, 1.kilograms.to(:tons), DELTA
19
+ assert_in_delta 2.20462262184878, 1.kilograms.to(:pounds), DELTA
20
+ assert_in_delta 1, ( 1.kilograms.to(:pounds) * 1.pounds.to(:kilograms) ), DELTA
21
+ end
22
+
23
+ def test_register
24
+ Conversions.register(:dollars, :cents, 100.0)
25
+ assert_in_delta 1000.0, 10.convert(:dollars, :cents), DELTA
26
+ assert_in_delta 1000.0, 10.dollars.to(:cents), DELTA
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+
3
+ if not defined?(ActiveRecord)
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ end
7
+
8
+ $:.unshift File.dirname(__FILE__) + '/../lib'
9
+ require 'conversions'
10
+
11
+ def reset_defaults
12
+ Conversions.clear
13
+ Conversions.load_defaults
14
+ end
15
+
16
+ DELTA = 0.0000001
data/test/unit_test.rb ADDED
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class UnitTest < Test::Unit::TestCase
4
+ def setup
5
+ reset_defaults
6
+ end
7
+
8
+ def test_exchange_rate
9
+ assert_in_delta 1.609344, Conversions::Unit.exchange_rate(:miles, :kilometres), DELTA
10
+ assert_in_delta 0.621371192237334, Conversions::Unit.exchange_rate(:kilometres, :miles), DELTA
11
+ assert_raises(ArgumentError) { Conversions::Unit.exchange_rate(:unknown, :miles) }
12
+ assert_raises(ArgumentError) { Conversions::Unit.exchange_rate(:miles, :unknown) }
13
+ assert_raises(ArgumentError) { Conversions::Unit.exchange_rate(nil, :miles) }
14
+ assert_raises(ArgumentError) { Conversions::Unit.exchange_rate(:miles, nil) }
15
+ end
16
+
17
+ def test_exchange_rate_for_identity_transform
18
+ Conversions.conversions.keys.each do |unit|
19
+ assert_equal 1, Conversions::Unit.exchange_rate(unit, unit)
20
+ end
21
+ end
22
+
23
+ def test_to
24
+ amount = Conversions::Unit.new(10.0, :miles)
25
+ assert_in_delta 16.09344, amount.to(:kilometres), DELTA
26
+
27
+ amount = Conversions::Unit.new(10.0, :kilograms)
28
+ assert_in_delta 22.0462262184878, amount.to(:pounds), DELTA
29
+ end
30
+
31
+ def test_to_with_options
32
+ amount = Conversions::Unit.new(10.0, :miles)
33
+ assert_equal 16.1, amount.to(:kilometres, :scale => 1)
34
+ assert_equal 16.09, amount.to(:kilometres, :scale => 2)
35
+ assert_equal 16.093, amount.to(:kilometres, :scale => 3)
36
+ end
37
+
38
+ def test_to_with_scale
39
+ amount = Conversions::Unit.new(10.0, :miles)
40
+ assert_equal 16.1, amount.to(:kilometres, 1)
41
+ assert_equal 16.09, amount.to(:kilometres, 2)
42
+ assert_equal 16.093, amount.to(:kilometres, 3)
43
+ end
44
+
45
+ def test_identity_transforms
46
+ Conversions.conversions.keys.each do |unit|
47
+ assert_equal 1.0, Conversions::Unit.new(1.0, unit).to(unit, :scale => 2)
48
+ end
49
+ end
50
+
51
+ def test_register
52
+ Conversions.register(:dollars, :cents, 100.0)
53
+ assert_in_delta 0.01, Conversions::Unit.exchange_rate(:cents, :dollars), DELTA
54
+ amount = Conversions::Unit.new(10.0, :dollars)
55
+ assert_equal 1000.0, amount.to(:cents, :scale => 2), DELTA
56
+ end
57
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: seamusabshere-conversions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Manfred Stienstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A Ruby on Rails plugin that adds conversion capabilities to numeric objects"
17
+ email: manfred@fngtps.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/conversions
26
+ - lib/conversions/active_record_accessors.rb
27
+ - lib/conversions/defaults.rb
28
+ - lib/conversions/unit.rb
29
+ - lib/conversions.rb
30
+ - test/accessor_test.rb
31
+ - test/conversions_test.rb
32
+ - test/ext_test.rb
33
+ - test/test_helper.rb
34
+ - test/unit_test.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/Fingertips/conversions/tree/master
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --inline-source
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: A Ruby on Rails plugin that adds conversion capabilities to numeric objects"
62
+ test_files: []
63
+