Skip to content

Commit

Permalink
Remove active_utils and offsite_payments as dependencies.
Browse files Browse the repository at this point in the history
- The necessary code has been copied over from active_utils-2.3.3
- The ActiveMerchant::Integrations shims to OffsitePayments has been removed.
  • Loading branch information
wvanbergen committed Jan 15, 2015
1 parent 5275200 commit d730e44
Show file tree
Hide file tree
Showing 14 changed files with 4,591 additions and 75 deletions.
3 changes: 0 additions & 3 deletions activemerchant.gemspec
Expand Up @@ -22,10 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', '>= 3.2.14', '< 5.0.0')
s.add_dependency('i18n', '>= 0.6.9')
s.add_dependency('builder', '>= 2.1.2', '< 4.0.0')
s.add_dependency('json', '~> 1.7')
s.add_dependency('active_utils', '~> 2.2.0')
s.add_dependency('nokogiri', "~> 1.4")
s.add_dependency("offsite_payments", "~> 2.0.0")

s.add_development_dependency('rake')
s.add_development_dependency('test-unit', '~> 3')
Expand Down
42 changes: 8 additions & 34 deletions lib/active_merchant.rb
Expand Up @@ -27,7 +27,7 @@
require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/enumerable.rb'
require 'active_support/core_ext/enumerable'

if(!defined?(ActiveSupport::VERSION) || (ActiveSupport::VERSION::STRING < "4.1"))
require 'active_support/core_ext/class/attribute_accessors'
Expand All @@ -44,44 +44,16 @@
require 'timeout'
require 'socket'

require 'active_utils/common/network_connection_retries'
silence_warnings{require 'active_utils/common/connection'}
require 'active_utils/common/post_data'
require 'active_utils/common/posts_data'
require 'active_merchant/network_connection_retries'
require 'active_merchant/connection'
require 'active_merchant/post_data'
require 'active_merchant/posts_data'

require 'active_merchant/billing'
require 'active_merchant/version'
require 'active_merchant/country'

I18n.enforce_available_locales = false

module ActiveMerchant #:nodoc:
OFFSITE_PAYMENT_EXTRACTION_MESSAGE = "Integrations have been extracted into a separate gem (https://github.com/Shopify/offsite_payments) and will no longer be loaded by ActiveMerchant 2.x."

module Billing #:nodoc:
def self.const_missing(name)
if name.to_s == "Integrations"
ActiveMerchant.deprecated(OFFSITE_PAYMENT_EXTRACTION_MESSAGE)
require "active_merchant/offsite_payments_shim"
ActiveMerchant::OffsitePaymentsShim
else
super
end
end

def self.included(klass)
def klass.const_missing(name)
if name.to_s == "Integrations"
ActiveMerchant.deprecated(OFFSITE_PAYMENT_EXTRACTION_MESSAGE)
require "active_merchant/offsite_payments_shim"
ActiveMerchant::OffsitePaymentsShim
else
super
end
end
end
end

module ActiveMerchant
def self.deprecated(message, caller=Kernel.caller[1])
warning = caller + ": " + message
if(respond_to?(:logger) && logger.present?)
Expand All @@ -91,3 +63,5 @@ def self.deprecated(message, caller=Kernel.caller[1])
end
end
end

I18n.enforce_available_locales = false
169 changes: 169 additions & 0 deletions lib/active_merchant/connection.rb
@@ -0,0 +1,169 @@
require 'uri'
require 'net/http'
require 'net/https'
require 'benchmark'

module ActiveMerchant
class Connection
include NetworkConnectionRetries

MAX_RETRIES = 3
OPEN_TIMEOUT = 60
READ_TIMEOUT = 60
VERIFY_PEER = true
CA_FILE = File.expand_path('../certs/cacert.pem', File.dirname(__FILE__))
CA_PATH = nil
RETRY_SAFE = false
RUBY_184_POST_HEADERS = { "Content-Type" => "application/x-www-form-urlencoded" }

attr_accessor :endpoint
attr_accessor :open_timeout
attr_accessor :read_timeout
attr_accessor :verify_peer
attr_accessor :ssl_version
attr_accessor :ca_file
attr_accessor :ca_path
attr_accessor :pem
attr_accessor :pem_password
attr_accessor :wiredump_device
attr_accessor :logger
attr_accessor :tag
attr_accessor :ignore_http_status
attr_accessor :max_retries
attr_accessor :proxy_address
attr_accessor :proxy_port

def initialize(endpoint)
@endpoint = endpoint.is_a?(URI) ? endpoint : URI.parse(endpoint)
@open_timeout = OPEN_TIMEOUT
@read_timeout = READ_TIMEOUT
@retry_safe = RETRY_SAFE
@verify_peer = VERIFY_PEER
@ca_file = CA_FILE
@ca_path = CA_PATH
@max_retries = MAX_RETRIES
@ignore_http_status = false
@ssl_version = nil
@proxy_address = nil
@proxy_port = nil
end

def request(method, body, headers = {})
request_start = Time.now.to_f

retry_exceptions(:max_retries => max_retries, :logger => logger, :tag => tag) do
begin
info "connection_http_method=#{method.to_s.upcase} connection_uri=#{endpoint}", tag

result = nil

