spreedly 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +66 -0
  3. data/README.txt +68 -0
  4. data/Rakefile +72 -0
  5. data/lib/spreedly.rb +154 -0
  6. data/lib/spreedly/common.rb +24 -0
  7. data/lib/spreedly/mock.rb +113 -0
  8. data/lib/spreedly/version.rb +3 -0
  9. data/test/spreedly_gem_test.rb +152 -0
  10. data/test/test_site.yml +2 -0
  11. data/vendor/httparty/History +108 -0
  12. data/vendor/httparty/MIT-LICENSE +20 -0
  13. data/vendor/httparty/Manifest +55 -0
  14. data/vendor/httparty/README +35 -0
  15. data/vendor/httparty/Rakefile +47 -0
  16. data/vendor/httparty/bin/httparty +98 -0
  17. data/vendor/httparty/cucumber.yml +1 -0
  18. data/vendor/httparty/examples/aaws.rb +32 -0
  19. data/vendor/httparty/examples/basic.rb +11 -0
  20. data/vendor/httparty/examples/delicious.rb +37 -0
  21. data/vendor/httparty/examples/google.rb +16 -0
  22. data/vendor/httparty/examples/rubyurl.rb +14 -0
  23. data/vendor/httparty/examples/twitter.rb +31 -0
  24. data/vendor/httparty/examples/whoismyrep.rb +10 -0
  25. data/vendor/httparty/features/basic_authentication.feature +20 -0
  26. data/vendor/httparty/features/command_line.feature +7 -0
  27. data/vendor/httparty/features/deals_with_http_error_codes.feature +26 -0
  28. data/vendor/httparty/features/handles_multiple_formats.feature +34 -0
  29. data/vendor/httparty/features/steps/env.rb +15 -0
  30. data/vendor/httparty/features/steps/httparty_response_steps.rb +26 -0
  31. data/vendor/httparty/features/steps/httparty_steps.rb +15 -0
  32. data/vendor/httparty/features/steps/mongrel_helper.rb +55 -0
  33. data/vendor/httparty/features/steps/remote_service_steps.rb +47 -0
  34. data/vendor/httparty/features/supports_redirection.feature +22 -0
  35. data/vendor/httparty/httparty.gemspec +37 -0
  36. data/vendor/httparty/lib/core_extensions.rb +189 -0
  37. data/vendor/httparty/lib/httparty.rb +201 -0
  38. data/vendor/httparty/lib/httparty/cookie_hash.rb +9 -0
  39. data/vendor/httparty/lib/httparty/exceptions.rb +7 -0
  40. data/vendor/httparty/lib/httparty/logging.rb +35 -0
  41. data/vendor/httparty/lib/httparty/module_inheritable_attributes.rb +25 -0
  42. data/vendor/httparty/lib/httparty/parsers.rb +4 -0
  43. data/vendor/httparty/lib/httparty/parsers/json.rb +74 -0
  44. data/vendor/httparty/lib/httparty/parsers/xml.rb +209 -0
  45. data/vendor/httparty/lib/httparty/request.rb +169 -0
  46. data/vendor/httparty/lib/httparty/response.rb +17 -0
  47. data/vendor/httparty/lib/httparty/version.rb +3 -0
  48. data/vendor/httparty/setup.rb +1585 -0
  49. data/vendor/httparty/spec/fixtures/delicious.xml +23 -0
  50. data/vendor/httparty/spec/fixtures/empty.xml +0 -0
  51. data/vendor/httparty/spec/fixtures/google.html +3 -0
  52. data/vendor/httparty/spec/fixtures/twitter.json +1 -0
  53. data/vendor/httparty/spec/fixtures/twitter.xml +403 -0
  54. data/vendor/httparty/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  55. data/vendor/httparty/spec/hash_spec.rb +49 -0
  56. data/vendor/httparty/spec/httparty/cookie_hash_spec.rb +38 -0
  57. data/vendor/httparty/spec/httparty/parsers/json_spec.rb +44 -0
  58. data/vendor/httparty/spec/httparty/parsers/xml_spec.rb +454 -0
  59. data/vendor/httparty/spec/httparty/request_spec.rb +203 -0
  60. data/vendor/httparty/spec/httparty/response_spec.rb +53 -0
  61. data/vendor/httparty/spec/httparty_spec.rb +259 -0
  62. data/vendor/httparty/spec/spec.opts +3 -0
  63. data/vendor/httparty/spec/spec_helper.rb +21 -0
  64. data/vendor/httparty/spec/string_spec.rb +29 -0
  65. data/vendor/httparty/website/css/common.css +47 -0
  66. data/vendor/httparty/website/index.html +74 -0
  67. metadata +141 -0
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2009-03-17
2
+
3
+ * 1 major enhancement
4
+
5
+ * Initial release.
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,66 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/spreedly.rb
6
+ lib/spreedly/common.rb
7
+ lib/spreedly/mock.rb
8
+ lib/spreedly/version.rb
9
+ test/spreedly_gem_test.rb
10
+ test/test_site.yml
11
+ vendor/httparty/History
12
+ vendor/httparty/MIT-LICENSE
13
+ vendor/httparty/Manifest
14
+ vendor/httparty/README
15
+ vendor/httparty/Rakefile
16
+ vendor/httparty/bin/httparty
17
+ vendor/httparty/cucumber.yml
18
+ vendor/httparty/examples/aaws.rb
19
+ vendor/httparty/examples/basic.rb
20
+ vendor/httparty/examples/delicious.rb
21
+ vendor/httparty/examples/google.rb
22
+ vendor/httparty/examples/rubyurl.rb
23
+ vendor/httparty/examples/twitter.rb
24
+ vendor/httparty/examples/whoismyrep.rb
25
+ vendor/httparty/features/basic_authentication.feature
26
+ vendor/httparty/features/command_line.feature
27
+ vendor/httparty/features/deals_with_http_error_codes.feature
28
+ vendor/httparty/features/handles_multiple_formats.feature
29
+ vendor/httparty/features/steps/env.rb
30
+ vendor/httparty/features/steps/httparty_response_steps.rb
31
+ vendor/httparty/features/steps/httparty_steps.rb
32
+ vendor/httparty/features/steps/mongrel_helper.rb
33
+ vendor/httparty/features/steps/remote_service_steps.rb
34
+ vendor/httparty/features/supports_redirection.feature
35
+ vendor/httparty/httparty.gemspec
36
+ vendor/httparty/lib/core_extensions.rb
37
+ vendor/httparty/lib/httparty.rb
38
+ vendor/httparty/lib/httparty/cookie_hash.rb
39
+ vendor/httparty/lib/httparty/exceptions.rb
40
+ vendor/httparty/lib/httparty/logging.rb
41
+ vendor/httparty/lib/httparty/module_inheritable_attributes.rb
42
+ vendor/httparty/lib/httparty/parsers.rb
43
+ vendor/httparty/lib/httparty/parsers/json.rb
44
+ vendor/httparty/lib/httparty/parsers/xml.rb
45
+ vendor/httparty/lib/httparty/request.rb
46
+ vendor/httparty/lib/httparty/response.rb
47
+ vendor/httparty/lib/httparty/version.rb
48
+ vendor/httparty/setup.rb
49
+ vendor/httparty/spec/fixtures/delicious.xml
50
+ vendor/httparty/spec/fixtures/empty.xml
51
+ vendor/httparty/spec/fixtures/google.html
52
+ vendor/httparty/spec/fixtures/twitter.json
53
+ vendor/httparty/spec/fixtures/twitter.xml
54
+ vendor/httparty/spec/fixtures/undefined_method_add_node_for_nil.xml
55
+ vendor/httparty/spec/hash_spec.rb
56
+ vendor/httparty/spec/httparty/cookie_hash_spec.rb
57
+ vendor/httparty/spec/httparty/parsers/json_spec.rb
58
+ vendor/httparty/spec/httparty/parsers/xml_spec.rb
59
+ vendor/httparty/spec/httparty/request_spec.rb
60
+ vendor/httparty/spec/httparty/response_spec.rb
61
+ vendor/httparty/spec/httparty_spec.rb
62
+ vendor/httparty/spec/spec.opts
63
+ vendor/httparty/spec/spec_helper.rb
64
+ vendor/httparty/spec/string_spec.rb
65
+ vendor/httparty/website/css/common.css
66
+ vendor/httparty/website/index.html
data/README.txt ADDED
@@ -0,0 +1,68 @@
1
+ = Spreedly-Gem
2
+
3
+ * http://terralien.com/static/projects/spreedly-gem/
4
+ * http://github.com/terralien/spreedly-gem
5
+
6
+ == DESCRIPTION:
7
+
8
+ The Spreedly gem provides a convenient Ruby wrapper for the goodness that is
9
+ the http://spreedly.com API. Created by Terralien[http://terralien.com].
10
+
11
+ == FEATURES:
12
+
13
+ * Makes it easy to get started.
14
+ * Fully tested.
15
+ * (Mostly) fully substitutable mock implementation for fast tests.
16
+ * Great example code.
17
+
18
+ == SYNOPSIS:
19
+
20
+ # For real
21
+ require 'spreedly'
22
+ Spreedly.configure('site name', 'crazy hash token')
23
+ url = Spreedly.subscribe_url(:id => 'customer id', :plan => 'plan id')
24
+ subscriber = Spreedly::Subscriber.find('customer id')
25
+ subscriber.active?
26
+
27
+ # For mock
28
+ require 'spreedly/mock'
29
+ Spreedly.configure('site name', 'crazy hash token')
30
+ url = Spreedly.subscribe_url(:id => 'customer id', :plan => 'plan id')
31
+ subscriber = Spreedly::Subscriber.find('customer id')
32
+ subscriber.active?
33
+
34
+ Yup, they're exactly the same except for the require and the speed!
35
+
36
+ == REQUIREMENTS:
37
+
38
+ * A (free) Spreedly account.
39
+ * HTTParty (vendored for now).
40
+
41
+ == INSTALL:
42
+
43
+ `sudo gem install spreedly-gem`
44
+
45
+ == LICENSE:
46
+
47
+ (The MIT License)
48
+
49
+ Copyright (c) 2009 Nathaniel Talbott
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining
52
+ a copy of this software and associated documentation files (the
53
+ 'Software'), to deal in the Software without restriction, including
54
+ without limitation the rights to use, copy, modify, merge, publish,
55
+ distribute, sublicense, and/or sell copies of the Software, and to
56
+ permit persons to whom the Software is furnished to do so, subject to
57
+ the following conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ require 'hoe'
2
+ require './lib/spreedly/version.rb'
3
+
4
+ ENV["COPYFILE_DISABLE"] = "true" # Lose all the fugly ._ files when tar'ing
5
+
6
+ hoe = nil
7
+ Hoe.new('spreedly', Spreedly::VERSION) do |project|
8
+ hoe = project
9
+ project.rubyforge_name = 'terralien'
10
+ project.developer('Nathaniel Talbott', 'nathaniel@terralien.com')
11
+ project.test_globs = ["test/**/*_test.rb"]
12
+ project.extra_dev_deps = ["thoughtbot-shoulda"]
13
+ end
14
+
15
+ def remove_task(*task_names)
16
+ task_names.each do |task_name|
17
+ Rake.application.instance_eval{@tasks.delete(task_name.to_s)}
18
+ end
19
+ end
20
+
21
+ def replace_task(task_name, *args, &block)
22
+ name = (task_name.is_a?(Hash) ? task_name.keys.first : task_name)
23
+ remove_task(name)
24
+ task(task_name, *args, &block)
25
+ end
26
+
27
+ remove_task :docs, 'doc/index.html'
28
+ Rake::RDocTask.new(:docs) do |rd|
29
+ rd.main = hoe.readme_file
30
+ rd.rdoc_dir = 'doc'
31
+
32
+ rd.rdoc_files += ['lib/spreedly.rb', 'lib/spreedly/common.rb', hoe.readme_file]
33
+
34
+ title = "Terralien's Spreedly Gem (#{hoe.version}) Documentation"
35
+
36
+ rd.options << "-t" << title << "-f" << "darkfish"
37
+ end
38
+
39
+ replace_task :publish_docs => [:clean, :docs] do
40
+ host = "terralien@terralien.biz"
41
+
42
+ remote_dir = "/var/www/terralien/www/shared/static/projects/spreedly-gem"
43
+ local_dir = 'doc'
44
+
45
+ sh %{rsync #{hoe.rsync_args} #{local_dir}/ #{host}:#{remote_dir}}
46
+ end
47
+
48
+ desc "Run tests with and without mocking."
49
+ replace_task :test do
50
+ hoe.run_tests
51
+
52
+ ENV["SPREEDLY_TEST"] = "REAL"
53
+ hoe.run_tests
54
+ end
55
+
56
+ desc "Run only mock tests."
57
+ task :test_mock do
58
+ hoe.run_tests
59
+ end
60
+
61
+ desc "Run only real tests."
62
+ task :test_real do
63
+ ENV["SPREEDLY_TEST"] = "REAL"
64
+ hoe.run_tests
65
+ end
66
+
67
+ desc "Run both sets of tests under multiruby"
68
+ replace_task :multi do
69
+ hoe.run_tests(true)
70
+ ENV["SPREEDLY_TEST"] = "REAL"
71
+ hoe.run_tests(true)
72
+ end
data/lib/spreedly.rb ADDED
@@ -0,0 +1,154 @@
1
+ require 'spreedly/common'
2
+ require 'httparty'
3
+
4
+ raise "Mock Spreedly already required!" if defined?(Spreedly::MOCK)
5
+
6
+ =begin rdoc
7
+ Provides a convenient wrapper around the http://spreedly.com API.
8
+ Instead of mucking around with http you can just Spreedly.configure
9
+ and Spreedly::Subscriber.find. Much of the functionality is hung off
10
+ of the Spreedly::Subscriber class, and there's also a
11
+ Spreedly::SubscriptionPlan class.
12
+
13
+ One of the goals of this wrapper is to keep your tests fast while
14
+ also keeping your app working. It does this by providing a drop-in
15
+ replacement for the real Spreedly functionality that skips the
16
+ network and gives you a simple (some might call it stupid)
17
+ implementation that will work for 90% of your tests. At least we
18
+ hope so.
19
+
20
+ Help us make the mock work better by telling us when it doesn't work
21
+ so we can improve it. Thanks!
22
+
23
+ ==Example mock usage:
24
+
25
+ if ENV["SPREEDLY"] == "REAL"
26
+ require 'spreedly'
27
+ else
28
+ require 'spreedly/mock'
29
+ end
30
+ =end
31
+
32
+ module Spreedly
33
+ REAL = "real" # :nodoc:
34
+
35
+ include HTTParty
36
+ headers 'Accept' => 'text/xml'
37
+ headers 'Content-Type' => 'text/xml'
38
+ format :xml
39
+
40
+ # Call this before you start using the API to set things up.
41
+ def self.configure(site_name, token)
42
+ base_uri "https://spreedly.com/api/v4/#{site_name}"
43
+ basic_auth token, 'X'
44
+ @site_name = site_name
45
+ end
46
+
47
+ def self.site_name # :nodoc:
48
+ @site_name
49
+ end
50
+
51
+ def self.to_xml_params(hash) # :nodoc:
52
+ hash.collect do |key, value|
53
+ tag = key.to_s.tr('_', '-')
54
+ result = "<#{tag}>"
55
+ if value.is_a?(Hash)
56
+ result << to_xml_params(value)
57
+ else
58
+ result << value.to_s
59
+ end
60
+ result << "</#{tag}>"
61
+ result
62
+ end.join('')
63
+ end
64
+
65
+ class Resource # :nodoc: all
66
+ def initialize(data)
67
+ @data = data
68
+ end
69
+
70
+ def method_missing(method, *args, &block)
71
+ if method.to_s =~ /\?$/
72
+ send(method.to_s[0..-2])
73
+ elsif @data.include?(method.to_s)
74
+ @data[method.to_s]
75
+ else
76
+ super
77
+ end
78
+ end
79
+ end
80
+
81
+ class Subscriber < Resource
82
+
83
+ # This will DELETE all the subscribers from the site.
84
+ #
85
+ # Only works for tests sites (enforced on the Spreedly side).
86
+ def self.wipe!
87
+ Spreedly.delete('/subscribers.xml')
88
+ end
89
+
90
+ # Creates a new subscriber on Spreedly. The subscriber will NOT
91
+ # be active - they have to pay or you have to comp them for that
92
+ # to happen.
93
+ def self.create!(id, email=nil, screen_name=nil)
94
+ result = Spreedly.post('/subscribers.xml', :body =>
95
+ Spreedly.to_xml_params(:subscriber => {:customer_id => id, :email => email, :screen_name => screen_name}))
96
+ case result.code
97
+ when /2../
98
+ new(result['subscriber'])
99
+ when '403'
100
+ raise "Could not create subscriber: no id passed OR already exists."
101
+ else
102
+ raise "Could not create subscriber: result code #{result.code}."
103
+ end
104
+ end
105
+
106
+ # Looks a subscriber up by id.
107
+ def self.find(id)
108
+ new(Spreedly.get("/subscribers/#{id}.xml")['subscriber'])
109
+ end
110
+
111
+ # Returns all the subscribers in your site.
112
+ def self.all
113
+ Spreedly.get('/subscribers.xml')['subscribers'].collect{|data| new(data)}
114
+ end
115
+
116
+ # Spreedly calls your id for the user the "customer id". This
117
+ # gives you a handy alias so you can just call it "id".
118
+ def id
119
+ customer_id
120
+ end
121
+
122
+ # Allows you to give a complimentary subscription (if the
123
+ # subscriber is inactive) or a complimentary time extension (if
124
+ # the subscriber is inactive). Automatically figures out which
125
+ # to do.
126
+ #
127
+ # Note: units must be one of "days" or "months" (Spreedly
128
+ # enforced).
129
+ def comp(quantity, units, feature_level=nil)
130
+ params = {:duration_quantity => quantity, :duration_units => units}
131
+ params[:feature_level] = feature_level if feature_level
132
+ endpoint = (active? ? "complimentary_time_extensions" : "complimentary_subscriptions")
133
+ result = Spreedly.post("/subscribers/#{id}/#{endpoint}.xml", :body => Spreedly.to_xml_params(endpoint[0..-2] => params))
134
+ case result.code
135
+ when /2../
136
+ when '404'
137
+ raise "Could not comp subscriber: no longer exists."
138
+ when '422'
139
+ raise "Could not comp subscriber: validation failed."
140
+ when '403'
141
+ raise "Could not comp subscriber: invalid comp type (#{endpoint})."
142
+ else
143
+ raise "Could not comp subscriber: result code #{result.code}."
144
+ end
145
+ end
146
+ end
147
+
148
+ class SubscriptionPlan < Resource
149
+ # Returns all of the subscription plans defined in your site.
150
+ def self.all
151
+ Spreedly.get('/subscription_plans.xml')['subscription_plans'].collect{|data| new(data)}
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,24 @@
1
+ Dir[File.dirname(__FILE__) + '/../../vendor/*'].each do |directory|
2
+ next unless File.directory?(directory)
3
+ $LOAD_PATH.unshift File.expand_path(directory + '/lib')
4
+ end
5
+
6
+ require 'uri'
7
+ require 'bigdecimal'
8
+
9
+ require 'spreedly/version'
10
+
11
+ module Spreedly
12
+ # Generates a subscribe url for the given user id and plan.
13
+ def self.subscribe_url(id, plan, screen_name=nil)
14
+ screen_name = (screen_name ? URI.escape(screen_name) : "")
15
+ "https://spreedly.com/#{site_name}/subscribers/#{id}/subscribe/#{plan}/#{screen_name}"
16
+ end
17
+
18
+ # Generates an edit subscriber for the given subscriber token. The
19
+ # token is returned with the subscriber info (i.e. by
20
+ # Subscriber.find).
21
+ def self.edit_subscriber_url(token)
22
+ "https://spreedly.com/#{site_name}/subscriber_accounts/#{token}"
23
+ end
24
+ end
@@ -0,0 +1,113 @@
1
+ require 'spreedly/common'
2
+
3
+ raise "Real Spreedly already required!" if defined?(Spreedly::REAL)
4
+
5
+ module Spreedly
6
+ MOCK = "mock"
7
+
8
+ def self.configure(name, token)
9
+ @site_name = name
10
+ end
11
+
12
+ def self.site_name
13
+ @site_name
14
+ end
15
+
16
+ class Resource
17
+ def self.attributes
18
+ @attributes ||= {}
19
+ end
20
+
21
+ def self.attributes=(value)
22
+ @attributes = value
23
+ end
24
+
25
+ def initialize(params={})
26
+ @attributes = params
27
+ self.class.attributes.each{|k,v| @attributes[k] = v.call}
28
+ end
29
+
30
+ def id
31
+ @attributes[:id]
32
+ end
33
+
34
+ def method_missing(method, *args)
35
+ if method.to_s =~ /\?$/
36
+ send(method.to_s[0..-2], *args)
37
+ elsif @attributes.include?(method)
38
+ @attributes[method]
39
+ else
40
+ super
41
+ end
42
+ end
43
+ end
44
+
45
+ class Subscriber < Resource
46
+ self.attributes = {
47
+ :created_at => proc{Time.now},
48
+ :token => proc{(rand * 1000).round},
49
+ :active => proc{false},
50
+ :store_credit => proc{BigDecimal("0.0")},
51
+ :active_until => proc{nil},
52
+ :feature_level => proc{""},
53
+ }
54
+
55
+ def self.wipe! # :nodoc: all
56
+ @subscribers = nil
57
+ end
58
+
59
+ def self.create!(id, email=nil, screen_name=nil) # :nodoc: all
60
+ sub = new({:id => id, :email => email, :screen_name => screen_name})
61
+
62
+ if subscribers[sub.id]
63
+ raise "Could not create subscriber: already exists."
64
+ end
65
+
66
+ subscribers[sub.id] = sub
67
+ sub
68
+ end
69
+
70
+ def self.find(id)
71
+ subscribers[id]
72
+ end
73
+
74
+ def self.subscribers
75
+ @subscribers ||= {}
76
+ end
77
+
78
+ def self.all
79
+ @subscribers.values
80
+ end
81
+
82
+ def initialize(params={})
83
+ super
84
+ if !id || id == ''
85
+ raise "Could not create subscriber: no id passed OR already exists."
86
+ end
87
+ end
88
+
89
+ def comp(quantity, units, feature_level=nil)
90
+ raise "Could not comp subscriber: no longer exists." unless self.class.find(id)
91
+ raise "Could not comp subscriber: validation failed." unless units && quantity
92
+ current_active_until = (active_until || Time.now)
93
+ @attributes[:active_until] = case units
94
+ when 'days'
95
+ current_active_until + (quantity.to_i * 86400)
96
+ when 'months'
97
+ current_active_until + (quantity.to_i * 30 * 86400)
98
+ end
99
+ @attributes[:feature_level] = feature_level if feature_level
100
+ @attributes[:active] = true
101
+ end
102
+ end
103
+
104
+ class SubscriptionPlan < Resource
105
+ def self.all
106
+ plans.values
107
+ end
108
+
109
+ def self.plans
110
+ @plans ||= {1 => new(:id => 1, :name => 'Default mock plan')}
111
+ end
112
+ end
113
+ end