paid 0.1.0 → 1.0.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.travis.yml +16 -0
  4. data/History.txt +4 -0
  5. data/README.md +58 -0
  6. data/Rakefile +9 -29
  7. data/VERSION +1 -0
  8. data/bin/paid-console +7 -0
  9. data/gemfiles/default-with-activesupport.gemfile +10 -0
  10. data/gemfiles/json.gemfile +12 -0
  11. data/gemfiles/yajl.gemfile +12 -0
  12. data/lib/paid.rb +129 -177
  13. data/lib/paid/account.rb +14 -1
  14. data/lib/paid/api_class.rb +336 -0
  15. data/lib/paid/api_list.rb +47 -0
  16. data/lib/paid/api_resource.rb +8 -25
  17. data/lib/paid/api_singleton.rb +5 -0
  18. data/lib/paid/customer.rb +36 -21
  19. data/lib/paid/errors/api_error.rb +6 -0
  20. data/lib/paid/event.rb +22 -1
  21. data/lib/paid/invoice.rb +16 -21
  22. data/lib/paid/plan.rb +18 -2
  23. data/lib/paid/subscription.rb +17 -11
  24. data/lib/paid/transaction.rb +19 -12
  25. data/lib/paid/util.rb +53 -106
  26. data/lib/paid/version.rb +1 -1
  27. data/paid.gemspec +10 -11
  28. data/tasks/api_test.rb +187 -0
  29. data/test/mock_resource.rb +69 -0
  30. data/test/paid/account_test.rb +41 -4
  31. data/test/paid/api_class_test.rb +412 -0
  32. data/test/paid/api_list_test.rb +17 -0
  33. data/test/paid/api_resource_test.rb +13 -343
  34. data/test/paid/api_singleton_test.rb +12 -0
  35. data/test/paid/authentication_test.rb +50 -0
  36. data/test/paid/customer_test.rb +189 -29
  37. data/test/paid/event_test.rb +74 -0
  38. data/test/paid/invoice_test.rb +101 -20
  39. data/test/paid/plan_test.rb +84 -8
  40. data/test/paid/status_codes_test.rb +63 -0
  41. data/test/paid/subscription_test.rb +100 -20
  42. data/test/paid/transaction_test.rb +110 -37
  43. data/test/paid/util_test.rb +15 -24
  44. data/test/test_data.rb +144 -93
  45. data/test/test_helper.rb +6 -4
  46. metadata +32 -26
  47. data/Gemfile.lock +0 -54
  48. data/README.rdoc +0 -35
  49. data/lib/data/ca-certificates.crt +0 -0
  50. data/lib/paid/alias.rb +0 -16
  51. data/lib/paid/api_operations/create.rb +0 -17
  52. data/lib/paid/api_operations/delete.rb +0 -11
  53. data/lib/paid/api_operations/list.rb +0 -17
  54. data/lib/paid/api_operations/update.rb +0 -57
  55. data/lib/paid/certificate_blacklist.rb +0 -55
  56. data/lib/paid/list_object.rb +0 -37
  57. data/lib/paid/paid_object.rb +0 -187
  58. data/lib/paid/singleton_api_resource.rb +0 -20
  59. data/lib/tasks/paid_tasks.rake +0 -4
  60. data/test/paid/alias_test.rb +0 -22
  61. data/test/paid/certificate_blacklist_test.rb +0 -18
  62. data/test/paid/list_object_test.rb +0 -16
  63. data/test/paid/paid_object_test.rb +0 -27
  64. data/test/paid/properties_test.rb +0 -103
data/paid.gemspec CHANGED
@@ -1,18 +1,16 @@
1
1
  $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
2
 
3
- # Maintain your gem's version:
4
- require "paid/version"
3
+ require 'paid/version'
5
4
 
6
- # Describe your gem and declare its dependencies:
7
- Gem::Specification.new do |s|
8
- s.name = 'paid'
9
- s.version = Paid::VERSION
10
- s.authors = ['Ryan Jackson']
11
- s.email = ['ryan@paidapi.com']
12
- s.homepage = 'http://docs.paidapi.com'
13
- s.summary = 'Ruby bindings for Paid API'
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'paid'
7
+ s.summary = 'Ruby bindings for Paid API'
14
8
  s.description = 'Paid is the programmatic way to manage payments. See https://paidapi.com for details.'
