gimme_gimme 0.2.2

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 (33) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +143 -0
  3. data/README.md +37 -0
  4. data/Rakefile +10 -0
  5. data/features/run_features.feature +40 -0
  6. data/features/step_definitions/rails_steps.rb +62 -0
  7. data/features/support/env.rb +2 -0
  8. data/features/support/file_helpers.rb +11 -0
  9. data/lib/generators/gimme_gimme/base.rb +17 -0
  10. data/lib/generators/gimme_gimme/install/install_generator.rb +34 -0
  11. data/lib/generators/gimme_gimme/install/templates/controllers/credit_cards_controller.rb +3 -0
  12. data/lib/generators/gimme_gimme/install/templates/create_gimme_gimme_tables.rb +29 -0
  13. data/lib/generators/gimme_gimme/install/templates/models/credit_card.rb +3 -0
  14. data/lib/generators/gimme_gimme/test_support/templates/factories/factories.rb +10 -0
  15. data/lib/generators/gimme_gimme/test_support/templates/support/fake_cim.rb +9 -0
  16. data/lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_gateway.rb +15 -0
  17. data/lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_server.rb +329 -0
  18. data/lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_spec_helpers.rb +21 -0
  19. data/lib/generators/gimme_gimme/test_support/test_support_generator.rb +23 -0
  20. data/lib/gimme_gimme.rb +7 -0
  21. data/lib/gimme_gimme/cim_gateway.rb +16 -0
  22. data/lib/gimme_gimme/credit_card.rb +125 -0
  23. data/lib/gimme_gimme/credit_cards_controller.rb +18 -0
  24. data/lib/gimme_gimme/engine.rb +7 -0
  25. data/lib/gimme_gimme/user.rb +43 -0
  26. data/lib/gimme_gimme/version.rb +3 -0
  27. data/spec/environment.rb +94 -0
  28. data/spec/factories/clearance.rb +8 -0
  29. data/spec/models/credit_card_spec.rb +219 -0
  30. data/spec/models/user_spec.rb +20 -0
  31. data/spec/spec_helper.rb +30 -0
  32. data/spec/testapp/config/routes.rb +4 -0
  33. metadata +226 -0
