Class: MX::Banxico::Historico::TipoDeCambio

Inherits:
Object
  • Object
show all
Includes:
HTTParty
Defined in:
lib/MX/Banxico/historico/tipo_de_cambio.rb

Overview

Clase para recuperar series históricas del tipo de cambio.

La información se obtiene de la página de Banxico, haciendo una petición POST que simula una consulta realizada por un usuario desde un navegador web.

Constant Summary collapse

URL =

URL de la página del Banco de México (Banxico).

"www.banxico.org.mx"
POST_PATH =

Ruta para realizar la petición POST para obtener la información.

"/SieInternet/consultarDirectorioInternetAction.do?accion=consultarSeries"
AÑO_INICIO_CONSULTA =

Año inicial de la consulta (por omisión).

2015
AÑO_FINAL_CONSULTA =

Año final de la consulta (por omisión).

2015
TIPOS_DE_CAMBIO =

Tipos de cambio soportados.

MX::Banxico::Series.tipos_de_cambio.keys.dup.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTipoDeCambio

Inicializador de la instancia.



62
63
64
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 62

def initialize
  @errores = nil
end

Instance Attribute Details

#erroresObject (readonly)

!@attribute [r] errores. @return [String] la descripción de los errores sucedidos al recuperar el histórico.



57
58
59
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 57

def errores
  @errores
end

Instance Method Details

#body_hash(serie, año_inicio_consulta = nil, año_termino_consulta = nil) ⇒ Hash (private)

Crea el cuerpo para la petición POST para recuperar el histórico de las series.

Parameters:

  • serie (String, Array<String>)

    el identificador o arreglo de identificadores de la serie o series de la que se desea recuperar el histórico.

  • año_inicio_consulta (Integer) (defaults to: nil)

    año de inicio de la consulta.

  • año_termino_consulta (Integer) (defaults to: nil)

    año de término de la consulta.

Returns:

  • (Hash)

    con los valores para el cuerpo de la petición POST.



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 247

def body_hash(serie, año_inicio_consulta = nil, año_termino_consulta = nil)
  { # Información recuparada de la petición POST al descargar desde la página web
    idCuadro: "CF102",
    sector: "6",
    version: "2",
    locale: "es",
    "formatoSDMX.x" => "41",
    "formatoSDMX.y" => "8",
    formatoHorizontal: false,
    metadatosWeb: true,
    tipoInformacion: nil,
    # años de consulta
    anoInicial: año_inicio_consulta || AÑO_INICIO_CONSULTA,
    anoFinal: año_termino_consulta || AÑO_FINAL_CONSULTA,
    # las series a descargar en la consulta
    series: serie
  }
end

#completo(año_inicio_consulta = nil, año_termino_consulta = nil) ⇒ Array<MX::Banxico::TipoDeCambio>?

Recupera todos los históricos de las series de tipo de cambio en Series.tipos_de_cambio.

Parameters:

  • año_inicio_consulta (Integer) (defaults to: nil)

    año de inicio de la consulta.

  • año_termino_consulta (Integer) (defaults to: nil)

    año de término de la consulta.

Returns:

  • (Array<MX::Banxico::TipoDeCambio>)

    si se recuperó la información, un arreglo con los tipos de cambio de la serie.

  • (nil)

    si hubo un error al recuperar la información.



104
105
106
107
108
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 104

def completo(año_inicio_consulta = nil, año_termino_consulta = nil)
  opts = { ids_series: MX::Banxico::Series.identificadores(:tipos_de_cambio) }
  body_str = hash_a_query_string(body_hash(opts[:ids_series], año_inicio_consulta, año_termino_consulta))
  return peticion_post(body_str, opts)
end

#de_serie(serie, año_inicio_consulta = nil, año_termino_consulta = nil) ⇒ Array<MX::Banxico::TipoDeCambio>?

Recupera el histórico de una serie de tipo de cambio de acuerdo al identificador o nombre de la serie dada.

Parameters:

  • serie (Symbol)

    el nombre de la serie de la que se desea recuperar el histórico.

  • año_inicio_consulta (Integer) (defaults to: nil)

    año de inicio de la consulta.

  • año_termino_consulta (Integer) (defaults to: nil)

    año de término de la consulta.