15
- s.license = 'MIT'
9
+ s.homepage = 'http://docs.paidapi.com'
10
+ s.authors = ['Jon Calhoun', 'Ryan Jackson']
11
+ s.email = ['joncalhoun@gmail.com', 'ryan@paidapi.com']
12
+ s.version = Paid::VERSION
13
+ s.license = 'MIT'
16
14
 
17
15
  s.add_dependency('rest-client', '~> 1.4')
18
16
  s.add_dependency('mime-types', '>= 1.25', '< 3.0')
@@ -28,3 +26,4 @@ Gem::Specification.new do |s|
28
26
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
29
27
  s.require_paths = ['lib']
30
28
  end
29
+
data/tasks/api_test.rb ADDED
@@ -0,0 +1,187 @@
1
+ require 'paid'
2
+
3
+ class APITest
4
+ def initialize(api_key)
5
+ Paid.api_key = api_key
6
+ end
7
+
8
+ def run
9
+ account = run_account_test
10
+ customer = run_customer_tests
11
+ plan = run_plan_tests
12
+ subscription = run_subscription_tests(customer, plan)
13
+ transaction = run_transaction_tests(customer)
14
+ invoice = run_invoice_tests(customer)
15
+ event = run_events_tests
16
+ run_advanced_tests(transaction, invoice, subscription)
17
+ end
18
+
19
+ def run_advanced_tests(transaction, invoice, subscription)
20
+ puts "Marking the transaction as paid..."
21
+ transaction.mark_as_paid
22
+ puts "Marked transaction=#{transaction.id} as paid."
23
+
24
+ puts "Issuing the invoice..."
25
+ invoice.issue
26
+ puts "Invoice=#{invoice.id} issued."
27
+
28
+ puts "Marking the invoice as paid..."
29
+ invoice.mark_as_paid(:via => :ach)
30
+ puts "Marked invoice=#{invoice.id} as paid."
31
+
32
+ puts "Cancelling subscription=#{subscription.id}..."
33
+ subscription.cancel
34
+ puts "Subscription cancelled."
35
+ end
36
+
37
+ def run_events_tests
38
+ puts "Looking up all events..."
39
+ events = Paid::Event.all
40
+ puts "Found #{events.length} events."
41
+
42
+ puts "Retrieving the first event..."
43
+ event = Paid::Event.retrieve(events.first.id)
44
+ puts "Retrieved the event with the id=#{event.id}"
45
+
46
+ event
47
+ end
48
+
49
+ def run_invoice_tests(customer)
50
+ puts "Creating an invoice with customer=#{customer.id}"
51
+ invoice = customer.generate_invoice
52
+ puts "Created: #{invoice.inspect}"
53
+
54
+ puts "Looking up all invoices..."
55
+ invoices = Paid::Invoice.all
56
+ puts "Found #{invoices.length} invoices."
57
+
58
+ puts "Retrieving the generated invoice..."
59
+ invoice = Paid::Invoice.retrieve(invoice.id)
60
+ puts "Retrieved the invoice with the id=#{invoice.id}"
61
+
62
+ puts "Retrieving the customer=#{customer.id}'s invoices..."
63
+ invoices = customer.invoices
64
+ puts "Retrieved: #{invoices.inspect}"
65
+
66
+ invoice
67
+ end
68
+
69
+ def run_transaction_tests(customer)
70
+ puts "Creating 2 transactions with customer=#{customer.id}"
71
+ transaction = Paid::Transaction.create({
72
+ :amount => 100,
73
+ :description => 'a description',
74
+ :customer => customer.id,
75
+ :paid => false
76
+ })
77
+ transaction_b = Paid::Transaction.create({
78
+ :amount => 1000,
79
+ :description => 'another description',
80
+ :customer => customer.id,
81
+ :paid => false
82
+ })
83
+ puts "Created: #{transaction.inspect} + 1 other"
84
+
85
+ puts "Looking up all transactions..."
86
+ transactions = Paid::Transaction.all
87
+ puts "Found #{transactions.length} transactions."
88
+
89
+ puts "Retrieving the created transaction..."
90
+ transaction = Paid::Transaction.retrieve(transaction.id)
91
+ puts "Retrieved the transaction with the id=#{transaction.id}"
92
+
93
+ puts "Updating the transaction's name..."
94
+ transaction.description = "an updated description..."
95
+ transaction.save
96
+ puts "Updated the transaction with id=#{transaction.id} with the new description #{transaction.description.inspect}"
97
+
98
+ puts "Looking up customer=#{customer.id}'s transactions..."
99
+ transactions = customer.transactions
100
+ puts "Retrieved: #{transactions.inspect}"
101
+
102
+ transaction
103
+ end
104
+
105
+ def run_subscription_tests(customer, plan)
106
+ puts "Creating a subscription for customer=#{customer.id} and plan=#{plan.id}..."
107
+ subscription = Paid::Subscription.create({
108
+ :starts_on => (Time.now + 1 * 60 * 60 * 24).strftime("%Y-%m-%d"),
109
+ :plan => plan.id,
110
+ :customer => customer.id
111
+ })
112
+ puts "Created: #{subscription.inspect}"
113
+
114
+ puts "Looking up all subscriptions..."
115
+ subscriptions = Paid::Subscription.all
116
+ puts "Found #{subscriptions.length} subscriptions."
117
+
118
+ puts "Retrieving the created subscription..."
119
+ subscription = Paid::Subscription.retrieve(subscription.id)
120
+ puts "Retrieved the subscription with the id=#{subscription.id}"
121
+
122
+ subscription
123
+ end
124
+
125
+ def run_plan_tests
126
+ puts "Creating a plan..."
127
+ plan = Paid::Plan.create({
128
+ :description => "Plan for testing stuff",
129
+ :name => "Test Plan #{Time.now.to_i}-#{rand(2000)}",
130
+ :interval => "month",
131
+ :interval_count => 1,
132
+ :amount => 5000
133
+ })
134
+ puts "Created: #{plan.inspect}"
135
+
136
+ puts "Looking up all plans..."
137
+ plans = Paid::Plan.all
138
+ puts "Found #{plans.length} plans."
139
+
140
+ puts "Retrieving the created plan..."
141
+ plan = Paid::Plan.retrieve(plan.id)
142
+ puts "Retrieved the plan with the id=#{plan.id}"
143
+
144
+ plan
145
+ end
146
+
147
+ def run_customer_tests
148
+ puts "Creating a customer..."
149
+ customer = Paid::Customer.create({
150
+ :name => "Paid",
151
+ :email => "hello@paidapi.com",
152
+ :description => "Obviously this is just a description.",
153
+ :phone => "4155069330",
154
+ :address_line1 => "2261 Market Street",
155
+ :address_line2 => "#567",
156
+ :address_city => "San Francisco",
157
+ :address_state => "CA",
158
+ :address_zip => "94114"
159
+ })
160
+ puts "Created: #{customer.inspect}"
161
+
162
+ puts "Looking up all customers..."
163
+ customers = Paid::Customer.all
164
+ puts "Found #{customers.length} customers"
165
+
166
+ puts "Retrieving the created customer..."
167
+ customer = Paid::Customer.retrieve(customer.id)
168
+ puts "Retrieved the customer with id=#{customer.id}"
169
+
170
+ # TODO(joncalhoun): Add by_external_id lookup test
171
+
172
+ puts "Updating the customer's name..."
173
+ customer.name = "Paid Inc"
174
+ customer.save
175
+ puts "Updated the customer with id=#{customer.id} with the new name #{customer.name.inspect}"
176
+
177
+ customer
178
+ end
179
+
180
+ def run_account_test
181
+ puts "Looking up the account..."
182
+ account = Paid::Account.retrieve
183
+ puts "Retrieved the account with id=#{account.id}"
184
+ account
185
+ end
186
+
187
+ end
@@ -0,0 +1,69 @@
1
+ # Setup a fake resource for testing the APIResource
2
+
3
+ class NestedResource < Paid::APIResource
4
+ attribute :price
5
+
6
+ def self.path
7
+ "/nested_resources"
8
+ end
9
+ end
10
+
11
+ class MockResource < Paid::APIResource
12
+ attribute :name
13
+ attribute :tarray
14
+ attribute :thash
15
+ attribute :nested, NestedResource
16
+
17
+ api_class_method :retrieve, :get, ":path/:id", :arguments => [:id]
18
+ api_class_method :all, :get, :constructor => Paid::APIList.constructor(MockResource)
19
+ api_class_method :create, :post
20
+ api_class_method :many_args_get, :get, ":path/:b/many", :arguments => [:a, :b, :c]
21
+ api_class_method :many_args_post, :post, ":path/:b/many", :arguments => [:a, :b, :c]
22
+ api_class_method :crazy_path, :get, ":crazy"
23
+
24
+ api_class_method :with_con_self, :get, :constructor => :self
25
+ api_class_method :with_con_class, :get, :constructor => MockResource
26
+ api_class_method :with_con_lambda, :get, :constructor => lambda{ |json| "lamdba result" }
27
+ api_class_method :with_con_default, :get
28
+
29
+ def self.default_lambda
30
+ lambda do |this|
31
+ self.default_values
32
+ end
33
+ end
34
+ api_class_method :with_lambda, :post, :default_params => self.default_lambda
35
+ api_class_method :with_symbol, :post, :default_params => :default_values
36
+ api_class_method :with_symbol_and_args, :post, :default_params => :default_values, :arguments => [:name]
37
+
38
+ api_instance_method :refresh, :get, :constructor => :self
39
+ api_instance_method :save, :put, :default_params => changed_lambda, :constructor => :self
40
+ api_instance_method :delete, :delete
41
+ api_instance_method :custom_path, :get, ":path", :arguments => [:path]
42
+ api_instance_method :name_path, :get, ":name"
43
+ api_instance_method :crazy_path, :get, ":crazy"
44
+
45
+ api_instance_method :with_con_self, :get, :constructor => :self
46
+ api_instance_method :with_con_class, :get, :constructor => MockResource
47
+ api_instance_method :with_con_lambda, :get, :constructor => lambda{ |json| "lamdba result" }
48
+ api_instance_method :with_con_default, :get
49
+
50
+ api_instance_method :with_lambda, :put, :default_params => changed_lambda
51
+ api_instance_method :with_symbol, :put, :default_params => :changed_attributes
52
+ api_instance_method :with_symbol_and_args, :put, :default_params => :changed_attributes, :arguments => [:name]
53
+
54
+ def self.path
55
+ "/mocks"
56
+ end
57
+
58
+ def self.crazy
59
+ "/crazy_path"
60
+ end
61
+
62
+ def self.default_values
63
+ {
64
+ :name => "default name",
65
+ :tarray => [1,2,3]
66
+ }
67
+ end
68
+
69
+ end
@@ -2,11 +2,48 @@ require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  module Paid
4
4
  class AccountTest < Test::Unit::TestCase
