bcn_ni 0.1.6 → 0.1.8
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 +4 -4
- data/README.md +3 -1
- data/lib/bcn_ni/core.rb +1 -1
- data/lib/bcn_ni/helpers/request.rb +69 -40
- data/lib/bcn_ni/version.rb +1 -1
- metadata +61 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f096b5bf6ea7fa64c15436453d3f13259a916b383f305066f50989eaab85dfc
|
|
4
|
+
data.tar.gz: 6466f3159fb88a11248a532a6bb626c24b69c67aa25f0ff9cf644c7796d01116
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d79136f6bcd9d1838cdea7baea3da138e76ce727bff4afb2bb1290ca5efb0dca0f4b298cc6493de97ac3c1a858b9db5afa951c93b6679b883a3fea485d7040e
|
|
7
|
+
data.tar.gz: 34587ba34e542d02ae192e48b795af3090b229b2d0a47a8e96024d44303d8ffe3cac725aa714e9c18093e500e9033f6d6dd9ba7200631dc9e1492faf2cb41d49
|
data/README.md
CHANGED
|
@@ -70,7 +70,7 @@ Add this line to your application's Gemfile:
|
|
|
70
70
|
gem 'bcn_ni', git: 'https://github.com/mldoscar/bcn_ni', branch: 'master'
|
|
71
71
|
|
|
72
72
|
# From ruby gems
|
|
73
|
-
gem 'bcn_ni', '
|
|
73
|
+
gem 'bcn_ni', '~> 0.1.8'
|
|
74
74
|
|
|
75
75
|
# Using gem install
|
|
76
76
|
gem install bcn_ni
|
|
@@ -85,6 +85,8 @@ $ bundle
|
|
|
85
85
|
## Changelog
|
|
86
86
|
|
|
87
87
|
```
|
|
88
|
+
2026.05.31 - SOAP request fallback for modern OpenSSL, XML parsing fixes, and test updates
|
|
89
|
+
2022.04.06 - Rails 6.x 7.x compatibility
|
|
88
90
|
2021.06.28 - Bugfix: Cambio en el URI para el request de scrapping, el BCN cambió de parámetros y ubicación de URL
|
|
89
91
|
```
|
|
90
92
|
|
data/lib/bcn_ni/core.rb
CHANGED
|
@@ -3,8 +3,14 @@ require 'net/http'
|
|
|
3
3
|
require 'net/https'
|
|
4
4
|
require 'nokogiri'
|
|
5
5
|
require 'open-uri'
|
|
6
|
-
require 'active_support/core_ext/hash'
|
|
7
6
|
require 'json'
|
|
7
|
+
require 'open3'
|
|
8
|
+
require 'active_support/core_ext/hash'
|
|
9
|
+
begin
|
|
10
|
+
require 'active_support/time'
|
|
11
|
+
rescue LoadError
|
|
12
|
+
end
|
|
13
|
+
|
|
8
14
|
|
|
9
15
|
module BcnNi
|
|
10
16
|
class Request
|
|
@@ -88,7 +94,7 @@ module BcnNi
|
|
|
88
94
|
|
|
89
95
|
# Generate the full url
|
|
90
96
|
full_url = request_url + '?' + args.to_param
|
|
91
|
-
|
|
97
|
+
|
|
92
98
|
# This loop prevents a random EOFError (main cause is not known yet)
|
|
93
99
|
retries = 0
|
|
94
100
|
response = nil
|
|
@@ -97,10 +103,10 @@ module BcnNi
|
|
|
97
103
|
raise StopIteration if retries >= 5
|
|
98
104
|
|
|
99
105
|
begin
|
|
100
|
-
response = open(full_url,
|
|
106
|
+
response = URI.open(full_url, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
|
101
107
|
# Exit loop if response has been assigned
|
|
102
108
|
break
|
|
103
|
-
rescue EOFError
|
|
109
|
+
rescue EOFError
|
|
104
110
|
# Sum retry and sleep the thread for a while
|
|
105
111
|
retries += 1
|
|
106
112
|
sleep(2.seconds)
|
|
@@ -140,90 +146,113 @@ module BcnNi
|
|
|
140
146
|
|
|
141
147
|
def soap__exchange_day(year, month, day)
|
|
142
148
|
# Build body through a XML envelope
|
|
143
|
-
body
|
|
144
|
-
|
|
149
|
+
body = soap__envelope(soap__letter_exchange_day(year, month, day))
|
|
150
|
+
xml_response = soap__request(body)
|
|
151
|
+
doc = Nokogiri::XML(xml_response)
|
|
145
152
|
|
|
146
|
-
# Get the result value
|
|
147
|
-
value_result = json_response['Envelope']['Body']['RecuperaTC_DiaResponse']['RecuperaTC_DiaResult']
|
|
148
153
|
# Parse the result value and finally return it
|
|
154
|
+
value_result = doc.at_xpath("//*[local-name()='RecuperaTC_DiaResult']")&.text
|
|
149
155
|
return value_result.to_f
|
|
150
156
|
end
|
|
151
157
|
|
|
152
158
|
def soap__exchange_month(year, month)
|
|
153
159
|
# Build body through a XML envelope
|
|
154
|
-
body
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
else
|
|
167
|
-
return []
|
|
160
|
+
body = soap__envelope(soap__letter_exchange_month(year, month))
|
|
161
|
+
xml_response = soap__request(body)
|
|
162
|
+
doc = Nokogiri::XML(xml_response)
|
|
163
|
+
|
|
164
|
+
# Parse the table to a custom and better JSON
|
|
165
|
+
# The format example will be: {date: as Date, value: as Float}
|
|
166
|
+
exchange_rows = doc.xpath("//*[local-name()='Detalle_TC']/*[local-name()='Tc']")
|
|
167
|
+
parsed_table = exchange_rows.map do |row|
|
|
168
|
+
{
|
|
169
|
+
date: Date.parse(row.at_xpath("*[local-name()='Fecha']")&.text.to_s),
|
|
170
|
+
value: row.at_xpath("*[local-name()='Valor']")&.text.to_f
|
|
171
|
+
}
|
|
168
172
|
end
|
|
173
|
+
|
|
174
|
+
# Sort the parsed table and finally return it
|
|
175
|
+
return parsed_table.sort_by { |x| x[:date] }
|
|
169
176
|
end
|
|
170
177
|
|
|
171
178
|
def soap__request(body)
|
|
172
|
-
|
|
179
|
+
xml_response = begin
|
|
180
|
+
soap__request_with_net_http(body)
|
|
181
|
+
rescue OpenSSL::SSL::SSLError => e
|
|
182
|
+
# BCN SOAP endpoint currently negotiates TLS in a way OpenSSL 3 may reject.
|
|
183
|
+
# Fallback to curl keeps compatibility in modern Ruby runtimes.
|
|
184
|
+
raise unless e.message.to_s.downcase.include?('unsupported protocol')
|
|
185
|
+
|
|
186
|
+
soap__request_with_curl(body)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
return xml_response
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def soap__request_with_net_http(body)
|
|
173
193
|
uri = URI.parse(request_url)
|
|
174
|
-
# Create protocol to the URI
|
|
175
194
|
request_engine = Net::HTTP.new(uri.host, uri.port)
|
|
176
195
|
request_engine.use_ssl = true
|
|
177
196
|
request_engine.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
178
197
|
request_engine.open_timeout = 15.seconds
|
|
198
|
+
request_engine.read_timeout = 15.seconds
|
|
179
199
|
|
|
180
|
-
# Create a new POST request as XML content type
|
|
181
200
|
req = Net::HTTP::Post.new(uri.path)
|
|
182
201
|
req['Content-Type'] = 'text/xml'
|
|
202
|
+
req.body = body
|
|
183
203
|
|
|
184
|
-
# Set the request body as a RAW SOAP XML request
|
|
185
|
-
req.body = body
|
|
186
|
-
# Process the request
|
|
187
204
|
res = request_engine.request(req)
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
return res.body
|
|
206
|
+
end
|
|
190
207
|
|
|
191
|
-
|
|
192
|
-
|
|
208
|
+
def soap__request_with_curl(body)
|
|
209
|
+
stdout, stderr, status = Open3.capture3(
|
|
210
|
+
'curl',
|
|
211
|
+
'--silent',
|
|
212
|
+
'--show-error',
|
|
213
|
+
'--insecure',
|
|
214
|
+
'--header', 'Content-Type: text/xml',
|
|
215
|
+
'--data-binary', '@-',
|
|
216
|
+
request_url,
|
|
217
|
+
stdin_data: body
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
raise StandardError, "curl SOAP request failed: #{stderr.strip}" unless status.success?
|
|
221
|
+
|
|
222
|
+
return stdout
|
|
193
223
|
end
|
|
194
224
|
|
|
195
225
|
def soap__envelope(letter)
|
|
196
|
-
envelope =
|
|
197
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
226
|
+
envelope = <<~XML
|
|
198
227
|
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
|
199
228
|
<soap:Body>
|
|
200
229
|
#{letter}
|
|
201
230
|
</soap:Body>
|
|
202
231
|
</soap:Envelope>
|
|
203
|
-
|
|
232
|
+
XML
|
|
204
233
|
return envelope
|
|
205
234
|
end
|
|
206
235
|
|
|
207
236
|
def soap__letter_exchange_day(year, month, day)
|
|
208
|
-
letter =
|
|
237
|
+
letter = <<-XML
|
|
209
238
|
<RecuperaTC_Dia xmlns="http://servicios.bcn.gob.ni/">
|
|
210
239
|
<Ano>#{year}</Ano>
|
|
211
240
|
<Mes>#{month}</Mes>
|
|
212
241
|
<Dia>#{day}</Dia>
|
|
213
242
|
</RecuperaTC_Dia>
|
|
214
|
-
|
|
243
|
+
XML
|
|
215
244
|
return letter
|
|
216
245
|
end
|
|
217
246
|
|
|
218
247
|
def soap__letter_exchange_month(year, month)
|
|
219
|
-
letter =
|
|
248
|
+
letter = <<-XML
|
|
220
249
|
<RecuperaTC_Mes xmlns="http://servicios.bcn.gob.ni/">
|
|
221
250
|
<Ano>#{year}</Ano>
|
|
222
251
|
<Mes>#{month}</Mes>
|
|
223
252
|
</RecuperaTC_Mes>
|
|
224
|
-
|
|
253
|
+
XML
|
|
225
254
|
return letter
|
|
226
255
|
end
|
|
227
256
|
|
|
228
257
|
end
|
|
229
|
-
end
|
|
258
|
+
end
|
data/lib/bcn_ni/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,71 +1,114 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bcn_ni
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oscar Rodriguez
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2026-06-01 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activesupport
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
18
|
version: '5.2'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '8.0'
|
|
20
22
|
type: :runtime
|
|
21
23
|
prerelease: false
|
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
25
|
requirements:
|
|
24
|
-
- - "
|
|
26
|
+
- - ">="
|
|
25
27
|
- !ruby/object:Gem::Version
|
|
26
28
|
version: '5.2'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '8.0'
|
|
27
32
|
- !ruby/object:Gem::Dependency
|
|
28
33
|
name: nokogiri
|
|
29
34
|
requirement: !ruby/object:Gem::Requirement
|
|
30
35
|
requirements:
|
|
31
|
-
- - "
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '1.14'
|
|
39
|
+
- - "<"
|
|
32
40
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
41
|
+
version: '2.0'
|
|
34
42
|
type: :runtime
|
|
35
43
|
prerelease: false
|
|
36
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
45
|
requirements:
|
|
38
|
-
- - "
|
|
46
|
+
- - ">="
|
|
39
47
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '1.
|
|
48
|
+
version: '1.14'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '2.0'
|
|
41
52
|
- !ruby/object:Gem::Dependency
|
|
42
53
|
name: rake
|
|
43
54
|
requirement: !ruby/object:Gem::Requirement
|
|
44
55
|
requirements:
|
|
45
|
-
- - "
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '13.0'
|
|
59
|
+
- - "<"
|
|
46
60
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
61
|
+
version: '14.0'
|
|
48
62
|
type: :development
|
|
49
63
|
prerelease: false
|
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
65
|
requirements:
|
|
52
|
-
- - "
|
|
66
|
+
- - ">="
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
68
|
+
version: '13.0'
|
|
69
|
+
- - "<"
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '14.0'
|
|
55
72
|
- !ruby/object:Gem::Dependency
|
|
56
73
|
name: rspec
|
|
57
74
|
requirement: !ruby/object:Gem::Requirement
|
|
58
75
|
requirements:
|
|
59
|
-
- - "
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '3.12'
|
|
79
|
+
- - "<"
|
|
60
80
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
81
|
+
version: '4.0'
|
|
62
82
|
type: :development
|
|
63
83
|
prerelease: false
|
|
64
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
85
|
requirements:
|
|
66
|
-
- - "
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '3.12'
|
|
89
|
+
- - "<"
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '4.0'
|
|
92
|
+
- !ruby/object:Gem::Dependency
|
|
93
|
+
name: byebug
|
|
94
|
+
requirement: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '11.0'
|
|
99
|
+
- - "<"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '12.0'
|
|
102
|
+
type: :development
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - ">="
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: '11.0'
|
|
109
|
+
- - "<"
|
|
67
110
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
111
|
+
version: '12.0'
|
|
69
112
|
description: This gem provides NIO (Córdoba Oro Nicaragüense) against USD (United
|
|
70
113
|
States Dollar) money exchange rates consuming the official Central Bank of Nicaragüa
|
|
71
114
|
(BCN) SOAP Service or HTML page
|
|
@@ -88,7 +131,6 @@ homepage: https://github.com/mldoscar/bcn_ni
|
|
|
88
131
|
licenses:
|
|
89
132
|
- MIT
|
|
90
133
|
metadata: {}
|
|
91
|
-
post_install_message:
|
|
92
134
|
rdoc_options: []
|
|
93
135
|
require_paths:
|
|
94
136
|
- lib
|
|
@@ -103,8 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
103
145
|
- !ruby/object:Gem::Version
|
|
104
146
|
version: '0'
|
|
105
147
|
requirements: []
|
|
106
|
-
rubygems_version: 3.
|
|
107
|
-
signing_key:
|
|
148
|
+
rubygems_version: 3.6.3
|
|
108
149
|
specification_version: 4
|
|
109
150
|
summary: This tool pretends to be helpful for developers who can request exchange
|
|
110
151
|
rates from Nicaragua in a easier way
|