Returns:

  • (Array<MX::Banxico::TipoDeCambio>)

    si se recuperó la información, un arreglo con los tipos de cambio de la serie.

  • (nil)

    si hubo un error al recuperar la información.



84
85
86
87
88
89
90
91
92
93
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 84

def de_serie(serie, año_inicio_consulta = nil, año_termino_consulta = nil)
  begin
    id_serie = MX::Banxico::Series.identificador(:tipos_de_cambio, serie)
    opts = { id_serie: id_serie, serie: serie }
    return peticion_post(body_hash(id_serie, año_inicio_consulta, año_termino_consulta), opts)
  rescue ArgumentError => e
    registrar_error(e.message)
  end
  nil
end

#errores?Boolean

Indica si hay algún errror.

Returns:

  • (Boolean)

    true si hay errores, false de lo contrario.



70
71
72
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 70

def errores?
  !@errores.nil?
end

#hash_a_query_string(hsh) ⇒ String (private)

Convierte un Hash a una cadena de petición HTTP.

Examples:

Conversión del Hash

hsh = { un_param: "foo", otro_param: "bar", param_arreglo: ["a", "b"] }
hash_a_query_string(hsh) #=> "un_param=foo&otro_param=bar&param_arreglo=a&param_arreglo=b"

Parameters:

  • hsh (Hash)

    el Hash a convertir a cadena.

Returns:

  • (String)

    la cadena de petición.



277
278
279
280
281
282
283
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 277

def hash_a_query_string(hsh)
  pares = []
  hsh.each_pair do |k,v|
    v.is_a?(Array) ? v.each{ |val| pares << par_query_string(k, val) } : pares << par_query_string(k, v)
  end
  pares.join("&")
end

#header_hashHash (private)

Encabezado para la petición POST para recuperar el histórico de las series.

Returns:

  • (Hash)

    con los valores para el encabezado de la petición POST.



233
234
235
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 233

def header_hash
  { "Content-Type" => "application/x-www-form-urlencoded" }
end

#par_query_string(llave, valor) ⇒ String (private)

Crea un par de llave y valor escapado para URIs.

Parameters:

  • llave (String, Symbol)

    la llave del par.

  • valor (String, Symbol)

    el valor del par.

Returns:

  • (String)

    el par escapado para URI.



293
294
295
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 293

def par_query_string(llave, valor)
  "#{URI.escape llave.to_s}=#{URI.escape valor.to_s}"
end

#peticion_post(body, opts = {}) ⇒ Array<MX::Banxico::TipoDeCambio>? (private)

Realiza la petición POST de acuerdo al cuerpo dado.

Parameters:

  • body (Hash, String)

    el cuerpo de la petición. Usamos una cadena cuando algún parámetro tiene como valor un arreglo, de lo contrario se usa un Hash.

  • opts (Hash) (defaults to: {})

    información adicional para procesar la respuesta.

Returns:

  • (Array<MX::Banxico::TipoDeCambio>)

    si se recuperó la información, un arreglo con los tipos de cambio de la serie.

  • (nil)

    si hubo un error al recuperar la información.



123
124
125
126
127
128
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 123

def peticion_post(body, opts = {})
  respuesta = self.class.post(POST_PATH, headers: header_hash, body: body)
  return procesar_respuesta(respuesta.body, opts) if respuesta.code == 200
  registrar_error("código de error #{respuesta.code}.")
  nil
end

#procesar_nodos_obs(nodos_obs, nombre_serie) ⇒ Array<MX::Banxico::TipoDeCambio> (private)

Procesa los nodos XML de la respuesta de la petición que contiene la información del tipo de cambio.

Parameters:

  • nodos_obs (Nokogiri::XML::Nodes)

    nodos de la serie con el valor y fecha del tipo de cambio (bm:Obs).

  • nombre_serie (Symbol)

    el nombre tipo de cambio solicitado. Ver TIPOS_DE_CAMBIO.

Returns:



203
204
205
206
207
208
209
210
211
212
213
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 203

def procesar_nodos_obs(nodos_obs, nombre_serie)
  historico = []
  nodos_obs.each do |nodo_obs|
    begin
      tdc = MX::Banxico::TipoDeCambio.new(nombre_serie, nodo_obs[:TIME_PERIOD], nodo_obs[:OBS_VALUE])
      historico << tdc
    rescue ArgumentError
    end
  end
  historico