5
- should "account should be retrievable" do
6
- resp = {:email => "test+bindings@paidapi.com"}
7
- @mock.expects(:get).once.returns(test_response(resp))
5
+ setup do
6
+ @account_url = "#{Paid.api_base}/v0/account"
7
+ end
8
+
9
+ should 'be retrievable' do
10
+ @mock.expects(:get).once.with(@account_url, anything, anything).returns(test_response(test_account))
8
11
  a = Paid::Account.retrieve
9
- assert_equal "test+bindings@paidapi.com", a.email
12
+ assert(a.is_a?(Paid::Account))
13
+ assert_equal(test_account[:id], a.id)
14
+ end
15
+
16
+ context 'Retrieved Paid::Account instance' do
17
+ setup do
18
+ @mock.expects(:get).once.returns(test_response(test_account))
19
+ @account = Paid::Account.retrieve
20
+ end
21
+
22
+ should 'have the id attribute' do
23
+ assert_equal(test_account[:id], @account.id)
24
+ end
25
+
26
+ should 'have the object attribute' do
27
+ assert_equal(test_account[:object], @account.object)
28
+ end
29
+
30
+ should 'have the business_name attribute' do
31
+ assert_equal(test_account[:business_name], @account.business_name)
32
+ end
33
+
34
+ should 'have the business_url attribute' do
35
+ assert_equal(test_account[:business_url], @account.business_url)
36
+ end
37
+
38
+ should 'have the business_logo attribute' do
39
+ assert_equal(test_account[:business_logo], @account.business_logo)
40
+ end
10
41
  end
