Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stripe-ruby V5 #815

Merged
merged 18 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,37 @@ inherit_from: .rubocop_todo.yml

AllCops:
DisplayCopNames: true
TargetRubyVersion: 2.1
TargetRubyVersion: 2.3

Layout/CaseIndentation:
EnforcedStyle: end

Layout/IndentArray:
Layout/IndentFirstArrayElement:
EnforcedStyle: consistent

Layout/IndentHash:
Layout/IndentFirstHashElement:
EnforcedStyle: consistent

Metrics/LineLength:
# This can be re-enabled once we're 2.3+ only and can use the squiggly heredoc
# operator. Prior to that, Rubocop recommended bringing in a library like
# ActiveSupport to get heredoc indentation, which is just terrible.
Layout/IndentHeredoc:
Enabled: false

Metrics/ClassLength:
Exclude:
- "test/**/*.rb"

Metrics/LineLength:
Exclude:
- "lib/stripe/resources/**/*.rb"
- "test/**/*.rb"

Metrics/MethodLength:
# There's ~2 long methods in `StripeClient`. If we want to truncate those a
# little, we could move this to be closer to ~30 (but the default of 10 is
# probably too short).
Max: 50
Max: 55

Metrics/ModuleLength:
Enabled: false
Expand All @@ -33,6 +43,9 @@ Style/AccessModifierDeclarations:
Style/FrozenStringLiteralComment:
EnforcedStyle: always

Style/NumericPredicate:
Enabled: false

Style/StringLiterals:
EnforcedStyle: double_quotes

Expand Down
19 changes: 10 additions & 9 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-05-24 10:18:48 -0700 using RuboCop version 0.57.2.
# on 2019-07-30 09:56:31 +0800 using RuboCop version 0.73.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 20
# Offense count: 23
Metrics/AbcSize:
Max: 53
Max: 51

# Offense count: 31
# Offense count: 33
# Configuration parameters: CountComments, ExcludedMethods.
# ExcludedMethods: refine
Metrics/BlockLength:
Max: 498
Max: 509

# Offense count: 11
# Offense count: 12
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 673
Max: 694

# Offense count: 12
Metrics/CyclomaticComplexity:
Expand All @@ -29,10 +30,10 @@ Metrics/CyclomaticComplexity:
Metrics/ParameterLists:
Max: 7

# Offense count: 7
# Offense count: 8
Metrics/PerceivedComplexity:
Max: 17

# Offense count: 84
# Offense count: 86
Style/Documentation:
Enabled: false
6 changes: 1 addition & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
language: ruby

rvm:
- 2.1
- 2.2
- 2.3
- 2.4
- 2.5
- 2.6
- jruby-9.0.5.0
- jruby-9.2.7.0

notifications:
email:
Expand All @@ -25,8 +23,6 @@ cache:
- stripe-mock

before_install:
# Install bundler 1.x, because we need to support Ruby 2.1 for now
- gem install bundler -v "~> 1.0"
# Unpack and start stripe-mock so that the test suite can talk to it
- |
if [ ! -d "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}" ]; then
Expand Down
14 changes: 2 additions & 12 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ gemspec
group :development do
gem "coveralls", require: false
gem "mocha", "~> 0.13.2"
gem "rack", ">= 2.0.6"
gem "rake"
gem "shoulda-context"
gem "test-unit"
Expand All @@ -18,18 +19,7 @@ group :development do
# `Gemfile.lock` checked in, so to prevent good builds from suddenly going
# bad, pin to a specific version number here. Try to keep this relatively
# up-to-date, but it's not the end of the world if it's not.
# Note that 0.57.2 is the most recent version we can use until we drop
# support for Ruby 2.1.
gem "rubocop", "0.57.2"

