exchangerate_host 0.0.1

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: 2b733284dbe5f7ac5c8a2319d805f045441b8584dd6ef32ab949bb872313ca2d
4
+ data.tar.gz: 0c32fc76a35080bb289fccc759c5a48f9de752508ae78198a7fafe2705306241
5
+ SHA512:
6
+ metadata.gz: d19e19648209895fb2e998550487e71df88b961702ac2be96847200ca0b181824fb9d41f197990842976b21647fa49e44cffa27ad5f3104342e919279a4da18b
7
+ data.tar.gz: 25b9af691b46c5d06474e04d65fef70b30b34439df2390509825310abff16e04db2155302aa09d28af5a9966f2b327ec5eedf3d336ad4d78c8b3f18b588f5f22
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/.rubocop.yml ADDED
@@ -0,0 +1,200 @@
1
+ AllCops:
2
+ # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
3
+ # to ignore them, so only the ones explicitly set in this file are enabled.
4
+ DisabledByDefault: true
5
+
6
+ # Prefer &&/|| over and/or.
7
+ Style/AndOr:
8
+ Enabled: true
9
+
10
+ # Align `when` with `case`.
11
+ Layout/CaseIndentation:
12
+ Enabled: true
13
+
14
+ Layout/ClosingHeredocIndentation:
15
+ Enabled: true
16
+
17
+ # Align comments with method definitions.
18
+ Layout/CommentIndentation:
19
+ Enabled: true
20
+
21
+ # Align if else statements.
22
+ Layout/ElseAlignment:
23
+ Enabled: true
24
+
25
+ # Align `end` with the matching keyword or starting expression except for
26
+ # assignments, where it should be aligned with the LHS.
27
+ Layout/EndAlignment:
28
+ Enabled: true
29
+ EnforcedStyleAlignWith: variable
30
+ AutoCorrect: true
31
+
32
+ # Leave a line after frozenstring comment.
33
+ Layout/EmptyLineAfterMagicComment:
34
+ Enabled: true
35
+
36
+ # Leave a line before access modifiers.
37
+ Layout/EmptyLinesAroundAccessModifier:
38
+ Enabled: true
39
+ EnforcedStyle: only_before
40
+
41
+ # In a regular block, no empty lines around the body.
42
+ Layout/EmptyLinesAroundBlockBody:
43
+ Enabled: true
44
+
45
+ # In a regular class definition, no empty lines around the body.
46
+ Layout/EmptyLinesAroundClassBody:
47
+ Enabled: true
48
+
49
+ # In a regular method definition, no empty lines around the body.
50
+ Layout/EmptyLinesAroundMethodBody:
51
+ Enabled: true
52
+
53
+ # In a regular module definition, no empty lines around the body.
54
+ Layout/EmptyLinesAroundModuleBody:
55
+ Enabled: true
56
+
57
+ # Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
58
+ Style/HashSyntax:
59
+ Enabled: true
60
+
61
+ Layout/FirstArgumentIndentation:
62
+ Enabled: true
63
+
64
+ # Method definitions after `private` or `protected` isolated calls need one
65
+ # extra level of indentation.
66
+ Layout/IndentationConsistency:
67
+ Enabled: true
68
+ EnforcedStyle: indented_internal_methods
69
+
70
+ # Two spaces, no tabs (for indentation).
71
+ Layout/IndentationWidth:
72
+ Enabled: true
73
+
74
+ Layout/LeadingCommentSpace:
75
+ Enabled: true
76
+
77
+ Layout/SpaceAfterColon:
78
+ Enabled: true
79
+
80
+ Layout/SpaceAfterComma:
81
+ Enabled: true
82
+
83
+ Layout/SpaceAfterSemicolon:
84
+ Enabled: true
85
+
86
+ Layout/SpaceAroundEqualsInParameterDefault:
87
+ Enabled: true
88
+
89
+ Layout/SpaceAroundKeyword:
90
+ Enabled: true
91
+
92
+ Layout/SpaceBeforeComma:
93
+ Enabled: true
94
+
95
+ Layout/SpaceBeforeComment:
96
+ Enabled: true
97
+
98
+ Layout/SpaceBeforeFirstArg:
99
+ Enabled: true
100
+
101
+ Style/DefWithParentheses:
102
+ Enabled: true
103
+
104
+ # Defining a method with parameters needs parentheses.
105
+ Style/MethodDefParentheses:
106
+ Enabled: true
107
+
108
+ Style/RedundantFreeze:
109
+ Enabled: true
110
+
111
+ # Use `foo {}` not `foo{}`.
112
+ Layout/SpaceBeforeBlockBraces:
113
+ Enabled: true
114
+
115
+ # Use `foo { bar }` not `foo {bar}`.
116
+ Layout/SpaceInsideBlockBraces:
117
+ Enabled: true
118
+ EnforcedStyleForEmptyBraces: space
119
+
120
+ # Use `{ a: 1 }` not `{a:1}`.
121
+ Layout/SpaceInsideHashLiteralBraces:
122
+ Enabled: true
123
+
124
+ Layout/SpaceInsideParens:
125
+ Enabled: true
126
+
127
+ # TODO: What quote style????
128
+ # Check quotes usage according to lint rule below.
129
+ # Style/StringLiterals:
130
+ # Enabled: true
131
+ # EnforcedStyle: single_quotes
132
+
133
+ # Detect hard tabs, no hard tabs.
134
+ Layout/IndentationStyle:
135
+ Enabled: true
136
+
137
+ # Blank lines should not have any spaces.
138
+ Layout/TrailingEmptyLines:
139
+ Enabled: true
140
+
141
+ # No trailing whitespace.
142
+ Layout/TrailingWhitespace:
143
+ Enabled: true
144
+
145
+ # Use quotes for string literals when they are enough.
146
+ Style/RedundantPercentQ:
147
+ Enabled: true
148
+
149
+ Lint/AmbiguousOperator:
150
+ Enabled: true
151
+
152
+ Lint/AmbiguousRegexpLiteral:
153
+ Enabled: true
154
+
155
+ Lint/ErbNewArguments:
156
+ Enabled: true
157
+
158
+ # Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
159
+ Lint/RequireParentheses:
160
+ Enabled: true
161
+
162
+ # Check for outer and inner local block barivales sharing a name.
163
+ # https://www.rubydoc.info/github/bbatsov/RuboCop/RuboCop/Cop/Lint/ShadowingOuterLocalVariable
164
+ Lint/ShadowingOuterLocalVariable:
165
+ Enabled: true
166
+
167
+ Lint/RedundantStringCoercion:
168
+ Enabled: true
169
+
170
+ Lint/UriEscapeUnescape:
171
+ Enabled: true
172
+
173
+ Lint/UselessAssignment:
174
+ Enabled: true
175
+
176
+ # Checks for depricated ruby class methods.
177
+ # https://www.rubydoc.info/github/bbatsov/RuboCop/RuboCop/Cop/Lint/DeprecatedClassMethods
178
+ Lint/DeprecatedClassMethods:
179
+ Enabled: true
180
+
181
+ Style/ParenthesesAroundCondition:
182
+ Enabled: true
183
+
184
+ Style/RedundantBegin:
185
+ Enabled: true
186
+
187
+ Style/RedundantReturn:
188
+ Enabled: true
189
+ AllowMultipleReturnValues: true
190
+
191
+ Style/Semicolon:
192
+ Enabled: true
193
+ AllowAsExpressionSeparator: true
194
+
195
+ # Prefer Foo.method over Foo::method
196
+ Style/ColonMethodCall:
197
+ Enabled: true
198
+
199
+ Style/TrivialAccessors:
200
+ Enabled: true
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.2
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in exchangerate_host.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
8
+
9
+ gem 'httparty'
data/Gemfile.lock ADDED
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ exchangerate_host (0.0.1)
5
+ httparty
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.4.4)
11
+ httparty (0.18.1)
12
+ mime-types (~> 3.0)
13
+ multi_xml (>= 0.5.2)
14
+ mime-types (3.3.1)
15
+ mime-types-data (~> 3.2015)
16
+ mime-types-data (3.2021.0704)
17
+ multi_xml (0.6.0)
18
+ rake (12.3.3)
19
+ rspec (3.10.0)
20
+ rspec-core (~> 3.10.0)
21
+ rspec-expectations (~> 3.10.0)
22
+ rspec-mocks (~> 3.10.0)
23
+ rspec-core (3.10.1)
24
+ rspec-support (~> 3.10.0)
25
+ rspec-expectations (3.10.1)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.10.0)
28
+ rspec-mocks (3.10.2)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.10.0)
31
+ rspec-support (3.10.2)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ exchangerate_host!
38
+ httparty
39
+ rake (~> 12.0)
40
+ rspec (~> 3.0)
41
+
42
+ BUNDLED WITH
43
+ 2.1.4
data/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # ExchangerateHost
2
+
3
+ ExchangerateHost is a very simple gem to integrate your ruby application with [exchangerate.host](https://exchangerate.host) 🚀
4
+ [exchangerate.host](https://exchangerate.host) is a great service that provide us completely free curreny exhange rates APIs (you don't even need to make an account & api key!! Really easy to get started 🙂 ).
5
+
6
+ Let's [support](https://exchangerate.host/#/donate) their mission if you're happy with the service and willing to donate 👼
7
+
8
+ ## Installation
9
+ Install with the following command:
10
+
11
+ $ gem install exchangerate_host
12
+
13
+ For rails appliactions, add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'exchangerate_host'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle install
22
+
23
+ ## Supported services
24
+ | Service | Descriptoin | Supported |
25
+ | ------------- | ------ |:-------------:|
26
+ | Latest rates | The latest exchage rate data updated on daily basis | ✔️ |
27
+ | Convert currency | Convert any amount from one currency to another | ✔️ |
28
+ | Historical rates | Currency rates histrical data all the way back to the year of `1999` | ✔️ |
29
+ | Time-Series data | Historical rates between two dates of your choic. (maximum time frame of `366 days`) | ✔️ |
30
+ | Fluctuation data | Historical data about how currencies fluctuate on a day-to-day basis (maximum time frame of `366 days`) | ✔️ |
31
+ | Supported symbols | All available currencies | ✔️ |
32
+ | EU VAT Rates | Request a single by country code or entire set EU VAT rates | WIP |
33
+
34
+ You can find details about services in [here](https://exchangerate.host/#/#our-services)
35
+ ## Parameters
36
+ | Parameter | Description | Example | Note |
37
+ | ------------- |:-------------:| :---: | :-----: |
38
+ | base | Your preferred base currency | JPY | non case sensitive |
39
+ | symbols | Currency codes to limit output currencies | [:JPY, :AUD, :USD] | non case sensitive |
40
+ | amount | The amount to be converted | 123.45 | - |
41
+ | places | Decimal places | 2 | - |
42
+ | format | Output format | CSV | CSV, TSV or XML |
43
+ | from | The currecny you would like to convert from | JPY | non case sensitive |
44
+ | to | The currecny you would like to convert to | AUD | non case sensitive |
45
+ | date | Historical exchange rate date | 2020-12-21 | format YYYY-MM-DD |
46
+ | start_date | The start date of your preferred timeframe | 2020-12-21 | format YYYY-MM-DD |
47
+ | end_date | The end date of your preferred timeframe | 2020-12-21 | format YYYY-MM-DD |
48
+
49
+ ## Service Paramters
50
+ 🟢 _optional_ 🔴 _required_
51
+ | Service | base | symbols | amount | places | format | from | to | date | start_date | end_date |
52
+ |:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|
53
+ | Latest rates | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | - | - | - | - | - |
54
+ | Convert currency | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | - | - |
55
+ | Historical rates | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | - | - | 🔴 | - | - |
56
+ | Time-Series data | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | - | - | - | 🔴 | 🔴 |
57
+ | Fluctuation data | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | - | - | - | 🔴 | 🔴 |
58
+ | Supported symbols | - | - | - | - | 🟢 | - | - | - | - | - |
59
+
60
+
61
+ If `base` option is not given, `EUR` will be assigned
62
+ If `amount` option is not given, `1` will be assigned
63
+
64
+ ## Usage
65
+ You can make call supporetd survices with following APIs
66
+ | Service | method | arguments |
67
+ | ------------- |:-------------:| :-------------:|
68
+ | Latest rates | ExchangerateHost.latest_rates(options) | options: Hash |
69
+ | Convert currency | ExchangerateHost.convert_currency(options) | options: Hash |
70
+ | Historical rates | ExchangerateHost.historical_rates(options) | options: Hash |
71
+ | Time-Series data | ExchangerateHost.time_series(options) | options: Hash |
72
+ | Fluctuation data | ExchangerateHost.fluctuation(options) | options: Hash |
73
+ | Supported symbols |ExchangerateHost.supported_symbols(options) | options: Hash |
74
+
75
+
76
+
77
+ _exmaples_
78
+ ```ruby
79
+ # Decimal place 2
80
+ ExchangerateHost.configurations.places = 2
81
+
82
+ # How much is 1000 yen in AUD and USD with the latest currency rates
83
+ res = ExchangerateHost.latest_rates({ base: :JPY, amount: 1000, symbols: [:AUD, :USD] })
84
+ res['rates'] #=> {"AUD"=>12.59, "USD"=>9.1}
85
+
86
+ # How much is 150 AUD in JPY on 2015 December 21st
87
+ res = ExchangerateHost.convert_currency({ from: :AUD, to: :JPY, date: '2015-12-21', amount: 150 })
88
+ res['result'] #=> 13038.47
89
+
90
+ # What were the all supported currency rates for 1 AUD on 2020 December 21st
91
+ res = ExchangerateHost.historical_rates({date: '2020-12-21', base: :USD })
92
+ res['rates'] #=> { "AED"=>3.69, "AFN"=>77.4, "ALL"=>101.52... }
93
+
94
+ # What were the JPY and USD rates based on 1 AUD between 2021 January 1st ~ 2021 Febrary 1st
95
+ res = ExchangerateHost.time_series({ base: :AUD, symbols: [:JPY, :USD], start_date: '2021-01-01', end_date: '2021-02-01' })
96
+ res['rates'] #=> {"2021-01-01"=>{"JPY"=>79.56, "USD"=>0.77}, "2021-01-02"=>{"JPY"=>79.29, "USD"=>0.77}, "2021-01-03"=>{"JPY"=>79.42, "USD"=>0.77},... }
97
+
98
+ # How much JPY and USD rates based on 1 AUD fluctuated between 2020 January 1st ~ 2020 October 1st
99
+ res = ExchangerateHost.fluctuation({ base: :AUD, symbols: [:JPY, :USD], start_date: '2020-01-01', end_date: '2020-10-01' })
100
+ res['rates'] #=> {"USD"=>{"start_rate"=>0.7, "end_rate"=>0.72, "change"=>-0.02, "change_pct"=>-0.03}, "JPY"=>{... }
101
+
102
+ # What are the suported currencies
103
+ res = ExchangerateHost.supported_symbols
104
+ res['symbols'] #=> {"AED"=>{"description"=>"United Arab Emirates Dirham", "code"=>"AED"}, "AFN"=>{"description"=>"Afghan Afghani", "code"=>"AFN"}... }
105
+ ```
106
+
107
+ ## Setup default parameter options
108
+ You can set default parameter options so that you don't need to pass the same options everytime.
109
+ ```ruby
110
+ ExchangerateHost.configure do |conf|
111
+ conf.base = :JPY
112
+ conf.symbols = [:AUD, :USD]
113
+ end
114
+ # or
115
+ ExchangerateHost.configurations.base = :JPY
116
+
117
+ # print currernt configuratoins
118
+ ExchangerateHost.configurations.to_options_hash #=> {:base=>:JPY, :symbols=>[:AUD, :USD]}
119
+
120
+ # reset configurations
121
+ ExchangerateHost.reset_configurations
122
+ ExchangerateHost.configurations.to_options_hash #=> {}
123
+
124
+ # default options will be overwritten by the passed option value
125
+ ExchangerateHost.configurations.base = :JPY
126
+
127
+ ExchangerateHost.latest_rates #=> the base option for the request is JPY
128
+ ExchangerateHost.latest_rates({ base: :AUD }) #=> the base option for the request is now AUD
129
+ ```
130
+
131
+
132
+
133
+ ## Contributing
134
+
135
+ Bug reports and pull requests are welcome on GitHub at https://github.com/MizukiZ/exchangerate_host. 🙏
136
+
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 "exchangerate_host"
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,29 @@
1
+ require_relative 'lib/exchangerate_host/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "exchangerate_host"
5
+ spec.version = ExchangerateHost::VERSION
6
+ spec.authors = ["Mizuki Zenta"]
7
+ spec.email = ["mizukisb@gmail.com"]
8
+
9
+ spec.summary = 'A Ruby library to integrate ruby application with exchangerate.host'
10
+ spec.description = 'A Ruby library to integrate ruby application with exchangerate.host'
11
+ spec.homepage = 'https://github.com/MizukiZ/exchangerate_host'
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+ spec.license = 'MIT'
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = 'https://github.com/MizukiZ/exchangerate_host'
17
+ spec.metadata["changelog_uri"] = 'https://github.com/MizukiZ/exchangerate_host'
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "httparty"
29
+ end
@@ -0,0 +1,16 @@
1
+ module ExchangerateHost
2
+ class Configurations
3
+ attr_accessor :base, :symbols, :places, :amount, :format, :from, :to, :date, :start_date, :end_date
4
+
5
+ def initialize
6
+ # Default value can be set here.
7
+ # @places = 2
8
+ end
9
+
10
+ def to_options_hash
11
+ self.instance_variables.map do |var|
12
+ [var[1..-1].to_sym, self.instance_variable_get(var)]
13
+ end.to_h
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,54 @@
1
+ require 'httparty'
2
+ require 'date'
3
+ require 'exchangerate_host/endpoints/validation'
4
+
5
+ module ExchangerateHost
6
+ module Endpoints
7
+ class Base
8
+ include HTTParty
9
+ extend Endpoints::Validation
10
+
11
+ base_uri 'https://api.exchangerate.host'
12
+
13
+ class << self
14
+ def available_options
15
+ required_options + optional_options
16
+ end
17
+
18
+ def endpoint_path
19
+ ''
20
+ end
21
+
22
+ def optional_options
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def required_options
27
+ []
28
+ end
29
+
30
+ def request(options)
31
+ @query = query_options(options)
32
+ res = get(
33
+ "/#{endpoint_path}",
34
+ { query: @query }
35
+ )
36
+ .body
37
+
38
+ res = JSON.parse(res) unless @query[:format]
39
+ res
40
+ end
41
+
42
+ def query_options(options)
43
+ config_options = ExchangerateHost.configurations.to_options_hash.slice(*available_options)
44
+ merged_options = config_options.merge(options)
45
+
46
+ validate(merged_options)
47
+ merged_options[:symbols] = to_upcase_csv(merged_options[:symbols]) if merged_options.key?(:symbols)
48
+
49
+ merged_options
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,20 @@
1
+ require 'exchangerate_host/endpoints/base'
2
+ module ExchangerateHost
3
+ module Endpoints
4
+ class ConvertCurrency < Base
5
+ class << self
6
+ def optional_options
7
+ [:base, :symbols, :places, :amount, :format, :date]
8
+ end
9
+
10
+ def required_options
11
+ [:from, :to]
12
+ end
13
+
14
+ def endpoint_path
15
+ 'convert'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'exchangerate_host/endpoints/base'
2
+ module ExchangerateHost
3
+ module Endpoints
4
+ class Fluctuation < Base
5
+ class << self
6
+ def optional_options
7
+ [:base, :symbols, :places, :amount, :format]
8
+ end
9
+
10
+ def required_options
11
+ [:start_date, :end_date]
12
+ end
13
+
14
+ def endpoint_path
15
+ 'fluctuation'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'exchangerate_host/endpoints/base'
2
+ module ExchangerateHost
3
+ module Endpoints
4
+ class HistoricalRates < Base
5
+ class << self
6
+ def optional_options
7
+ [:base, :symbols, :places, :amount, :format]
8
+ end
9
+
10
+ def required_options
11
+ [:date]
12
+ end
13
+
14
+ def endpoint_path
15
+ @query[:date]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ require 'exchangerate_host/endpoints/base'
2
+ module ExchangerateHost
3
+ module Endpoints
4
+ class LatestRates < Base
5
+ class << self
6
+ def optional_options
7
+ [:base, :symbols, :places, :amount, :format]
8
+ end
9
+
10
+ def endpoint_path
11
+ 'latest'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'exchangerate_host/endpoints/base'
2
+ module ExchangerateHost
3
+ module Endpoints
4
+ class SupportedSymbols < Base
5
+ class << self
6
+ def optional_options
7
+ [:format]
8
+ end
9
+
10
+ def endpoint_path
11
+ 'symbols'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require 'exchangerate_host/endpoints/base'
2
+ module ExchangerateHost
3
+ module Endpoints
4
+ class TimeSeries < Base
5
+ class << self
6
+ def optional_options
7
+ [:base, :symbols, :places, :amount, :format]
8
+ end
9
+
10
+ def required_options
11
+ [:start_date, :end_date]
12
+ end
13
+
14
+ def endpoint_path
15
+ 'timeseries'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,106 @@
1
+ module ExchangerateHost
2
+ module Endpoints
3
+ module Validation
4
+ ACCEPTABLE_DATE_FORMAT = /^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$/.freeze
5
+
6
+ VALID_SYMBOLS = [
7
+ :AED, :AFN, :ALL, :AMD, :ANG, :AOA, :ARS, :AUD, :AWG, :AZN, :BAM, :BBD,
8
+ :BDT, :BGN, :BHD, :BIF, :BMD, :BND, :BOB, :BRL, :BSD, :BTC, :BTN, :BWP,
9
+ :BYN, :BZD, :CAD, :CDF, :CHF, :CLF, :CLP, :CNH, :CNY, :COP, :CRC, :CUC,
10
+ :CUP, :CVE, :CZK, :DJF, :DKK, :DOP, :DZD, :EGP, :ERN, :ETB, :EUR, :FJD,
11
+ :FKP, :GBP, :GEL, :GGP, :GHS, :GIP, :GMD, :GNF, :GTQ, :GYD, :HKD, :HNL,
12
+ :HRK, :HTG, :HUF, :IDR, :ILS, :IMP, :INR, :IQD, :IRR, :ISK, :JEP, :JMD,
13
+ :JOD, :JPY, :KES, :KGS, :KHR, :KMF, :KPW, :KRW, :KWD, :KYD, :KZT, :LAK,
14
+ :LBP, :LKR, :LRD, :LSL, :LYD, :MAD, :MDL, :MGA, :MKD, :MMK, :MNT, :MOP,
15
+ :MRO, :MRU, :MUR, :MVR, :MWK, :MXN, :MYR, :MZN, :NAD, :NGN, :NIO, :NOK,
16
+ :NPR, :NZD, :OMR, :PAB, :PEN, :PGK, :PHP, :PKR, :PLN, :PYG, :QAR, :RON,
17
+ :RSD, :RUB, :RWF, :SAR, :SBD, :SCR, :SDG, :SEK, :SGD, :SHP, :SLL, :SOS,
18
+ :SRD, :SSP, :STD, :STN, :SVC, :SYP, :SZL, :THB, :TJS, :TMT, :TND, :TOP,
19
+ :TRY, :TTD, :TWD, :TZS, :UAH, :UGX, :USD, :UYU, :UZS, :VEF, :VES, :VND,
20
+ :VUV, :WST, :XAF, :XAG, :XAU, :XCD, :XDR, :XOF, :XPD, :XPF, :XPT, :YER,
21
+ :ZAR, :ZMW, :ZWL].freeze
22
+
23
+ VALID_FORMAT = [:CSV, :TSV, :XML].freeze
24
+
25
+ ALL_OPTIONS_WITH_VALIDATE_METHODS = {
26
+ from: { validate_with: VALID_SYMBOLS, action: :subset },
27
+ to: { validate_with: VALID_SYMBOLS, action: :subset },
28
+ date: { validate_with: ACCEPTABLE_DATE_FORMAT, action: :date },
29
+ start_date: { validate_with: ACCEPTABLE_DATE_FORMAT, action: :date },
30
+ end_date: { validate_with: ACCEPTABLE_DATE_FORMAT, action: :date },
31
+ base: { validate_with: VALID_SYMBOLS, action: :subset },
32
+ symbols: { validate_with: VALID_SYMBOLS, action: :subset },
33
+ places: { validate_with: :PositiveInteger, action: :type },
34
+ amount: { validate_with: :Numeric, action: :type },
35
+ format: { validate_with: VALID_FORMAT, action: :subset },
36
+ }.freeze
37
+
38
+ def to_upcase_csv(array)
39
+ return '' unless array
40
+
41
+ array.map(&:upcase).join(',')
42
+ end
43
+
44
+ def validate_with_type(option, validate_with, v)
45
+ if validate_with == :PositiveInteger
46
+ raise "#{option} must be a positive whole number" unless is_positive_integer?(v)
47
+ elsif validate_with == :Numeric
48
+ raise "#{option} must be a number" unless v.is_a?(Numeric)
49
+ end
50
+ end
51
+
52
+ def is_positive_integer?(v)
53
+ v.is_a?(Integer) && v > 0
54
+ end
55
+
56
+ def non_subset_values(array, original_array)
57
+ array.map(&:to_sym) - original_array
58
+ end
59
+
60
+ def validate_subset(array, original_array)
61
+ invalid_values = non_subset_values(array, original_array)
62
+ raise "Invalid options/values detected: #{invalid_values.join(', ')}" if invalid_values.any?
63
+ end
64
+
65
+ def validate_date(date)
66
+ raise 'Invalid date format. Please use YYYY-MM-DD' unless date =~ ACCEPTABLE_DATE_FORMAT
67
+ Date.parse(date) # this is only for validation purpose
68
+ end
69
+
70
+ def validate(options)
71
+ validate_options(options)
72
+
73
+ options.each do |option, value|
74
+ validate_option_values(option, value)
75
+ end
76
+ end
77
+
78
+ def validate_options(options)
79
+ option_keys = options.keys
80
+
81
+ validate_required_options(option_keys) if required_options.any?
82
+
83
+ return if option_keys.empty?
84
+ validate_subset(option_keys, available_options)
85
+ end
86
+
87
+ def validate_option_values(option, v)
88
+ validate_with = ALL_OPTIONS_WITH_VALIDATE_METHODS[option][:validate_with]
89
+ validate_action = ALL_OPTIONS_WITH_VALIDATE_METHODS[option][:action]
90
+
91
+ if validate_action == :subset
92
+ validate_subset(Array(v).map(&:upcase), validate_with)
93
+ elsif validate_action == :type
94
+ validate_with_type(option, validate_with, v)
95
+ elsif validate_action == :date
96
+ validate_date(v)
97
+ end
98
+ end
99
+
100
+ def validate_required_options(option_keys)
101
+ missing_required_options = required_options - option_keys
102
+ raise "Missing required options: #{missing_required_options.join(', ')}" if missing_required_options.any?
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,3 @@
1
+ module ExchangerateHost
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,50 @@
1
+ require 'exchangerate_host/endpoints/latest_rates'
2
+ require 'exchangerate_host/endpoints/supported_symbols'
3
+ require 'exchangerate_host/endpoints/convert_currency'
4
+ require 'exchangerate_host/endpoints/historical_rates'
5
+ require 'exchangerate_host/endpoints/time_series'
6
+ require 'exchangerate_host/endpoints/fluctuation'
7
+ require 'exchangerate_host/version'
8
+ require 'exchangerate_host/configurations'
9
+
10
+ module ExchangerateHost
11
+ class << self
12
+ attr_accessor :configurations
13
+
14
+ def configurations
15
+ @configurations ||= Configurations.new
16
+ end
17
+
18
+ def configure
19
+ yield(configurations)
20
+ end
21
+
22
+ def reset_configurations
23
+ @configurations = Configurations.new
24
+ end
25
+
26
+ def latest_rates(options = {})
27
+ Endpoints::LatestRates.request(options)
28
+ end
29
+
30
+ def convert_currency(options = {})
31
+ Endpoints::ConvertCurrency.request(options)
32
+ end
33
+
34
+ def historical_rates(options = {})
35
+ Endpoints::HistoricalRates.request(options)
36
+ end
37
+
38
+ def time_series(options = {})
39
+ Endpoints::TimeSeries.request(options)
40
+ end
41
+
42
+ def fluctuation(options = {})
43
+ Endpoints::Fluctuation.request(options)
44
+ end
45
+
46
+ def supported_symbols(options = {})
47
+ Endpoints::SupportedSymbols.request(options)
48
+ end
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exchangerate_host
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mizuki Zenta
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-09-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A Ruby library to integrate ruby application with exchangerate.host
28
+ email:
29
+ - mizukisb@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - ".rspec"
36
+ - ".rubocop.yml"
37
+ - ".travis.yml"
38
+ - Gemfile
39
+ - Gemfile.lock
40
+ - README.md
41
+ - Rakefile
42
+ - bin/console
43
+ - bin/setup
44
+ - exchangerate_host.gemspec
45
+ - lib/exchangerate_host.rb
46
+ - lib/exchangerate_host/configurations.rb
47
+ - lib/exchangerate_host/endpoints/base.rb
48
+ - lib/exchangerate_host/endpoints/convert_currency.rb
49
+ - lib/exchangerate_host/endpoints/fluctuation.rb
50
+ - lib/exchangerate_host/endpoints/historical_rates.rb
51
+ - lib/exchangerate_host/endpoints/latest_rates.rb
52
+ - lib/exchangerate_host/endpoints/supported_symbols.rb
53
+ - lib/exchangerate_host/endpoints/time_series.rb
54
+ - lib/exchangerate_host/endpoints/validation.rb
55
+ - lib/exchangerate_host/version.rb
56
+ homepage: https://github.com/MizukiZ/exchangerate_host
57
+ licenses:
58
+ - MIT
59
+ metadata:
60
+ homepage_uri: https://github.com/MizukiZ/exchangerate_host
61
+ source_code_uri: https://github.com/MizukiZ/exchangerate_host
62
+ changelog_uri: https://github.com/MizukiZ/exchangerate_host
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 2.3.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.1.4
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A Ruby library to integrate ruby application with exchangerate.host
82
+ test_files: []