paid 1.0.1 → 1.0.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 +4 -4
- data/History.txt +6 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/paid.rb +32 -207
- data/lib/paid/account.rb +22 -10
- data/lib/paid/api_list.rb +56 -17
- data/lib/paid/api_method.rb +93 -0
- data/lib/paid/api_resource.rb +130 -8
- data/lib/paid/customer.rb +104 -40
- data/lib/paid/errors/api_connection_error.rb +1 -1
- data/lib/paid/errors/api_error.rb +25 -3
- data/lib/paid/errors/authentication_error.rb +1 -1
- data/lib/paid/errors/paid_error.rb +2 -9
- data/lib/paid/event.rb +28 -17
- data/lib/paid/event_data.rb +10 -0
- data/lib/paid/headers_builder.rb +75 -0
- data/lib/paid/invoice.rb +55 -16
- data/lib/paid/params_builder.rb +26 -0
- data/lib/paid/path_builder.rb +38 -0
- data/lib/paid/plan.rb +38 -13
- data/lib/paid/refund_list.rb +26 -0
- data/lib/paid/requester.rb +97 -0
- data/lib/paid/subscription.rb +47 -16
- data/lib/paid/transaction.rb +64 -16
- data/lib/paid/util.rb +14 -40
- data/paid.gemspec +1 -1
- data/test/paid/{api_class_test.rb → _api_resource_test.rb} +31 -17
- data/test/paid/account_test.rb +3 -3
- data/test/paid/api_list_test.rb +14 -8
- data/test/paid/api_method_test.rb +89 -0
- data/test/paid/customer_test.rb +20 -10
- data/test/paid/event_test.rb +3 -4
- data/test/paid/headers_builder_test.rb +39 -0
- data/test/paid/invoice_test.rb +3 -3
- data/test/paid/params_builder_test.rb +57 -0
- data/test/paid/path_builder_test.rb +67 -0
- data/test/paid/plan_test.rb +3 -3
- data/test/paid/requester_test.rb +86 -0
- data/test/paid/subscription_test.rb +3 -3
- data/test/paid/transaction_test.rb +4 -4
- data/test/paid/util_test.rb +36 -35
- data/test/test_data.rb +9 -2
- data/test/test_helper.rb +14 -14
- metadata +23 -19
- data/lib/paid/api_class.rb +0 -338
- data/lib/paid/api_singleton.rb +0 -5
- data/lib/paid/errors/invalid_request_error.rb +0 -10
- data/test/mock_resource.rb +0 -69
- data/test/paid/api_resource_test.rb +0 -28
- data/test/paid/api_singleton_test.rb +0 -12
- data/test/paid/authentication_test.rb +0 -50
- data/test/paid/status_codes_test.rb +0 -63
data/test/test_helper.rb
CHANGED
@@ -4,29 +4,29 @@ require 'mocha/setup'
|
|
4
4
|
require 'stringio'
|
5
5
|
require 'shoulda'
|
6
6
|
require File.expand_path('../test_data', __FILE__)
|
7
|
-
require File.expand_path('../mock_resource', __FILE__)
|
7
|
+
# require File.expand_path('../mock_resource', __FILE__)
|
8
8
|
|
9
9
|
# monkeypatch request methods
|
10
10
|
module Paid
|
11
|
-
|
12
|
-
|
13
|
-
def self.mock_rest_client=(mock_client)
|
14
|
-
@mock_rest_client = mock_client
|
11
|
+
class << self
|
12
|
+
attr_accessor :mock_rest_client
|
15
13
|
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
module Requester
|
16
|
+
def self.request(method, url, params, headers)
|
17
|
+
case method
|
18
|
+
when :get then Paid::mock_rest_client.get(url, headers, params)
|
19
|
+
when :put then Paid::mock_rest_client.put(url, headers, params)
|
20
|
+
when :post then Paid::mock_rest_client.post(url, headers, params)
|
21
|
+
when :delete then Paid::mock_rest_client.delete(url, headers, params)
|
22
|
+
else
|
23
|
+
raise "Invalid method"
|
24
|
+
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
class Test::Unit::TestCase
|
29
|
+
class ::Test::Unit::TestCase
|
30
30
|
include Paid::TestData
|
31
31
|
include Mocha
|
32
32
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Calhoun
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -118,7 +118,7 @@ dependencies:
|
|
118
118
|
description: Paid is the programmatic way to manage payments. See https://paidapi.com
|
119
119
|
for details.
|
120
120
|
email:
|
121
|
-
-
|
121
|
+
- jon@apibits.com
|
122
122
|
- ryan@paidapi.com
|
123
123
|
executables:
|
124
124
|
- paid-console
|
@@ -139,37 +139,41 @@ files:
|
|
139
139
|
- gemfiles/yajl.gemfile
|
140
140
|
- lib/paid.rb
|
141
141
|
- lib/paid/account.rb
|
142
|
-
- lib/paid/api_class.rb
|
143
142
|
- lib/paid/api_list.rb
|
143
|
+
- lib/paid/api_method.rb
|
144
144
|
- lib/paid/api_resource.rb
|
145
|
-
- lib/paid/api_singleton.rb
|
146
145
|
- lib/paid/customer.rb
|
147
146
|
- lib/paid/errors/api_connection_error.rb
|
148
147
|
- lib/paid/errors/api_error.rb
|
149
148
|
- lib/paid/errors/authentication_error.rb
|
150
|
-
- lib/paid/errors/invalid_request_error.rb
|
151
149
|
- lib/paid/errors/paid_error.rb
|
152
150
|
- lib/paid/event.rb
|
151
|
+
- lib/paid/event_data.rb
|
152
|
+
- lib/paid/headers_builder.rb
|
153
153
|
- lib/paid/invoice.rb
|
154
|
+
- lib/paid/params_builder.rb
|
155
|
+
- lib/paid/path_builder.rb
|
154
156
|
- lib/paid/plan.rb
|
157
|
+
- lib/paid/refund_list.rb
|
158
|
+
- lib/paid/requester.rb
|
155
159
|
- lib/paid/subscription.rb
|
156
160
|
- lib/paid/transaction.rb
|
157
161
|
- lib/paid/util.rb
|
158
162
|
- lib/paid/version.rb
|
159
163
|
- paid.gemspec
|
160
164
|
- tasks/api_test.rb
|
161
|
-
- test/
|
165
|
+
- test/paid/_api_resource_test.rb
|
162
166
|
- test/paid/account_test.rb
|
163
|
-
- test/paid/api_class_test.rb
|
164
167
|
- test/paid/api_list_test.rb
|
165
|
-
- test/paid/
|
166
|
-
- test/paid/api_singleton_test.rb
|
167
|
-
- test/paid/authentication_test.rb
|
168
|
+
- test/paid/api_method_test.rb
|
168
169
|
- test/paid/customer_test.rb
|
169
170
|
- test/paid/event_test.rb
|
171
|
+
- test/paid/headers_builder_test.rb
|
170
172
|
- test/paid/invoice_test.rb
|
173
|
+
- test/paid/params_builder_test.rb
|
174
|
+
- test/paid/path_builder_test.rb
|
171
175
|
- test/paid/plan_test.rb
|
172
|
-
- test/paid/
|
176
|
+
- test/paid/requester_test.rb
|
173
177
|
- test/paid/subscription_test.rb
|
174
178
|
- test/paid/transaction_test.rb
|
175
179
|
- test/paid/util_test.rb
|
@@ -195,23 +199,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
199
|
version: '0'
|
196
200
|
requirements: []
|
197
201
|
rubyforge_project:
|
198
|
-
rubygems_version: 2.
|
202
|
+
rubygems_version: 2.4.5
|
199
203
|
signing_key:
|
200
204
|
specification_version: 4
|
201
205
|
summary: Ruby bindings for Paid API
|
202
206
|
test_files:
|
203
|
-
- test/
|
207
|
+
- test/paid/_api_resource_test.rb
|
204
208
|
- test/paid/account_test.rb
|
205
|
-
- test/paid/api_class_test.rb
|
206
209
|
- test/paid/api_list_test.rb
|
207
|
-
- test/paid/
|
208
|
-
- test/paid/api_singleton_test.rb
|
209
|
-
- test/paid/authentication_test.rb
|
210
|
+
- test/paid/api_method_test.rb
|
210
211
|
- test/paid/customer_test.rb
|
211
212
|
- test/paid/event_test.rb
|
213
|
+
- test/paid/headers_builder_test.rb
|
212
214
|
- test/paid/invoice_test.rb
|
215
|
+
- test/paid/params_builder_test.rb
|
216
|
+
- test/paid/path_builder_test.rb
|
213
217
|
- test/paid/plan_test.rb
|
214
|
-
- test/paid/
|
218
|
+
- test/paid/requester_test.rb
|
215
219
|
- test/paid/subscription_test.rb
|
216
220
|
- test/paid/transaction_test.rb
|
217
221
|
- test/paid/util_test.rb
|
data/lib/paid/api_class.rb
DELETED
@@ -1,338 +0,0 @@
|
|
1
|
-
module Paid
|
2
|
-
class APIClass
|
3
|
-
attr_accessor :json
|
4
|
-
|
5
|
-
def self.path
|
6
|
-
raise NotImplementedError.new("APIClass is an abstract class. Please refer to its subclasses: #{subclasses}")
|
7
|
-
end
|
8
|
-
|
9
|
-
def path
|
10
|
-
raise NotImplementedError.new("APIClass is an abstract class. Please refer to its subclasses: #{APIClass.subclasses}")
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.api_class_method(name, method, path=nil, opts={})
|
14
|
-
singleton = class << self; self end
|
15
|
-
singleton.send(:define_method, name, api_lambda(name, method, path, opts))
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.api_instance_method(name, method, path=nil, opts={})
|
19
|
-
self.send(:define_method, name, api_lambda(name, method, path, opts))
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.attribute(name, klass=nil)
|
23
|
-
@attribute_names ||= Set.new
|
24
|
-
@attribute_names << name.to_sym
|
25
|
-
|
26
|
-
self.send(:define_method, "#{name}", attribute_get_lambda(name))
|
27
|
-
self.send(:define_method, "#{name}=", attribute_set_lambda(name, klass))
|
28
|
-
end
|
29
|
-
|
30
|
-
def attributes
|
31
|
-
attributes = {}
|
32
|
-
self.class.attribute_names.each do |attr|
|
33
|
-
attributes[attr.to_sym] = self.send(attr)
|
34
|
-
end
|
35
|
-
attributes
|
36
|
-
end
|
37
|
-
|
38
|
-
def non_nil_attributes
|
39
|
-
attributes.select{|k, v| !v.nil? }
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.attribute_names
|
43
|
-
@attribute_names ||= Set.new
|
44
|
-
unless self == APIClass
|
45
|
-
@attribute_names + self.superclass.attribute_names
|
46
|
-
else
|
47
|
-
@attribute_names
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def mark_attribute_changed(attr_name)
|
52
|
-
@changed_attribute_names ||= Set.new
|
53
|
-
@changed_attribute_names << attr_name.to_sym
|
54
|
-
end
|
55
|
-
|
56
|
-
def changed_attribute_names
|
57
|
-
@changed_attribute_names ||= Set.new
|
58
|
-
attributes.each do |key, val|
|
59
|
-
next if @changed_attribute_names.include?(key)
|
60
|
-
if val.is_a?(Array) || val.is_a?(Hash)
|
61
|
-
@changed_attribute_names << key if json[key] != val
|
62
|
-
end
|
63
|
-
end
|
64
|
-
@changed_attribute_names
|
65
|
-
end
|
66
|
-
|
67
|
-
def changed_attributes
|
68
|
-
ret = {}
|
69
|
-
changed_attribute_names.each do |attr|
|
70
|
-
ret[attr] = send(attr)
|
71
|
-
end
|
72
|
-
ret
|
73
|
-
end
|
74
|
-
|
75
|
-
def clear_changed_attributes
|
76
|
-
@changed_attribute_names = Set.new
|
77
|
-
end
|
78
|
-
|
79
|
-
|
80
|
-
def self.changed_lambda
|
81
|
-
# This runs in the context of an instance since it is used in
|
82
|
-
# an api_instance_method
|
83
|
-
lambda do |instance|
|
84
|
-
instance.changed_attributes
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def initialize(id=nil)
|
89
|
-
refresh_from(id)
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.construct(json={})
|
93
|
-
self.new.refresh_from(json)
|
94
|
-
end
|
95
|
-
|
96
|
-
def refresh_from(json={})
|
97
|
-
unless json.is_a?(Hash)
|
98
|
-
json = { :id => json }
|
99
|
-
end
|
100
|
-
self.json = Util.sorta_deep_clone(json)
|
101
|
-
|
102
|
-
json.each do |k, v|
|
103
|
-
if self.class.attribute_names.include?(k.to_sym)
|
104
|
-
self.send("#{k}=", v)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
clear_changed_attributes
|
108
|
-
self
|
109
|
-
end
|
110
|
-
|
111
|
-
# Alias, but dont declare it as one because we need to use overloaded methods.
|
112
|
-
def construct(json={})
|
113
|
-
refresh_from(json)
|
114
|
-
end
|
115
|
-
|
116
|
-
def self.subclasses
|
117
|
-
return @subclasses ||= Set.new
|
118
|
-
end
|
119
|
-
|
120
|
-
def self.subclass_fetch(name)
|
121
|
-
@subclasses_hash ||= {}
|
122
|
-
if @subclasses_hash.has_key?(name)
|
123
|
-
@subclasses_hash[name]
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def self.register_subclass(subclass, name=nil)
|
128
|
-
@subclasses ||= Set.new
|
129
|
-
@subclasses << subclass
|
130
|
-
|
131
|
-
unless name.nil?
|
132
|
-
@subclasses_hash ||= {}
|
133
|
-
@subclasses_hash[name] = subclass
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def inspect
|
138
|
-
id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
|
139
|
-
"#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(attributes)
|
140
|
-
end
|
141
|
-
|
142
|
-
def to_json(*a)
|
143
|
-
JSON.generate(attributes)
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
private
|
148
|
-
|
149
|
-
def instance_variables_include?(name)
|
150
|
-
if RUBY_VERSION <= '1.9'
|
151
|
-
instance_variables.include?("@#{name}")
|
152
|
-
else
|
153
|
-
instance_variables.include?("@#{name}".to_sym)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def self.attribute_get_lambda(name)
|
158
|
-
lambda do
|
159
|
-
unless instance_variables_include?(name)
|
160
|
-
nil
|
161
|
-
else
|
162
|
-
instance_variable_get("@#{name}")
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def self.attribute_set_lambda(name, klass=nil)
|
168
|
-
lambda do |val|
|
169
|
-
if klass
|
170
|
-
val = klass.construct(val)
|
171
|
-
end
|
172
|
-
instance_variable_set("@#{name}", val)
|
173
|
-
mark_attribute_changed(name)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def self.api_lambda(out_name, out_method, out_path=nil, out_opts={})
|
178
|
-
# Path, Opts, and Klass are all optional, so we have to determine
|
179
|
-
# which were provided using the criteria:
|
180
|
-
temp = [out_path, out_opts]
|
181
|
-
out_path = temp.select{ |t| t.is_a?(String) }.first || nil
|
182
|
-
out_opts = temp.select{ |t| t.is_a?(Hash) }.first || {}
|
183
|
-
|
184
|
-
out_arg_names = out_opts[:arguments] || []
|
185
|
-
out_constructor = out_opts[:constructor] || :self
|
186
|
-
out_default_params = out_opts[:default_params] || {}
|
187
|
-
|
188
|
-
lambda do |*args|
|
189
|
-
# Make sure we have clean data
|
190
|
-
constructor = out_constructor
|
191
|
-
method = out_method
|
192
|
-
path = nil
|
193
|
-
path = out_path.dup if out_path
|
194
|
-
arg_names = nil
|
195
|
-
arg_names = out_arg_names.dup if out_arg_names
|
196
|
-
default_params = out_default_params # dont need to dup this since it isn't modified directly
|
197
|
-
|
198
|
-
validate_args(arg_names, *args)
|
199
|
-
arguments = compose_arguments(method, arg_names, *args)
|
200
|
-
composed_path = compose_api_path(path, arguments)
|
201
|
-
unused_args = determine_unused_args(path, arg_names, arguments)
|
202
|
-
arguments[:params] = compose_params(arguments[:params], unused_args, default_params)
|
203
|
-
|
204
|
-
resp = Paid.request(method, composed_path, arguments[:params], arguments[:opts])
|
205
|
-
|
206
|
-
|
207
|
-
puts "Making a call to #{composed_path}"
|
208
|
-
|
209
|
-
if constructor.is_a?(Class)
|
210
|
-
constructor.construct(resp)
|
211
|
-
elsif constructor.is_a?(Proc)
|
212
|
-
constructor.call(resp)
|
213
|
-
elsif constructor.is_a?(Symbol)
|
214
|
-
if constructor == :self
|
215
|
-
self.construct(resp)
|
216
|
-
else
|
217
|
-
raise ArgumentError.new("Invalid constructor. See method definition.")
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def self.validate_args(arg_names, *args)
|
224
|
-
# Make sure we have valid arguments
|
225
|
-
if args.length > arg_names.length
|
226
|
-
if args.length > arg_names.length + 2 # more than params and opts were included
|
227
|
-
raise ArgumentError.new("Too many arguments")
|
228
|
-
else
|
229
|
-
# Params and opts are allowed, but they must be hashes
|
230
|
-
args[arg_names.length..-1].each do |arg|
|
231
|
-
unless arg.is_a?(Hash) || arg.nil?
|
232
|
-
raise ArgumentError.new("Invalid Param or Opts argument")
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
if args.length < arg_names.length
|
239
|
-
missing = arg_names[args.length..-1]
|
240
|
-
raise ArgumentError.new("Missing arguments #{missing}")
|
241
|
-
end
|
242
|
-
end
|
243
|
-
def validate_args(arg_names, *args)
|
244
|
-
self.class.validate_args(arg_names, *args)
|
245
|
-
end
|
246
|
-
|
247
|
-
# Priority: params > unused_args > default_params
|
248
|
-
def self.compose_params(params={}, unused_args={}, default_params={}, this=self)
|
249
|
-
ret = {}
|
250
|
-
|
251
|
-
# Handle the default params
|
252
|
-
if default_params.is_a?(Proc)
|
253
|
-
default_params = default_params.call(this)
|
254
|
-
elsif default_params.is_a?(Symbol)
|
255
|
-
default_params = this.send(default_params)
|
256
|
-
end
|
257
|
-
|
258
|
-
ret.update(default_params || {})
|
259
|
-
ret.update(unused_args || {})
|
260
|
-
ret.update(params || {})
|
261
|
-
ret
|
262
|
-
end
|
263
|
-
def compose_params(params={}, unused_args={}, default_params={})
|
264
|
-
self.class.compose_params(params, unused_args, default_params, self)
|
265
|
-
end
|
266
|
-
|
267
|
-
def self.compose_arguments(method, arg_names, *args)
|
268
|
-
arguments = {}
|
269
|
-
names = arg_names.dup + [:params, :opts]
|
270
|
-
|
271
|
-
names.each_with_index do |k, i|
|
272
|
-
arguments[k] = args[i] if args.length > i
|
273
|
-
end
|
274
|
-
arguments[:params] ||= {}
|
275
|
-
arguments[:opts] ||= {}
|
276
|
-
|
277
|
-
arguments
|
278
|
-
end
|
279
|
-
def compose_arguments(method, arg_names, *args)
|
280
|
-
self.class.compose_arguments(method, arg_names, *args)
|
281
|
-
end
|
282
|
-
|
283
|
-
def self.compose_api_path(path, arguments, this=self)
|
284
|
-
# Setup the path using the following attribute order:
|
285
|
-
# 1. Args passed in
|
286
|
-
# 2. Args on this
|
287
|
-
# 3. Args on this.class
|
288
|
-
ret = (path || this.path).dup
|
289
|
-
if ret.include?(":")
|
290
|
-
missing = Set.new
|
291
|
-
matches = ret.scan(/:([^\/]*)/).flatten.map(&:to_sym)
|
292
|
-
matches.each do |match|
|
293
|
-
value = arguments[match]
|
294
|
-
begin
|
295
|
-
value ||= this.send(match)
|
296
|
-
rescue NoMethodError
|
297
|
-
end
|
298
|
-
begin
|
299
|
-
value ||= this.class.send(match) unless this.class == Class
|
300
|
-
rescue NoMethodError
|
301
|
-
end
|
302
|
-
|
303
|
-
if value.nil?
|
304
|
-
missing << match
|
305
|
-
end
|
306
|
-
|
307
|
-
ret.sub!(match.inspect, "#{value}")
|
308
|
-
end
|
309
|
-
|
310
|
-
unless missing.empty?
|
311
|
-
raise InvalidRequestError.new("Could not determine the full URL to request. Missing the following values: #{missing.to_a.join(', ')}.")
|
312
|
-
end
|
313
|
-
end
|
314
|
-
ret
|
315
|
-
end
|
316
|
-
def compose_api_path(path, arguments)
|
317
|
-
self.class.compose_api_path(path, arguments, self)
|
318
|
-
end
|
319
|
-
|
320
|
-
def self.determine_unused_args(path, arg_names, arguments, this=self)
|
321
|
-
unused = Set.new(arg_names)
|
322
|
-
path ||= this.path
|
323
|
-
if path.include?(":")
|
324
|
-
matches = path.scan(/:([^\/]*)/).flatten.map(&:to_sym)
|
325
|
-
matches.each{ |m| unused.delete(m) }
|
326
|
-
end
|
327
|
-
ret = {}
|
328
|
-
unused.each do |arg_name|
|
329
|
-
ret[arg_name] = arguments[arg_name]
|
330
|
-
end
|
331
|
-
ret
|
332
|
-
end
|
333
|
-
def determine_unused_args(path, arg_names, arguments)
|
334
|
-
self.class.determine_unused_args(path, arg_names, arguments, self)
|
335
|
-
end
|
336
|
-
|
337
|
-
end
|
338
|
-
end
|