# Rack 2.0+ requires Ruby >= 2.2.2 which is problematic for the test suite on
# older Ruby versions. Check Ruby the version here and put a maximum
# constraint on Rack if necessary.
if RUBY_VERSION >= "2.2.2"
gem "rack", ">= 2.0.6"
else
gem "rack", ">= 1.6.11", "< 2.0" # rubocop:disable Bundler/DuplicatedGem
end
gem "rubocop", "0.73"

platforms :mri do
gem "byebug"
Expand Down
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ gem build stripe.gemspec

### Requirements

- Ruby 2.1+.
- Ruby 2.3+.

### Bundler

Expand Down Expand Up @@ -112,15 +112,13 @@ Stripe::Charge.retrieve(
)
```

### Configuring a Client
### Accessing a response object

While a default HTTP client is used by default, it's also possible to have the
library use any client supported by [Faraday][faraday] by initializing a
`Stripe::StripeClient` object and giving it a connection:
Get access to response objects by initializing a client and using its `request`
method:

```ruby
conn = Faraday.new
client = Stripe::StripeClient.new(conn)
client = Stripe::StripeClient.new
charge, resp = client.request do
Stripe::Charge.retrieve(
"ch_18atAXCdGbJFKhCuBAa4532Z",
Expand Down Expand Up @@ -275,7 +273,6 @@ Update the bundled [stripe-mock] by editing the version number found in
[api-keys]: https://dashboard.stripe.com/account/apikeys
[connect]: https://stripe.com/connect
[curl]: http://curl.haxx.se/docs/caextract.html
[faraday]: https://github.com/lostisland/faraday
[idempotency-keys]: https://stripe.com/docs/api/ruby#idempotent_requests
[stripe-mock]: https://github.com/stripe/stripe-mock
[versioning]: https://stripe.com/docs/api/ruby#versioning
Expand Down
15 changes: 8 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ RuboCop::RakeTask.new

desc "Update bundled certs"
task :update_certs do
require "faraday"
require "net/http"
require "uri"

fetch_file "https://curl.haxx.se/ca/cacert.pem",
::File.expand_path("../lib/data/ca-certificates.crt", __FILE__)
Expand All @@ -23,14 +24,14 @@ end
# helpers
#

def fetch_file(url, dest)
def fetch_file(uri, dest)
::File.open(dest, "w") do |file|
resp = Faraday.get(url)
unless resp.status == 200
abort("bad response when fetching: #{url}\n" \
"Status #{resp.status}: #{resp.body}")
resp = Net::HTTP.get_response(URI.parse(uri))
unless resp.code.to_i == 200
abort("bad response when fetching: #{uri}\n" \
"Status #{resp.code}: #{resp.body}")
end
file.write(resp.body)
puts "Successfully fetched: #{url}"
puts "Successfully fetched: #{uri}"
end
end
15 changes: 4 additions & 11 deletions lib/stripe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
# Stripe Ruby bindings
# API spec at https://stripe.com/docs/api
require "cgi"
require "faraday"
require "json"
require "logger"
require "net/http"
require "openssl"
require "rbconfig"
require "securerandom"
Expand All @@ -28,10 +28,13 @@
require "stripe/errors"
require "stripe/object_types"
require "stripe/util"
require "stripe/connection_manager"
require "stripe/multipart_encoder"
require "stripe/stripe_client"
require "stripe/stripe_object"
require "stripe/stripe_response"
require "stripe/list_object"
require "stripe/error_object"
require "stripe/api_resource"
require "stripe/singleton_api_resource"
require "stripe/webhook"
Expand Down Expand Up @@ -193,16 +196,6 @@ def self.set_app_info(name, partner_id: nil, url: nil, version: nil)
version: version,
}
end

# DEPRECATED. Use `Util#encode_parameters` instead.
def self.uri_encode(params)
Util.encode_parameters(params)
end
private_class_method :uri_encode
class << self
extend Gem::Deprecate
deprecate :uri_encode, "Stripe::Util#encode_parameters", 2016, 1
end
end