@@ -0,0 +1,18 @@
1
+ module GimmeGimme
2
+ module CreditCardsController
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_filter :authenticate
7
+ end
8
+
9
+ module InstanceMethods
10
+ def destroy
11
+ @user = current_user
12
+ credit_card = @user.credit_cards.find(params[:id])
13
+
14
+ credit_card.destroy
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ require "gimme_gimme"
2
+ require "rails/railtie"
3
+
4
+ module GimmeGimme
5
+ class Engine < Rails::Engine
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ module GimmeGimme
2
+ module User
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :credit_cards
7
+ before_validation :generate_customer_key, :create_cim_customer_profile_id, :on => :create
8
+ validates_presence_of :remote_customer_profile_id
9
+ end
10
+
11
+ module InstanceMethods
12
+ def credit_card_count
13
+ self.credit_cards.count
14
+ end
15
+
16
+ def create_cim_customer_profile_id
17
+ response = self.class.gateway.create_customer_profile(
18
+ :profile => { :merchant_customer_id => customer_key,
19
+ :description => description,
20
+ :email => email }
21
+ )
22
+ self.remote_customer_profile_id = response.authorization
23
+ end
24
+ private :create_cim_customer_profile_id
25
+
26
+ def generate_customer_key
27
+ unhashed_key = [Time.now.to_s, rand].join('--')
28
+ self.customer_key = Digest::MD5.hexdigest(unhashed_key).slice(0, 20)
29
+ end
30
+ private :generate_customer_key
31
+
32
+ def description
33
+ email
34
+ end
35
+ end
36
+
37
+ module ClassMethods
38
+ def gateway
39
+ ::GimmeGimme::CimGateway.instance.gateway
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module GimmeGimme
2
+ VERSION = '0.2.2'
3
+ end
@@ -0,0 +1,94 @@
1
+ PROJECT_ROOT = File.expand_path('../..', __FILE__)
2
+ $LOAD_PATH << File.join(PROJECT_ROOT)
3
+ $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
4
+
5
+ require 'rails/all'
6
+ require 'gimme_gimme'
7
+ require 'clearance'
8
+ require 'factory_girl'
9
+ require 'bourne'
10
+ require 'spec/factories/clearance'
11
+ require 'lib/generators/gimme_gimme/test_support/templates/factories/factories'
12
+ require 'lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_gateway'
13
+ require 'lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_server'
14
+ require 'lib/generators/gimme_gimme/test_support/templates/testing/fake_cim_spec_helpers'
15
+ require 'lib/generators/gimme_gimme/install/templates/create_gimme_gimme_tables'
16
+
17
+ class User < ActiveRecord::Base
18
+ include Clearance::User
19
+ include GimmeGimme::User
20
+ end
21
+
22
+
23
+ class CreditCard < ActiveRecord::Base
24
+ include GimmeGimme::CreditCard
25
+ end
26
+
27
+ class ApplicationController < ActionController::Base
28
+ include Clearance::Authentication
29
+ end
30
+
31
+ module Testapp
32
+ class Application < Rails::Application
33
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
34
+ config.encoding = 'utf-8'
35
+ config.paths.config.database = 'spec/testapp/config/database.yml'
36
+ config.paths.app.models << 'lib/generators/gimme_gimme/install/templates/models'
37
+ config.paths.config.routes << 'spec/testapp/config/routes.rb'
38
+ config.paths.app.views << 'spec/testapp/views'
39
+ config.paths.log = 'tmp/log'
40
+ config.cache_classes = true
41
+ config.whiny_nils = true
42
+ config.consider_all_requests_local = true
43
+ config.action_controller.perform_caching = false
44
+ config.action_dispatch.show_exceptions = false
45
+ config.action_controller.allow_forgery_protection = false
46
+ config.action_mailer.delivery_method = :test
47
+ config.active_support.deprecation = :stderr
48
+ end
49
+ end
50
+
51
+ Testapp::Application.initialize!
52
+
53
+ class ClearanceCreateUsers < ActiveRecord::Migration
54
+ def self.up
55
+ create_table(:users) do |t|
56
+ t.string :email
57
+ t.string :encrypted_password, :limit => 128
58
+ t.string :salt, :limit => 128
59
+ t.string :confirmation_token, :limit => 128
60
+ t.string :remember_token, :limit => 128
61
+ t.boolean :email_confirmed, :default => false, :null => false
62
+ t.timestamps
63
+ end
64
+
65
+ add_index :users, :email
66
+ add_index :users, :remember_token
67
+ end
68
+ end
69
+
70
+ class ClearanceMailer
71
+ def self.change_password(user)
72
+ new
73
+ end
74
+
75
+ def self.confirmation(user)
76
+ new
77
+ end
78
+
79
+ def self.deliver_change_password(user)
80
+ end
81
+
82
+ def self.deliver_confirmation(user)
83
+ end
84
+
85
+ def deliver
86
+ end
87
+ end
88
+
89
+ Clearance.configure do |config|
90
+ end
91
+
92
+ FileUtils.rm_f(File.join(PROJECT_ROOT, 'tmp', 'test.sqlite3'))
93
+ ClearanceCreateUsers.suppress_messages { ClearanceCreateUsers.migrate(:up) }
94
+ CreateGimmeGimmeTables.suppress_messages { CreateGimmeGimmeTables.migrate(:up) }
@@ -0,0 +1,8 @@
1
+ Factory.sequence :email do |n|
2
+ "user#{n}@example.com"
3
+ end
4
+
5
+ Factory.define :user do |user|
6
+ user.email { Factory.next :email }
7
+ user.password { "password" }
8
+ end
@@ -0,0 +1,219 @@
1
+ require 'spec_helper'
2
+
3
+ describe CreditCard, 'by default' do
4
+ it { should belong_to(:user) }
5
+ it { should validate_presence_of(:expiration_month) }
6
+ it { should validate_presence_of(:expiration_year) }
7
+ it { should validate_presence_of(:card_type) }
8
+ it { should allow_value('Visa').for(:card_type) }
9
+ it { should allow_value('Master Card').for(:card_type) }
10
+ it { should allow_value('American Express').for(:card_type) }
11
+ it { should_not allow_value('').for(:card_type) }
12
+ it { should ensure_inclusion_of(:expiration_month).in_range(1..12) }
13
+ end
14
+
15
+ describe CreditCard, 'being created' do
16
+ subject { Factory.build(:credit_card) }
17
+
18
+ it { should validate_presence_of(:card_number) }
19
+ it { should validate_presence_of(:security_code) }
20
+ end
21
+
22
+ describe CreditCard, 'last four digits' do
23
+ subject { Factory(:credit_card, :card_number => '4111111111110000') }
24
+ its(:last_four_digits) { should == '0000' }
25
+
26
+ it "updates with the credit card" do
27
+ subject.update_attributes(:card_number => '4111111111110001')
28
+ subject.last_four_digits.should == '0001'
29
+ end
30
+ end
31
+
32
+ describe CreditCard, 'successful payment profile id' do
33
+ it 'stores the correct payment profile id' do
34
+ fake_cim do |server|
35
+ credit_card = Factory(:credit_card)
36
+ credit_card.remote_customer_payment_profile_id.should ==
37
+ server.last_customer_payment_profile.customer_payment_profile_id.to_s
38
+ end
39
+ end
40
+
41
+ it "sends first and last name" do
42
+ fake_cim do |server|
43
+ credit_card = Factory(:credit_card)
44
+ profile = server.last_customer_payment_profile
45
+ profile.first_name.should == credit_card.first_name
46
+ profile.last_name.should == credit_card.last_name
47
+ end
48
+ end
49
+ end
50
+
51
+ describe CreditCard, "updating a payment profile id" do
52
+ it 'updates the credit card on the server' do
53
+ fake_cim do |server|
54
+ credit_card = Factory(:credit_card)
55
+ credit_card.update_attributes!(:expiration_month => 11, :first_name => 'Joe')
56
+ payment_profile =
57
+ server.customer_payment_profiles[credit_card.remote_customer_payment_profile_id.to_i]
58
+ payment_profile.should_not be_nil
59
+ payment_profile.expiration_month.should == 11
60
+ payment_profile.first_name.should == 'Joe'
61
+ end
62
+ end
63
+ end
64
+
65
+ describe CreditCard, 'failing to create a payment profile' do
66
+ it 'stores the correct payment profile id' do
67
+ fake_cim do |server|
68
+ server.register_error_for('createCustomerPaymentProfileRequest', 'bad credit card')
69
+ credit_card = Factory.build(:credit_card, :card_number => 'bad')
70
+ expect { credit_card.save }.to raise_error(CreditCard::InvalidCreditCard)
71
+ end
72
+ end
73
+ end
74
+
75
+ shared_examples_for 'rounds amount' do
76
+ it 'rounds to amount up when appropiate' do
77
+ fake_cim do |server|
78
+ credit_card = Factory(:credit_card)
79
+ action.call(credit_card, 0.185999)
80
+ server.last_transaction.amount.should == 0.19
81
+ end
82
+ end
83
+
84
+ it 'rounds to amount down when appropiate' do
85
+ fake_cim do |server|
86
+ credit_card = Factory(:credit_card)
87
+ action.call(credit_card, 0.181999)
88
+ server.last_transaction.amount.should == 0.18
89
+ end
90
+ end
91
+ end
92
+
93
+ describe CreditCard, 'successful authorization' do
94
+ it 'returns the approval code' do
95
+ fake_cim do |server|
96
+ credit_card = Factory(:credit_card)
97
+ credit_card.authorize(:amount => 200).should == server.last_transaction.authorization_code.to_s
98
+ end
99
+ end
100
+ it_behaves_like 'rounds amount' do
101
+ let(:action) do
102
+ lambda do |credit_card, amount|
103
+ credit_card.authorize(:amount => amount)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ describe CreditCard, 'failed authorization' do
110
+ subject { Factory(:credit_card) }
111
+ it 'raises AuthorizationError' do
112
+ fake_cim do |server|
113
+ server.register_error_for('createCustomerProfileTransactionRequest', 'server down or something')
114
+ lambda { subject.authorize(:amount => 200) }.
115
+ should raise_error CreditCard::AuthorizationError
116
+ end
117
+ end
118
+ end
119
+
120
+ describe CreditCard, 'successful charge' do
121
+ it 'creates the transaction with the provided amount' do
122
+ fake_cim do |server|
123
+ amount = 20.25
124
+ credit_card = Factory(:credit_card)
125
+ approval_code = credit_card.authorize(:amount => amount)
126
+ credit_card.charge(:amount => amount, :approval_code => approval_code)
127
+ transaction = server.transactions.last
128
+ transaction.should_not be_nil
129
+ transaction.card_number.should == credit_card.card_number
130
+ transaction.amount.should == amount
131
+ end
132
+ end
133
+
134
+ it_behaves_like 'rounds amount' do
135
+ let(:action) do
136
+ lambda do |credit_card, amount|
137
+ approval_code = credit_card.authorize(:amount => amount)
138
+ credit_card.charge(:amount => amount, :approval_code => approval_code)
139
+ end
140
+ end
141
+ end
142
+
143
+ it 'returns the transaction id' do
144
+ fake_cim do |server|
145
+ amount = 20.25
146
+ credit_card = Factory(:credit_card)
147
+ approval_code = credit_card.authorize(:amount => amount)
148
+ result = credit_card.charge(:amount => amount, :approval_code => approval_code)
149
+ transaction = server.last_transaction
150
+ result.should == transaction.transaction_id.to_s
151
+ end
152
+ end
153
+ end
154
+
155
+ describe CreditCard, 'failed charge' do
156
+ subject { Factory(:credit_card) }
157
+ it 'raises ChargeError' do
158
+ fake_cim do |server|
159
+ approval_code = subject.authorize(:amount => 200)
160
+ server.register_error_for('createCustomerProfileTransactionRequest', 'server down or something')
161
+ lambda { subject.charge(:amount => 200, :approval_code => approval_code) }.
162
+ should raise_error CreditCard::ChargeError
163
+ end
164
+ end
165
+ end
166
+
167
+ describe CreditCard, "unauthorized charge" do
168
+ subject { Factory(:credit_card) }
169
+ it 'raises ChargeError' do
170
+ fake_cim do |server|
171
+ lambda { subject.charge(:amount => 200, :approval_code => 'bogus') }.
172
+ should raise_error CreditCard::ChargeError
173
+ end
174
+ end
175
+ end
176
+
177
+ describe CreditCard, "charge with incorrect approval code" do
178
+ subject { Factory(:credit_card) }
179
+ it 'raises ChargeError' do
180
+ fake_cim do |server|
181
+ subject.authorize(:amount => 200)
182
+ lambda { subject.charge(:amount => 200, :approval_code => 'bogus') }.
183
+ should raise_error CreditCard::ChargeError
184
+ end
185
+ end
186
+ end
187
+
188
+ describe CreditCard, 'gateway' do
189
+ it "delegates to the CimGateway instance" do
190
+ CreditCard.gateway.should == GimmeGimme::CimGateway.instance.gateway
191
+ end
192
+ end
193
+
194
+ describe CreditCard, 'destroy' do
195
+ it 'should delete a customer payment profile when destroyed' do
196
+ fake_cim do |server|
197
+ credit_card = Factory(:credit_card)
198
+ credit_card.destroy
199
+ server.customer_payment_profiles.should be_empty
200
+ end
201
+ end
202
+ end
203
+
204
+ describe CreditCard, 'store_card?' do
205
+ subject { Factory(:credit_card) }
206
+
207
+ it 'returns false when store_card is not set' do
208
+ subject.store_card?.should be_false
209
+ end
210
+
211
+ it 'returns false when store_card is false' do
212
+ subject.store_card?.should be_false
213
+ end
214
+
215
+ it 'returns true when store_card is true' do
216
+ subject.store_card = true
217
+ subject.store_card?.should be_true
218
+ end
219
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe User do
4
+ it "adds description and email to cim" do
5
+ response = stub('response', :authorization => 'abc123')
6
+ User.gateway.stubs(:create_customer_profile => response)
7
+
8
+ user = Factory(:user)
9
+
10
+ User.gateway.should have_received(:create_customer_profile).
11
+ with(:profile => { :merchant_customer_id => user.customer_key,
12
+ :description => user.description,
13
+ :email => user.email })
14
+ end
15
+
16
+ it "defaults the description to email" do
17
+ user = Factory.build(:user)
18
+ user.description.should == user.email
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+
4
+ if File.exist?("config/environment.rb")
5
+ require "config/environment"
6
+ else
7
+ require File.expand_path("../environment", __FILE__)
8
+ end
9
+
10
+ require 'rspec/rails'
11
+ require 'shoulda'
12
+
13
+ # Requires supporting ruby files with custom matchers and macros, etc,
14
+ # in spec/support/ and its subdirectories.
15
+ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
16
+
17
+ RSpec.configure do |config|
18
+ config.mock_with :mocha
19
+
20
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
21
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
22
+
23
+ # instead of true.
24
+ config.use_transactional_fixtures = true
25
+ config.backtrace_clean_patterns << %r{gems/}
26
+ config.include GimmeGimme::FakeCimHelpers
27
+ end
28
+
29
+ server = GimmeGimme::FakeCimServer.new
30
+ GimmeGimme::CimGateway.instance.gateway = GimmeGimme::FakeCimGateway.new(:server => server)