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/lib/paid/invoice.rb CHANGED
@@ -1,26 +1,65 @@
1
1
  module Paid
2
2
  class Invoice < APIResource
3
- # attributes :id and :object inherited from APIResource
4
- attribute :summary
5
- attribute :chase_schedule
6
- attribute :next_chase_on
7
- attribute :customer
8
- attribute :issued_at
9
- attribute :terms
10
- attribute :url
3
+ attr_reader :id
4
+ attr_reader :object
5
+ attr_accessor :summary
6
+ attr_accessor :chase_schedule
7
+ attr_accessor :next_chase_on
8
+ attr_accessor :customer
9
+ attr_accessor :issued_at
10
+ attr_accessor :terms
11
+ attr_accessor :url
11
12
 
12
- api_class_method :all, :get, :constructor => APIList.constructor(Invoice)
13
- api_class_method :retrieve, :get, ":path/:id", :arguments => [:id]
14
- api_class_method :create, :post
13
+ def self.all(params={}, headers={})
14
+ method = APIMethod.new(:get, "/invoices", params, headers, self)
15
+ APIList.new(self, method.execute, method)
16
+ end
17
+
18
+ def self.retrieve(id, params={}, headers={})
19
+ params = ParamsBuilder.merge(params, {
20
+ :id => id
21
+ })
22
+ method = APIMethod.new(:get, "/invoices/:id", params, headers, self)
23
+ self.new(method.execute, method)
24
+ end
25
+
26
+ def self.create(params={}, headers={})
27
+ method = APIMethod.new(:post, "/invoices", params, headers, self)
28
+ self.new(method.execute, method)
29
+ end
15
30
 
16
- api_instance_method :issue, :post, ":path/issue"
17
- api_instance_method :mark_as_paid, :post, ":path/mark_as_paid" # requires :via in params
31
+ def refresh(params={}, headers={})
32
+ method = APIMethod.new(:get, "/invoices/:id", params, headers, self)
33
+ self.refresh_from(method.execute, method)
34
+ end
18
35
 
36
+ def issue(params={}, headers={})
37
+ method = APIMethod.new(:post, "/invoices/:id/issue", params, headers, self)
38
+ self.refresh_from(method.execute, method)
39
+ end
19
40
 
20
- def self.path
21
- "/v0/invoices"
41
+ def mark_as_paid(params={}, headers={})
42
+ method = APIMethod.new(:post, "/invoices/:id/mark_as_paid", params, headers, self)
43
+ self.refresh_from(method.execute, method)
22
44
  end
23
45
 
24
- APIClass.register_subclass(self, "invoice")
46
+ def void(params={}, headers={})
47
+ method = APIMethod.new(:post, "/invoices/:id/void", params, headers, self)
48
+ method.execute
49
+ end
50
+
51
+
52
+ APIResource.register_api_subclass(self, "invoice")
53
+ @api_attributes = {
54
+ :id => { :readonly => true },
55
+ :object => { :readonly => true },
56
+ :summary => nil,
57
+ :chase_schedule => nil,
58
+ :next_chase_on => nil,
59
+ :customer => nil,
60
+ :issued_at => nil,
61
+ :terms => nil,
62
+ :url => nil
63
+ }
25
64
  end
26
65
  end
@@ -0,0 +1,26 @@
1
+ module Paid
2
+ module ParamsBuilder
3
+
4
+ def self.clean(params)
5
+ Util.symbolize_keys(params || {})
6
+ end
7
+
8
+ # Clean the params, and the hash to_merge, and then merge them.
9
+ # This ensures that we dont get something like { "id" => 123, :id => 321 }.
10
+ def self.merge(params, to_merge)
11
+ params = clean(params)
12
+ to_merge = clean(to_merge)
13
+ params.merge(to_merge)
14
+ end
15
+
16
+ def self.build(params, api_key=nil, auth_key=nil)
17
+ clean(params)
18
+ end
19
+
20
+ end
21
+ end
22
+
23
+
24
+
25
+
26
+
@@ -0,0 +1,38 @@
1
+ module Paid
2
+ module PathBuilder
3
+
4
+ # Take a path like:
5
+ # ":path/:id/dogs/:dog_id"
6
+ # and convert it to:
7
+ # "#{object.path}/#{object.id}/dogs/#{params[:id]}" => "/objects/1/dogs/2"
8
+ #
9
+ # Path priority is:
10
+ # 1. Object - this will be a class or an instance of a class.
11
+ # 2. Params - this is a hash of key values. All keys *must* be symbolized.
12
+ def self.build(path, object, params)
13
+ ret = path.dup
14
+ if ret.include?(":")
15
+ matches = ret.scan(/:([^\/]*)/).flatten.map(&:to_sym)
16
+ missing = Set.new(matches)
17
+
18
+ matches.each do |match|
19
+ value = determine_value(match, object, params)
20
+ missing.delete(match) unless value.nil?
21
+ ret.sub!(match.inspect, "#{value}")
22
+ end
23
+
24
+ if missing.any?
25
+ raise ArgumentError.new("Could not determine the full URL. The following values of the path are missing: #{missing.to_a.join(', ')}. Try setting them in your params.")
26
+ end
27
+ end
28
+ ret
29
+ end
30
+
31
+ def self.determine_value(match, object, params)
32
+ value = object.send(match) if object && object.respond_to?(match)
33
+ value ||= params[match] if params && params.has_key?(match)
34
+ value
35
+ end
36
+
37
+ end
38
+ end
data/lib/paid/plan.rb CHANGED
@@ -1,22 +1,47 @@
1
1
  module Paid
