recurly 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of recurly might be problematic. Click here for more details.

data/README.md CHANGED
@@ -16,22 +16,26 @@ Installation
16
16
 
17
17
  This library can be installed as a gem or a plugin. Your choice.
18
18
 
19
- **Rails3 Bundle Integration:**
19
+ **Rails3 Stable Version:**
20
20
 
21
- gem "recurly", :git => "http://github.com/railsjedi/recurly-client-ruby.git"
21
+ gem 'recurly'
22
22
 
23
+ **Bleeding Edge Version:**
24
+ gem 'recurly', :git => "http://github.com/recurly/recurly-client-ruby.git"
23
25
 
24
- **Plugin Installation (not recommended):**
25
26
 
26
- script/plugin install http://github.com/railsjedi/recurly-client-ruby.git
27
-
28
-
29
- Authentication
27
+ Setup (Rails 3)
30
28
  --------------
31
29
 
32
30
  The Recurly Ruby Client requires a username and password to connect. We recommend creating a user just for your API. Please see the [Authentication](http://support.recurly.com/faqs/api/authentication) documentation for more information.
33
31
 
34
- Create a file in your Rails app at __/config/initializers/recurly_config.rb__ with contents like:
32
+ If using Rails 3, the easiest way to get Recurly set up is to run `rake recurly:setup`. This will create a config/recurly.yml that has your recurly account authentication, and the Recurly rails initializer will pick it up on restart of your web app.
33
+
34
+
35
+ Setup (Rails 2 and other frameworks)
36
+ --------------
37
+
38
+ Alternatively, if not using Rails 3, just make sure to call a Recurly configure block somewhere in your applications initialization.
35
39
 
36
40
  Recurly.configure do |c|
37
41
  c.username = 'api@yourcompany.com'
@@ -39,8 +43,35 @@ Create a file in your Rails app at __/config/initializers/recurly_config.rb__ wi
39
43
  c.site = 'https://my-recurly-site.recurly.com'
40
44
  end
41
45
 
46
+ In Rails 2.x, this code should be in config/initializers/recurly.rb
47
+
48
+ In Sinatra, it should be within a `configure` block.
49
+
50
+
51
+ Manual Setup via YAML
52
+ --------------
53
+
54
+ You can also configure Recurly via a YAML file by using:
55
+
56
+ Recurly.configure_via_yaml("./config/recurly.yml")
57
+
42
58
 
43
- Demo Application
59
+ The Recurly Configuration YAML is in the format of:
60
+
61
+ username: myrecurlyuser@domain.com
62
+ password: myrecurlypassword
63
+ site: https://myrecurlysite.recurly.com
64
+
65
+
66
+ Clearing test data (Rails3)
67
+ ----------------
68
+
69
+ The Recurly Railtie (for rails3) includes a rake task that allows you to easily clear out the test data on your Account. This is useful when automating the testing of the api interation within your own app.
70
+
71
+ rake recurly:clear_test_data
72
+
73
+
74
+ Rails Demo Application
44
75
  ----------------
45
76
 
46
77
  [Recurly Ruby Demo App](http://github.com/recurly/recurly-client-ruby-demo)
@@ -67,7 +98,7 @@ The next thing is to setup all the spec dependencies
67
98
 
68
99
  bundle
69
100
 
70
- When first running the specs, you'll need to setup a recurly test account. Use the provided rake task to walk you through creating spec/spec_settings.yml with all the authentication info.
101
+ When first running the specs, you'll need to setup a recurly test account. Use the provided rake task to walk you through creating spec/config/recurly.yml with all the authentication info.
71
102
 
72
103
  rake recurly:setup
73
104
 
@@ -80,20 +111,14 @@ Something go Wrong?
80
111
  You can view the full http interactions with Recurly at spec/vcr. Please attached these to any bug reports so we can replicate.
81
112
 
82
113
 
83
- Clearing Test Data
84
- ------------------
85
-
86
- You can delete the spec/vcr folder at any time, and it will regenerate the requests to recurly's apis. However if you do this, you'll also need to clear the test data on your recurly account. Here's how (manually):
87
-
88
- * Login to Recurly
89
- * Click "Configuration"" on the top right menu
90
- * Select "Clear Test Data"
114
+ Clearing Test Data in Specs
115
+ ----------------------------
91
116
 
92
- This is also automated via a rake task. It will delete the spec/vcr files, and clear the data for you on the server (using your spec_settings.yml authentication info).
117
+ You can delete the spec/vcr folder at any time, and it will regenerate the requests to recurly's apis. However if you do this, you'll also need to clear the test data on your recurly account. To do this run:
93
118
 
94
119
  rake recurly:clear
95
120
 
96
-
121
+ This will run `recurly:clear_test_data` (using your spec/config/recurly.yml authentication info) to clear out the test data on the server and then delete the associated spec/vcr files so you can start from scratch.
97
122
 
98
123
  API Documentation
99
124
  -----------------
data/lib/recurly.rb CHANGED
@@ -1,16 +1,42 @@
1
- require 'rubygems'
2
- require "active_resource"
3
-
1
+ require 'active_resource'
4
2
  require 'cgi'
5
3
 
6
4
  require 'recurly/version'
7
5
  require 'recurly/formats/xml_with_pagination'
6
+ require 'recurly/config_parser'
7
+ require 'recurly/rails/railtie' if defined?(::Rails::Railtie)
8
8
 
9
9
  # configuration
10
10
  module Recurly
11
+
12
+ autoload :RecurlyBase, 'recurly/base'
13
+ autoload :Account, 'recurly/account'
14
+ autoload :BillingInfo, 'recurly/billing_info'
15
+ autoload :Charge, 'recurly/charge'
16
+ autoload :Credit, 'recurly/credit'
17
+ autoload :Invoice, 'recurly/invoice'
18
+ autoload :Plan, 'recurly/plan'
19
+ autoload :Subscription, 'recurly/subscription'
20
+ autoload :Transaction, 'recurly/transaction'
21
+
11
22
  class << self
12
23
  attr_accessor :username, :password, :site
13
24
 
25
+ # default Recurly.settings_path to config/recurly.yml
26
+ unless respond_to?(:settings_path)
27
+ def settings_path
28
+ @settings_path || "config/recurly.yml"
29
+ end
30
+
31
+ def settings_path=(new_settings_path)
32
+ @settings_path = new_settings_path
33
+ end
34
+ end
35
+
36
+ def configured?
37
+ RecurlyBase.user && RecurlyBase.password && RecurlyBase.site
38
+ end
39
+
14
40
  def configure
15
41
  yield self
16
42
 
@@ -20,15 +46,18 @@ module Recurly
20
46
 
21
47
  true
22
48
  end
49
+
50
+ def configure_from_yaml(path = nil)
51
+ configure do |c|
52
+ # parse configuration from yml
53
+ recurly_config = ConfigParser.parse(path)
54
+
55
+ if recurly_config.present?
56
+ c.username = recurly_config["username"]
57
+ c.password = recurly_config["password"]
58
+ c.site = recurly_config["site"]
59
+ end
60
+ end
61
+ end
23
62
  end
24
- end
25
-
26
- require 'recurly/base'
27
- require 'recurly/account'
28
- require 'recurly/billing_info'
29
- require 'recurly/charge'
30
- require 'recurly/credit'
31
- require 'recurly/invoice'
32
- require 'recurly/plan'
33
- require 'recurly/subscription'
34
- require 'recurly/transaction'
63
+ end
@@ -3,6 +3,16 @@ module Recurly
3
3
  self.element_name = "account"
4
4
  self.primary_key = :account_code
5
5
 
6
+ attr_accessor :account_code_was
7
+ def account_code=(new_account_code)
8
+ self.account_code_was = self.account_code
9
+ super
10
+ end
11
+
12
+ def to_param
13
+ account_code_was || account_code
14
+ end
15
+
6
16
  # Maps the
7
17
  SHOW_PARAMS = {
8
18
  :active => "active_subscribers",
@@ -30,8 +40,8 @@ module Recurly
30
40
  destroy
31
41
  end
32
42
 
33
- def charges
34
- Charge.list(account_code)
43
+ def charges(status = :all)
44
+ Charge.list(account_code, status)
35
45
  end
36
46
  memoize :charges
37
47
 
@@ -49,7 +59,7 @@ module Recurly
49
59
  end
50
60
 
51
61
  def transactions(status)
52
- Transaction.list(account_code, status)
62
+ Transaction.list_for_account(account_code, status)
53
63
  end
54
64
  memoize :transactions
55
65
 
data/lib/recurly/base.rb CHANGED
@@ -17,6 +17,10 @@ module Recurly
17
17
  false
18
18
  end
19
19
 
20
+ def valid?
21
+ self.errors.blank?
22
+ end
23
+
20
24
  # See http://github.com/rails/rails/commit/1488c6cc9e6237ce794e3c4a6201627b9fd4ca09
21
25
  # Errors in Rails 2.3.4 are not parsed correctly.
22
26
  def save
@@ -3,8 +3,14 @@ module Recurly
3
3
  self.element_name = "charge"
4
4
  self.prefix = "/accounts/:account_code/"
5
5
 
6
- def self.list(account_code)
7
- find(:all, :params => { :account_code => account_code })
6
+ def self.list(account_code, status = :all)
7
+ params = {:account_code => account_code}
8
+
9
+ if status != :all
10
+ params[:show] = status.to_s
11
+ end
12
+
13
+ find(:all, :params => params)
8
14
  end
9
15
 
10
16
  def self.lookup(account_code, id)
@@ -0,0 +1,28 @@
1
+ require 'fileutils'
2
+ module Recurly
3
+ module ConfigParser
4
+ class << self
5
+
6
+ def parse(path = nil)
7
+ path ||= Recurly.settings_path
8
+ settings = {}
9
+ if File.exists?(path)
10
+ settings = YAML.load_file(path) || {}
11
+ else
12
+ puts "\n#{path} file not found. Run rake recurly:setup to create one\n\n"
13
+ end
14
+
15
+ settings
16
+ end
17
+
18
+ def save(settings = {}, path = nil)
19
+ path ||= Recurly.settings_path
20
+ FileUtils.mkdir_p(File.dirname(path))
21
+ File.open(path, 'w' ) do |out|
22
+ YAML.dump(settings, out)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ end
@@ -15,20 +15,21 @@ module Recurly
15
15
 
16
16
  # convert the data into a paginated resultset (array with singleton methods)
17
17
  def paginate_data(data)
18
+
18
19
  # find the first array and use that as the resultset (lame workaround)
19
- results = data.values.select{|v| v.is_a?(Array)}.first
20
+ results = data.values.select{|v| v.is_a?(Array)}.first || []
20
21
 
21
- # use a singleton methods for now (maybe wrap in WillPaginate later?)
22
+ # define total_entries accessor on result object
22
23
  total_entries = data["total_entries"] || 0
23
- def results.total_entries; total_entries; end
24
+ results.instance_eval "def total_entries; #{total_entries.to_i}; end"
24
25
 
25
- current_page = data["current_page"] || 1
26
- def results.current_page; current_page; end
26
+ # define current_page accessor on result object
27
+ current_page = data["current_page"] || 0
28
+ results.instance_eval "def current_page; #{current_page.to_i}; end"
27
29
 
28
- per_page = data["per_page"]
29
- if per_page
30
- def results.per_page; per_page; end
31
- end
30
+ # define per_page accessor on result object
31
+ per_page = data["per_page"] || 0
32
+ results.instance_eval "def per_page; #{per_page.to_i}; end"
32
33
 
33
34
  results
34
35
  end
@@ -0,0 +1,13 @@
1
+ module Recurly
2
+ class Railtie < ::Rails::Railtie
3
+ rake_tasks do
4
+ load 'recurly/rails/recurly.rake'
5
+ end
6
+
7
+ config.after_initialize do
8
+ # setup recurly authentication details for testing
9
+ ::Recurly.configure_from_yaml unless Recurly.configured?
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,77 @@
1
+ namespace :recurly do
2
+
3
+ # loads settings
4
+ task :load_settings => :environment do
5
+ # load the recurly.yml file
6
+ @recurly_config = Recurly::ConfigParser.parse
7
+ end
8
+
9
+ desc "Clears out the Test data from your configured Recurly site (does not touch your production site)"
10
+ task :clear_test_data => :load_settings do
11
+ puts "\n"
12
+
13
+ begin
14
+ require 'restclient'
15
+ rescue LoadError
16
+ puts "Install the 'rest-client' gem in order to automatically clear your recurly test data via a rake task. If using Bundler, add it to your project's Gemfile (in :development group) and run again."
17
+ exit
18
+ end
19
+
20
+ username = @recurly_config["username"]
21
+ password = @recurly_config["password"]
22
+
23
+ # lets try logging into site
24
+ login_response = nil
25
+ begin
26
+ RestClient.post "https://app.recurly.com/login",
27
+ :user_session => {
28
+ :email => username,
29
+ :password => password
30
+ }
31
+
32
+ # yes, RestClient api is weird
33
+ raise "Login Failed for #{username} (we should have gotten a redirect)"
34
+ rescue RestClient::Found => e
35
+ # we got a redirect. horray!
36
+ login_response = e.response
37
+ end
38
+
39
+ # now lets clear site data
40
+ begin
41
+ RestClient.post( @recurly_config["site"]+"/site/test_data",
42
+ {"_method"=>"delete"},
43
+ :cookies => login_response.cookies)
44
+ raise "Clearing Didn't work for some reason. Is your site setting correct?"
45
+ rescue RestClient::Found => e
46
+ puts "Test Data Cleared from: #{@recurly_config["site"]}"
47
+ end
48
+ end
49
+
50
+ desc "Creates a recurly.yml config file"
51
+ task :setup => :environment do
52
+
53
+ # load the recurly.yml file
54
+ Rake::Task["recurly:load_settings"].invoke
55
+
56
+ puts "Creating a recurly.yml config file for your project\n"
57
+
58
+ begin
59
+ require 'highline/import'
60
+ rescue LoadError
61
+ puts "Install the 'highline' gem for a more interactive experience. If using Bundler, add it to your project's Gemfile (in :development group) and run again."
62
+ exit
63
+ end
64
+
65
+ # ask for the username
66
+ say "\nStep 1) Go to recurly.com and set up a test account...\n"
67
+ @recurly_config["username"] = ask("\nStep 2) Enter your recurly username (email):", String)
68
+
69
+ @recurly_config["password"] = ask("\nStep 3) Enter your recurly password:", String){ |q| q.echo = "*" }
70
+
71
+ @recurly_config["site"] = ask("\nStep 4) Enter your recurly base site url (e.g. https://testrecurly2-test.recurly.com):", String)
72
+
73
+ # saves the yml file
74
+ Recurly::ConfigParser.save(@recurly_config)
75
+ puts "\nYour settings were saved in:\n#{Recurly.settings_path}\n"
76
+ end
77
+ end
@@ -2,7 +2,18 @@ module Recurly
2
2
  class Transaction < RecurlyBase
3
3
  self.element_name = "transaction"
4
4
 
5
- def self.list(account_code, status = :all)
5
+
6
+ def self.list(status = :all)
7
+
8
+ options = {}
9
+ if status != :all
10
+ options[:params] = {:show => status.to_s}
11
+ end
12
+
13
+ find(:all, options)
14
+ end
15
+
16
+ def self.list_for_account(account_code, status = :all)
6
17
  results = find(:all, :from => "/accounts/#{CGI::escape(account_code || '')}/transactions")
7
18
 
8
19
  # filter by status
@@ -17,5 +28,13 @@ module Recurly
17
28
  find(id, :params => { :account_code => account_code })
18
29
  end
19
30
 
31
+ def void
32
+ connection.delete(element_path(:action => "void"), self.class.headers)
33
+ end
34
+
35
+ def refund(amount_in_cents)
36
+ connection.delete(element_path(:action => "refund", :amount => amount_in_cents), self.class.headers)
37
+ end
38
+
20
39
  end
21
40
  end
@@ -1,3 +1,3 @@
1
1
  module Recurly #:nodoc
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,4 @@
1
+ ---
2
+ username: railsjedi@sogetthis.com
3
+ site: https://recurlytest3-test.recurly.com
4
+ password: 7PCB2mZC
@@ -4,15 +4,69 @@ module Recurly
4
4
  describe Account do
5
5
  timestamp = File.mtime(__FILE__).to_i
6
6
 
7
+ describe "#new" do
8
+ let(:attributes) { Factory.account_attributes("account-new-#{timestamp}") }
9
+ before(:each) do
10
+ @account = Account.new(attributes)
11
+ end
12
+
13
+ it "should be valid" do
14
+ @account.should be_valid
15
+ end
16
+
17
+ it "should set the attributes correctly" do
18
+ attributes.each do |key, val|
19
+ @account.attributes[key].should == val
20
+ end
21
+ end
22
+ end
23
+
7
24
  describe "#create" do
8
- use_vcr_cassette "account/create/#{timestamp}"
25
+ context "with full data" do
26
+ use_vcr_cassette "account/create/#{timestamp}"
9
27
 
10
- before(:each) do
11
- @account = Factory.create_account("account-create-#{timestamp}")
28
+ let(:attributes) { Factory.account_attributes("account-create-#{timestamp}") }
29
+ before(:each) do
30
+ @account = Account.create(attributes)
31
+ end
32
+
33
+ it "should be valid" do
34
+ @account.should be_valid
35
+ end
36
+
37
+ it "should set a created_at date from the server" do
38
+ @account.created_at.should_not be_nil
39
+ end
40
+
41
+ it "should set a hosted_login_token from the server" do
42
+ @account.hosted_login_token.should_not be_nil
43
+ end
44
+
45
+ it "should set the balance to 0" do
46
+ @account.balance_in_cents.should == 0
47
+ end
48
+
49
+ it "should set the account status to active" do
50
+ @account.state.should == "active"
51
+ @account.closed?.should be_false
52
+ end
12
53
  end
13
54
 
14
- it "should have a created_at date" do
15
- @account.created_at.should_not be_nil
55
+ context "with just account-code" do
56
+ use_vcr_cassette "account/create-min/#{timestamp}"
57
+
58
+ before(:each) do
59
+ @account = Account.create(:account_code => "d00d-#{timestamp}")
60
+ end
61
+
62
+ it "should be valid" do
63
+ @account.should be_valid
64
+ end
65
+
66
+ it "should set a created_at date from the server" do
67
+ @account.created_at.should_not be_nil
68
+ end
69
+
16
70
  end
17
71
  end
18
72
 
@@ -45,10 +99,49 @@ module Recurly
45
99
  @account.first_name.should == orig.first_name
46
100
  end
47
101
  end
102
+
103
+ context "looking for a non-existant account" do
104
+ it "should raise an ActiveResource::ResourceNotFound exception" do
105
+ pending "This isn't supposed to throw 500 errors..."
106
+
107
+ expect {
108
+ Account.find('account-that-doesnt-exist')
109
+ }.to raise_error ActiveResource::ResourceNotFound
110
+ end
111
+ end
48
112
  end
49
113
 
114
+
115
+ # spec list queries for finding acocunts
116
+ describe "#list" do
117
+ use_vcr_cassette "account/list/#{timestamp}"
118
+
119
+ before(:each) do
120
+
121
+ # create new ones
122
+ @accounts = []
123
+ 8.times do |i|
124
+ @accounts << Factory.create_account("account-list-num-#{i}-#{timestamp}")
125
+ end
126
+
127
+ # mark the 4 of them as paid
128
+ @subscriptions = []
129
+ @accounts[0, 4].each do |account|
130
+ @subscriptions << Factory.create_subscription(account, :paid)
131
+ end
132
+ end
133
+
134
+ it "should return a list of accounts with matching criteria" do
135
+ Account.list(:all).total_entries.should >= 8
136
+ Account.list(:paid).total_entries.should >= 4
137
+ Account.list(:free).total_entries.should >= 4
138
+ end
139
+
140
+ end
141
+
142
+
50
143
  describe "#update" do
51
- around(:each){ |e| VCR.use_cassette("account/update/#{timestamp}", &e) }
144
+ use_vcr_cassette "account/update/#{timestamp}"
52
145
 
53
146
  let(:orig){ Factory.create_account("account-update-#{timestamp}") }
54
147
 
@@ -76,17 +169,80 @@ module Recurly
76
169
  end
77
170
  end
78
171
 
172
+ # EDITING ACCOUNT_CODE NOT YET SUPPORTED ON RECURLY API
173
+ # describe "#update with account_code" do
174
+ # use_vcr_cassette "account/update2/#{timestamp}"
175
+ #
176
+ # let(:orig){ Factory.create_account("account-update2-#{timestamp}") }
177
+ #
178
+ # let(:new_account_code){ "#{orig.account_code}-edited" }
179
+ #
180
+ # before(:each) do
181
+ # # update account data
182
+ # @account = Account.find(orig.account_code)
183
+ # @account.account_code = new_account_code
184
+ # @account.save!
185
+ #
186
+ # @account = Account.find(new_account_code)
187
+ # end
188
+ #
189
+ # it "should update the account code" do
190
+ # @account.account_code.should == new_account_code
191
+ # end
192
+ # end
193
+
79
194
  describe "#close_account" do
80
195
  use_vcr_cassette "account/close/#{timestamp}"
81
196
  let(:account){ Factory.create_account("account-close-#{timestamp}") }
82
197
 
83
198
  before(:each) do
84
199
  account.close_account
200
+
201
+ # load a fresh account
202
+ @account = Account.find(account.account_code)
85
203
  end
86
204
 
87
205
  it "should mark the account as closed" do
88
- Account.find(account.account_code).state.should == "closed"
206
+ @account.state.should == "closed"
207
+ @account.closed?.should be_true
208
+ end
209
+ end
210
+
211
+ describe "validations" do
212
+ context "with blank data" do
213
+ use_vcr_cassette "account/create-blank/#{timestamp}"
214
+
215
+ before(:each) do
216
+ @account = Account.create({:account_code => ""})
217
+ end
218
+
219
+ it "should not be valid" do
220
+ @account.should_not be_valid
221
+ end
222
+
223
+ it "should require setting an account code" do
224
+ @account.errors[:account_code].should include("can't be blank")
225
+ @account.errors[:account_code].should include("is invalid")
226
+ end
227
+ end
228
+
229
+ context "with duplicate data" do
230
+ use_vcr_cassette "account/create-duplicate/#{timestamp}"
231
+
232
+ before(:each) do
233
+ Account.create({:account_code => "account-exists"})
234
+ @account = Account.create({:account_code => "account-exists"})
235
+ end
236
+
237
+ it "should not be valid" do
238
+ @account.should_not be_valid
239
+ end
240
+
241
+ it "should require setting an account code" do
242
+ @account.errors[:account_code].should include("has already been taken")
243
+ end
89
244
  end
90
245
  end
246
+
91
247
  end
92
248
  end
@@ -18,7 +18,7 @@ module Recurly
18
18
  billing_info.credit_card.last_four.should == billing_attributes[:credit_card][:number]
19
19
  end
20
20
 
21
- describe "#create" do
21
+ describe "create an account's billing information" do
22
22
  use_vcr_cassette "billing/create/#{timestamp}"
23
23
  let(:account){ Factory.create_account("billing-create-#{timestamp}") }
24
24
 
@@ -42,7 +42,28 @@ module Recurly
42
42
  end
43
43
  end
44
44
 
45
- describe "#find" do
45
+ describe "update an account's billing information" do
46
+ use_vcr_cassette "billing/update/#{timestamp}"
47
+ let(:account){ Factory.create_account_with_billing_info("billing-update-#{timestamp}") }
48
+
49
+ before(:each) do
50
+ @new_billing_attributes = Factory.billing_attributes({
51
+ :account_code => account.account_code,
52
+ :first_name => account.first_name,
53
+ :last_name => account.last_name,
54
+ :address1 => "1st Ave South, Apt 5001"
55
+ })
56
+
57
+ @billing_info = BillingInfo.create(@new_billing_attributes)
58
+ end
59
+
60
+ it "should set the correct billing_info on the server " do
61
+ billing_info = BillingInfo.find(account.account_code)
62
+ verify_billing_info(billing_info, @new_billing_attributes)
63
+ end
64
+ end
65
+
66
+ describe "get account's billing information" do
46
67
  use_vcr_cassette "billing/find/#{timestamp}"
47
68
  let(:account){ Factory.create_account_with_billing_info("billing-find-#{timestamp}") }
48
69
 
@@ -62,5 +83,37 @@ module Recurly
62
83
  end
63
84
  end
64
85
 
86
+ describe "clearing an account's billing information" do
87
+ use_vcr_cassette "billing/destroy/#{timestamp}"
88
+ let(:account){ Factory.create_account("billing-destroy-#{timestamp}") }
89
+
90
+ before(:each) do
91
+ @billing_attributes = Factory.billing_attributes({
92
+ :account_code => account.account_code,
93
+ :first_name => account.first_name,
94
+ :last_name => account.last_name,
95
+ :address1 => "500 South Central Blvd",
96
+ :city => "Los Angeles",
97
+ :state => "CA",
98
+ :zip => "90001"
99
+ })
100
+
101
+ BillingInfo.create(@billing_attributes)
102
+
103
+ @billing_info = BillingInfo.find(account.account_code)
104
+ end
105
+
106
+ it "should allow destroying the billing info for an account" do
107
+ @billing_info.destroy
108
+
109
+ fresh = BillingInfo.find(account.account_code)
110
+ fresh.first_name.should be_nil
111
+ fresh.last_name.should be_nil
112
+ fresh.address1.should be_nil
113
+ fresh.city.should be_nil
114
+ fresh.state.should be_nil
115
+ fresh.zip.should be_nil
116
+ end
117
+ end
65
118
  end
66
119
  end
@@ -5,7 +5,78 @@ module Recurly
5
5
  # version accounts based on this current files modification dates
6
6
  timestamp = File.mtime(__FILE__).to_i
7
7
 
8
- describe "create a charge" do
8
+ describe "list an account's charges" do
9
+
10
+ context "all charges" do
11
+ use_vcr_cassette "charge/list-all/#{timestamp}"
12
+
13
+ let(:account) { Factory.create_account("charge-list-all-#{timestamp}") }
14
+ before(:each) do
15
+ Factory.create_charge(account.account_code)
16
+ Factory.create_charge(account.account_code)
17
+ Factory.create_charge(account.account_code)
18
+
19
+ @charges = Charge.list(account.account_code)
20
+ end
21
+
22
+ it "should return all the charges" do
23
+ @charges.length.should == 3
24
+ end
25
+
26
+ it "should also be available via Account#charges" do
27
+ account.charges.should == @charges
28
+ end
29
+ end
30
+
31
+ context "pending charges" do
32
+ use_vcr_cassette "charge/list-pending/#{timestamp}"
33
+
34
+ let(:account) { Factory.create_account("charge-list-pending-#{timestamp}") }
35
+ before(:each) do
36
+ Factory.create_charge(account.account_code)
37
+ Factory.create_charge(account.account_code)
38
+ Factory.create_charge(account.account_code)
39
+
40
+ @charges = Charge.list(account.account_code, :pending)
41
+ end
42
+
43
+ it "should return all the charges" do
44
+ @charges.length.should == 3
45
+ end
46
+
47
+ it "should also be available via Account#charges" do
48
+ account.charges(:pending).should == @charges
49
+ end
50
+ end
51
+
52
+ context "invoiced charges" do
53
+ use_vcr_cassette "charge/list-invoiced/#{timestamp}"
54
+
55
+ let(:account) { Factory.create_account("charge-list-invoiced-#{timestamp}") }
56
+ before(:each) do
57
+ Factory.create_charge(account.account_code)
58
+ Factory.create_charge(account.account_code)
59
+ Factory.create_charge(account.account_code)
60
+
61
+ Invoice.create(:account_code => account.account_code)
62
+
63
+ Factory.create_charge(account.account_code)
64
+
65
+ @charges = Charge.list(account.account_code, :invoiced)
66
+ end
67
+
68
+ it "should return all the charges that were invoiced" do
69
+ @charges.length.should == 3
70
+ end
71
+
72
+ it "should also be available via Account#charges" do
73
+ account.charges(:invoiced).should == @charges
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ describe "charge an account" do
9
80
  use_vcr_cassette "charge/create/#{timestamp}"
10
81
 
11
82
  let(:account) { Factory.create_account_with_billing_info("charge-create-#{timestamp}") }
@@ -31,28 +102,6 @@ module Recurly
31
102
  end
32
103
  end
33
104
 
34
- describe "list charges for an account" do
35
- use_vcr_cassette "charge/list/#{timestamp}"
36
-
37
- let(:account) { Factory.create_account("charge-list-#{timestamp}") }
38
- before(:each) do
39
- Factory.create_charge(account.account_code)
40
- Factory.create_charge(account.account_code)
41
- Factory.create_charge(account.account_code)
42
-
43
- @charges = Charge.list(account.account_code)
44
- end
45
-
46
- it "should return all the transactions" do
47
- @charges.length.should == 3
48
- end
49
-
50
- it "should also be available via Account#charges" do
51
- account.charges.should == @charges
52
- end
53
-
54
- end
55
-
56
105
  describe "lookup a charge" do
57
106
  use_vcr_cassette "charge/lookup/#{timestamp}"
58
107
 
@@ -5,7 +5,7 @@ module Recurly
5
5
  # version accounts based on this current files modification dates
6
6
  timestamp = File.mtime(__FILE__).to_i
7
7
 
8
- describe "create a credit" do
8
+ describe "credit an account" do
9
9
  use_vcr_cassette "credit/create/#{timestamp}"
10
10
 
11
11
  let(:account){ Factory.create_account("credit-create-#{timestamp}") }
@@ -31,7 +31,7 @@ module Recurly
31
31
  end
32
32
  end
33
33
 
34
- describe "list credits for an account" do
34
+ describe "list an account's credits" do
35
35
  use_vcr_cassette "credit/list/#{timestamp}"
36
36
  let(:account){ Factory.create_account("credit-list-#{timestamp}") }
37
37
 
@@ -25,6 +25,18 @@ module Recurly
25
25
  @invoice.line_items.length.should == 2
26
26
  end
27
27
  end
28
+
29
+ context "without charges" do
30
+ use_vcr_cassette "invoice/create-no-charges/#{timestamp}"
31
+
32
+ let(:account) { Factory.create_account_with_billing_info("invoice-create-no-charges-#{timestamp}") }
33
+
34
+ it "should not be created since no charges were posted" do
35
+ pending "Server should not throw a 500 error here"
36
+ @invoice = Invoice.create(:account_code => account.account_code)
37
+ @invoice.should_not be_valid
38
+ end
39
+ end
28
40
  end
29
41
 
30
42
  describe "listing invoices" do
@@ -2,6 +2,9 @@ require 'spec_helper'
2
2
 
3
3
  module Recurly
4
4
  describe Plan do
5
+ # version accounts based on this current files modification dates
6
+ timestamp = File.mtime(__FILE__).to_i
7
+
5
8
  describe "list all plans" do
6
9
  use_vcr_cassette "plan/all"
7
10
 
@@ -53,8 +56,8 @@ module Recurly
53
56
  Plan.find("test")
54
57
  end
55
58
 
59
+ # setup test plan
56
60
  before(:each) do
57
-
58
61
  begin
59
62
  @test_plan = test_plan
60
63
  rescue ActiveResource::ResourceNotFound => e
@@ -69,17 +72,44 @@ module Recurly
69
72
  }).save!
70
73
  @test_plan = test_plan
71
74
  end
75
+ end
72
76
 
73
- # double the price
77
+ it "should update the plan" do
74
78
  @test_plan.unit_amount_in_cents = 200
75
79
  @test_plan.save!
76
- end
77
80
 
78
- it "should update the plan" do
79
81
  @test_plan = test_plan
80
82
  @test_plan.unit_amount_in_cents.should == 200
81
83
  end
82
84
  end
83
85
 
86
+ describe "delete a plan" do
87
+ use_vcr_cassette "plan/delete/#{timestamp}"
88
+
89
+ let(:plan) do
90
+ plan = Plan.new({
91
+ :plan_code => "test_#{timestamp}",
92
+ :name => "Test Plan #{timestamp}",
93
+ :unit_amount_in_cents => 100,
94
+ :plan_interval_length => 1,
95
+ :plan_interval_unit => "months",
96
+ :trial_interval_length => 0,
97
+ :trial_interval_unit => "months"
98
+ })
99
+ plan.save!
100
+ plan
101
+ end
102
+
103
+ it "should delete the plan" do
104
+ @plan = Plan.find(plan.plan_code)
105
+ @plan.destroy
106
+
107
+ expect {
108
+ Plan.find(@plan.plan_code)
109
+ }.to raise_error(ActiveResource::ResourceNotFound)
110
+ end
111
+
112
+ end
113
+
84
114
  end
85
115
  end
@@ -5,7 +5,23 @@ module Recurly
5
5
  # version accounts based on this current files modification dates
6
6
  timestamp = File.mtime(__FILE__).to_i
7
7
 
8
- describe "#create" do
8
+ describe "look up a subscription" do
9
+ use_vcr_cassette "subscription/find/#{timestamp}"
10
+
11
+ let(:account){ Factory.create_account("subscription-find-#{timestamp}") }
12
+
13
+ before(:each) do
14
+ Factory.create_subscription(account, :paid)
15
+ end
16
+
17
+ it "should return the subscription" do
18
+ subscription = Subscription.find(account.account_code)
19
+ subscription.state.should == "active"
20
+ subscription.plan.plan_code.should == "paid"
21
+ end
22
+ end
23
+
24
+ describe "create a new subscription" do
9
25
  use_vcr_cassette "subscription/create/#{timestamp}"
10
26
 
11
27
  let(:account){ Factory.create_account("subscription-create-#{timestamp}") }
@@ -29,9 +45,10 @@ module Recurly
29
45
  it "should be started" do
30
46
  @subscription.current_period_started_at.should_not be_nil
31
47
  end
48
+
32
49
  end
33
50
 
34
- describe "#update" do
51
+ describe "updates and downgrades" do
35
52
  use_vcr_cassette "subscription/update/#{timestamp}"
36
53
 
37
54
  let(:account){ Factory.create_account("subscription-update-#{timestamp}") }
@@ -48,7 +65,7 @@ module Recurly
48
65
  end
49
66
  end
50
67
 
51
- describe "#cancel" do
68
+ describe "cancel a subscription" do
52
69
  use_vcr_cassette "subscription/cancel/#{timestamp}"
53
70
 
54
71
  let(:account){ Factory.create_account("subscription-cancel-#{timestamp}") }
@@ -69,7 +86,7 @@ module Recurly
69
86
  end
70
87
  end
71
88
 
72
- describe "#refund" do
89
+ describe "refund a subscription" do
73
90
  use_vcr_cassette "subscription/refund/#{timestamp}"
74
91
 
75
92
  let(:account){ Factory.create_account("subscription-refund-#{timestamp}") }
@@ -57,7 +57,7 @@ module Recurly
57
57
  let(:account) { Factory.create_account_with_billing_info("transaction-list-empty-#{timestamp}") }
58
58
 
59
59
  before(:each) do
60
- @transactions = Transaction.list(account.account_code)
60
+ @transactions = Transaction.list_for_account(account.account_code)
61
61
  end
62
62
 
63
63
  it "should return an empty array of transactions" do
@@ -74,7 +74,7 @@ module Recurly
74
74
  Factory.create_transaction account.account_code, :amount_in_cents => 200, :description => "two"
75
75
  Factory.create_transaction account.account_code, :amount_in_cents => 300, :description => "three"
76
76
 
77
- @successful_transactions = Transaction.list(account.account_code, :success)
77
+ @successful_transactions = Transaction.list_for_account(account.account_code, :success)
78
78
  end
79
79
 
80
80
  it "should return a list of transactions made on the account" do
@@ -109,8 +109,37 @@ module Recurly
109
109
  it "should also be available via Account#lookup_transaction" do
110
110
  account.lookup_transaction(@transaction3.id).should == @transaction3
111
111
  end
112
+ end
113
+
114
+ describe "void a transaction" do
115
+ use_vcr_cassette "transaction/void/#{timestamp}"
116
+ let(:account) { Factory.create_account_with_billing_info("transaction-void-#{timestamp}") }
117
+
118
+ before(:each) do
119
+ @transaction = Factory.create_transaction account.account_code, :amount_in_cents => 100, :description => "one"
120
+ end
121
+
122
+ it "should void a transaction" do
123
+ @transaction.void
124
+ Transaction.list(:voided).should include(@transaction)
125
+ end
126
+ end
127
+
128
+ describe "refund a transaction" do
129
+ use_vcr_cassette "transaction/refund/#{timestamp}"
130
+ let(:account) { Factory.create_account_with_billing_info("transaction-refund-#{timestamp}") }
112
131
 
132
+ before(:each) do
133
+ @transaction = Factory.create_transaction account.account_code, :amount_in_cents => 1000, :description => "one"
134
+ end
135
+
136
+ it "should refund the transaction" do
137
+ pending "Server should refund the transaction"
138
+ @transaction.refund(1000)
139
+ Transaction.list(:refunds).should include(@transaction)
140
+ end
113
141
  end
114
142
 
143
+
115
144
  end
116
145
  end
data/spec/spec_helper.rb CHANGED
@@ -3,18 +3,14 @@ require 'rubygems'
3
3
  require "bundler"
4
4
  Bundler.setup
5
5
 
6
- require File.dirname(__FILE__) + '/../lib/recurly'
6
+ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
7
+ require 'recurly'
8
+
9
+ Recurly.settings_path = "#{File.dirname(__FILE__)}/config/recurly.yml"
7
10
 
8
11
  # Requires supporting files with custom matchers and macros, etc,
9
12
  # in ./support/ and its subdirectories.
10
13
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
14
 
12
- # loads the settings from yml
13
- Recurly::SpecSettings.reload!
14
-
15
15
  # setup recurly authentication details for testing
16
- Recurly.configure do |c|
17
- c.username = Recurly::SpecSettings["username"]
18
- c.password = Recurly::SpecSettings["password"]
19
- c.site = Recurly::SpecSettings["site"]
20
- end
16
+ Recurly.configure_from_yaml
@@ -1,8 +1,15 @@
1
1
  module Recurly
2
2
  module Factory
3
3
 
4
+
4
5
  # creates an account
5
6
  def self.create_account(account_code, overrides = {})
7
+ account = Account.new(account_attributes(account_code, overrides))
8
+ account.save!
9
+ account
10
+ end
11
+
12
+ def self.account_attributes(account_code, overrides = {})
6
13
  attributes = {
7
14
  # version is used to avoid duplicate account errors on recurly's api, pass in a different one every time
8
15
  :account_code => account_code,
@@ -17,9 +24,7 @@ module Recurly
17
24
  attributes[key] = val
18
25
  end
19
26
 
20
- account = Account.new(attributes)
21
- account.save!
22
- account
27
+ attributes
23
28
  end
24
29
 
25
30
  # creates an account with associated billing information
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recurly
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Isaac Hall
@@ -16,13 +16,11 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-10-08 00:00:00 -07:00
19
+ date: 2010-10-12 00:00:00 -07:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
- name: activeresource
24
- prerelease: false
25
- requirement: &id001 !ruby/object:Gem::Requirement
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
26
24
  none: false
27
25
  requirements:
28
26
  - - ">="
@@ -32,17 +30,17 @@ dependencies:
32
30
  - 2
33
31
  - 3
34
32
  version: "2.3"
33
+ requirement: *id001
35
34
  type: :runtime
36
- version_requirements: *id001
37
- - !ruby/object:Gem::Dependency
38
- name: rspec
35
+ name: activeresource
39
36
  prerelease: false
40
- requirement: &id002 !ruby/object:Gem::Requirement
37
+ - !ruby/object:Gem::Dependency
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
41
39
  none: false
42
40
  requirements:
43
41
  - - ">="
44
42
  - !ruby/object:Gem::Version
45
- hash: 2632228393
43
+ hash: 62196431
46
44
  segments:
47
45
  - 2
48
46
  - 0
@@ -50,12 +48,12 @@ dependencies:
50
48
  - beta
51
49
  - 22
52
50
  version: 2.0.0.beta.22
51
+ requirement: *id002
53
52
  type: :development
54
- version_requirements: *id002
55
- - !ruby/object:Gem::Dependency
56
- name: webmock
53
+ name: rspec
57
54
  prerelease: false
58
- requirement: &id003 !ruby/object:Gem::Requirement
55
+ - !ruby/object:Gem::Dependency
56
+ version_requirements: &id003 !ruby/object:Gem::Requirement
59
57
  none: false
60
58
  requirements:
61
59
  - - ">="
@@ -64,12 +62,12 @@ dependencies:
64
62
  segments:
65
63
  - 0
66
64
  version: "0"
65
+ requirement: *id003
67
66
  type: :development
68
- version_requirements: *id003
69
- - !ruby/object:Gem::Dependency
70
- name: vcr
67
+ name: webmock
71
68
  prerelease: false
72
- requirement: &id004 !ruby/object:Gem::Requirement
69
+ - !ruby/object:Gem::Dependency
70
+ version_requirements: &id004 !ruby/object:Gem::Requirement
73
71
  none: false
74
72
  requirements:
75
73
  - - ">="
@@ -78,8 +76,10 @@ dependencies:
78
76
  segments:
79
77
  - 0
80
78
  version: "0"
79
+ requirement: *id004
81
80
  type: :development
82
- version_requirements: *id004
81
+ name: vcr
82
+ prerelease: false
83
83
  description: A Ruby API wrapper for Recurly. Super Simple Subscription billing.
84
84
  email:
85
85
  - support@recurly.com
@@ -94,10 +94,13 @@ files:
94
94
  - lib/recurly/base.rb
95
95
  - lib/recurly/billing_info.rb
96
96
  - lib/recurly/charge.rb
97
+ - lib/recurly/config_parser.rb
97
98
  - lib/recurly/credit.rb
98
99
  - lib/recurly/formats/xml_with_pagination.rb
99
100
  - lib/recurly/invoice.rb
100
101
  - lib/recurly/plan.rb
102
+ - lib/recurly/rails/railtie.rb
103
+ - lib/recurly/rails/recurly.rake
101
104
  - lib/recurly/subscription.rb
102
105
  - lib/recurly/transaction.rb
103
106
  - lib/recurly/version.rb
@@ -105,6 +108,7 @@ files:
105
108
  - init.rb
106
109
  - LICENSE
107
110
  - README.md
111
+ - spec/config/recurly.yml
108
112
  - spec/integration/account_spec.rb
109
113
  - spec/integration/billing_info_spec.rb
110
114
  - spec/integration/charge_spec.rb
@@ -114,9 +118,7 @@ files:
114
118
  - spec/integration/subscription_spec.rb
115
119
  - spec/integration/transaction_spec.rb
116
120
  - spec/spec_helper.rb
117
- - spec/spec_settings.yml.example
118
121
  - spec/support/factory.rb
119
- - spec/support/spec_settings.rb
120
122
  - spec/support/vcr.rb
121
123
  has_rdoc: true
122
124
  homepage: http://github.com/recurly/recurly-client-ruby
@@ -153,6 +155,7 @@ signing_key:
153
155
  specification_version: 3
154
156
  summary: Ruby API wrapper for Recurly
155
157
  test_files:
158
+ - spec/config/recurly.yml
156
159
  - spec/integration/account_spec.rb
157
160
  - spec/integration/billing_info_spec.rb
158
161
  - spec/integration/charge_spec.rb
@@ -162,7 +165,5 @@ test_files:
162
165
  - spec/integration/subscription_spec.rb
163
166
  - spec/integration/transaction_spec.rb
164
167
  - spec/spec_helper.rb
165
- - spec/spec_settings.yml.example
166
168
  - spec/support/factory.rb
167
- - spec/support/spec_settings.rb
168
169
  - spec/support/vcr.rb
@@ -1,3 +0,0 @@
1
- username: ''
2
- password: ''
3
- site: 'https://yoursitename-test.recurly.com'
@@ -1,36 +0,0 @@
1
- require 'fileutils'
2
- require 'yaml'
3
-
4
- module Recurly
5
- module SpecSettings
6
- SETTINGS_PATH = File.dirname(__FILE__) + '/../spec_settings.yml'
7
-
8
- class << self
9
- # keep all the settings global for now
10
- attr_accessor :settings
11
-
12
- def [](val)
13
- self.settings[val]
14
- end
15
-
16
- def []=(key,val)
17
- self.settings[key] = val
18
- end
19
-
20
- def reload!
21
- if File.exists?(SETTINGS_PATH)
22
- self.settings = YAML.load_file(SETTINGS_PATH)
23
- else
24
- raise "spec/spec_settings.yml file not found. Run rake recurly:setup to create one"
25
- end
26
- end
27
-
28
- def save!
29
- File.open(SETTINGS_PATH, 'w' ) do |out|
30
- YAML.dump(self.settings, out)
31
- end
32
- end
33
- end
34
- end
35
- end
36
-