Stripe.log_level = ENV["STRIPE_LOG"] unless ENV["STRIPE_LOG"].nil?
6 changes: 0 additions & 6 deletions lib/stripe/api_operations/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ def list(filters = {}, opts = {})

obj
end

# The original version of #list was given the somewhat unfortunate name of
# #all, and this alias allows us to maintain backward compatibility (the
# choice was somewhat misleading in the way that it only returned a single
# page rather than all objects).
alias all list
end
end
end
122 changes: 122 additions & 0 deletions lib/stripe/connection_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

module Stripe
# Manages connections across multiple hosts which is useful because the
# library may connect to multiple hosts during a typical session (main API,
# Connect, Uploads). Ruby doesn't provide an easy way to make this happen
# easily, so this class is designed to track what we're connected to and
# manage the lifecycle of those connections.
#
# Note that this class in itself is *not* thread safe. We expect it to be
# instantiated once per thread.
#
# Note also that this class doesn't currently clean up after itself because
# it expects to only ever have a few connections. It'd be possible to tank
# memory by constantly changing the value of `Stripe.api_base` or the like. A
# possible improvement might be to detect and prune old connections whenever
# a request is executed.
class ConnectionManager
def initialize
@active_connections = {}
end

# Gets a connection for a given URI. This is for internal use only as it's
# subject to change (we've moved between HTTP client schemes in the past
# and may do it again).
#
# `uri` is expected to be a string.
def connection_for(uri)
u = URI.parse(uri)
connection = @active_connections[[u.host, u.port]]

if connection.nil?
connection = create_connection(u)

# TODO: what happens after TTL?
connection.start

@active_connections[[u.host, u.port]] = connection
end

connection
end

# Executes an HTTP request to the given URI with the given method. Also
# allows a request body, headers, and query string to be specified.
def execute_request(method, uri, body: nil, headers: nil, query: nil)
# Perform some basic argument validation because it's easy to get
# confused between strings and hashes for things like body and query
# parameters.
raise ArgumentError, "method should be a symbol" \
unless method.is_a?(Symbol)
raise ArgumentError, "uri should be a string" \
unless uri.is_a?(String)
raise ArgumentError, "body should be a string" \
if body && !body.is_a?(String)
raise ArgumentError, "headers should be a hash" \
if headers && !headers.is_a?(Hash)
raise ArgumentError, "query should be a string" \
if query && !query.is_a?(String)

connection = connection_for(uri)

u = URI.parse(uri)
path = if query
u.path + "?" + query
else
u.path
end

connection.send_request(method.to_s.upcase, path, body, headers)
end

#
# private
#

# `uri` should be a parsed `URI` object.
private def create_connection(uri)
# These all come back as `nil` if no proxy is configured.
proxy_host, proxy_port, proxy_user, proxy_pass = proxy_parts

connection = Net::HTTP.new(uri.host, uri.port,
proxy_host, proxy_port,
proxy_user, proxy_pass)

connection.open_timeout = Stripe.open_timeout
connection.read_timeout = Stripe.read_timeout

connection.use_ssl = uri.scheme == "https"

if Stripe.verify_ssl_certs
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
connection.cert_store = Stripe.ca_store
else
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE

unless @verify_ssl_warned
@verify_ssl_warned = true
warn("WARNING: Running without SSL cert verification. " \
"You should never do this in production. " \
"Execute `Stripe.verify_ssl_certs = true` to enable " \
"verification.")
end
end

connection
end

# `Net::HTTP` somewhat awkwardly requires each component of a proxy URI
# (host, port, etc.) rather than the URI itself. This method simply parses
# out those pieces to make passing them into a new connection a little less
# ugly.
private def proxy_parts
if Stripe.proxy.nil?
[nil, nil, nil, nil]
else
u = URI.parse(Stripe.proxy)
[u.host, u.port, u.user, u.password]
end
end
end
end
Loading