end

#procesar_respuesta(respuesta, opts = {}) ⇒ Array<MX::Banxico::TipoDeCambio>? (private)

Procesa la respuesta de la petición, que es un Hash generado por HTTParty.

Parameters:

  • respuesta (String)

    la respuesta de la petición realizada.

  • opts (Hash) (defaults to: {})

    información adicional para procesar la respuesta.

Returns:

  • (Array<MX::Banxico::TipoDeCambio>)

    si no hubo errores, un arreglo con los tipos de cambio encontrados en la respuesta.

  • (nil)

    si hubo un error al recuperar la información.



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 139

def procesar_respuesta(respuesta, opts = {})
  xml_doc = Nokogiri::XML(respuesta, nil, 'UTF-8')
  if xml_doc.root.respond_to?(:xpath)
    if opts[:id_serie] && opts[:serie]
      return procesar_serie(xml_doc, opts[:id_serie], opts[:serie])
    else
      return procesar_series(xml_doc, opts[:ids_series])
    end
  else
    registrar_error("La respuesta de la petición no tiene el formato correcto.")
  end
  nil
end

#procesar_serie(xml_doc, id_serie, serie) ⇒ Array<MX::Banxico::TipoDeCambio>? (private)

Procesa el XML, buscando la serie del identificador dado.

Parameters:

  • xml_doc (Nokogiri::XML::Document)

    el XML con la respuesta de la petición realizada.

  • id_serie (String)

    el identificador de la serie.

  • serie (Symbol)

    el nombre de la serie.

Returns:

  • (Array<MX::Banxico::TipoDeCambio>)

    si no hubo errores, un arreglo con los tipos de cambio encontrados en la respuesta.

  • (nil)

    si hubo un error al recuperar la información.



163
164
165
166
167
168
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 163

def procesar_serie(xml_doc, id_serie, serie)
  historico = procesar_nodos_obs(xml_doc.root.xpath(%Q{bm:DataSet/bm:Series[@IDSERIE="#{id_serie}"]/bm:Obs}), serie)
  return historico unless historico.empty?
  registrar_error("No se encontró información de la serie #{serie} dentro de la respuesta de la petición.")
  nil
end

#procesar_series(xml_doc, ids_series) ⇒ Array<MX::Banxico::TipoDeCambio>? (private)

Procesa el XML de las series encontradas.

Parameters:

  • xml_doc (Nokogiri::XML::Document)

    el XML con la respuesta de la petición realizada.

Returns:

  • (Array<MX::Banxico::TipoDeCambio>)

    si no hubo errores, un arreglo con los tipos de cambio encontrados en la respuesta.

  • (nil)

    si hubo un error al recuperar la información.



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 178

def procesar_series(xml_doc, ids_series)
  historico = []
  xml_doc.root.xpath("bm:DataSet/bm:Series").each do |serie|
    if serie["IDSERIE"] && ids_series.include?(serie["IDSERIE"])
      begin
        nombre_serie = MX::Banxico::Series.nombre(serie["IDSERIE"], :tipos_de_cambio)
        historico += procesar_nodos_obs(serie.xpath("bm:Obs"), nombre_serie)
      rescue ArgumentError # no se encontró el nombre de la serie
      rescue Nokogiri::XML::XPath::SyntaxError # no jaló el xpath
      end
    end
  end
  return historico.sort unless historico.empty?
  registrar_error("No se encontraron series en la respuesta.")  
  nil
end

#registrar_error(error, desc = nil) ⇒ String (private)

Registra el mensaje de error en #errores.

Parameters:

  • error (String)

    el error que se agregará a #errores

  • desc (String) (defaults to: nil)

    descripción del error.

Returns:

  • (String)

    la nueva cadena de error.



223
224
225
226
# File 'lib/MX/Banxico/historico/tipo_de_cambio.rb', line 223

def registrar_error(error, desc = nil)
  err = "Error#{(desc.nil? or desc.empty?) ? "" : " (#{desc})"}: #{error}"
  @errores = errores? ? "#{@errores} #{err}" : err
end