forked from stripe/stripe-ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client_api_operations.rb
107 lines (90 loc) · 3.96 KB
/
client_api_operations.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# frozen_string_literal: true
module Stripe
# Define instance methods on the including class (i.e. StripeClient)
# to access API resources.
module ClientAPIOperations
# Proxy object to inject the client into API resources. When included,
# all resources are defined as singleton methods on the client in the
# plural form (e.g. Stripe::Account => client.accounts).
class ClientProxy
def initialize(client:, resource: nil)
@client = client
@resource = resource
end
attr_reader :client
def with_client(client)
@client = client
self
end
# Used to either send a method to the API resource or the nested
# ClientProxy when a resource is namespaced.
def method_missing(method, *args)
super unless @resource.respond_to?(method)
update_args_with_client!(method, args)
@resource.public_send(method, *args)
end
def respond_to_missing?(symbol, include_private = false)
super unless @resource
@resource.respond_to?(symbol) || super
end
# Since the method signature differs when operating on a collection versus
# a singular resource, it's required to perform introspection on the
# parameters to respect any passed in options or overrides.
#
# Two noteworthy caveats:
# 1) Does not merge into methods that use `_opts` as that means
# the param is unused.
# 2) Preserves incorrect options (e.g. passing nil) so that APIResource
# can handle errors.
def update_args_with_client!(method, args)
opts_pos = @resource.method(method).parameters.index(%i[opt opts])
return unless opts_pos
opts = opts_pos >= args.length ? {} : args[opts_pos]
normalized_opts = Stripe::Util.normalize_opts(opts)
args[opts_pos] = { client: @client }.merge(normalized_opts)
end
end
def self.included(base)
base.class_eval do
# Sigma, unlike other namespaced API objects, is not separated by a
# period so we modify the object name to follow the expected convention.
api_resources = Stripe::Util.api_object_classes
sigma_class = api_resources.delete("scheduled_query_run")
api_resources["sigma.scheduled_query_run"] = sigma_class
# Update `invoiceitems` to match snake case convention
invoice_item_class = api_resources.delete("invoiceitem")
api_resources["invoice_item"] = invoice_item_class
# Group namespaces that have mutiple resourses
grouped_resources = api_resources.group_by do |key, _|
key.include?(".") ? key.split(".").first : key
end
grouped_resources.each do |resource_namespace, resources|
# Namespace resource names are separated with a period by convention.
if resources[0][0].include?(".")
# Defines the methods required for chaining calls for resources that
# are namespaced. A proxy object is created so that all resource
# methods can be defined at once.
proxy = ClientProxy.new(client: nil)
resources.each do |resource_name, resource_class|
method_name = resource_name.split(".").last
proxy.define_singleton_method(method_name) do
ClientProxy.new(client: proxy.client, resource: resource_class)
end
end
# Defines the first method for resources that are namespaced. By
# convention these methods are singular. A proxy object is returned
# so that the client can be injected along the method chain.
define_method(resource_namespace) do
proxy.with_client(self)
end
else
# Defines plural methods for non-namespaced resources
define_method(resource_namespace.to_sym) do
ClientProxy.new(client: self, resource: resources[0][1])
end
end
end
end
end
end
end