rspreedly 0.0.1

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 (49) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +78 -0
  5. data/Rakefile +48 -0
  6. data/VERSION +1 -0
  7. data/lib/rspreedly/base.rb +76 -0
  8. data/lib/rspreedly/complimentary_subscription.rb +5 -0
  9. data/lib/rspreedly/complimentary_time_extension.rb +5 -0
  10. data/lib/rspreedly/config.rb +84 -0
  11. data/lib/rspreedly/error.rb +9 -0
  12. data/lib/rspreedly/invoice.rb +61 -0
  13. data/lib/rspreedly/line_item.rb +5 -0
  14. data/lib/rspreedly/payment_method.rb +7 -0
  15. data/lib/rspreedly/subscriber.rb +153 -0
  16. data/lib/rspreedly/subscription_plan.rb +40 -0
  17. data/lib/rspreedly.rb +29 -0
  18. data/spec/config_spec.rb +144 -0
  19. data/spec/fixtures/complimentary_failed_active.xml +1 -0
  20. data/spec/fixtures/complimentary_failed_inactive.xml +1 -0
  21. data/spec/fixtures/complimentary_not_valid.xml +3 -0
  22. data/spec/fixtures/complimentary_success.xml +25 -0
  23. data/spec/fixtures/create_subscriber.xml +25 -0
  24. data/spec/fixtures/existing_subscriber.xml +1 -0
  25. data/spec/fixtures/free_plan_not_elligable.xml +1 -0
  26. data/spec/fixtures/free_plan_not_free.xml +1 -0
  27. data/spec/fixtures/free_plan_not_set.xml +1 -0
  28. data/spec/fixtures/free_plan_success.xml +25 -0
  29. data/spec/fixtures/invalid_subscriber.xml +1 -0
  30. data/spec/fixtures/invalid_update.xml +1 -0
  31. data/spec/fixtures/invoice_created.xml +43 -0
  32. data/spec/fixtures/invoice_invalid.xml +1 -0
  33. data/spec/fixtures/no_plans.xml +2 -0
  34. data/spec/fixtures/no_subscribers.xml +2 -0
  35. data/spec/fixtures/payment_already_paid.xml +1 -0
  36. data/spec/fixtures/payment_invalid.xml +1 -0
  37. data/spec/fixtures/payment_not_found.xml +1 -0
  38. data/spec/fixtures/payment_success.xml +43 -0
  39. data/spec/fixtures/plan_disabled.xml +1 -0
  40. data/spec/fixtures/plan_not_found.xml +1 -0
  41. data/spec/fixtures/subscriber.xml +25 -0
  42. data/spec/fixtures/subscriber_not_found.xml +1 -0
  43. data/spec/fixtures/subscribers.xml +75 -0
  44. data/spec/fixtures/subscription_plan_list.xml +91 -0
  45. data/spec/invoice_spec.rb +129 -0
  46. data/spec/spec_helper.rb +44 -0
  47. data/spec/subscriber_spec.rb +442 -0
  48. data/spec/subscription_plan_spec.rb +36 -0
  49. metadata +107 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Richard Livsey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,78 @@
