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
@@ -1,187 +0,0 @@
1
- module Paid
2
- class PaidObject
3
- include Enumerable
4
-
5
- attr_accessor :api_key
6
- @@permanent_attributes = Set.new([:api_key, :id])
7
-
8
- # The default :id method is deprecated and isn't useful to us
9
- if method_defined?(:id)
10
- undef :id
11
- end
12
-
13
- def initialize(id=nil, api_key=nil)
14
- # parameter overloading!
15
- if id.kind_of?(Hash)
16
- @retrieve_options = id.dup
17
- @retrieve_options.delete(:id)
18
- id = id[:id]
19
- else
20
- @retrieve_options = {}
21
- end
22
-
23
- @api_key = api_key
24
- @values = {}
25
- # This really belongs in APIResource, but not putting it there allows us
26
- # to have a unified inspect method
27
- @unsaved_values = Set.new
28
- @transient_values = Set.new
29
- @values[:id] = id if id
30
- end
31
-
32
- def self.construct_from(values, api_key=nil)
33
- self.new(values[:id], api_key).refresh_from(values, api_key)
34
- end
35
-
36
- def to_s(*args)
37
- JSON.pretty_generate(@values)
38
- end
39
-
40
- def inspect
41
- id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
42
- "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
43
- end
44
-
45
- def refresh_from(values, api_key, partial=false)
46
- @api_key = api_key
47
-
48
- @previous_properties = values[:properties]
49
- removed = partial ? Set.new : Set.new(@values.keys - values.keys)
50
- added = Set.new(values.keys - @values.keys)
51
-
52
- instance_eval do
53
- remove_accessors(removed)
54
- add_accessors(added)
55
- end
56
- removed.each do |k|
57
- @values.delete(k)
58
- @transient_values.add(k)
59
- @unsaved_values.delete(k)
60
- end
61
- values.each do |k, v|
62
- @values[k] = Util.convert_to_paid_object(v, api_key)
63
- @transient_values.delete(k)
64
- @unsaved_values.delete(k)
65
- end
66
-
67
- return self
68
- end
69
-
70
- def [](k)
71
- @values[k.to_sym]
72
- end
73
-
74
- def []=(k, v)
75
- send(:"#{k}=", v)
76
- end
77
-
78
- def keys
79
- @values.keys
80
- end
81
-
82
- def values
83
- @values.values
84
- end
85
-
86
- def to_json(*a)
87
- JSON.generate(@values)
88
- end
89
-
90
- def as_json(*a)
91
- @values.as_json(*a)
92
- end
93
-
94
- def to_hash
95
- @values.inject({}) do |acc, (key, value)|
96
- acc[key] = value.respond_to?(:to_hash) ? value.to_hash : value
97
- acc
98
- end
99
- end
100
-
101
- def each(&blk)
102
- @values.each(&blk)
103
- end
104
-
105
- def _dump(level)
106
- Marshal.dump([@values, @api_key])
107
- end
108
-
109
- def self._load(args)
110
- values, api_key = Marshal.load(args)
111
- construct_from(values, api_key)
112
- end
113
-
114
- if RUBY_VERSION < '1.9.2'
115
- def respond_to?(symbol)
116
- @values.has_key?(symbol) || super
117
- end
118
- end
119
-
120
- protected
121
-
122
- def metaclass
123
- class << self; self; end
124
- end
125
-
126
- def remove_accessors(keys)
127
- metaclass.instance_eval do
128
- keys.each do |k|
129
- next if @@permanent_attributes.include?(k)
130
- k_eq = :"#{k}="
131
- remove_method(k) if method_defined?(k)
132
- remove_method(k_eq) if method_defined?(k_eq)
133
- end
134
- end
135
- end
136
-
137
- def add_accessors(keys)
138
- metaclass.instance_eval do
139
- keys.each do |k|
140
- next if @@permanent_attributes.include?(k)
141
- k_eq = :"#{k}="
142
- define_method(k) { @values[k] }
143
- define_method(k_eq) do |v|
144
- if v == ""
145
- raise ArgumentError.new(
146
- "You cannot set #{k} to an empty string." +
147
- "We interpret empty strings as nil in requests." +
148
- "You may set #{self}.#{k} = nil to delete the property.")
149
- end
150
- @values[k] = v
151
- @unsaved_values.add(k)
152
- end
153
- end
154
- end
155
- end
156
-
157
- def method_missing(name, *args)
158
- # TODO: only allow setting in updateable classes.
159
- if name.to_s.end_with?('=')
160
- attr = name.to_s[0...-1].to_sym
161
- add_accessors([attr])
162
- begin
163
- mth = method(name)
164
- rescue NameError
165
- raise NoMethodError.new("Cannot set #{attr} on this object. HINT: you can't set: #{@@permanent_attributes.to_a.join(', ')}")
166
- end
167
- return mth.call(args[0])
168
- else
169
- return @values[name] if @values.has_key?(name)
170
- end
171
-
172
- begin
173
- super
174
- rescue NoMethodError => e
175
- if @transient_values.include?(name)
176
- raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Paid's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
177
- else
178
- raise
179
- end
180
- end
181
- end
182
-
183
- def respond_to_missing?(symbol, include_private = false)
184
- @values && @values.has_key?(symbol) || super
185
- end
186
- end
187
- end
@@ -1,20 +0,0 @@
1
- module Paid
2
- class SingletonAPIResource < APIResource
3
- def self.api_url
4
- if self == SingletonAPIResource
5
- raise NotImplementedError.new('SingletonAPIResource is an abstract class. You should perform actions on its subclasses (Account, etc.)')
6
- end
7
- "/v0/#{CGI.escape(class_name.downcase)}"
8
- end
9
-
10
- def api_url
11
- self.class.api_url
12
- end
13
-
14
- def self.retrieve(api_key=nil)
15
- instance = self.new(nil, api_key)
16
- instance.refresh
17
- instance
18
- end
19
- end
20
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :paid do
3
- # # Task goes here
4
- # end
@@ -1,22 +0,0 @@
1
- require File.expand_path('../../test_helper', __FILE__)
2
-
3
- module Paid
4
- # AliasTest
5
- class AliasTest < Test::Unit::TestCase
6
- should 'aliases should not be deletable' do
7
- assert_raises NoMethodError do
8
- # Expect twice because Paid::Alias.retrieve returns a customer object
9
- @mock.expects(:get).twice.returns(test_response(test_customer))
10
- c = Paid::Alias.retrieve('test_alias')
11
- c.delete
12
- end
13
- end
14
-
15
- should 'retrieve should retrieve alias' do
16
- # Expect twice because Paid::Alias.retrieve returns a customer object
17
- @mock.expects(:get).twice.returns(test_response(test_alias))
18
- i = Paid::Alias.retrieve('in_test_alias')
19
- assert_equal 'al_test_alias', i.id
20
- end
21
- end
22
- end
@@ -1,18 +0,0 @@
1
- # require File.expand_path('../../test_helper', __FILE__)
2
-
3
- # module Paid
4
-
5
- # class CertificateBlacklistTest < Test::Unit::TestCase
6
- # should "not trust revoked certificates" do
7
- # assert_raises(Paid::APIConnectionError) {
8
- # Paid::CertificateBlacklist.check_ssl_cert("https://revoked.paidapi.com:444",
9
- # Paid::DEFAULT_CA_BUNDLE_PATH)
10
- # }
11
- # end
12
-
13
- # should "trust api.paidapi.com" do
14
- # assert_true Paid::CertificateBlacklist.check_ssl_cert("https://api.paidapi.com",
15
- # Paid::DEFAULT_CA_BUNDLE_PATH)
16
- # end
17
- # end
18
- # end
@@ -1,16 +0,0 @@
1
- require File.expand_path('../../test_helper', __FILE__)
2
-
3
- module Paid
4
- class ListObjectTest < Test::Unit::TestCase
5
- should "be able to retrieve full lists given a listobject" do
6
- @mock.expects(:get).twice.returns(test_response(test_transaction_array))
7
- c = Paid::Transaction.all
8
- assert c.kind_of?(Paid::ListObject)
9
- assert_equal('/v0/transactions', c.url)
10
- all = c.all
11
- assert all.kind_of?(Paid::ListObject)
12
- assert_equal('/v0/transactions', all.url)
13
- assert all.data.kind_of?(Array)
14
- end
15
- end
16
- end
@@ -1,27 +0,0 @@
1
- require File.expand_path('../../test_helper', __FILE__)
2
-
3
- module Paid
4
- class PaidObjectTest < Test::Unit::TestCase
5
- should "implement #respond_to correctly" do
6
- obj = Paid::PaidObject.construct_from({ :id => 1, :foo => 'bar' })
7
- assert obj.respond_to?(:id)
8
- assert obj.respond_to?(:foo)
9
- assert !obj.respond_to?(:baz)
10
- end
11
-
12
- should "marshal a paid object correctly" do
13
- obj = Paid::PaidObject.construct_from({ :id => 1, :name => 'Paid' }, 'apikey')
14
- m = Marshal.load(Marshal.dump(obj))
15
- assert_equal 1, m.id
16
- assert_equal 'Paid', m.name
17
- assert_equal 'apikey', m.api_key
18
- end
19
-
20
- should "recursively call to_hash on its values" do
21
- nested = Paid::PaidObject.construct_from({ :id => 7, :foo => 'bar' })
22
- obj = Paid::PaidObject.construct_from({ :id => 1, :nested => nested })
23
- expected_hash = { :id => 1, :nested => { :id => 7, :foo => 'bar' } }
24
- assert_equal expected_hash, obj.to_hash
25
- end
26
- end
27
- end
@@ -1,103 +0,0 @@
1
- require File.expand_path('../../test_helper', __FILE__)
2
-
3
- module Paid
4
- class PropertiesTest < Test::Unit::TestCase
5
- setup do
6
- @properties_supported = {
7
- transaction: {
8
- new: Paid::Transaction.method(:new),
9
- test: method(:test_transaction),
10
- url: "/v0/transactions/#{test_transaction[:id]}"
11
- },
12
- customer: {
13
- new: Paid::Customer.method(:new),
14
- test: method(:test_customer),
15
- url: "/v0/customers/#{test_customer[:id]}"
16
- }
17
- }
18
-
19
- @base_url = 'https://api.paidapi.com'
20
- end
21
-
22
- should 'not touch properties' do
23
- update_actions = ->(obj) { obj.description = 'test' }
24
- check_properties({ properties: { 'initial' => 'true' } },
25
- 'description=test',
26
- update_actions)
27
- end
28
-
29
- should 'update properties as a whole' do
30
- update_actions = ->(obj) { obj.properties = { 'uuid' => '6735' } }
31
- check_properties({ properties: {} },
32
- 'properties[uuid]=6735',
33
- update_actions)
34
-
35
- if is_greater_than_ruby_1_9?
36
- check_properties({ properties: { initial: 'true' } },
37
- 'properties[uuid]=6735&properties[initial]=',
38
- update_actions)
39
- end
40
- end
41
-
42
- should 'update properties keys individually' do
43
- update_actions = ->(obj) { obj.properties['txn_id'] = '134a13' }
44
- check_properties({ properties: { 'initial' => 'true' } },
45
- 'properties[txn_id]=134a13',
46
- update_actions)
47
- end
48
-
49
- should 'clear properties as a whole' do
50
- update_actions = ->(obj) { obj.properties = nil }
51
- check_properties({ properties: { 'initial' => 'true' } },
52
- 'properties=',
53
- update_actions)
54
- end
55
-
56
- should 'clear properties keys individually' do
57
- update_actions = ->(obj) { obj.properties['initial'] = nil }
58
- check_properties({ properties: { 'initial' => 'true' } },
59
- 'properties[initial]=',
60
- update_actions)
61
- end
62
-
63
- should 'handle combinations of whole and partial properties updates' do
64
- if is_greater_than_ruby_1_9?
65
- update_actions = lambda do |obj|
66
- obj.properties = { 'type' => 'summer' }
67
- obj.properties['uuid'] = '6735'
68
- end
69
- params = { properties: { 'type' => 'summer', 'uuid' => '6735' } }
70
- curl_args = Paid.uri_encode(params)
71
- check_properties({ properties: { 'type' => 'christmas' } },
72
- curl_args,
73
- update_actions)
74
- end
75
- end
76
-
77
- def check_properties(initial_params, curl_args, properties_update)
78
- @properties_supported.each do |_name, methods|
79
- neu = methods[:new]
80
- test = methods[:test]
81
- url = @base_url + methods[:url]
82
-
83
- initial_test_obj = test.call(initial_params)
84
- @mock.expects(:get).once.returns(test_response(initial_test_obj))
85
-
86
- final_test_obj = test.call
87
- @mock.expects(:post).once
88
- .returns(test_response(final_test_obj))
89
- .with(url, nil, curl_args)
90
-
91
- obj = neu.call('test')
92
- obj.refresh
93
- properties_update.call(obj)
94
- obj.save
95
- end
96
- end
97
-
98
- def is_greater_than_ruby_1_9?
99
- version = RUBY_VERSION.dup # clone preserves frozen state
100
- Gem::Version.new(version) >= Gem::Version.new('1.9')
101
- end
102
- end
103
- end