2
2
  class Plan < APIResource
3
+ attr_reader :id
4
+ attr_reader :object
5
+ attr_reader :created_at
6
+ attr_accessor :name
7
+ attr_accessor :description
8
+ attr_accessor :interval
9
+ attr_accessor :interval_count
10
+ attr_accessor :amount
3
11
 
4
- # attributes :id and :object inherited from APIResource
5
- attribute :name
6
- attribute :description
7
- attribute :interval
8
- attribute :interval_count
9
- attribute :amount
10
- attribute :created_at
12
+ def self.all(params={}, headers={})
13
+ method = APIMethod.new(:get, "/plans", params, headers, self)
14
+ APIList.new(self, method.execute, method)
15
+ end
16
+
17
+ def self.retrieve(id, params={}, headers={})
18
+ params = ParamsBuilder.merge(params, {
19
+ :id => id
20
+ })
21
+ method = APIMethod.new(:get, "/plans/:id", params, headers, self)
22
+ self.new(method.execute, method)
23
+ end
11
24
 
12
- api_class_method :all, :get, :constructor => APIList.constructor(Plan)
13
- api_class_method :retrieve, :get, ":path/:id", :arguments => [:id]
14
- api_class_method :create, :post
25
+ def self.create(params={}, headers={})
26
+ method = APIMethod.new(:post, "/plans", params, headers, self)
27
+ self.new(method.execute, method)
28
+ end
15
29
 
16
- def self.path
17
- "/v0/plans"
30
+ def refresh(params={}, headers={})
31
+ method = APIMethod.new(:get, "/plans/:id", params, headers, self)
32
+ self.refresh_from(method.execute, method)
18
33
  end
19
34
 
20
- APIClass.register_subclass(self, "plan")
35
+ APIResource.register_api_subclass(self, "plan")
36
+ @api_attributes = {
37
+ :id => { :readonly => true },
38
+ :object => { :readonly => true },
39
+ :created_at => { :readonly => true },
40
+ :name => nil,
41
+ :description => nil,
42
+ :interval => nil,
43
+ :interval_count => nil,
44
+ :amount => nil,
45
+ }
21
46
  end
22
47
  end
