paid 0.1.0 → 1.0.0

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