realtime = Benchmark.realtime do
result = case method
when :get
raise ArgumentError, "GET requests do not support a request body" if body
http.get(endpoint.request_uri, headers)
when :post
debug body
http.post(endpoint.request_uri, body, RUBY_184_POST_HEADERS.merge(headers))
when :put
debug body
http.put(endpoint.request_uri, body, headers)
when :delete
# It's kind of ambiguous whether the RFC allows bodies
# for DELETE requests. But Net::HTTP's delete method
# very unambiguously does not.
raise ArgumentError, "DELETE requests do not support a request body" if body
http.delete(endpoint.request_uri, headers)
else
raise ArgumentError, "Unsupported request method #{method.to_s.upcase}"
end
end

info "--> %d %s (%d %.4fs)" % [result.code, result.message, result.body ? result.body.length : 0, realtime], tag
debug result.body
result
end
end

ensure
info "connection_request_total_time=%.4fs" % [Time.now.to_f - request_start], tag
end

private
def http
http = Net::HTTP.new(endpoint.host, endpoint.port, proxy_address, proxy_port)
configure_debugging(http)
configure_timeouts(http)
configure_ssl(http)
configure_cert(http)
http
end

def configure_debugging(http)
http.set_debug_output(wiredump_device)
end

def configure_timeouts(http)
http.open_timeout = open_timeout
http.read_timeout = read_timeout
end

def configure_ssl(http)
return unless endpoint.scheme == "https"

http.use_ssl = true
http.ssl_version = ssl_version if ssl_version

if verify_peer
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = ca_file
http.ca_path = ca_path
else
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

end

def configure_cert(http)
return if pem.blank?

http.cert = OpenSSL::X509::Certificate.new(pem)

if pem_password
http.key = OpenSSL::PKey::RSA.new(pem, pem_password)
else
http.key = OpenSSL::PKey::RSA.new(pem)
end
end

def handle_response(response)
if @ignore_http_status then
return response.body
else
case response.code.to_i
when 200...300
response.body
else
raise ResponseError.new(response)
end
end
end

def debug(message, tag = nil)
log(:debug, message, tag)
end

def info(message, tag = nil)
log(:info, message, tag)
end

def error(message, tag = nil)
log(:error, message, tag)
end

def log(level, message, tag)
message = "[#{tag}] #{message}" if tag
logger.send(level, message) if logger
end
end
end
73 changes: 73 additions & 0 deletions lib/active_merchant/network_connection_retries.rb
@@ -0,0 +1,73 @@
module ActiveMerchant
module NetworkConnectionRetries
DEFAULT_RETRIES = 3
DEFAULT_CONNECTION_ERRORS = {
EOFError => "The remote server dropped the connection",
Errno::ECONNRESET => "The remote server reset the connection",
Timeout::Error => "The connection to the remote server timed out",
Errno::ETIMEDOUT => "The connection to the remote server timed out",
SocketError => "The connection to the remote server could not be established",
OpenSSL::SSL::SSLError => "The SSL connection to the remote server could not be established"
}

def self.included(base)
base.send(:attr_accessor, :retry_safe)
end

def retry_exceptions(options={})
connection_errors = DEFAULT_CONNECTION_ERRORS.merge(options[:connection_exceptions] || {})

retry_network_exceptions(options) do
begin
yield
rescue Errno::ECONNREFUSED => e
raise ActiveMerchant::RetriableConnectionError, "The remote server refused the connection"
rescue OpenSSL::X509::CertificateError => e
NetworkConnectionRetries.log(options[:logger], :error, e.message, options[:tag])
raise ActiveMerchant::ClientCertificateError, "The remote server did not accept the provided SSL certificate"
rescue Zlib::BufError => e
raise ActiveMerchant::InvalidResponseError, "The remote server replied with an invalid response"
rescue *connection_errors.keys => e
raise ActiveMerchant::ConnectionError, connection_errors[e.class]
end
end
end

private

def retry_network_exceptions(options = {})
initial_retries = options[:max_retries] || DEFAULT_RETRIES
retries = initial_retries
request_start = nil

begin
request_start = Time.now.to_f
result = yield
log_with_retry_details(options[:logger], initial_retries-retries + 1, Time.now.to_f - request_start, "success", options[:tag])
result
rescue ActiveMerchant::RetriableConnectionError => e
retries -= 1

log_with_retry_details(options[:logger], initial_retries-retries, Time.now.to_f - request_start, e.message, options[:tag])
retry unless retries.zero?
raise ActiveMerchant::ConnectionError, e.message
rescue ActiveMerchant::ConnectionError, ActiveMerchant::InvalidResponseError => e
retries -= 1
log_with_retry_details(options[:logger], initial_retries-retries, Time.now.to_f - request_start, e.message, options[:tag])
retry if (options[:retry_safe] || retry_safe) && !retries.zero?
raise
end
end

def self.log(logger, level, message, tag=nil)
tag ||= self.class.to_s
message = "[#{tag}] #{message}"
logger.send(level, message) if logger
end

private
def log_with_retry_details(logger, attempts, time, message, tag)
NetworkConnectionRetries.log(logger, :info, "connection_attempt=%d connection_request_time=%.4fs connection_msg=\"%s\"" % [attempts, time, message], tag)
end
end
end
19 changes: 0 additions & 19 deletions lib/active_merchant/offsite_payments_shim.rb

This file was deleted.

24 changes: 24 additions & 0 deletions lib/active_merchant/post_data.rb
@@ -0,0 +1,24 @@
require 'cgi'

module ActiveMerchant
class PostData < Hash
class_attribute :required_fields, :instance_writer => false
self.required_fields = []

def []=(key, value)
return if value.blank? && !required?(key)
super
end

def to_post_data
collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
end

alias_method :to_s, :to_post_data

private
def required?(key)
required_fields.include?(key)
end
end
end

0 comments on commit d730e44

Please sign in to comment.