convertable 0.1.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: 16f51bd062c3c1d298ce8c4c0d29cd05892a0ad7bc6bfe1120a23e518fbc839e
4
+ data.tar.gz: 0f73afa024d8f4a4aef30b95bbda68c669621509ccaf14c399841c3ac8b80d29
5
+ SHA512:
6
+ metadata.gz: bf31d8d146c223840dcec77487ba5153369cbf510212a5b2beaa149820e00ccbf18cff8fbdeea191cdd93d6fcebb0e00964b642bb3efd7e8d17d820d86524763
7
+ data.tar.gz: 5d8583dc12f4c8dc3410349d112e9e3a3217743b80c53a4ea138bdb39c0d0efc1635fe61cf0cda0e995d133578afd8631f76074db76c84c074459359dff5c314
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ convertable (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (5.2.2)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (>= 0.7, < 2)
12
+ minitest (~> 5.1)
13
+ tzinfo (~> 1.1)
14
+ concurrent-ruby (1.1.4)
15
+ diff-lcs (1.3)
16
+ i18n (1.5.3)
17
+ concurrent-ruby (~> 1.0)
18
+ minitest (5.11.3)
19
+ rake (10.5.0)
20
+ rspec (3.8.0)
21
+ rspec-core (~> 3.8.0)
22
+ rspec-expectations (~> 3.8.0)
23
+ rspec-mocks (~> 3.8.0)
24
+ rspec-core (3.8.0)
25
+ rspec-support (~> 3.8.0)
26
+ rspec-expectations (3.8.2)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-mocks (3.8.0)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-support (3.8.0)
33
+ thread_safe (0.3.6)
34
+ tzinfo (1.2.5)
35
+ thread_safe (~> 0.1)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ activesupport
42
+ bundler (~> 1.16)
43
+ convertable!
44
+ rake (~> 10.0)
45
+ rspec (~> 3.0)
46
+
47
+ BUNDLED WITH
48
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # Convertable
2
+
3
+ ![Convertable](http://www.clker.com/cliparts/H/f/Z/C/p/v/convertible-sports-car-md.png)
4
+
5
+ *Convertible* was taken, and *convertable* was close enough.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'convertable'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install convertable
22
+
23
+ ## Usage
24
+
25
+ Convertable is composed of two main ideas:
26
+
27
+ ### Units
28
+
29
+ Units are the basic building blocks of the library. There are two types of units, simple and composed. They both know how to convert to other units with `convert_to(magnitude, new_unit)`. Composed units can be any permutation of multiple simple units. All units are namespaced under `Convertable::Units`.
30
+
31
+ *e.g. convert 1.0 square meter to square feet*
32
+
33
+ ```ruby
34
+ Convertable::Units::SquareMeter.convert_to(1.0, Convertable::Units::SquareFoot)
35
+ => 10.7639
36
+ ```
37
+
38
+ If a unit does not know how to convert to another unit an exception is raised
39
+
40
+ ```ruby
41
+ Convertable::Units::SquareMeter.convert_to(1.0, Convertable::Units::Year)
42
+ => Convertable::Units::UnsupportedConversion (Convertable::Units::UnsupportedConversion)
43
+ ```
44
+
45
+ *Simple Units*
46
+
47
+ * Year
48
+ * Month
49
+ * SquareFoot
50
+ * SquareMeter
51
+ * Currency (Usd, Eur, Gbp)
52
+
53
+ *Composed Units*
54
+ ```ruby
55
+ Convertable::Units::ComposedUnit.new("Usd/SquareFoot/Year").inspect
56
+
57
+ => <Convertable::Units::ComposedUnit:0x00007fa4819c7638
58
+ @multiplying_unit=Convertable::Units::Usd,
59
+ @dividing_units=[Convertable::Units::SquareFoot, Convertable::Units::Year]
60
+ >
61
+ ```
62
+
63
+ ```ruby
64
+ currency = Convertable::Units::Usd
65
+ area = Convertable::Units::SquareFoot
66
+ period = Convertable::Units::Year
67
+ Convertable::Units::ComposedUnit.new("#{currency}/#{area}/#{period}").inspect
68
+
69
+ => <Convertable::Units::ComposedUnit:0x00007fa4819c7638
70
+ @multiplying_unit=Convertable::Units::Usd,
71
+ @dividing_units=[Convertable::Units::SquareFoot, Convertable::Units::Year]
72
+ >
73
+ ```
74
+
75
+ ### Measures
76
+
77
+ A measure is a standardized representation of a real-life physical quantity relative to some reference, called unit of measurement. It is described by its class name (UsdPerAreaPerYear) and has two components
78
+
79
+ * Magnitude - numeric value - 20.5
80
+ * Unit - one of the units above - `Convertable::Units::ComposedUnit.new('Usd/SquareFoot/Year')`
81
+
82
+ All Convertable are namespaced under `Convertable::Measures`.
83
+
84
+ *e.g. $20.5 / square foot / year*
85
+
86
+ ```ruby
87
+ magnitude = 20.5
88
+ unit = Convertable::Units::ComposedUnit.new('Usd/SquareFoot/Year')
89
+ measure = Convertable::Measures::MoneyPerAreaPerPeriod.new(magnitude, unit)
90
+ ```
91
+ ##### Unit Conversions
92
+ Measures can be converted to different units with `Measure#in(new_unit)`:
93
+
94
+ *$/sf/year => $/sm/year*
95
+
96
+ ```ruby
97
+ measure.in(Convertable::Units::ComposedUnit.new('Usd/SquareMeter/Year')).inspect
98
+
99
+ => <Convertable::Measures::MoneyPerAreaPerPeriod:0x00007fbcb32dcf40
100
+ @magnitude=220.65994999999998,
101
+ @unit=Convertable::Units::ComposedUnit.new('Usd/SquareMeter/Year')
102
+ >
103
+ ```
104
+
105
+ *$/sf/year => $/sf/month*
106
+
107
+ ```ruby
108
+ measure.in(Convertable::Units::ComposedUnit.new('Usd/SquareFoot/Month')).inspect
109
+
110
+ => <Convertable::Measures::MoneyPerAreaPerPeriod:0x00007fbcaefc07e8
111
+ @magnitude=1.7083333333333333,
112
+ @unit=Convertable::Units::ComposedUnit.new('Usd/SquareFoot/Month')
113
+ >
114
+ ```
115
+
116
+ ##### Measure Conversions
117
+
118
+ Measures can be converted to other measures provided a measure:
119
+
120
+ *provided sf we can convert $/year to $/sf/year*
121
+
122
+ ```ruby
123
+ unit = Convertable::Units::MoneyPerYear
124
+ magnitude = 200.0
125
+ measure = Convertable::Measures::MoneyPerPeriod.new(magnitude, unit)
126
+
127
+ magnitude = 50.0
128
+ area = Convertable::Measures::Area.new(magnitude, Convertable::Units::SquareFoot)
129
+ measure.to_money_per_area_per_period(area)
130
+
131
+ => <Convertable::Measures::MoneyPerAreaPerPeriod:0x00007fc0551720d8
132
+ @magnitude=4.0,
133
+ @unit=Convertable::Units::ComposedUnit.new('Usd/SquareFoot/Year')
134
+ >
135
+ ```
136
+
137
+ ## Development
138
+
139
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
140
+
141
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
142
+
143
+ ## Contributing
144
+
145
+ Bug reports and pull requests are welcome on GitHub at https://github.com/viewthespace/convertable.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "convertable"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "convertable/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "convertable"
7
+ spec.version = Convertable::VERSION
8
+ spec.authors = ["Alex Wheeler"]
9
+ spec.email = ["aw@viewthespace.com"]
10
+
11
+ spec.summary = %q{Units of measure made easy.}
12
+ spec.description = %q{Convert between units SquareFoot -> SquareMeter and Measures $/SquareFoot/Year -> $/SquareMeter/Month.}
13
+ spec.homepage = "https://www.github.com/viewthespace/convertable"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.16"
23
+ spec.add_development_dependency "activesupport"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ end
@@ -0,0 +1,8 @@
1
+ require "convertable/version"
2
+ require "convertable/measure"
3
+ require "convertable/measures"
4
+ require "convertable/units"
5
+
6
+ module Convertable
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,21 @@
1
+ module Convertable
2
+ class Measure
3
+ attr_reader :magnitude, :unit
4
+
5
+ def initialize(magnitude, unit)
6
+ @magnitude = magnitude
7
+ @unit = unit
8
+ end
9
+
10
+ def in(new_unit)
11
+ return self if new_unit == unit
12
+
13
+ new_magnitude = unit.convert_to(magnitude, new_unit)
14
+ self.class.new(new_magnitude, new_unit)
15
+ end
16
+
17
+ def ==(other_measure)
18
+ magnitude == other_measure.magnitude && unit == other_measure.unit
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ module Convertable
2
+ module Measures
3
+ class Area < Measure
4
+ end
5
+
6
+ class Period < Measure
7
+ end
8
+
9
+ class MoneyPerPeriod < Measure
10
+ def to_money_per_period(_area)
11
+ self
12
+ end
13
+
14
+ def to_money_per_area_per_period(area)
15
+ area_unit = area.unit.to_s
16
+ currency = self.unit.multiplying_unit
17
+ period = self.unit.dividing_units[0]
18
+
19
+ new_unit = Convertable::Units::ComposedUnit.new("#{currency}/#{area_unit}/#{period}")
20
+ MoneyPerAreaPerPeriod.new(magnitude / area.magnitude, new_unit)
21
+ end
22
+ end
23
+
24
+ class MoneyPerAreaPerPeriod < Measure
25
+ def to_money_per_area_per_period(_area)
26
+ self
27
+ end
28
+
29
+ def to_money_per_period(area)
30
+ area_unit = self.unit.dividing_units[0]
31
+ period = self.unit.dividing_units[1]
32
+ converted_area = area.in(area_unit)
33
+
34
+ new_unit = Convertable::Units::ComposedUnit.new("#{self.unit.multiplying_unit}/#{period}")
35
+
36
+ MoneyPerPeriod.new(magnitude * converted_area.magnitude, new_unit)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,124 @@
1
+ module Convertable
2
+ module Units
3
+ def self.find(unit)
4
+ if const_defined?(unit.camelize)
5
+ const_get(unit.camelize)
6
+ else
7
+ raise UnsupportedUnit.new(unit.camelize)
8
+ end
9
+ end
10
+
11
+ class UnsupportedConversion < StandardError
12
+ end
13
+
14
+ class UnsupportedUnit < StandardError
15
+ end
16
+
17
+ class SimpleUnit
18
+ def self.to_json
19
+ name
20
+ end
21
+ end
22
+
23
+ class SquareFoot < SimpleUnit
24
+ def self.convert_to(magnitude, new_unit)
25
+ if new_unit == SquareFoot
26
+ magnitude
27
+ elsif new_unit == SquareMeter
28
+ magnitude / 10.7639
29
+ else
30
+ raise UnsupportedConversion
31
+ end
32
+ end
33
+ end
34
+
35
+ class SquareMeter < SimpleUnit
36
+ def self.convert_to(magnitude, new_unit)
37
+ if new_unit == SquareMeter
38
+ magnitude
39
+ elsif new_unit == SquareFoot
40
+ magnitude * 10.7639
41
+ else
42
+ raise UnsupportedConversion
43
+ end
44
+ end
45
+ end
46
+
47
+ class Year < SimpleUnit
48
+ def self.convert_to(magnitude, new_unit)
49
+ if new_unit == Month
50
+ magnitude * 12
51
+ elsif new_unit == Year
52
+ magnitude
53
+ else
54
+ raise UnsupportedConversion
55
+ end
56
+ end
57
+ end
58
+
59
+ class Month < SimpleUnit
60
+ def self.convert_to(magnitude, new_unit)
61
+ if new_unit == Month
62
+ magnitude
63
+ elsif new_unit == Year
64
+ magnitude / 12
65
+ else
66
+ raise UnsupportedConversion
67
+ end
68
+ end
69
+ end
70
+
71
+ class Currency < SimpleUnit
72
+ def self.convert_to(magnitude, new_unit)
73
+ if new_unit == self
74
+ magnitude
75
+ else
76
+ raise UnsupportedConversion
77
+ end
78
+ end
79
+ end
80
+
81
+ class Usd < Currency
82
+ end
83
+
84
+ class Gbp < Currency
85
+ end
86
+
87
+ class ComposedUnit
88
+ attr_reader :multiplying_unit, :dividing_units
89
+
90
+ def initialize(units)
91
+ @multiplying_unit, *@dividing_units = units.split("/").map { |unit| Convertable::Units.find(unit) }
92
+ end
93
+
94
+ def convert_to(magnitude, new_composed_unit)
95
+ raise UnsupportedConversion unless new_composed_unit.is_a?(ComposedUnit)
96
+
97
+ magnitude * conversion_factor_to(new_composed_unit)
98
+ end
99
+
100
+ def ==(other_composed_unit)
101
+ multiplying_unit == other_composed_unit.multiplying_unit &&
102
+ dividing_units == other_composed_unit.dividing_units
103
+ end
104
+
105
+ def to_json
106
+ name
107
+ end
108
+
109
+ private
110
+
111
+ def conversion_factor_to(other_composed_unit)
112
+ conversion_factor = 1
113
+
114
+ conversion_factor *= multiplying_unit.convert_to(1.0, other_composed_unit.multiplying_unit)
115
+
116
+ dividing_units.each_with_index do |dividing_unit, index|
117
+ conversion_factor /= dividing_unit.convert_to(1.0, other_composed_unit.dividing_units[index])
118
+ end
119
+
120
+ conversion_factor
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ module Convertable
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: convertable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Wheeler
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Convert between units SquareFoot -> SquareMeter and Measures $/SquareFoot/Year
70
+ -> $/SquareMeter/Month.
71
+ email:
72
+ - aw@viewthespace.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - convertable.gemspec
87
+ - lib/convertable.rb
88
+ - lib/convertable/measure.rb
89
+ - lib/convertable/measures.rb
90
+ - lib/convertable/units.rb
91
+ - lib/convertable/version.rb
92
+ homepage: https://www.github.com/viewthespace/convertable
93
+ licenses: []
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.7.6
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Units of measure made easy.
115
+ test_files: []