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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +6 -0
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/lib/paid.rb +32 -207
  6. data/lib/paid/account.rb +22 -10
  7. data/lib/paid/api_list.rb +56 -17
  8. data/lib/paid/api_method.rb +93 -0
  9. data/lib/paid/api_resource.rb +130 -8
  10. data/lib/paid/customer.rb +104 -40
  11. data/lib/paid/errors/api_connection_error.rb +1 -1
  12. data/lib/paid/errors/api_error.rb +25 -3
  13. data/lib/paid/errors/authentication_error.rb +1 -1
  14. data/lib/paid/errors/paid_error.rb +2 -9
  15. data/lib/paid/event.rb +28 -17
  16. data/lib/paid/event_data.rb +10 -0
  17. data/lib/paid/headers_builder.rb +75 -0
  18. data/lib/paid/invoice.rb +55 -16
  19. data/lib/paid/params_builder.rb +26 -0
  20. data/lib/paid/path_builder.rb +38 -0
  21. data/lib/paid/plan.rb +38 -13
  22. data/lib/paid/refund_list.rb +26 -0
  23. data/lib/paid/requester.rb +97 -0
  24. data/lib/paid/subscription.rb +47 -16
  25. data/lib/paid/transaction.rb +64 -16
  26. data/lib/paid/util.rb +14 -40
  27. data/paid.gemspec +1 -1
  28. data/test/paid/{api_class_test.rb → _api_resource_test.rb} +31 -17
  29. data/test/paid/account_test.rb +3 -3
  30. data/test/paid/api_list_test.rb +14 -8
  31. data/test/paid/api_method_test.rb +89 -0
  32. data/test/paid/customer_test.rb +20 -10
  33. data/test/paid/event_test.rb +3 -4
  34. data/test/paid/headers_builder_test.rb +39 -0
  35. data/test/paid/invoice_test.rb +3 -3
  36. data/test/paid/params_builder_test.rb +57 -0
  37. data/test/paid/path_builder_test.rb +67 -0
  38. data/test/paid/plan_test.rb +3 -3
  39. data/test/paid/requester_test.rb +86 -0
  40. data/test/paid/subscription_test.rb +3 -3
  41. data/test/paid/transaction_test.rb +4 -4
  42. data/test/paid/util_test.rb +36 -35
  43. data/test/test_data.rb +9 -2
  44. data/test/test_helper.rb +14 -14
  45. metadata +23 -19
  46. data/lib/paid/api_class.rb +0 -338
  47. data/lib/paid/api_singleton.rb +0 -5
  48. data/lib/paid/errors/invalid_request_error.rb +0 -10
  49. data/test/mock_resource.rb +0 -69
  50. data/test/paid/api_resource_test.rb +0 -28
  51. data/test/paid/api_singleton_test.rb +0 -12
  52. data/test/paid/authentication_test.rb +0 -50
  53. 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
- @mock_rest_client = nil
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
- def self.execute_request(opts)
18
- headers = opts[:headers]
19
- post_params = opts[:payload]
20
- case opts[:method]
21
- when :get then @mock_rest_client.get opts[:url], headers, post_params
22
- when :put then @mock_rest_client.put opts[:url], headers, post_params
23
- when :post then @mock_rest_client.post opts[:url], headers, post_params
24
- when :delete then @mock_rest_client.delete opts[:url], headers, post_params
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.1
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-03-15 00:00:00.000000000 Z
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
- - joncalhoun@gmail.com
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/mock_resource.rb
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/api_resource_test.rb
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/status_codes_test.rb
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.2.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/mock_resource.rb
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/api_resource_test.rb
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/status_codes_test.rb
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
@@ -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