1
+ = RSpreedly
2
+
3
+ Ruby library to access the Spreedly API (all of it).
4
+
5
+ == Why another one?
6
+
7
+ There are a few of these knocking about, but I couldn't find one particular to my needs:
8
+
9
+ * Well covered with specs (aiming for 100%)
10
+ * Covers the entire API, not just parts of it
11
+ * Rubyish/ActiveRecord style access
12
+
13
+ == Usage
14
+
15
+ Configure your API key and site name for your account:
16
+
17
+ RSpreedly::Config.setup do |config|
18
+ config.api_key = "your-api-key"
19
+ config.site_name = "your-site-name"
20
+ end
21
+
22
+ List subscribers
23
+
24
+ RSpreedly::Subscriber.find(:all).each do |subscriber|
25
+ # do something
26
+ end
27
+
28
+ CRUD access to subscribers
29
+
30
+ sub = RSpreedly::Subscriber.new(:customer_id => 42, :email => "somewhere@example.com")
31
+ sub.token => nil
32
+ sub.save
33
+ sub.token => "6af9994a57e420345897b1abb4c27a9db27fa4d0"
34
+
35
+ sub = RSpreedly::Subscriber.find(42)
36
+ sub.email = "new-email@example.com"
37
+ sub.save
38
+
39
+ sub = RSpreedly::Subscriber.find(69)
40
+ sub.destroy
41
+
42
+ Comp subscriptions
43
+
44
+ comp = RSpreedly::ComplimentarySubscription.new(:duration_quantity => 3, :duration_units => "months", :feature_level => "Pro")
45
+ sub = RSpreedly::Subscriber.find(69)
46
+ sub.comp_subscription(comp)
47
+
48
+ View all plans
49
+
50
+ plans = RSpreedly::SubscriptionPlan.find(:all)
51
+
52
+ Raise an invoice, and pay it
53
+
54
+ invoice = Invoice.new(:subscription_plan_id => 5, :subscriber => sub)
55
+ invoice.save
56
+
57
+ payment = RSpreedly::PaymentMethod::CreditCard(:card_type => 'visa', :number => '123456', ...)
58
+ invoice.pay(payment)
59
+
60
+ See the specs and API docs at Spreedly for more details and the rest of the things it can do.
61
+
62
+ https://www.spreedly.com/manual/integration-reference/url-reference/
63
+
64
+ == Todo
65
+
66
+ * Better specs for data we send to Spreedly.
67
+ * Better docs.
68
+
69
+ == Thanks
70
+
71
+ I studied the other Spreedly APIs and there may well be a line or three this library has in common with:
72
+
73
+ * http://github.com/terralien/spreedly-gem
74
+ * http://github.com/erbmicha/spreedly/
75
+
76
+ == Copyright
77
+
78
+ Copyright (c) 2009 Richard Livsey. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rspreedly"
8
+ gem.summary = %Q{Ruby library for the Spreedly API}
9
+ gem.email = "richard@livsey.org"
10
+ gem.homepage = "http://github.com/rlivsey/rspreedly"
11
+ gem.authors = ["Richard Livsey"]
12
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
13
+ end
14
+
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ require 'spec/rake/spectask'
20
+ Spec::Rake::SpecTask.new(:spec) do |spec|
21
+ spec.libs << 'lib' << 'spec'
22
+ spec.spec_files = FileList['spec/**/*_spec.rb']
23
+ end
24
+
25
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.pattern = 'spec/**/*_spec.rb'
28
+ spec.rcov = true
29
+ end
30
+
31
+
32
+ task :default => :spec
33
+
34
+ require 'rake/rdoctask'
35
+ Rake::RDocTask.new do |rdoc|
36
+ if File.exist?('VERSION.yml')
37
+ config = YAML.load(File.read('VERSION.yml'))
38
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
39
+ else
40
+ version = ""
41
+ end
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "RSpreedly #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
48
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,76 @@
1
+ module RSpreedly
2
+
3
+ class Base
4
+ include HTTParty
5
+ format :xml
6
+ base_uri "https://spreedly.com/api/v4"
7
+
8
+ def self.api_request(type, path, options={})
9
+ path = "#{RSpreedly::Config.site_name}#{path}"
10
+ options.merge!({
11
+ :basic_auth => {:username => RSpreedly::Config.api_key, :password => 'X'},
12
+ :headers => {"Content-Type" => 'application/xml'}
13
+ })
14
+ self.do_request(type, path, options)
15
+ end
16
+
17
+ def self.do_request(type, path, options)
18
+ response = self.send(type.to_s, path, options)
19
+ message = "#{response.code}: #{response.body}"
20
+
21
+ case response.code.to_i
22
+ when 403
23
+ raise(RSpreedly::Error::Forbidden.new, message)
24
+ when 422
25
+ raise(RSpreedly::Error::BadRequest.new, message)
26
+ when 404
27
+ raise(RSpreedly::Error::NotFound.new, message)
28
+ end
29
+
30
+ response
31
+ end
32
+
33
+ def initialize(attrs={})
34
+ self.attributes = attrs
35
+ end
36
+
37
+ def attributes=(attrs)
38
+ attrs.each do |k, v|
39
+ self.send("#{k}=", v)
40
+ end
41
+ end
42
+
43
+ def api_request(type, path, options={})
44
+ self.class.api_request(type, path, options)
45
+ end
46
+
47
+ # TODO - do this nicer
48
+ # rather eew at the moment and hand made XML is not nice
49
+ def to_xml(opts={})
50
+ exclude = opts[:exclude] || []
51
+
52
+ tag = opts[:tag] || RSpreedly.underscore(self.class.to_s)
53
+ inner = opts[:inner]
54
+ outer = opts[:outer]
55
+ no_tag = opts[:no_tag]
56
+
57
+ xml = ""
58
+ xml << "<#{outer}>" if outer
59
+ xml << "<#{tag}>" unless no_tag
60
+ xml << "<#{inner}>" if inner
61
+ self.instance_variables.each do |var|
62
+ name = var.gsub('@', '')
63
+ next if exclude.include?(name.to_sym)
64
+ value = self.instance_variable_get(var)
65
+ if value.respond_to?(:to_xml)
66
+ value = value.to_xml(:no_tag => (RSpreedly.underscore(value.class.to_s) == name))
67
+ end
68
+ xml << "<#{name}>#{value}</#{name}>"
69
+ end
70
+ xml << "</#{inner}>" if inner
71
+ xml << "</#{tag}>" unless no_tag
72
+ xml << "</#{outer}>" if outer
73
+ xml
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ module RSpreedly
2
+ class ComplimentarySubscription < Base
3
+ attr_accessor :duration_quantity, :duration_units, :feature_level
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module RSpreedly
2
+ class ComplimentaryTimeExtension < Base
3
+ attr_accessor :duration_quantity, :duration_units
4
+ end
5
+ end
@@ -0,0 +1,84 @@
1
+ module RSpreedly
2
+ module Config
3
+ class << self
4
+
5
+ # the configuration hash itself
6
+ def configuration
7
+ @configuration ||= defaults
8
+ end
9
+
10
+ def defaults
11
+ {
12
+ :logger => defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT),
13
+ :debug => false,
14
+ :site_name => nil, # your site name
15
+ :key => nil # your API key
16
+ }
17
+ end
18
+
19
+ def [](key)
20
+ configuration[key]
21
+ end
22
+
23
+ def []=(key, val)
24
+ configuration[key] = val
25
+ end
26
+
27
+ # remove an item from the configuration
28
+ def delete(key)
29
+ configuration.delete(key)
30
+ end
31
+
32
+ # Return the value of the key, or the default if doesn't exist
33
+ #
34
+ # ==== Examples
35
+ #
36
+ # RSpreedly::Config.fetch(:monkey, false)
37
+ # => false
38
+ #
39
+ def fetch(key, default)
40
+ configuration.fetch(key, default)
41
+ end
42
+
43
+ def to_hash
44
+ configuration
45
+ end
46
+
47
+ # Yields the configuration.
48
+ #
49
+ # ==== Examples
50
+ # RSpreedly::Config.use do |config|
51
+ # config[:debug] = true
52
+ # config.something = false
53
+ # end
54
+ #
55
+ def setup
56
+ yield self
57
+ nil
58
+ end
59
+
60
+ def clear
61
+ @configuration = {}
62
+ end
63
+
64
+ def reset
65
+ @configuration = defaults
66
+ end
67
+
68
+ # allow getting and setting properties via RSpreedly::Config.xxx
69
+ #
70
+ # ==== Examples
71
+ # RSpreedly::Config.debug
72
+ # RSpreedly::Config.debug = false
73
+ #
74
+ def method_missing(method, *args)
75
+ if method.to_s[-1,1] == '='
76
+ configuration[method.to_s.tr('=','').to_sym] = *args
77
+ else
78
+ configuration[method]
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,9 @@
1
+ module RSpreedly
2
+ module Error
3
+ class Base < StandardError; end
4
+ class Forbidden < Base; end # 403 errors
5
+ class BadRequest < Base; end # 422 errors
6
+ class NotFound < Base; end # 404 errors
7
+ class GatewayTimeout < Base; end # 504 errors
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ module RSpreedly
2
+
3
+ class Invoice < Base
4
+
5
+ attr_accessor :subscription_plan_id,
6
+ :subscriber,
7
+ :closed,
8
+ :created_at,
9
+ :token,
10
+ :updated_at,
11
+ :price,
12
+ :amount,
13
+ :currency_code,
14
+ :line_items
15
+
16
+ # Create an Invoice (more)
17
+ # POST /api/v4/[short site name]/invoices.xml
18
+ def create
19
+ begin
20
+ create!
21
+ rescue RSpreedly::Error::Base
22
+ # gulp those errors down
23
+ # TODO - set self.errors or something?
24
+ end
25
+ end
26
+
27
+ def create!
28
+ result = api_request(:post, "/invoices.xml", :body => self.to_xml)
29
+ self.attributes = result["invoice"]
30
+ true
31
+ end
32
+
33
+ alias_method :save, :create
34
+ alias_method :save!, :create!
35
+
36
+ def subscriber=(data)
37
+ if data.is_a? Hash
38
+ data = RSpreedly::Subscriber.new(data)
39
+ end
40
+ @subscriber = data
41
+ end
42
+
43
+ def line_items=(data)
44
+ @line_items = []
45
+ data.each do |item|
46
+ if item.is_a? Hash
47
+ item = RSpreedly::LineItem.new(item)
48
+ end
49
+ @line_items << item
50
+ end
51
+ end
52
+
53
+ # Pay an Invoice (more)
54
+ # PUT /api/v4/[short site name]/invoices/[invoice token]/pay.xml
55
+ def pay(payment)
56
+ result = api_request(:put, "/invoices/#{self.token}/pay.xml", :body => payment.to_xml(:outer => 'payment'))
57
+ self.attributes = result["invoice"]
58
+ true
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ module RSpreedly
2
+ class LineItem < Base
3
+ attr_accessor :price, :notes, :amount, :description, :currency_code
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module RSpreedly
2
+ module PaymentMethod
3
+ class CreditCard < Base
4
+ attr_accessor :number, :verification_value, :month, :year, :first_name, :last_name, :card_type
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,153 @@
1
+ module RSpreedly
2
+
3
+ class Subscriber < Base
4
+
5
+ attr_accessor :active,
6
+ :active_until,
7
+ :billing_first_name,
8
+ :billing_last_name,
9
+ :card_expires_before_next_auto_renew,
10
+ :created_at,
11
+ :customer_id,
12
+ :eligible_for_free_trial,
13
+ :email,
14
+ :feature_level,
15
+ :grace_until,
16
+ :in_grace_period,
17
+ :lifetime_subscription,
18
+ :new_customer_id,
19
+ :on_trial,
20
+ :payment_method,
21
+ :ready_to_renew,
22
+ :recurring,
23
+ :screen_name,
24
+ :store_credit,
25
+ :store_credit_currency_code,
26
+ :subscription_plan_name,
27
+ :token,
28
+ :updated_at
29
+
30
+ class << self
31
+
32
+ # Get a subscriber’s details
33
+ # GET /api/v4/[short site name]/subscribers/[subscriber id].xml
34
+ def find(id)
35
+ return all if id == :all
36
+
37
+ begin
38
+ data = api_request(:get, "/subscribers/#{id}.xml")
39
+ Subscriber.new(data["subscriber"])
40
+ rescue RSpreedly::Error::NotFound
41
+ nil
42
+ end
43
+ end
44
+
45
+ # Get a list of all subscribers (more)
46
+ # GET /api/v4/[short site name]/subscribers.xml
47
+ def all
48
+ response = api_request(:get, "/subscribers.xml")
49
+ return [] unless response.has_key?("subscribers")
50
+ response["subscribers"].collect{|data| Subscriber.new(data)}
51
+ end
52
+
53
+ # Clear all subscribers from a *test* site (more)
54
+ # DELETE /api/v4/[short site name]/subscribers.xml
55
+ def delete_all
56
+ !! api_request(:delete, "/subscribers.xml")
57
+ end
58
+
59
+ alias_method :destroy_all, :delete_all
60
+
61
+ end
62
+
63
+ def new_record?
64
+ !self.token
65
+ end
66
+
67
+ def save
68
+ self.new_record? ? self.create : self.update
69
+ end
70
+
71
+ # Create a subscriber (more)
72
+ # POST /api/v4/[short site name]/subscribers.xml
73
+ def create!
74
+ result = api_request(:post, "/subscribers.xml", :body => self.to_xml)
75
+ self.attributes = result["subscriber"]
76
+ true
77
+ end
78
+
79
+ def create
80
+ begin
81
+ create!
82
+ rescue RSpreedly::Error::Base
83
+ # gulp those errors down
84
+ # TODO - set self.errors or something?
85
+ end
86
+ end
87
+
88
+ # Update a Subscriber (more)
89
+ # PUT /api/v4/[short site name]/subscribers/[subscriber id].xml
90
+ def update!
91
+ !! api_request(:put, "/subscribers/#{self.customer_id}.xml", :body => self.to_xml(:exclude => [:customer_id]))
92
+ end
93
+
94
+ def update
95
+ begin
96
+ update!
97
+ rescue RSpreedly::Error::Base
98
+ # gulp those errors down
99
+ # TODO - set self.errors or something?
100
+ end
101
+ end
102
+
103
+ # Delete one subscriber from a *test* site (more)
104
+ # DELETE /api/v4/[short site name]/subscribers/[subscriber id].xml
105
+ def destroy
106
+ begin
107
+ !! api_request(:delete, "/subscribers/#{self.customer_id}.xml")
108
+ rescue RSpreedly::Error::NotFound
109
+ nil
110
+ end
111
+ end
112
+ alias_method :delete, :destroy
113
+
114
+ # Give a subscriber a complimentary subscription (more)
115
+ # POST /api/v4/[short site name]/subscribers/[subscriber id]/complimentary_subscriptions.xml
116
+ def comp_subscription(subscription)
117
+ result = api_request(:post, "/subscribers/#{self.customer_id}/complimentary_subscriptions.xml", :body => subscription.to_xml)
118
+ self.attributes = result["subscriber"]
119
+ true
120
+ end
121
+
122
+ # Give a subscriber a complimentary time extension (more)
123
+ # POST /api/v4/[short site name]/subscribers/[subscriber id]/complimentary_time_extension.xml
124
+ def comp_time_extension(extension)
125
+ result = api_request(:post, "/subscribers/#{self.customer_id}/complimentary_time_extensions.xml", :body => extension.to_xml)
126
+ self.attributes = result["subscriber"]
127
+ true
128
+ end
129
+
130
+ # Programatically Stopping Auto Renew of a Subscriber (more)
131
+ # POST /api/v4/[short site name]/subscribers/[subscriber id]/stop_auto_renew.xml
132
+ def stop_auto_renew
133
+ !! api_request(:post, "/subscribers/#{self.customer_id}/stop_auto_renew.xml")
134
+ end
135
+
136
+ # Programatically Subscribe a Subscriber to a Free Trial Plan (more)
137
+ # POST /api/v4/[short site name]/subscribers/[subscriber id]/subscribe_to_free_trial.xml
138
+ def subscribe_to_free_trial(plan)
139
+ result = api_request(:post, "/subscribers/#{self.customer_id}/subscribe_to_free_trial.xml", :body => plan.to_xml)
140
+ self.attributes = result["subscriber"]
141
+ true
142
+ end
143
+
144
+ # Programatically Allow Another Free Trial (more)
145
+ # POST /api/v4/[short site name]/subscribers/[subscriber id]/allow_free_trial.xml
146
+ def allow_free_trial
147
+ result = api_request(:post, "/subscribers/#{self.customer_id}/allow_free_trial.xml")
148
+ self.attributes = result["subscriber"]
149
+ true
150
+ end
151
+
152
+ end
153
+ end
@@ -0,0 +1,40 @@
1
+ module RSpreedly
2
+
3
+ class SubscriptionPlan < Base
4
+
5
+ attr_accessor :amount,
6
+ :charge_after_first_period,
7
+ :charge_later_duration_quantity,
8
+ :charge_later_duration_units,
9
+ :created_at,
10
+ :currency_code,
11
+ :description,
12
+ :duration_quantity,
13
+ :duration_units,
14
+ :enabled,
15
+ :feature_level,
16
+ :force_recurring,
17
+ :id,
18
+ :name,
19
+ :needs_to_be_renewed,
20
+ :plan_type,
21
+ :price,
22
+ :return_url,
23
+ :terms,
24
+ :updated_at
25
+
26
+ # there's no API method for just getting one plan, so we fake it!
27
+ def self.find(id)
28
+ return all if id == :all
29
+ all.find{|plan| plan.id == id}
30
+ end
31
+
32
+ # Get a list of all subscription plans (more)
33
+ # GET /api/v4/[short site name]/subscription_plans.xml
34
+ def self.all
35
+ response = api_request(:get, "/subscription_plans.xml")
36
+ return [] unless response.has_key?("subscription_plans")
37
+ response["subscription_plans"].collect{|data| SubscriptionPlan.new(data)}
38
+ end
39
+ end
40
+ end
data/lib/rspreedly.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'logger'
2
+ require 'httparty'
3
+
4
+ require 'rspreedly/error'
5
+ require 'rspreedly/config'
6
+ require 'rspreedly/base'
7
+ require 'rspreedly/invoice'
8
+ require 'rspreedly/line_item'
9
+ require 'rspreedly/subscriber'
10
+ require 'rspreedly/subscription_plan'
11
+ require 'rspreedly/payment_method'
12
+ require 'rspreedly/complimentary_subscription'
13
+ require 'rspreedly/complimentary_time_extension'
14
+
15
+
16
+ module RSpreedly
17
+
18
+ # a few utility methods
19
+
20
+ # pretty much stolen from Rails
21
+ def self.underscore(camel_cased_word)
22
+ camel_cased_word.to_s.gsub(/\w+::/, '').
23
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
24
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
25
+ tr("-", "_").
26
+ downcase
27
+ end
28
+
29
+ end