paid 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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