puppy_money 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,89 @@
1
+ require 'active_support/core_ext/object/try'
2
+ require_relative 'currencies'
3
+ require_relative 'rate_api'
4
+
5
+ class Money
6
+ attr_reader :amount, :currency, :manual_currencies
7
+
8
+ # use optional_rates for manual currency conversion
9
+ def initialize amount, currency, manual_currencies={}
10
+ validate_amount amount
11
+ validate_currency currency.try(:upcase)
12
+
13
+ # validate manual currency unless it is a non_standard currency (e.g. bitcoin)
14
+ manual_currencies.each do |name, info|
15
+ validate_currency name.to_s.upcase unless info[:non_standard]
16
+ end
17
+
18
+ @amount = amount.round(2)
19
+ @currency = currency.try(:upcase)
20
+ @manual_currencies = manual_currencies
21
+ end
22
+
23
+ def inspect
24
+ "#{@amount} #{@currency}"
25
+ end
26
+
27
+ def convert_to transfer_currency, non_standard=false
28
+ transfer_currency = transfer_currency.try(:upcase)
29
+ # validate the transfer currency unless:
30
+ ## 1. user explicitly specifies that it is a non-standard currency (e.g. bitcoin)
31
+ validate_currency transfer_currency unless non_standard
32
+ # use real-time exchange rates if:
33
+ ## 1. user has not provided exchange rates manually
34
+ ## 2 given transfer currency is not supported
35
+ if @manual_currencies.empty? && RateApi.fetch_symbols(@currency).include?(transfer_currency)
36
+ transfer_rate = RateApi.fetch_transfer_rate(@currency, transfer_currency)
37
+ Money.new(@amount * transfer_rate, transfer_currency)
38
+ end
39
+ end
40
+
41
+ # money object arithmetic and comparison
42
+ [:+, :-, :*, :/, :==, :>, :>=, :<, :<=, :<=>].each do |operation|
43
+ define_method(operation) do |other|
44
+ if [:+, :-, :*, :/].include? operation
45
+ if @currency == other.currency
46
+ Money.new(@amount.public_send(operation, other.amount), @currency)
47
+ else
48
+ [
49
+ Money.new(@amount.public_send(operation, other.conversion_amount(@currency)), @currency),
50
+ Money.new(self.conversion_amount(other.currency).public_send(operation, other.amount), other.currency)
51
+ ]
52
+ end
53
+ else
54
+ if @currency == other.currency
55
+ @amount.public_send(operation, other.amount)
56
+ else
57
+ @amount.public_send(operation, other.conversion_amount(@currency))
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def conversion_amount transfer_currency
64
+ convert_to(transfer_currency).amount
65
+ end
66
+
67
+ private
68
+
69
+ def validate_amount amount
70
+ # ensure that the amount is a numeric value
71
+ raise ArgumentError, "Amount must be a number" unless amount.is_a? Numeric
72
+ end
73
+
74
+ def validate_currency currency, manual_rates=false
75
+ if manual_rates
76
+ currency.map do |c|
77
+ # raise an error if manual rate is not recognized
78
+ raise ArgumentError, currency_error(currency) unless Currencies.abbreviations.include? c.to_s.upcase
79
+ end
80
+ else
81
+ # raise an error if base rate is not recognized
82
+ raise ArgumentError, currency_error(currency) unless Currencies.abbreviations.include?(currency)
83
+ end
84
+ end
85
+
86
+ def currency_error(currency)
87
+ " `#{currency}` is an invalid (currency abbreviation must be a recognized 3 character string)"
88
+ end
89
+ end
@@ -0,0 +1,22 @@
1
+ require 'httparty'
2
+
3
+ module RateApi
4
+ # needs refactoring!!!
5
+
6
+ # get complete rate info
7
+ def self.rate_request base_currency
8
+ HTTParty.get "http://api.fixer.io/latest?base=#{base_currency}"
9
+ end
10
+
11
+ # get rate symbol abbreviations
12
+ def self.fetch_symbols base_currency
13
+ rate_info = self.rate_request base_currency
14
+ rate_info['rates'].keys << rate_info['base']
15
+ end
16
+
17
+ # return a transfer rate
18
+ def self.fetch_transfer_rate base_currency, transfer_currency
19
+ xrates = self.rate_request base_currency
20
+ xrates['rates'][transfer_currency]
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module PuppyMoney
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,5 @@
1
+ require "puppy_money/version"
2
+ require "puppy_money/money"
3
+
4
+ module PuppyMoney
5
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'puppy_money/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "puppy_money"
8
+ spec.version = PuppyMoney::VERSION
9
+ spec.authors = ["Dan Kreiger"]
10
+ spec.email = ["dan@dankreiger.com"]
11
+
12
+ spec.summary = %q{Easy currency conversion}
13
+ spec.description = %q{Convert currencies using live exchange rates.}
14
+ spec.homepage = "https://github.com/dankreiger/puppy_money"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ # development
25
+ spec.add_development_dependency "bundler", "~> 1.14"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "pry", "~> 0.10"
29
+ spec.add_development_dependency "irbtools", "~> 2.1"
30
+
31
+ # runtime
32
+ spec.add_runtime_dependency "activesupport", "~> 5.0"
33
+ spec.add_runtime_dependency "httparty", "~> 0.14"
34
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puppy_money
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dan Kreiger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-04-25 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.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: irbtools
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: httparty
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.14'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.14'
111
+ description: Convert currencies using live exchange rates.
112
+ email:
113
+ - dan@dankreiger.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".travis.yml"
121
+ - CODE_OF_CONDUCT.md
122
+ - Gemfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - bin/console
127
+ - bin/setup
128
+ - lib/puppy_money.rb
129
+ - lib/puppy_money/currencies.rb
130
+ - lib/puppy_money/currency_data.rb
131
+ - lib/puppy_money/money.rb
132
+ - lib/puppy_money/rate_api.rb
133
+ - lib/puppy_money/version.rb
134
+ - puppy_money.gemspec
135
+ homepage: https://github.com/dankreiger/puppy_money
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.6.11
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Easy currency conversion
159
+ test_files: []