correios-frete-alternative 1.10.2
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 +7 -0
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.rdoc +107 -0
- data/Gemfile +2 -0
- data/README.rdoc +364 -0
- data/Rakefile +25 -0
- data/bin/console +12 -0
- data/correios-frete-alternative.gemspec +31 -0
- data/lib/correios-frete-alternative.rb +9 -0
- data/lib/correios/frete.rb +19 -0
- data/lib/correios/frete/calculador.rb +69 -0
- data/lib/correios/frete/pacote.rb +69 -0
- data/lib/correios/frete/pacote_item.rb +27 -0
- data/lib/correios/frete/parser.rb +20 -0
- data/lib/correios/frete/servico.rb +97 -0
- data/lib/correios/frete/version.rb +6 -0
- data/lib/correios/frete/web_service.rb +87 -0
- data/misc/correios_frete_manual_v1.7.pdf +0 -0
- data/misc/correios_frete_manual_v1.9.pdf +0 -0
- data/spec/correios/frete/calculador_spec.rb +174 -0
- data/spec/correios/frete/pacote_item_spec.rb +45 -0
- data/spec/correios/frete/pacote_spec.rb +188 -0
- data/spec/correios/frete/parser_spec.rb +44 -0
- data/spec/correios/frete/servico_spec.rb +185 -0
- data/spec/correios/frete/web_service_spec.rb +18 -0
- data/spec/correios/frete_spec.rb +79 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/mock_request.rb +17 -0
- data/spec/support/responses/success-response-many-services.xml +27 -0
- data/spec/support/responses/success-response-one-service.xml +15 -0
- metadata +187 -0
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
6
|
+
spec.pattern = FileList["spec/**/*_spec.rb"]
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
10
|
+
spec.pattern = "spec/**/*_spec.rb"
|
11
|
+
spec.rcov = true
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :spec
|
15
|
+
|
16
|
+
require 'rdoc/task'
|
17
|
+
Rake::RDocTask.new do |rdoc|
|
18
|
+
version = File.exist?("VERSION") ? File.read("VERSION") : ""
|
19
|
+
|
20
|
+
rdoc.rdoc_dir = "rdoc"
|
21
|
+
rdoc.title = "correios-frete #{version}"
|
22
|
+
rdoc.rdoc_files.include("README*")
|
23
|
+
rdoc.rdoc_files.include("CHANGELOG*")
|
24
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
25
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'correios/frete/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "correios-frete-alternative"
|
7
|
+
gem.version = Correios::Frete::VERSION
|
8
|
+
gem.authors = ["Fernando Hamasaki de Amorim"]
|
9
|
+
gem.email = ["prodis@gmail.com"]
|
10
|
+
gem.summary = "Calculo de frete dos Correios."
|
11
|
+
gem.description = "Calculo de frete utilizando o Web Service dos Correios (http://www.correios.com.br/webservices).\nOs servicos de frete suportados sao PAC, SEDEX, SEDEX a Cobrar, SEDEX 10, SEDEX Hoje e e-SEDEX."
|
12
|
+
gem.homepage = "http://prodis.blog.br/correios-frete-gem-para-calculo-de-frete-dos-correios"
|
13
|
+
gem.licenses = ["MIT"]
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.platform = Gem::Platform::RUBY
|
21
|
+
gem.required_ruby_version = Gem::Requirement.new(">= 1.9.3")
|
22
|
+
|
23
|
+
gem.add_dependency "log-me", "~> 0.0.10"
|
24
|
+
gem.add_dependency "nokogiri"
|
25
|
+
gem.add_dependency "sax-machine", "~> 1.3"
|
26
|
+
|
27
|
+
gem.add_development_dependency "rake", "~> 12.0"
|
28
|
+
gem.add_development_dependency "pry", "~> 0.10"
|
29
|
+
gem.add_development_dependency "rspec", "~> 3.5"
|
30
|
+
gem.add_development_dependency "webmock", "~> 3.0"
|
31
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'correios/frete'
|
4
|
+
require 'correios/frete/calculador'
|
5
|
+
require 'correios/frete/pacote'
|
6
|
+
require 'correios/frete/pacote_item'
|
7
|
+
require 'correios/frete/parser'
|
8
|
+
require 'correios/frete/servico'
|
9
|
+
require 'correios/frete/web_service'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'log-me'
|
3
|
+
|
4
|
+
module Correios
|
5
|
+
module Frete
|
6
|
+
extend LogMe
|
7
|
+
|
8
|
+
module Timeout
|
9
|
+
DEFAULT_REQUEST_TIMEOUT = 10 #seconds
|
10
|
+
attr_writer :request_timeout
|
11
|
+
|
12
|
+
def request_timeout
|
13
|
+
(@request_timeout ||= DEFAULT_REQUEST_TIMEOUT).to_i
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
extend Timeout
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Correios
|
3
|
+
module Frete
|
4
|
+
class Calculador
|
5
|
+
attr_accessor :cep_origem, :cep_destino
|
6
|
+
attr_accessor :diametro, :mao_propria, :aviso_recebimento, :valor_declarado
|
7
|
+
attr_accessor :codigo_empresa, :senha, :encomenda
|
8
|
+
attr_writer :peso, :comprimento, :largura, :altura, :formato
|
9
|
+
|
10
|
+
DEFAULT_OPTIONS = {
|
11
|
+
:peso => 0.0,
|
12
|
+
:comprimento => 0.0,
|
13
|
+
:largura => 0.0,
|
14
|
+
:altura => 0.0,
|
15
|
+
:diametro => 0.0,
|
16
|
+
:formato => :caixa_pacote,
|
17
|
+
:mao_propria => false,
|
18
|
+
:aviso_recebimento => false,
|
19
|
+
:valor_declarado => 0.0
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize(options = {})
|
23
|
+
DEFAULT_OPTIONS.merge(options).each do |attr, value|
|
24
|
+
self.send("#{attr}=", value)
|
25
|
+
end
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
[:peso, :comprimento, :largura, :altura, :formato].each do |method|
|
31
|
+
define_method method do
|
32
|
+
@encomenda ? @encomenda.send(method) : instance_variable_get("@#{method}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def calcular(*service_types)
|
37
|
+
response = web_service(service_types).request!
|
38
|
+
services = parser.servicos(response)
|
39
|
+
|
40
|
+
if service_types.size == 1
|
41
|
+
services.values.first
|
42
|
+
else
|
43
|
+
services
|
44
|
+
end
|
45
|
+
end
|
46
|
+
alias calculate calcular
|
47
|
+
|
48
|
+
def method_missing(method_name, *args)
|
49
|
+
return calcular($2.to_sym) if method_name.to_s =~ /^(calcular|calculate)_(.*)/ && Correios::Frete::Servico.code_from_type($2.to_sym)
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def respond_to?(method_name)
|
54
|
+
return true if method_name.to_s =~ /^(calcular|calculate)_(.*)/ && Correios::Frete::Servico.code_from_type($2.to_sym)
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def web_service(service_types)
|
61
|
+
Correios::Frete::WebService.new(self, service_types)
|
62
|
+
end
|
63
|
+
|
64
|
+
def parser
|
65
|
+
@parser ||= Correios::Frete::Parser.new
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Correios
|
3
|
+
module Frete
|
4
|
+
class Pacote
|
5
|
+
attr_reader :peso, :comprimento, :altura, :largura, :volume
|
6
|
+
|
7
|
+
MIN_DIMENSIONS = {
|
8
|
+
:comprimento => 16.0,
|
9
|
+
:largura => 11.0,
|
10
|
+
:altura => 2.0
|
11
|
+
}
|
12
|
+
|
13
|
+
def initialize(itens = nil)
|
14
|
+
@peso = @comprimento = @largura = @altura = @volume = 0.0
|
15
|
+
@itens = []
|
16
|
+
|
17
|
+
itens.each { |item| adicionar_item(item) } if itens
|
18
|
+
end
|
19
|
+
|
20
|
+
def formato
|
21
|
+
:caixa_pacote
|
22
|
+
end
|
23
|
+
|
24
|
+
def itens
|
25
|
+
@itens
|
26
|
+
end
|
27
|
+
alias items itens
|
28
|
+
|
29
|
+
def adicionar_item(item)
|
30
|
+
return unless item
|
31
|
+
|
32
|
+
item = Correios::Frete::PacoteItem.new(item) if item.is_a?(Hash)
|
33
|
+
@itens << item
|
34
|
+
|
35
|
+
calcular_medidas(item)
|
36
|
+
item
|
37
|
+
end
|
38
|
+
alias add_item adicionar_item
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def calcular_medidas(item)
|
43
|
+
@peso += item.peso
|
44
|
+
@volume += item.volume
|
45
|
+
|
46
|
+
if @itens.size == 1
|
47
|
+
@comprimento = item.comprimento
|
48
|
+
@largura = item.largura
|
49
|
+
@altura = item.altura
|
50
|
+
else
|
51
|
+
dimensao = @volume.to_f**(1.0/3)
|
52
|
+
@comprimento = @largura = @altura = dimensao
|
53
|
+
end
|
54
|
+
|
55
|
+
min_dimension_values
|
56
|
+
end
|
57
|
+
|
58
|
+
def min_dimension_values()
|
59
|
+
@comprimento = min(@comprimento, MIN_DIMENSIONS[:comprimento])
|
60
|
+
@largura = min(@largura, MIN_DIMENSIONS[:largura])
|
61
|
+
@altura = min(@altura, MIN_DIMENSIONS[:altura])
|
62
|
+
end
|
63
|
+
|
64
|
+
def min(value, minimum)
|
65
|
+
(value < minimum) ? minimum : value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Correios
|
3
|
+
module Frete
|
4
|
+
class PacoteItem
|
5
|
+
attr_accessor :peso, :comprimento, :largura, :altura
|
6
|
+
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:peso => 0.0,
|
9
|
+
:comprimento => 0.0,
|
10
|
+
:largura => 0.0,
|
11
|
+
:altura => 0.0
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
DEFAULT_OPTIONS.merge(options).each do |attr, value|
|
16
|
+
self.send("#{attr}=", value)
|
17
|
+
end
|
18
|
+
|
19
|
+
yield self if block_given?
|
20
|
+
end
|
21
|
+
|
22
|
+
def volume
|
23
|
+
@comprimento * @largura * @altura
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Correios
|
5
|
+
module Frete
|
6
|
+
class Parser
|
7
|
+
def servicos(xml)
|
8
|
+
servicos = {}
|
9
|
+
xml = xml.encode("UTF-8", "ISO-8859-1")
|
10
|
+
|
11
|
+
Nokogiri::XML(xml).root.elements.each do |element|
|
12
|
+
servico = Correios::Frete::Servico.new.parse(element.to_xml)
|
13
|
+
servicos[servico.tipo] = servico
|
14
|
+
end
|
15
|
+
|
16
|
+
servicos
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'sax-machine'
|
3
|
+
|
4
|
+
module Correios
|
5
|
+
module Frete
|
6
|
+
class Servico
|
7
|
+
include SAXMachine
|
8
|
+
|
9
|
+
AVAILABLE_SERVICES = {
|
10
|
+
"04510" => { :type => :pac, :name => "PAC", :description => "PAC sem contrato" },
|
11
|
+
"41068" => { :type => :pac_com_contrato, :name => "PAC", :description => "PAC com contrato" },
|
12
|
+
"41300" => { :type => :pac_gf, :name => "PAC GF", :description => "PAC para grandes formatos" },
|
13
|
+
"04014" => { :type => :sedex, :name => "SEDEX", :description => "SEDEX sem contrato" },
|
14
|
+
"40045" => { :type => :sedex_a_cobrar, :name => "SEDEX a Cobrar", :description => "SEDEX a Cobrar, sem contrato" },
|
15
|
+
"40126" => { :type => :sedex_a_cobrar_com_contrato, :name => "SEDEX a Cobrar", :description => "SEDEX a Cobrar, com contrato" },
|
16
|
+
"40215" => { :type => :sedex_10, :name => "SEDEX 10", :description => "SEDEX 10, sem contrato" },
|
17
|
+
"40290" => { :type => :sedex_hoje, :name => "SEDEX Hoje", :description => "SEDEX Hoje, sem contrato" },
|
18
|
+
"40096" => { :type => :sedex_com_contrato_1, :name => "SEDEX", :description => "SEDEX com contrato" },
|
19
|
+
"40436" => { :type => :sedex_com_contrato_2, :name => "SEDEX", :description => "SEDEX com contrato" },
|
20
|
+
"40444" => { :type => :sedex_com_contrato_3, :name => "SEDEX", :description => "SEDEX com contrato" },
|
21
|
+
"40568" => { :type => :sedex_com_contrato_4, :name => "SEDEX", :description => "SEDEX com contrato" },
|
22
|
+
"40606" => { :type => :sedex_com_contrato_5, :name => "SEDEX", :description => "SEDEX com contrato" },
|
23
|
+
"81019" => { :type => :e_sedex, :name => "e-SEDEX", :description => "e-SEDEX, com contrato" },
|
24
|
+
"81027" => { :type => :e_sedex_prioritario, :name => "e-SEDEX", :description => "e-SEDEX Prioritário, com contrato" },
|
25
|
+
"81035" => { :type => :e_sedex_express, :name => "e-SEDEX", :description => "e-SEDEX Express, com contrato" },
|
26
|
+
"81868" => { :type => :e_sedex_grupo_1, :name => "e-SEDEX", :description => "(Grupo 1) e-SEDEX, com contrato" },
|
27
|
+
"81833" => { :type => :e_sedex_grupo_2, :name => "e-SEDEX", :description => "(Grupo 2) e-SEDEX, com contrato" },
|
28
|
+
"81850" => { :type => :e_sedex_grupo_3, :name => "e-SEDEX", :description => "(Grupo 3) e-SEDEX, com contrato" }
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
element :Codigo, :as => :codigo
|
32
|
+
element :Valor, :as => :valor
|
33
|
+
element :PrazoEntrega, :as => :prazo_entrega
|
34
|
+
element :ValorMaoPropria, :as => :valor_mao_propria
|
35
|
+
element :ValorAvisoRecebimento, :as => :valor_aviso_recebimento
|
36
|
+
element :ValorValorDeclarado, :as => :valor_valor_declarado
|
37
|
+
element :EntregaDomiciliar, :as => :entrega_domiciliar
|
38
|
+
element :EntregaSabado, :as => :entrega_sabado
|
39
|
+
element :Erro, :as => :erro
|
40
|
+
element :MsgErro, :as => :msg_erro
|
41
|
+
attr_reader :tipo, :nome, :descricao
|
42
|
+
|
43
|
+
alias_method :original_parse, :parse
|
44
|
+
|
45
|
+
def parse(xml_text)
|
46
|
+
original_parse xml_text
|
47
|
+
|
48
|
+
if AVAILABLE_SERVICES[codigo]
|
49
|
+
@tipo = AVAILABLE_SERVICES[codigo][:type]
|
50
|
+
@nome = AVAILABLE_SERVICES[codigo][:name]
|
51
|
+
@descricao = AVAILABLE_SERVICES[codigo][:description]
|
52
|
+
end
|
53
|
+
|
54
|
+
cast_to_float! :valor, :valor_mao_propria, :valor_aviso_recebimento, :valor_valor_declarado
|
55
|
+
cast_to_int! :prazo_entrega
|
56
|
+
cast_to_boolean! :entrega_domiciliar, :entrega_sabado
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def success?
|
61
|
+
valor > 0.0
|
62
|
+
end
|
63
|
+
alias sucesso? success?
|
64
|
+
|
65
|
+
def error?
|
66
|
+
!success?
|
67
|
+
end
|
68
|
+
alias erro? error?
|
69
|
+
|
70
|
+
def self.code_from_type(type)
|
71
|
+
# I don't use select method for Ruby 1.8.7 compatibility.
|
72
|
+
AVAILABLE_SERVICES.map { |key, value| key if value[:type] == type }.compact.first
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def cast_to_float!(*attributes)
|
78
|
+
attributes.each do |attr|
|
79
|
+
value = send(attr).to_s.gsub(".", "").gsub("," ,".")
|
80
|
+
instance_variable_set("@#{attr}", value.to_f)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def cast_to_int!(*attributes)
|
85
|
+
attributes.each do |attr|
|
86
|
+
instance_variable_set("@#{attr}", send(attr).to_i)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def cast_to_boolean!(*attributes)
|
91
|
+
attributes.each do |attr|
|
92
|
+
instance_variable_set("@#{attr}", send(attr) == "S")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Correios
|
6
|
+
module Frete
|
7
|
+
class WebService
|
8
|
+
URL = "http://ws.correios.com.br/calculador/CalcPrecoPrazo.aspx"
|
9
|
+
FORMATS = { :caixa_pacote => 1, :rolo_prisma => 2, :envelope => 3 }
|
10
|
+
CONDITIONS = { true => "S", false => "N" }
|
11
|
+
|
12
|
+
def initialize(frete, service_types)
|
13
|
+
@url = "#{URL}?#{params_for(frete, service_types)}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def request!
|
17
|
+
response = with_log { http_request(@url) }
|
18
|
+
response.body
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def http_request(url)
|
24
|
+
uri = URI.parse(url)
|
25
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
26
|
+
|
27
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
28
|
+
http.open_timeout = Correios::Frete.request_timeout
|
29
|
+
http.request(request)
|
30
|
+
end
|
31
|
+
|
32
|
+
def params_for(frete, service_types)
|
33
|
+
"sCepOrigem=#{frete.cep_origem}&" +
|
34
|
+
"sCepDestino=#{frete.cep_destino}&" +
|
35
|
+
"nVlPeso=#{frete.peso}&" +
|
36
|
+
"nVlComprimento=#{format_decimal(frete.comprimento)}&" +
|
37
|
+
"nVlLargura=#{format_decimal(frete.largura)}&" +
|
38
|
+
"nVlAltura=#{format_decimal(frete.altura)}&" +
|
39
|
+
"nVlDiametro=#{format_decimal(frete.diametro)}&" +
|
40
|
+
"nCdFormato=#{FORMATS[frete.formato]}&" +
|
41
|
+
"sCdMaoPropria=#{CONDITIONS[frete.mao_propria]}&" +
|
42
|
+
"sCdAvisoRecebimento=#{CONDITIONS[frete.aviso_recebimento]}&" +
|
43
|
+
"nVlValorDeclarado=#{format_decimal(format("%.2f" % frete.valor_declarado))}&" +
|
44
|
+
"nCdServico=#{service_codes_for(service_types)}&" +
|
45
|
+
"nCdEmpresa=#{frete.codigo_empresa}&" +
|
46
|
+
"sDsSenha=#{frete.senha}&" +
|
47
|
+
"StrRetorno=xml"
|
48
|
+
end
|
49
|
+
|
50
|
+
def format_decimal(value)
|
51
|
+
value.to_s.gsub(".", ",")
|
52
|
+
end
|
53
|
+
|
54
|
+
def service_codes_for(service_types)
|
55
|
+
service_types.map { |type| Correios::Frete::Servico.code_from_type(type) }.join(",")
|
56
|
+
end
|
57
|
+
|
58
|
+
def with_log
|
59
|
+
Correios::Frete.log format_request_message
|
60
|
+
response = yield
|
61
|
+
Correios::Frete.log format_response_message(response)
|
62
|
+
response
|
63
|
+
end
|
64
|
+
|
65
|
+
def format_request_message
|
66
|
+
message = with_line_break { "Correios-Frete Request:" }
|
67
|
+
message << with_line_break { "GET #{@url}" }
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_response_message(response)
|
71
|
+
message = with_line_break { "Correios-Frete Response:" }
|
72
|
+
message << with_line_break { "HTTP/#{response.http_version} #{response.code} #{response.message}" }
|
73
|
+
message << with_line_break { format_headers_for(response) } if Correios::Frete.log_level == :debug
|
74
|
+
message << with_line_break { response.body }
|
75
|
+
end
|
76
|
+
|
77
|
+
def format_headers_for(http)
|
78
|
+
# I'm using an empty block in each_header method for Ruby 1.8.7 compatibility.
|
79
|
+
http.each_header{}.map { |name, values| "#{name}: #{values.first}" }.join("\n")
|
80
|
+
end
|
81
|
+
|
82
|
+
def with_line_break
|
83
|
+
"#{yield}\n"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|