xe_currency 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8bca0457f3d2ce4e44ccb2ebbb2befe123018317
4
+ data.tar.gz: 3af1b1f671e089ef3298ca27bd6999f20f8a7d94
5
+ SHA512:
6
+ metadata.gz: 52458ea2e80438b5609e8e76b547ceee49453b0cdbccdd84c44dd2c24b43612a69ca9e31d159071194bbc16ef302b027dfc44a327c1980843d40c61e9169a3de
7
+ data.tar.gz: bdac0e614ec9a62bce067fa9c717d44158a7d91ae3f7d3fe2a981f803e74a47f40df35bad678e0fa4ad7d274548d0ab6286c1988e30daa646c9c48ec99940def
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Chris Salzberg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # XeCurrency
2
+
3
+ This gem extends Money::Bank::VariableExchange with Money::Bank::XeCurrency and
4
+ gives you access to the current rates from the XE currency converter.
5
+
6
+ Usage
7
+ -----
8
+
9
+ ```ruby
10
+ require 'money'
11
+ require 'money/bank/xe_currency'
12
+
13
+ # (optional)
14
+ # set the seconds after than the current rates are automatically expired
15
+ # by default, they never expire
16
+ Money::Bank::XeCurrency.ttl_in_seconds = 86400
17
+
18
+ # set default bank to instance of XeCurrency
19
+ Money.default_bank = Money::Bank::XeCurrency.new
20
+
21
+ # create a new money object, and use the standard #exchange_to method
22
+ money = Money.new(1_00, "USD") # amount is in cents
23
+ money.exchange_to(:EUR)
24
+
25
+ # or install and use the 'monetize' gem
26
+ require 'monetize'
27
+ money = 1.to_money(:USD)
28
+ money.exchange_to(:EUR)
29
+ ```
30
+
31
+ ## License
32
+
33
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
34
+
@@ -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
@@ -0,0 +1,163 @@
1
+ require 'money'
2
+ require 'money/rates_store/rate_removal_support'
3
+ require 'open-uri'
4
+ require "nokogiri"
5
+
6
+ class Money
7
+ module Bank
8
+ class XeCurrency < Money::Bank::VariableExchange
9
+ # Raised when there is an unexpected error in extracting exchange rates
10
+ # from Xe Finance Calculator
11
+ class XeCurrencyFetchError < Error
12
+ end
13
+
14
+ SERVICE_HOST = "www.xe.com"
15
+ SERVICE_PATH = "/currencyconverter/convert"
16
+
17
+ # @return [Hash] Stores the currently known rates.
18
+ attr_reader :rates
19
+
20
+
21
+ class << self
22
+ # @return [Integer] Returns the Time To Live (TTL) in seconds.
23
+ attr_reader :ttl_in_seconds
24
+
25
+ # @return [Time] Returns the time when the rates expire.
26
+ attr_reader :rates_expiration
27
+
28
+ ##
29
+ # Set the Time To Live (TTL) in seconds.
30
+ #
31
+ # @param [Integer] the seconds between an expiration and another.
32
+ def ttl_in_seconds=(value)
33
+ @ttl_in_seconds = value
34
+ refresh_rates_expiration! if ttl_in_seconds
35
+ end
36
+
37
+ ##
38
+ # Set the rates expiration TTL seconds from the current time.
39
+ #
40
+ # @return [Time] The next expiration.
41
+ def refresh_rates_expiration!
42
+ @rates_expiration = Time.now + ttl_in_seconds
43
+ end
44
+ end
45
+
46
+ def initialize(*)
47
+ super
48
+ @store.extend Money::RatesStore::RateRemovalSupport
49
+ end
50
+
51
+ ##
52
+ # Clears all rates stored in @rates
53
+ #
54
+ # @return [Hash] The empty @rates Hash.
55
+ #
56
+ # @example
57
+ # @bank = XeCurrency.new #=> <Money::Bank::XeCurrency...>
58
+ # @bank.get_rate(:USD, :EUR) #=> 0.776337241
59
+ # @bank.flush_rates #=> {}
60
+ def flush_rates
61
+ store.clear_rates
62
+ end
63
+
64
+ ##
65
+ # Clears the specified rate stored in @rates.
66
+ #
67
+ # @param [String, Symbol, Currency] from Currency to convert from (used
68
+ # for key into @rates).
69
+ # @param [String, Symbol, Currency] to Currency to convert to (used for
70
+ # key into @rates).
71
+ #
72
+ # @return [Float] The flushed rate.
73
+ #
74
+ # @example
75
+ # @bank = XeCurrency.new #=> <Money::Bank::XeCurrency...>
76
+ # @bank.get_rate(:USD, :EUR) #=> 0.776337241
77
+ # @bank.flush_rate(:USD, :EUR) #=> 0.776337241
78
+ def flush_rate(from, to)
79
+ store.remove_rate(from, to)
80
+ end
81
+
82
+ ##
83
+ # Returns the requested rate.
84
+ #
85
+ # It also flushes all the rates when and if they are expired.
86
+ #
87
+ # @param [String, Symbol, Currency] from Currency to convert from
88
+ # @param [String, Symbol, Currency] to Currency to convert to
89
+ #
90
+ # @return [Float] The requested rate.
91
+ #
92
+ # @example
93
+ # @bank = XeCurrency.new #=> <Money::Bank::XeCurrency...>
94
+ # @bank.get_rate(:USD, :EUR) #=> 0.776337241
95
+ def get_rate(from, to)
96
+ expire_rates
97
+ store.get_rate(from, to) || store.add_rate(from, to, fetch_rate(from, to))
98
+ end
99
+
100
+ ##
101
+ # Flushes all the rates if they are expired.
102
+ #
103
+ # @return [Boolean]
104
+ def expire_rates
105
+ if self.class.ttl_in_seconds && self.class.rates_expiration <= Time.now
106
+ flush_rates
107
+ self.class.refresh_rates_expiration!
108
+ true
109
+ else
110
+ false
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ ##
117
+ # Queries for the requested rate and returns it.
118
+ #
119
+ # @param [String, Symbol, Currency] from Currency to convert from
120
+ # @param [String, Symbol, Currency] to Currency to convert to
121
+ #
122
+ # @return [BigDecimal] The requested rate.
123
+ def fetch_rate(from, to)
124
+ from, to = Currency.wrap(from), Currency.wrap(to)
125
+
126
+ data = build_uri(from, to).read
127
+ rate = extract_rate(data);
128
+
129
+ if (rate < 0.1)
130
+ rate = 1/extract_rate(build_uri(to, from).read)
131
+ end
132
+
133
+ rate
134
+ end
135
+
136
+ ##
137
+ # Build a URI for the given arguments.
138
+ #
139
+ # @param [Currency] from The currency to convert from.
140
+ # @param [Currency] to The currency to convert to.
141
+ #
142
+ # @return [URI::HTTP]
143
+ def build_uri(from, to)
144
+ uri = URI::HTTP.build(
145
+ :host => SERVICE_HOST,
146
+ :path => SERVICE_PATH,
147
+ :query => "Amount=1&From=#{from.iso_code}&To=#{to.iso_code}"
148
+ )
149
+ end
150
+
151
+ ##
152
+ # Takes the response from Xe and extract the rate.
153
+ #
154
+ # @param [String] data The xe rate string to decode.
155
+ #
156
+ # @return [BigDecimal]
157
+ def extract_rate(data)
158
+ rate = ::Nokogiri::HTML(data).css('.uccResultAmount')[0].inner_text
159
+ BigDecimal(rate)
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,27 @@
1
+ module Money::RatesStore
2
+ module RateRemovalSupport
3
+ # Remove a conversion rate and returns it. Uses +Mutex+ to synchronize data access.
4
+ #
5
+ # @param [String] currency_iso_from Currency to exchange from.
6
+ # @param [String] currency_iso_to Currency to exchange to.
7
+ #
8
+ # @return [Numeric]
9
+ #
10
+ # @example
11
+ # store = Money::RatesStore::Memory.new
12
+ # store.remove_rate("USD", "CAD")
13
+ # store.remove_rate("CAD", "USD")
14
+ def remove_rate(currency_iso_from, currency_iso_to)
15
+ transaction { index.delete rate_key_for(currency_iso_from, currency_iso_to) }
16
+ end
17
+
18
+ # Clears all conversion rates. Uses +Mutex+ to synchronize data access.
19
+ #
20
+ # @example
21
+ # store = Money::RatesStore::Memory.new
22
+ # store.clear_rates
23
+ def clear_rates
24
+ transaction { @index = {} }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "xe_currency"
@@ -0,0 +1,11 @@
1
+ require "spec_helper"
2
+
3
+ describe XeCurrency do
4
+ it "has a version number" do
5
+ expect(XeCurrency::VERSION).not_to be nil
6
+ end
7
+
8
+ it "does something useful" do
9
+ expect(false).to eq(true)
10
+ end
11
+ 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
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "xe_currency"
7
+ spec.version = "0.0.1"
8
+ spec.authors = ["Chris Salzberg"]
9
+ spec.email = ["csalzberg@degica.com"]
10
+
11
+ spec.summary = %q{Access XE currency rate data.}
12
+ spec.homepage = "https://github.com/degica/xe_currency"
13
+ spec.license = "MIT"
14
+
15
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
17
+ if spec.respond_to?(:metadata)
18
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
19
+ else
20
+ raise "RubyGems 2.0 or newer is required to protect against " \
21
+ "public gem pushes."
22
+ end
23
+
24
+ spec.add_dependency "money", "~> 6.7"
25
+ spec.add_dependency "nokogiri", "~> 1.7", ">= 1.7.2"
26
+
27
+ spec.files = Dir.glob("{lib,spec}/**/*")
28
+ spec.files += %w(LICENSE.txt README.md)
29
+ spec.files += %w(Rakefile xe_currency.gemspec)
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.13"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec", "~> 3.0"
34
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xe_currency
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Chris Salzberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: money
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 1.7.2
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.7'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.7.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.13'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.13'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '10.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '10.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.0'
89
+ description:
90
+ email:
91
+ - csalzberg@degica.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - lib/money/bank/xe_currency.rb
100
+ - lib/money/rates_store/rate_removal_support.rb
101
+ - spec/spec_helper.rb
102
+ - spec/xe_currency_spec.rb
103
+ - xe_currency.gemspec
104
+ homepage: https://github.com/degica/xe_currency
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.5.1
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Access XE currency rate data.
128
+ test_files: []