@@ -0,0 +1,26 @@
1
+ module Paid
2
+ class RefundList < APIList
3
+ attr_accessor :parent_id
4
+
5
+ def initialize(json={}, api_method=nil, parent_id=nil)
6
+ @klass = Util.constantize(:Transaction)
7
+ @parent_id = parent_id
8
+ refresh_from(json, api_method)
9
+ end
10
+
11
+ # Not live on the server yet.
12
+ # def all(params={}, headers={})
13
+ # method = APIMethod.new(:get, "/transactions/:parent_id/refunds", params, headers, self)
14
+ # APIList.new(:Transaction, method.execute, method)
15
+ # end
16
+
17
+ def create(params={}, headers={})
18
+ method = APIMethod.new(:post, "/transactions/:parent_id/refunds", params, headers, self)
19
+ Util.constantize(:Transaction).new(method.execute, method)
20
+ end
21
+
22
+ @api_attributes = {
23
+ :data => { :readonly => true }
24
+ }
25
+ end
26
+ end
@@ -0,0 +1,97 @@
1
+ module Paid
2
+ module Requester
3
+
4
+ def self.request(method, url, params, headers)
5
+ method = method.to_sym
6
+ url, params = prepare_params(url, params, method)
7
+ request_opts = {
8
+ :method => method,
9
+ :url => url,
10
+ :headers => headers,
11
+ :payload => params,
12
+
13
+ :verify_ssl => false,
14
+ :open_timeout => 30,
15
+ :timeout => 60
16
+ }
17
+
18
+ execute_request(request_opts)
19
+ end
20
+
21
+ def self.execute_request(opts)
22
+ RestClient::Request.execute(opts)
23
+ end
24
+
25
+ def self.get(url, params, headers)
26
+ self.request(:get, url, params, headers)
27
+ end
28
+
29
+ def self.delete(url, params, headers)
30
+ self.request(:delete, url, params, headers)
31
+ end
32
+
33
+ def self.put(url, params, headers)
34
+ self.request(:put, url, params, headers)
35
+ end
36
+
37
+ def self.post(url, params, headers)
38
+ self.request(:post, url, params, headers)
39
+ end
40
+
41
+ def self.prepare_params(url, params, method)
42
+ if [:get, :head, :delete].include?(method)
43
+ unless params.empty?
44
+ url += URI.parse(url).query ? '&' : '?' + query_string(params)
45
+ end
46
+ params = nil
47
+ else
48
+ if !RestClient::Payload.has_file?(params)
49
+ params = query_string(params)
50
+ end
51
+ end
52
+ [url, params]
53
+ end
54
+
55
+ def self.query_string(params)
56
+ params ||= {}
57
+ if params.any?
58
+ query_array(params).join('&')
59
+ else
60
+ ""
61
+ end
62
+ end
63
+
64
+ # Three major use cases (and nesting of them needs to be supported):
65
+ # { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
66
+ # { :a => [1, 2] } => ["a[]=1", "a[]=2"]
67
+ # { :a => "value" } => ["a=value"]
68
+ def self.query_array(params, key_prefix=nil)
69
+ ret = []
70
+ params.each do |key, value|
71
+ if params.is_a?(Array)
72
+ value = key
73
+ key = ''
74
+ end
75
+ key_suffix = escape(key)
76
+ full_key = key_prefix ? "#{key_prefix}[#{key_suffix}]" : key_suffix
77
+
78
+ if value.is_a?(Hash) || value.is_a?(Array)
79
+ # Handles the following cases:
80
+ # { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
81
+ # { :a => [1, 2] } => ["a[]=1", "a[]=2"]
82
+ ret += query_array(value, full_key)
83
+ else
84
+ # Handles the base case with just key and value:
85
+ # { :a => "value" } => ["a=value"]
86
+ ret << "#{full_key}=#{escape(value)}"
87
+ end
88
+ end
89
+ ret
90
+ end
91
+
92
+ def self.escape(val)
93
+ URI.escape(val.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
94
+ end
95
+
96
+ end
97
+ end
@@ -1,25 +1,56 @@
1
1
  module Paid
2
2
  class Subscription < APIResource
3
- # attributes :id and :object inherited from APIResource
4
- attribute :created_at
5
- attribute :starts_on
6
- attribute :next_transaction_on
7
- attribute :plan, Plan
8
- attribute :customer
9
- attribute :started_at
10
- attribute :ended_at
11
- attribute :cancelled_at
3
+ attr_reader :id
4
+ attr_reader :object
5
+ attr_reader :created_at
6
+ attr_accessor :starts_on
7
+ attr_accessor :next_transaction_on
8
+ attr_accessor :plan
9
+ attr_accessor :customer
10
+ attr_accessor :started_at
11
+ attr_accessor :ended_at
12
+ attr_accessor :cancelled_at
12
13
 
13
- api_class_method :all, :get, :constructor => APIList.constructor(Subscription)
14
- api_class_method :retrieve, :get, ":path/:id", :arguments => [:id]
15
- api_class_method :create, :post
14
+ def self.all(params={}, headers={})
15
+ method = APIMethod.new(:get, "/subscriptions", params, headers, self)
16
+ APIList.new(self, method.execute, method)
17
+ end
18
+
19
+ def self.retrieve(id, params={}, headers={})
20
+ params = ParamsBuilder.merge(params, {
21
+ :id => id
22
+ })
23
+ method = APIMethod.new(:get, "/subscriptions/:id", params, headers, self)
24
+ self.new(method.execute, method)
25
+ end
16
26
 
17
- api_instance_method :cancel, :post, ":path/cancel"
27
+ def self.create(params={}, headers={})
28
+ method = APIMethod.new(:post, "/subscriptions", params, headers, self)
29
+ self.new(method.execute, method)
30
+ end
31
+
32
+ def refresh(params={}, headers={})
33
+ method = APIMethod.new(:get, "/subscriptions/:id", params, headers, self)
34
+ self.refresh_from(method.execute, method)
35
+ end
18
36
 
19
- def self.path
20
- "/v0/subscriptions"
37
+ def cancel(params={}, headers={})
38
+ method = APIMethod.new(:post, "/subscriptions/:id/cancel", params, headers, self)
39
+ self.refresh_from(method.execute, method)
21
40
  end
22
41
 
23
- APIClass.register_subclass(self, "subscription")
42
+ APIResource.register_api_subclass(self, "subscription")
43
+ @api_attributes = {
44
+ :id => { :readonly => true },
45
+ :object => { :readonly => true },
46
+ :created_at => { :readonly => true },
47
+ :starts_on => nil,
48
+ :next_transaction_on => nil,
49
+ :plan => { :constructor => :Plan },
50
+ :customer => nil,
51
+ :started_at => nil,
52
+ :ended_at => nil,
53
+ :cancelled_at => nil
54
+ }
24
55
  end
25
56
  end
@@ -1,26 +1,74 @@
1
1
  module Paid
2
2
  class Transaction < APIResource
3
- # attributes :id and :object inherited from APIResource
4
- attribute :amount
5
- attribute :description
6
- attribute :customer
7
- attribute :paid
8
- attribute :paid_on
9
- attribute :properties
10
- attribute :invoice
3
+ attr_reader :id
4
+ attr_reader :object
5
+ attr_accessor :amount
6
+ attr_accessor :description
7
+ attr_accessor :customer
8
+ attr_accessor :paid
9
+ attr_accessor :paid_on
10
+ attr_accessor :properties
11
+ attr_accessor :invoice
12
+ attr_accessor :refunds
11
13
 
12
- api_class_method :create, :post
13
- api_class_method :retrieve, :get, ":path/:id", :arguments => [:id]
14
- api_class_method :all, :get, :constructor => APIList.constructor(Transaction)
14
+ def refresh_from(json={}, api_method=nil)
15
+ super(json, api_method)
16
+ json = { :id => json } unless json.is_a?(Hash)
17
+ @refunds = RefundList.new(json[:refunds], nil, id)
18
+ self
19
+ end
20
+
21
+ def self.all(params={}, headers={})
22
+ method = APIMethod.new(:get, "/transactions", params, headers, self)
23
+ APIList.new(self, method.execute, method)
24
+ end
15
25
 
16
- api_instance_method :save, :put, :default_params => changed_lambda
17
- api_instance_method :mark_as_paid, :post, ":path/mark_as_paid"
26
+ def self.retrieve(id, params={}, headers={})
27
+ params = ParamsBuilder.merge(params, {
28
+ :id => id
29
+ })
30
+ method = APIMethod.new(:get, "/transactions/:id", params, headers, self)
31
+ self.new(method.execute, method)
32
+ end
33
+
34
+ def self.create(params={}, headers={})
35
+ method = APIMethod.new(:post, "/transactions", params, headers, self)
36
+ self.new(method.execute, method)
37
+ end
18
38
 
39
+ def refresh(params={}, headers={})
40
+ method = APIMethod.new(:get, "/transactions/:id", params, headers, self)
41
+ self.refresh_from(method.execute, method)
42
+ end
43
+
44
+ def save(params={}, headers={})
45
+ params = ParamsBuilder.merge(params, changed_api_attributes)
46
+ method = APIMethod.new(:put, "/transactions/:id", params, headers, self)
47
+ self.refresh_from(method.execute, method)
48
+ end
49
+
50
+ def delete(params={}, headers={})
51
+ method = APIMethod.new(:delete, "/transactions/:id", params, headers, self)
52
+ self.refresh_from(method.execute, method)
53
+ end
19
54
 
20
- def self.path
21
- "/v0/transactions"
55
+ def mark_as_paid(params={}, headers={})
56
+ method = APIMethod.new(:post, "/transactions/:id/mark_as_paid", params, headers, self)
57
+ self.refresh_from(method.execute, method)
22
58
  end
23
59
 
24
- APIClass.register_subclass(self, "transaction")
60
+ APIResource.register_api_subclass(self, "transaction")
61
+ @api_attributes = {
62
+ :id => { :readonly => true },
63
+ :object => { :readonly => true },
64
+ :amount => nil,
65
+ :description => nil,
66
+ :customer => nil,
67
+ :paid => nil,
68
+ :paid_on => nil,
69
+ :properties => nil,
70
+ :invoice => nil,
71
+ :refunds => { :constructor => :RefundList, :provide_parent => true }
72
+ }
25
73
  end
26
74
  end