bcn_ni 0.1.7 → 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 +2 -1
- data/lib/bcn_ni/core.rb +1 -1
- data/lib/bcn_ni/helpers/request.rb +59 -35
- data/lib/bcn_ni/version.rb +1 -1
- metadata +53 -18
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', '~> 0.1.
|
|
73
|
+
gem 'bcn_ni', '~> 0.1.8'
|
|
74
74
|
|
|
75
75
|
# Using gem install
|
|
76
76
|
gem install bcn_ni
|
|
@@ -85,6 +85,7 @@ $ bundle
|
|
|
85
85
|
## Changelog
|
|
86
86
|
|
|
87
87
|
```
|
|
88
|
+
2026.05.31 - SOAP request fallback for modern OpenSSL, XML parsing fixes, and test updates
|
|
88
89
|
2022.04.06 - Rails 6.x 7.x compatibility
|
|
89
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
|
|
90
91
|
```
|
data/lib/bcn_ni/core.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'net/https'
|
|
|
4
4
|
require 'nokogiri'
|
|
5
5
|
require 'open-uri'
|
|
6
6
|
require 'json'
|
|
7
|
+
require 'open3'
|
|
7
8
|
require 'active_support/core_ext/hash'
|
|
8
9
|
begin
|
|
9
10
|
require 'active_support/time'
|
|
@@ -93,7 +94,7 @@ module BcnNi
|
|
|
93
94
|
|
|
94
95
|
# Generate the full url
|
|
95
96
|
full_url = request_url + '?' + args.to_param
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
# This loop prevents a random EOFError (main cause is not known yet)
|
|
98
99
|
retries = 0
|
|
99
100
|
response = nil
|
|
@@ -102,10 +103,10 @@ module BcnNi
|
|
|
102
103
|
raise StopIteration if retries >= 5
|
|
103
104
|
|
|
104
105
|
begin
|
|
105
|
-
response = open(full_url,
|
|
106
|
+
response = URI.open(full_url, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
|
106
107
|
# Exit loop if response has been assigned
|
|
107
108
|
break
|
|
108
|
-
rescue EOFError
|
|
109
|
+
rescue EOFError
|
|
109
110
|
# Sum retry and sleep the thread for a while
|
|
110
111
|
retries += 1
|
|
111
112
|
sleep(2.seconds)
|
|
@@ -145,67 +146,90 @@ module BcnNi
|
|
|
145
146
|
|
|
146
147
|
def soap__exchange_day(year, month, day)
|
|
147
148
|
# Build body through a XML envelope
|
|
148
|
-
body
|
|
149
|
-
|
|
149
|
+
body = soap__envelope(soap__letter_exchange_day(year, month, day))
|
|
150
|
+
xml_response = soap__request(body)
|
|
151
|
+
doc = Nokogiri::XML(xml_response)
|
|
150
152
|
|
|
151
|
-
# Get the result value
|
|
152
|
-
value_result = json_response['Envelope']['Body']['RecuperaTC_DiaResponse']['RecuperaTC_DiaResult']
|
|
153
153
|
# Parse the result value and finally return it
|
|
154
|
+
value_result = doc.at_xpath("//*[local-name()='RecuperaTC_DiaResult']")&.text
|
|
154
155
|
return value_result.to_f
|
|
155
156
|
end
|
|
156
157
|
|
|
157
158
|
def soap__exchange_month(year, month)
|
|
158
159
|
# Build body through a XML envelope
|
|
159
|
-
body
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
else
|
|
172
|
-
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
|
+
}
|
|
173
172
|
end
|
|
173
|
+
|
|
174
|
+
# Sort the parsed table and finally return it
|
|
175
|
+
return parsed_table.sort_by { |x| x[:date] }
|
|
174
176
|
end
|
|
175
177
|
|
|
176
178
|
def soap__request(body)
|
|
177
|
-
|
|
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)
|
|
178
193
|
uri = URI.parse(request_url)
|
|
179
|
-
# Create protocol to the URI
|
|
180
194
|
request_engine = Net::HTTP.new(uri.host, uri.port)
|
|
181
195
|
request_engine.use_ssl = true
|
|
182
196
|
request_engine.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
183
197
|
request_engine.open_timeout = 15.seconds
|
|
198
|
+
request_engine.read_timeout = 15.seconds
|
|
184
199
|
|
|
185
|
-
# Create a new POST request as XML content type
|
|
186
200
|
req = Net::HTTP::Post.new(uri.path)
|
|
187
201
|
req['Content-Type'] = 'text/xml'
|
|
202
|
+
req.body = body
|
|
188
203
|
|
|
189
|
-
# Set the request body as a RAW SOAP XML request
|
|
190
|
-
req.body = body
|
|
191
|
-
# Process the request
|
|
192
204
|
res = request_engine.request(req)
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
return res.body
|
|
206
|
+
end
|
|
195
207
|
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
198
223
|
end
|
|
199
224
|
|
|
200
225
|
def soap__envelope(letter)
|
|
201
|
-
envelope =
|
|
202
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
226
|
+
envelope = <<~XML
|
|
203
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/">
|
|
204
228
|
<soap:Body>
|
|
205
229
|
#{letter}
|
|
206
230
|
</soap:Body>
|
|
207
231
|
</soap:Envelope>
|
|
208
|
-
XML
|
|
232
|
+
XML
|
|
209
233
|
return envelope
|
|
210
234
|
end
|
|
211
235
|
|
|
@@ -231,4 +255,4 @@ XML
|
|
|
231
255
|
end
|
|
232
256
|
|
|
233
257
|
end
|
|
234
|
-
end
|
|
258
|
+
end
|
data/lib/bcn_ni/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
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
|
|
@@ -34,44 +33,82 @@ dependencies:
|
|
|
34
33
|
name: nokogiri
|
|
35
34
|
requirement: !ruby/object:Gem::Requirement
|
|
36
35
|
requirements:
|
|
37
|
-
- - "
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '1.14'
|
|
39
|
+
- - "<"
|
|
38
40
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
41
|
+
version: '2.0'
|
|
40
42
|
type: :runtime
|
|
41
43
|
prerelease: false
|
|
42
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
45
|
requirements:
|
|
44
|
-
- - "
|
|
46
|
+
- - ">="
|
|
45
47
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '1.
|
|
48
|
+
version: '1.14'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '2.0'
|
|
47
52
|
- !ruby/object:Gem::Dependency
|
|
48
53
|
name: rake
|
|
49
54
|
requirement: !ruby/object:Gem::Requirement
|
|
50
55
|
requirements:
|
|
51
|
-
- - "
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '13.0'
|
|
59
|
+
- - "<"
|
|
52
60
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
61
|
+
version: '14.0'
|
|
54
62
|
type: :development
|
|
55
63
|
prerelease: false
|
|
56
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
65
|
requirements:
|
|
58
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '13.0'
|
|
69
|
+
- - "<"
|
|
59
70
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
71
|
+
version: '14.0'
|
|
61
72
|
- !ruby/object:Gem::Dependency
|
|
62
73
|
name: rspec
|
|
63
74
|
requirement: !ruby/object:Gem::Requirement
|
|
64
75
|
requirements:
|
|
65
|
-
- - "
|
|
76
|
+
- - ">="
|
|
66
77
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '3.
|
|
78
|
+
version: '3.12'
|
|
79
|
+
- - "<"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '4.0'
|
|
68
82
|
type: :development
|
|
69
83
|
prerelease: false
|
|
70
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
85
|
requirements:
|
|
72
|
-
- - "
|
|
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
|
+
- - "<"
|
|
73
110
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '
|
|
111
|
+
version: '12.0'
|
|
75
112
|
description: This gem provides NIO (Córdoba Oro Nicaragüense) against USD (United
|
|
76
113
|
States Dollar) money exchange rates consuming the official Central Bank of Nicaragüa
|
|
77
114
|
(BCN) SOAP Service or HTML page
|
|
@@ -94,7 +131,6 @@ homepage: https://github.com/mldoscar/bcn_ni
|
|
|
94
131
|
licenses:
|
|
95
132
|
- MIT
|
|
96
133
|
metadata: {}
|
|
97
|
-
post_install_message:
|
|
98
134
|
rdoc_options: []
|
|
99
135
|
require_paths:
|
|
100
136
|
- lib
|
|
@@ -109,8 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
145
|
- !ruby/object:Gem::Version
|
|
110
146
|
version: '0'
|
|
111
147
|
requirements: []
|
|
112
|
-
rubygems_version: 3.
|
|
113
|
-
signing_key:
|
|
148
|
+
rubygems_version: 3.6.3
|
|
114
149
|
specification_version: 4
|
|
115
150
|
summary: This tool pretends to be helpful for developers who can request exchange
|
|
116
151
|
rates from Nicaragua in a easier way
|