42
+
43
+ should 'be registered' do
44
+ assert(APIClass.subclasses.include?(Paid::Account))
45
+ assert_equal(Paid::Account, APIClass.subclass_fetch("account"))
46
+ end
47
+
11
48
  end
12
49
  end
@@ -0,0 +1,412 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../test_helper', __FILE__)
3
+
4
+ module Paid
5
+ class ApiClassTest < Test::Unit::TestCase
6
+
7
+ context 'Non-network actions' do
8
+ setup do
9
+ @mock.expects(:get).never
10
+ @mock.expects(:post).never
11
+ @mock.expects(:put).never
12
+ @mock.expects(:delete).never
13
+ end
14
+
15
+ should 'not fetch over the network when creating a new APIClass' do
16
+ MockResource.new('fake_id')
17
+ end
18
+
19
+ should 'not fetch over the network when creating a new APIClass from a hash' do
20
+ MockResource.construct(test_mock_resource)
21
+ end
22
+
23
+ should 'not fetch over the network when setting an attribute' do
24
+ c = MockResource.new('fake_id')
25
+ c.name = 'Another Name'
26
+ end
27
+
28
+ should 'not fetch over the network when accessing an attribute' do
29
+ c = MockResource.new('fake_id')
30
+ c.id
31
+ end
32
+ end
33
+
34
+ # These GET, POST, etc should really be in a dif test, but this is easier.
35
+ context 'Making a GET request' do
36
+ should 'urlencode values in params and have no payload' do
37
+ response = test_response(test_mock_resource_list)
38
+
39
+ # The test is to basically make sure this expectation passes.
40
+ @mock.expects(:get).with do |url, headers, params|
41
+ (url == "#{Paid.api_base}#{MockResource.path}?page=1&filter=test%20filter" ||
42
+ url == "#{Paid.api_base}#{MockResource.path}?filter=test%20filter&page=1") &&
43
+ params == nil
44
+ end.returns(response)
45
+
46
+ list = MockResource.all({
47
+ :page => 1,
48
+ :filter => 'test filter',
49
+ })
50
+ end
51
+ end
52
+
53
+ context 'Making a DELETE request' do
54
+ should 'urlencode values in params and have no payload' do
55
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
56
+ mock_resource = MockResource.retrieve("fake_id")
57
+
58
+ @mock.expects(:delete).once.with do |url, headers, payload|
59
+ url == "#{Paid.api_base}#{mock_resource.path}?reason=delinquent%20payments" && payload.nil?
60
+ end.returns(test_response({}))
61
+ mock_resource.delete(:reason => "delinquent payments")
62
+ end
63
+ end
64
+
65
+ context 'Making a PUT request' do
66
+ should 'have a payload with no query string' do
67
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
68
+ mock_resource = MockResource.retrieve("fake_id")
69
+ mock_resource.name = "new name"
70
+
71
+ @mock.expects(:put).once.with do |url, header, payload|
72
+ payload == { :name => "new name" }
73
+ end.returns(test_response(test_mock_resource))
74
+ mock_resource.save
75
+ end
76
+ end
77
+
78
+ context 'Making a POST request' do
79
+ should 'have a payload with no query string' do
80
+ params = { :name => "some name" }
81
+ @mock.expects(:post).once.with("#{Paid.api_base}#{MockResource.path}", anything, params).returns(test_response(test_mock_resource))
82
+
83
+ MockResource.create(params)
84
+ end
85
+ end
86
+
87
+ context 'APIClass :default_params' do
88
+ context 'api_instance_method' do
89
+ setup do
90
+ @response = test_response(test_mock_resource_list)
91
+ @mr = MockResource.new(test_mock_resource)
92
+ end
93
+
94
+ should 'call any provided lambdas and set the result as params' do
95
+ @mock.expects(:put).once.with(anything, anything, { :name => "new name" }).returns(@response)
96
+
97
+ @mr.name = "new name"
98
+ @mr.with_lambda
99
+ end
100
+
101
+ should 'call any method defined by a symbol and set the results as params' do
102
+ @mock.expects(:put).once.with(anything, anything, { :name => "new name" }).returns(@response)
103
+
104
+ @mr.name = "new name"
105
+ @mr.with_symbol
106
+ end
107
+
108
+ context 'order priority' do
109
+ # Intended order of priority:
110
+ # 1. Params. eg in mock_resource.save(params, opts), params should take
111
+ # priority with duplicate keys.
112
+ # 2. Arguments. These should take priority over default values from :params
113
+ # method.
114
+ # 3. Results from method defined by :default_params. This can be a symbol that is
115
+ # the name of a method to call, or a lambda (though a hash is preferred).
116
+ should 'prioritize params argument over args and default_params method' do
117
+ params = {
118
+ :name => "not arg name",
119
+ :tarray => ["not method tarray"],
120
+ :thash => { :val => "custom hash" }
121
+ }
122
+ @mock.expects(:put).once.with(anything, anything, params).returns(@response)
123
+
124
+ @mr.tarray = ["method array"]
125
+ @mr.with_symbol_and_args("arg name", params)
126
+ end
127
+
128
+ should 'prioritize arguments over :default_params' do
129
+ @mock.expects(:put).once.with(anything, anything, { :name => "arg name" }).returns(@response)
130
+
131
+ @mr.name = "changed name"
132
+ @mr.with_symbol_and_args("arg name")
133
+ end
134
+
135
+ should 'use :default_params method as even when args and params are present' do
136
+ @mock.expects(:put).once.with(anything, anything, {
137
+ :thash => { :val => "params hash" },
138
+ :name => "arg name",
139
+ :tarray => ["change array"],
140
+ }).returns(@response)
141
+
142
+ @mr.tarray = ["change array"]
143
+ @mr.with_symbol_and_args("arg name", { :thash => { :val => "params hash" } })
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'api_class_method' do
149
+ setup do
150
+ @response = test_response(test_mock_resource_list)
151
+ end
152
+
153
+ should 'call any provided lambdas and set the result as params' do
154
+ @mock.expects(:post).once.with(anything, anything, MockResource.default_values).returns(@response)
155
+ MockResource.with_lambda
156
+ end
157
+
158
+ should 'call any method defined by a symbol and set the results as params' do
159
+ @mock.expects(:post).once.with(anything, anything, MockResource.default_values).returns(@response)
160
+ MockResource.with_symbol
161
+ end
162
+
163
+ context 'order priority' do
164
+ # Intended order of priority:
165
+ # 1. Params. eg in mock_resource.save(params, opts), params should take
166
+ # priority with duplicate keys.
167
+ # 2. Arguments. These should take priority over default values from :params
168
+ # method.
169
+ # 3. Results from method defined by :default_params. This can be a symbol that is
170
+ # the name of a method to call, or a lambda (though a hash is preferred).
171
+ should 'prioritize params argument over args and default_params method' do
172
+ params = {
173
+ :name => "not arg name",
174
+ :tarray => ["not method tarray"],
175
+ :thash => { :val => "custom hash" }
176
+ }
177
+ @mock.expects(:post).once.with(anything, anything, params).returns(@response)
178
+ MockResource.with_symbol_and_args("name arg", params)
179
+ end
180
+
181
+ should 'prioritize arguments over :default_params' do
182
+ params = Util.sorta_deep_clone(MockResource.default_values)
183
+ params[:name] = "name arg"
184
+ @mock.expects(:post).once.with(anything, anything, params).returns(@response)
185
+ MockResource.with_symbol_and_args("name arg", params)
186
+ end
187
+
188
+ should 'use :default_params method as even when args and params are present' do
189
+ params = {
190
+ :name => "name arg",
191
+ :tarray => MockResource.default_values[:tarray].dup,
192
+ :thash => { :val => "custom hash" }
193
+ }
194
+ @mock.expects(:post).once.with(anything, anything, params).returns(@response)
195
+ MockResource.with_symbol_and_args("name arg", { :thash => { :val => "custom hash" }})
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+
202
+ context 'APIClass#attribute' do
203
+ should 'create a getter method' do
204
+ assert(MockResource.method_defined?(:name))
205
+ end
206
+
207
+ should 'create a setter method' do
208
+ assert(MockResource.method_defined?(:name=))
209
+ end
210
+
211
+ should 'have no changed attributes after init' do
212
+ mr = MockResource.new(test_mock_resource)
213
+ assert(mr.changed_attributes.empty?)
214
+ end
215
+
216
+ should 'keep track of changed attributes' do
217
+ mr = MockResource.new(test_mock_resource)
218
+ assert(mr.changed_attributes.empty?)
219
+ mr.name = "new name"
220
+ assert_equal({:name => "new name"}, mr.changed_attributes)
221
+ end
222
+
223
+ should 'keep track of changed arrays' do
224
+ mr = MockResource.new(test_mock_resource)
225
+ assert(mr.changed_attributes.empty?)
226
+ mr.tarray << "new"
227
+ assert_equal({:tarray => test_mock_resource[:tarray] + ["new"]}, mr.changed_attributes)
228
+ end
229
+
230
+ should 'keep track of changed hashes' do
231
+ mr = MockResource.new(test_mock_resource)
232
+ assert(mr.changed_attributes.empty?)
233
+ mr.thash[:some_key] = "new value"
234
+ assert_equal({:thash => { :some_key => "new value" }}, mr.changed_attributes)
235
+ end
236
+
237
+ context 'constructors' do
238
+ should 'instantiate on #new' do
239
+ mr = MockResource.new(test_mock_resource)
240
+ assert(mr.nested.is_a?(NestedResource))
241
+ end
242
+
243
+ should 'instantiate on #construct' do
244
+ mr = MockResource.construct(test_mock_resource)
245
+ assert(mr.nested.is_a?(NestedResource))
246
+ end
247
+
248
+ should 'instantiate on #refresh_from' do
249
+ mr = MockResource.new('fake_id')
250
+ assert(mr.nested.nil?)
251
+
252
+ mr.refresh_from(test_mock_resource)
253
+ assert(mr.nested.is_a?(NestedResource))
254
+ end
255
+ end
256
+ end
257
+
258
+ context 'APIClass :constructor' do
259
+ context 'for api_class_method' do
260
+ setup do
261
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
262
+ end
263
+
264
+ should 'create a new MockResource with :constructor => :self' do
265
+ mr = MockResource.with_con_self
266
+ assert(mr.is_a?(MockResource))
267
+ end
268
+
269
+ should 'create a new MockResource with :constructor => MockResource' do
270
+ mr = MockResource.with_con_class
271
+ assert(mr.is_a?(MockResource))
272
+ end
273
+
274
+ should 'use the lambda with :constructor => lambda{...}' do
275
+ mr = MockResource.with_con_lambda
276
+ assert_equal("lamdba result", mr)
277
+ end
278
+
279
+ should 'create a new MockResource with no constructor defined' do
280
+ mr = MockResource.with_con_default
281
+ assert(mr.is_a?(MockResource))
282
+ end
283
+ end
284
+
285
+ context 'for api_instance_method' do
286
+ setup do
287
+ @mock.expects(:get).once.returns(test_response(test_mock_resource))
288
+ @mr = MockResource.new('fake_id')
289
+ end
290
+
291
+ should 'update this instance with :constructor => :self' do
292
+ @mr.with_con_self
293
+ assert(@mr.is_a?(MockResource))
294
+ assert_equal(test_mock_resource[:id], @mr.id)
295
+ end
296
+
297
+ should 'create a new instance with :constructor => MockResource' do
298
+ res = @mr.with_con_class
299
+ assert(res.is_a?(MockResource))
300
+ assert_not_equal(@mr.id, res.id)
301
+ end
302
+
303
+ should 'use the lambda with :constructor => lambda{...}' do
304
+ res = @mr.with_con_lambda
305
+ assert_equal("lamdba result", res)
306
+ end
307
+
308
+ should 'update this instance with no constructor defined' do
309
+ @mr.with_con_default
310
+ assert(@mr.is_a?(MockResource))
311
+ assert_equal(test_mock_resource[:id], @mr.id)
312
+ end
313
+ end
314
+ end
315
+
316
+ context 'APIClass api_*_method arguments' do
317
+ should 'throw an ArgumentError if too few arguments are provided' do
318
+ assert_raises(ArgumentError) { MockResource.retrieve }
319
+ end
320
+
321
+ should 'throw an ArgumentError with the name of missing arguments' do
322
+ begin
323
+ MockResource.retrieve
324
+ assert(false, "ArgumentError was expected.")
325
+ rescue ArgumentError => e
326
+ assert(e.message =~ /id/)
327
+ end
328
+ end
329
+
330
+ should 'throw an ArgumentError if too many arguments are provided' do
331
+ assert_raises(ArgumentError) { MockResource.retrieve(1, 2, {}, {}) }
332
+ end
333
+
334
+ should 'throw an ArgumentError if the param argument is invalid' do
335
+ assert_raises(ArgumentError) { MockResource.retrieve(1, 2) }
336
+ end
337
+
338
+ should 'throw an ArgumentError if the opts argument is invalid' do
339
+ assert_raises(ArgumentError) { MockResource.retrieve(1, {}, "abc") }
340
+ end
341
+
342
+ should 'not throw an ArgumentError if the opts or params are nil' do
343
+ @mock.expects(:get).times(2).returns(test_response(test_mock_resource))
344
+ MockResource.retrieve(1, nil, {})
345
+ MockResource.retrieve(1, {}, nil)
346
+ end
347
+
348
+ should 'urlencode unused arguments via GET' do
349
+ response = test_response(test_mock_resource)
350
+ @mock.expects(:get).with do |url, headers, params|
351
+ (url == "#{Paid.api_base}#{MockResource.path}/bval/many?a=aval&c=cval" ||
352
+ url == "#{Paid.api_base}#{MockResource.path}/bval/many?c=cval&a=aval") &&
353
+ params.nil?
354
+ end.returns(response)
355
+
356
+ MockResource.many_args_get("aval", "bval", "cval")
357
+ end
358
+
359
+ should 'assign unused arguments to the params payload via POST' do
360
+ response = test_response(test_mock_resource)
361
+ @mock.expects(:post).with("#{Paid.api_base}#{MockResource.path}/bval/many", anything, { :a => "aval", :c => "cval" }).returns(response)
362
+
363
+ MockResource.many_args_post("aval", "bval", "cval")
364
+ end
365
+ end
366
+
367
+ context 'APIClass api_instance_method paths' do
368
+ should 'use the provided argument if it is present' do
369
+ response = test_response(test_mock_resource)
370
+ @mock.expects(:get).with("#{Paid.api_base}/custom_path", anything, anything).returns(response)
371
+
372
+ mr = MockResource.new(test_mock_resource)
373
+ mr.custom_path("/custom_path")
374
+ end
375
+
376
+ should 'use the instance method for path values' do
377
+ response = test_response(test_mock_resource)
378
+ name = "/mock_resource_name"
379
+ @mock.expects(:get).with("#{Paid.api_base}#{name}", anything, anything).returns(response)
380
+
381
+ mr = MockResource.new(test_mock_resource)
382
+ mr.name = name
383
+ mr.name_path
384
+ end
385
+
386
+ should 'use the class method for path values' do
387
+ response = test_response(test_mock_resource)
388
+ @mock.expects(:get).with("#{Paid.api_base}#{MockResource.crazy}", anything, anything).returns(response)
389
+
390
+ mr = MockResource.new(test_mock_resource)
391
+ mr.crazy_path
392
+ end
393
+ end
394
+
395
+ context 'APIClass api_class_method paths' do
396
+ should 'use the provided argument if it is present' do
397
+ response = test_response(test_mock_resource)
398
+ @mock.expects(:get).with("#{Paid.api_base}#{MockResource.path}/fake_id", anything, anything).returns(response)
399
+
400
+ mr = MockResource.retrieve('fake_id')
401
+ end
402
+
403
+ should 'use the class method for path values' do
404
+ response = test_response(test_mock_resource)
405
+ @mock.expects(:get).with("#{Paid.api_base}#{MockResource.crazy}", anything, anything).returns(response)
406
+
407
+ MockResource.crazy_path
408
+ end
409
+ end
410
+
411
+ end
412
+ end