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/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