mousetrap 0.3.3

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.
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,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ spec/integration/settings.yml
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jon Larkowski
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.textile ADDED
@@ -0,0 +1,25 @@
1
+ h1. Mousetrap
2
+
3
+ _CheddarGetter API Client in Ruby_
4
+
5
+ Docs coming Real Soon Now (TM).
6
+
7
+
8
+ h2. Tests
9
+
10
+ h3. Unit Tests
11
+
12
+ Run RSpec tests with the @rake spec@ task.
13
+
14
+ h3. Integration Smoke Test
15
+
16
+ This test runs against the actual CheddarGetter service, using a test Product
17
+ that you create.
18
+
19
+ # Set up a CheddarGetter account.
20
+ # Add a Product with a code named: @MOUSETRAP_TEST@
21
+ # Add a Plan with a code named: @TEST@
22
+ # Put your credentials into a _spec/integration/settings.yml_ file.
23
+ # Run the tests with: @spec -c -fn spec/integration/smoke_test.rb@
24
+
25
+ Copyright (c) 2009 Hashrocket. See MIT-LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "mousetrap"
8
+ gem.summary = %Q{CheddarGetter API Client in Ruby}
9
+ gem.description = %Q{CheddarGetter API Client in Ruby}
10
+ gem.email = "jonlarkowski@gmail.com"
11
+ gem.homepage = "http://github.com/hashrocket/mousetrap"
12
+ gem.authors = ["Jon Larkowski", "Sandro Turriate", "Wolfram Arnold", "Corey Grusden"]
13
+ gem.add_dependency 'httparty', '>= 0.4.2'
14
+ gem.add_development_dependency "activesupport", '>= 2.3.3'
15
+ gem.add_development_dependency "rspec", '>= 1.2.8'
16
+ gem.add_development_dependency 'thoughtbot-factory_girl', '>= 1.2.2'
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.spec_opts = ['--options', 'spec/spec.opts']
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :spec => :check_dependencies
36
+
37
+ task :default => :spec
38
+
39
+ require 'rake/rdoctask'
40
+ Rake::RDocTask.new do |rdoc|
41
+ if File.exist?('VERSION')
42
+ version = File.read('VERSION')
43
+ else
44
+ version = ""
45
+ end
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "mousetrap #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.3
data/lib/mousetrap.rb ADDED
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ begin; require 'rubygems'; rescue LoadError; end
4
+ require 'httparty'
5
+
6
+ module Mousetrap
7
+ NO_BUSINESS_NEED = "No business need at this time."
8
+ API_UNSUPPORTED = "CheddarGetter API doesn't support this."
9
+
10
+ autoload :Customer, 'mousetrap/customer'
11
+ autoload :Plan, 'mousetrap/plan'
12
+ autoload :Resource, 'mousetrap/resource'
13
+ autoload :Subscription, 'mousetrap/subscription'
14
+
15
+ class << self
16
+ attr_accessor :product_code
17
+
18
+ def authenticate(user, password)
19
+ Resource.basic_auth user, password
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,118 @@
1
+ module Mousetrap
2
+ class Customer < Resource
3
+ attr_accessor \
4
+ :id,
5
+ :code,
6
+ :email,
7
+ :first_name,
8
+ :last_name,
9
+ :subscription
10
+
11
+ def subscription_attributes=(attributes)
12
+ self.subscription = Subscription.new attributes
13
+ end
14
+
15
+ def attributes
16
+ {
17
+ :id => id,
18
+ :code => code,
19
+ :email => email,
20
+ :first_name => first_name,
21
+ :last_name => last_name,
22
+ }
23
+ end
24
+
25
+ def attributes_for_api
26
+ a = self.class.attributes_for_api(attributes, new_record?)
27
+
28
+ if subscription
29
+ a[:subscription] = subscription.attributes_for_api
30
+ end
31
+
32
+ a
33
+ end
34
+
35
+ def cancel
36
+ member_action 'cancel' unless new_record?
37
+ end
38
+
39
+ def save
40
+ new? ? create : update
41
+ end
42
+
43
+ def self.all
44
+ response = get_resources plural_resource_name
45
+
46
+ if response['error']
47
+ if response['error'] == 'Resource not found: No customers found.'
48
+ return []
49
+ else
50
+ raise response['error']
51
+ end
52
+ end
53
+
54
+ build_resources_from response
55
+ end
56
+
57
+ def self.create(attributes)
58
+ object = new(attributes)
59
+ response = object.save
60
+ returned_customer = build_resource_from response
61
+ object.id = returned_customer.id
62
+ # TODO: what other attrs to copy over?
63
+ object
64
+ end
65
+
66
+ def self.new_from_api(attributes)
67
+ customer = new(attributes_from_api(attributes))
68
+ subscription_attrs = attributes['subscriptions']['subscription']
69
+ customer.subscription = Subscription.new_from_api(subscription_attrs.kind_of?(Array) ? subscription_attrs.first : subscription_attrs)
70
+ customer
71
+ end
72
+
73
+
74
+ protected
75
+
76
+ def self.plural_resource_name
77
+ 'customers'
78
+ end
79
+
80
+ def self.singular_resource_name
81
+ 'customer'
82
+ end
83
+
84
+ def self.attributes_for_api(attributes, new_record = true)
85
+ mutated_hash = {
86
+ :email => attributes[:email],
87
+ :firstName => attributes[:first_name],
88
+ :lastName => attributes[:last_name],
89
+ }
90
+ mutated_hash.merge!(:code => attributes[:code]) if new_record
91
+ mutated_hash
92
+ end
93
+
94
+ def self.attributes_from_api(attributes)
95
+ {
96
+ :id => attributes['id'],
97
+ :code => attributes['code'],
98
+ :first_name => attributes['firstName'],
99
+ :last_name => attributes['lastName'],
100
+ :email => attributes['email']
101
+ }
102
+ end
103
+
104
+ def create
105
+ response = self.class.post_resource 'customers', 'new', attributes_for_api
106
+
107
+ raise response['error'] if response['error']
108
+
109
+ returned_customer = self.class.build_resource_from response
110
+ self.id = returned_customer.id
111
+ response
112
+ end
113
+
114
+ def update
115
+ self.class.put_resource 'customers', 'edit-customer', code, attributes_for_api
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,30 @@
1
+ module Mousetrap
2
+ class Plan < Resource
3
+ attr_accessor \
4
+ :code,
5
+ :name
6
+
7
+ def self.all
8
+ response = get_resources plural_resource_name
9
+ return [] unless response['plans']
10
+ build_resources_from response
11
+ end
12
+
13
+ protected
14
+
15
+ def self.plural_resource_name
16
+ 'plans'
17
+ end
18
+
19
+ def self.singular_resource_name
20
+ 'plan'
21
+ end
22
+
23
+ def self.attributes_from_api(attributes)
24
+ {
25
+ :code => attributes['code'],
26
+ :name => attributes['name']
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,137 @@
1
+ module Mousetrap
2
+ class Resource
3
+ include HTTParty
4
+ headers 'User-Agent' => 'Mousetrap Ruby Client'
5
+ base_uri 'https://cheddargetter.com'
6
+
7
+ def initialize(hash={})
8
+ hash.each do |key, value|
9
+ self.send("#{key}=", value)
10
+ end
11
+ end
12
+
13
+ def self.[](code)
14
+ response = get_resource plural_resource_name, code
15
+ build_resource_from response
16
+ end
17
+
18
+ def self.create(attributes = {})
19
+ raise NotImplementedError, NO_BUSINESS_NEED
20
+ end
21
+
22
+ def self.delete(code)
23
+ raise NotImplementedError, NO_BUSINESS_NEED
24
+ end
25
+
26
+ def self.destroy_all
27
+ all.each { |object| object.destroy }
28
+ end
29
+
30
+ def self.exists?(code)
31
+ raise NotImplementedError, NO_BUSINESS_NEED
32
+ end
33
+
34
+ def destroy
35
+ member_action 'delete' unless new_record?
36
+ end
37
+
38
+ def exists?(code)
39
+ raise NotImplementedError, NO_BUSINESS_NEED
40
+ end
41
+
42
+ def new?
43
+ id.nil?
44
+ end
45
+
46
+ alias new_record? new?
47
+
48
+ def self.new_from_api(attributes)
49
+ new(attributes_from_api(attributes))
50
+ end
51
+
52
+ def save
53
+ raise NotImplementedError, NO_BUSINESS_NEED
54
+ end
55
+
56
+
57
+ protected
58
+
59
+ def member_action(action)
60
+ self.class.member_action(self.class.plural_resource_name, action, code)
61
+ end
62
+
63
+ def self.resource_path(resource, action, code = nil)
64
+ path = "/xml/#{resource}/#{action}/productCode/#{uri_encode(Mousetrap.product_code)}"
65
+ path += "/code/#{uri_encode(code)}" if code
66
+ path
67
+ end
68
+
69
+ def self.uri_encode(value)
70
+ URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
71
+ end
72
+
73
+ def self.member_action(resource, action, code, attributes = nil)
74
+ path = resource_path(resource, action, code)
75
+
76
+ if attributes
77
+ post path, :body => attributes
78
+ else
79
+ post path
80
+ end
81
+ end
82
+
83
+ def self.delete_resource(resource, code)
84
+ member_action(resource, 'delete', code)
85
+ end
86
+
87
+ def self.put_resource(resource, action, code, attributes)
88
+ member_action(resource, action, code, attributes)
89
+ end
90
+
91
+ def self.get_resource(resource, code)
92
+ get resource_path(resource, 'get', code)
93
+ end
94
+
95
+ def self.get_resources(resource)
96
+ get resource_path(resource, 'get')
97
+ end
98
+
99
+ def self.post_resource(resource, action, attributes)
100
+ path = resource_path(resource, action)
101
+ post path, :body => attributes
102
+ end
103
+
104
+ def self.plural_resource_name
105
+ raise 'You must implement self.plural_resource_name in your subclass.'
106
+ end
107
+
108
+ def self.singular_resource_name
109
+ raise 'You must implement self.singular_resource_name in your subclass.'
110
+ end
111
+
112
+ def self.build_resource_from(response)
113
+ resource_attributes = extract_resources(response)
114
+ new_from_api(resource_attributes)
115
+ end
116
+
117
+ def self.build_resources_from(response)
118
+ resources = []
119
+
120
+ response_resources = extract_resources(response)
121
+
122
+ if response_resources.is_a?(Array)
123
+ extract_resources(response).each do |resource_attributes|
124
+ resources << new_from_api(resource_attributes)
125
+ end
126
+ else
127
+ resources << new_from_api(response_resources)
128
+ end
129
+
130
+ resources
131
+ end
132
+
133
+ def self.extract_resources(response)
134
+ response[plural_resource_name][singular_resource_name]
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,100 @@
1
+ module Mousetrap
2
+ class Subscription < Resource
3
+ # Attributes we send _to_ the API.
4
+ attr_accessor \
5
+ :plan_code,
6
+ :billing_first_name,
7
+ :billing_last_name,
8
+ :credit_card_number,
9
+ :credit_card_expiration_month,
10
+ :credit_card_expiration_year,
11
+ :billing_zip_code,
12
+ :plan,
13
+
14
+ :customer_code # belongs to customer
15
+
16
+ # Attributes that come _from_ the API.
17
+ attr_reader \
18
+ :id,
19
+ :canceled_at,
20
+ :created_at,
21
+ :credit_card_expiration_date,
22
+ :credit_card_last_four_digits,
23
+ :credit_card_type
24
+
25
+ # TODO: not sure if .all or .[] will work
26
+
27
+ def attributes
28
+ {
29
+ :id => id,
30
+ :plan_code => plan_code,
31
+ :billing_first_name => billing_first_name,
32
+ :billing_last_name => billing_last_name,
33
+ :credit_card_number => credit_card_number,
34
+ :credit_card_expiration_month => credit_card_expiration_month,
35
+ :credit_card_expiration_year => credit_card_expiration_year,
36
+ :billing_zip_code => billing_zip_code,
37
+ }
38
+ end
39
+
40
+ def attributes_for_api
41
+ self.class.attributes_for_api(attributes)
42
+ end
43
+
44
+ def destroy
45
+ raise NotImplementedError, API_UNSUPPORTED
46
+ end
47
+
48
+ def save
49
+ mutated_attributes = attributes_for_api
50
+ self.class.put_resource('customers', 'edit-subscription', customer_code, mutated_attributes)
51
+ end
52
+
53
+ def self.new_from_api(attributes)
54
+ subscription = new(attributes_from_api(attributes))
55
+ subscription.plan = Plan.new_from_api(attributes['plans']['plan'])
56
+ subscription
57
+ end
58
+
59
+ protected
60
+
61
+ attr_writer \
62
+ :id,
63
+ :canceled_at,
64
+ :created_at,
65
+ :credit_card_expiration_date,
66
+ :credit_card_last_four_digits,
67
+ :credit_card_type
68
+
69
+ def self.plural_resource_name
70
+ 'subscriptions'
71
+ end
72
+
73
+ def self.singular_resource_name
74
+ 'subscription'
75
+ end
76
+
77
+ def self.attributes_for_api(attributes)
78
+ {
79
+ :planCode => attributes[:plan_code],
80
+ :ccFirstName => attributes[:billing_first_name],
81
+ :ccLastName => attributes[:billing_last_name],
82
+ :ccNumber => attributes[:credit_card_number],
83
+ :ccExpMonth => "%02d" % attributes[:credit_card_expiration_month],
84
+ :ccExpYear => attributes[:credit_card_expiration_year],
85
+ :ccZip => attributes[:billing_zip_code],
86
+ }
87
+ end
88
+
89
+ def self.attributes_from_api(attributes)
90
+ {
91
+ :id => attributes['id'],
92
+ :canceled_at => attributes['canceledDatetime'],
93
+ :created_at => attributes['createdDatetime'],
94
+ :credit_card_expiration_date => attributes['ccExpirationDate'],
95
+ :credit_card_last_four_digits => attributes['ccLastFour'],
96
+ :credit_card_type => attributes['ccType'],
97
+ }
98
+ end
99
+ end
100
+ end