pippin 0.1.1 → 1.0.0

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.
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ script: bundle exec rspec spec
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
data/Gemfile CHANGED
@@ -3,5 +3,5 @@ source :rubygems
3
3
  gemspec
4
4
 
5
5
  gem 'combustion',
6
- :git => 'git://github.com/freelancing-god/combustion.git',
6
+ :git => 'git://github.com/pat/combustion.git',
7
7
  :ref => 'd7a6836269a8fb528bc3721e9b8c06b5a62ef3cc'
data/HISTORY CHANGED
@@ -1,3 +1,8 @@
1
+ 1.0.0 - September 25th 2012
2
+ * Rails/ActiveSupport 3.1 or better.
3
+ * No listener - use ActiveSupport::Notifications instead.
4
+ * No controller - just as easy running things as a simple Rack app.
5
+
1
6
  0.1.1 - January 4th 2012
2
7
  * Don't require a specific version of Rails, just 3.0 or better.
3
8
 
@@ -2,25 +2,37 @@ h1. Pippin
2
2
 
3
3
  Pippin is a Rails Engine for processing PayPal IPN requests. It automatically adds a route to your Rails application that validates and processes IPNs.
4
4
 
5
- If you want to do something with those IPN objects (and I recommend you do), then all you need to do is attach a listener, and that'll fire as each valid IPN is received.
5
+ If you want to do something with those IPN objects (and I recommend you do), then all you need to do is attach a subscriber to the notification, and that'll fire as each valid IPN is received.
6
+
7
+ *Using Rails 3.0?* Then you better jump back to the "0.1.1 release":https://github.com/pat/pippin/tree/v0.1.1 of this library, which does not use ActiveSupport::Notifications.
6
8
 
7
9
  h2. Installation and Usage
8
10
 
9
11
  Just add it to your Gemfile:
10
12
 
11
- <pre><code>gem 'pippin', '~> 0.1'</code></pre>
13
+ <pre><code>gem 'pippin', '~> 1.0.0'</code></pre>
12
14
 
13
- Then, in an initializer, add your listener:
15
+ Then somewhere (perhaps in an initializer), subscribe to the notifications:
14
16
 
15
- <pre><code># config/initializers/pippin.rb
16
- Pippin.listener = lambda { |ipn|
17
+ <pre><code>ActiveSupport::Notifications.subscribe('received.ipn') do |*args|
18
+ event = ActiveSupport::Notifications::Event.new(*args)
19
+ ipn = event.payload[:ipn]
17
20
  # use IPN data
18
21
  ipn.params # => {'business' => 'email@domain.com', 'txn_type' => ...}
19
- }</code></pre>
22
+ end</code></pre>
23
+
24
+ There are also notifications fired using the txn_type parameter as the prefix, if you wish to subscribe to certain types:
25
+
26
+ <pre><code>ActiveSupport::Notifications.subscribe('subscr_signup.ipn') do |*args|
27
+ event = ActiveSupport::Notifications::Event.new(*args)
28
+ ipn = event.payload[:ipn]
29
+
30
+ # A subscription sign up has happened. Do something!
31
+ end</code></pre>
20
32
 
21
33
  Any parameters with the suffix '_date' in their name will automatically be translated from PayPal's ugly date string to an appropriate Time object.
22
34
 
23
- When constructing your form that redirects people to PayPal, you'll want to set the @notify_url@ parameter to use Pippin's URL that has automatically been added to your routes: @pippin_ipns_url@.
35
+ When constructing your form that redirects people to PayPal, you'll want to set the @notify_url@ parameter to use Pippin's URL that has automatically been added to your routes: @'pippin/ipns'@.
24
36
 
25
37
  h2. Contributing
26
38
 
@@ -1,5 +1,3 @@
1
1
  Rails.application.routes.draw do
2
- namespace :pippin do
3
- resources :ipns, :only => [:create]
4
- end
2
+ mount Pippin::App.new => '/pippin/ipns'
5
3
  end
@@ -1,15 +1,11 @@
1
- require 'rails'
1
+ require 'active_support/notifications'
2
+ require 'active_support/core_ext/module/delegation'
2
3
 
3
4
  module Pippin
4
- def self.listener
5
- @listener
6
- end
7
-
8
- def self.listener=(listener)
9
- @listener = listener
10
- end
5
+ #
11
6
  end
12
7
 
13
- require 'pippin/engine'
8
+ require 'pippin/app'
14
9
  require 'pippin/ipn'
15
- require 'pippin/version'
10
+
11
+ require 'pippin/engine' if defined?(Rails)
@@ -0,0 +1,17 @@
1
+ class Pippin::App
2
+ delegate :instrument, :to => ActiveSupport::Notifications
3
+
4
+ def call(env)
5
+ request = Rack::Request.new(env)
6
+ ipn = Pippin::IPN.new request.params, request.body.read
7
+
8
+ if ipn.valid?
9
+ instrument 'received.ipn', :ipn => ipn
10
+ instrument "#{ipn.params['txn_type']}.ipn", :ipn => ipn
11
+
12
+ [200, {}, [' ']]
13
+ else
14
+ [400, {}, [' ']]
15
+ end
16
+ end
17
+ end
@@ -1,4 +1,3 @@
1
1
  class Pippin::Engine < Rails::Engine
2
- paths['app/controllers'] << 'app/controllers'
3
2
  paths['config'] << 'config'
4
3
  end
@@ -38,6 +38,9 @@ class Pippin::IPN
38
38
  end
39
39
 
40
40
  def paypal_uri
41
- "https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate&#{body}"
41
+ domain = 'www.paypal.com'
42
+ domain = 'www.sandbox.paypal.com' if params['test_ipn'] == '1'
43
+
44
+ "https://#{domain}/cgi-bin/webscr?cmd=_notify-validate&#{body}"
42
45
  end
43
46
  end
@@ -1,10 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  $:.push File.expand_path('../lib', __FILE__)
3
- require 'pippin/version'
4
3
 
5
4
  Gem::Specification.new do |s|
6
5
  s.name = 'pippin'
7
- s.version = Pippin::VERSION
6
+ s.version = '1.0.0'
8
7
  s.authors = ['Pat Allan']
9
8
  s.email = ['pat@freelancing-gods.com']
10
9
  s.homepage = ''
@@ -18,7 +17,7 @@ Gem::Specification.new do |s|
18
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
18
  s.require_paths = ['lib']
20
19
 
21
- s.add_runtime_dependency 'rails', '>= 3.0'
20
+ s.add_runtime_dependency 'activesupport', '>= 3.1'
22
21
 
23
22
  s.add_development_dependency 'fakeweb', '1.3.0'
24
23
  s.add_development_dependency 'fakeweb-matcher', '1.2.2'
@@ -22,6 +22,18 @@ describe Pippin::IPN do
22
22
  'https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate&foo=bar')
23
23
  end
24
24
 
25
+ it "checks the validity in the sandbox if it is a test IPN" do
26
+ FakeWeb.register_uri :get,
27
+ /^https:\/\/www\.sandbox\.paypal\.com\/cgi-bin\/webscr/,
28
+ :body => 'VERIFIED'
29
+
30
+ ipn = Pippin::IPN.new({'test_ipn' => '1'}, 'foo=bar')
31
+ ipn.valid?
32
+
33
+ FakeWeb.should have_requested(:get,
34
+ 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate&foo=bar')
35
+ end
36
+
25
37
  it "returns true if PayPal confirms the validity of the data" do
26
38
  FakeWeb.register_uri :get, /^https:\/\/www\.paypal\.com\/cgi-bin\/webscr/,
27
39
  :body => 'VERIFIED'
@@ -13,12 +13,16 @@ describe 'IPN Submission' do
13
13
  response.status.should == 200
14
14
  end
15
15
 
16
- it "calls the provided listener" do
16
+ it "calls the provided subscriber" do
17
17
  ipn_lodged = false
18
- Pippin.listener = lambda { |ipn| ipn_lodged = true }
18
+ sub = ActiveSupport::Notifications.subscribe('received.ipn') { |payload|
19
+ ipn_lodged = true
20
+ }
19
21
 
20
22
  post '/pippin/ipns'
21
23
 
24
+ ActiveSupport::Notifications.unsubscribe sub
25
+
22
26
  ipn_lodged.should be_true
23
27
  end
24
28
  end
@@ -37,10 +41,14 @@ describe 'IPN Submission' do
37
41
 
38
42
  it "does not call the provided listener" do
39
43
  ipn_lodged = false
40
- Pippin.listener = lambda { |ipn| ipn_lodged = true }
44
+ sub = ActiveSupport::Notifications.subscribe('received.ipn') { |payload|
45
+ ipn_lodged = true
46
+ }
41
47
 
42
48
  post '/pippin/ipns'
43
49
 
50
+ ActiveSupport::Notifications.unsubscribe sub
51
+
44
52
  ipn_lodged.should be_false
45
53
  end
46
54
  end
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'bundler'
3
+ require 'rails'
3
4
 
4
5
  Bundler.require :default, :development
5
6
 
metadata CHANGED
@@ -1,125 +1,164 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: pippin
3
- version: !ruby/object:Gem::Version
4
- version: 0.1.1
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
5
  prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Pat Allan
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-01-04 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rails
16
- requirement: &70102267855040 !ruby/object:Gem::Requirement
17
+
18
+ date: 2012-09-25 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activesupport
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
17
24
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '3.0'
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 5
29
+ segments:
30
+ - 3
31
+ - 1
32
+ version: "3.1"
22
33
  type: :runtime
23
- prerelease: false
24
- version_requirements: *70102267855040
25
- - !ruby/object:Gem::Dependency
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
26
36
  name: fakeweb
27
- requirement: &70102277736820 !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
28
39
  none: false
29
- requirements:
30
- - - =
31
- - !ruby/object:Gem::Version
40
+ requirements:
41
+ - - "="
42
+ - !ruby/object:Gem::Version
43
+ hash: 27
44
+ segments:
45
+ - 1
46
+ - 3
47
+ - 0
32
48
  version: 1.3.0
33
49
  type: :development
34
- prerelease: false
35
- version_requirements: *70102277736820
36
- - !ruby/object:Gem::Dependency
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
37
52
  name: fakeweb-matcher
38
- requirement: &70102277735200 !ruby/object:Gem::Requirement
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
39
55
  none: false
40
- requirements:
41
- - - =
42
- - !ruby/object:Gem::Version
56
+ requirements:
57
+ - - "="
58
+ - !ruby/object:Gem::Version
59
+ hash: 27
60
+ segments:
61
+ - 1
62
+ - 2
63
+ - 2
43
64
  version: 1.2.2
44
65
  type: :development
45
- prerelease: false
46
- version_requirements: *70102277735200
47
- - !ruby/object:Gem::Dependency
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
48
68
  name: rspec-rails
49
- requirement: &70102277734220 !ruby/object:Gem::Requirement
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
50
71
  none: false
51
- requirements:
52
- - - =
53
- - !ruby/object:Gem::Version
72
+ requirements:
73
+ - - "="
74
+ - !ruby/object:Gem::Version
75
+ hash: 19
76
+ segments:
77
+ - 2
78
+ - 7
79
+ - 0
54
80
  version: 2.7.0
55
81
  type: :development
56
- prerelease: false
57
- version_requirements: *70102277734220
58
- - !ruby/object:Gem::Dependency
82
+ version_requirements: *id004
83
+ - !ruby/object:Gem::Dependency
59
84
  name: sqlite3
60
- requirement: &70102277733580 !ruby/object:Gem::Requirement
85
+ prerelease: false
86
+ requirement: &id005 !ruby/object:Gem::Requirement
61
87
  none: false
62
- requirements:
63
- - - =
64
- - !ruby/object:Gem::Version
88
+ requirements:
89
+ - - "="
90
+ - !ruby/object:Gem::Version
91
+ hash: 17
92
+ segments:
93
+ - 1
94
+ - 3
95
+ - 5
65
96
  version: 1.3.5
66
97
  type: :development
67
- prerelease: false
68
- version_requirements: *70102277733580
69
- description: Accepts IPN requests, validates them and passes them on to a defined
70
- listener.
71
- email:
98
+ version_requirements: *id005
99
+ description: Accepts IPN requests, validates them and passes them on to a defined listener.
100
+ email:
72
101
  - pat@freelancing-gods.com
73
102
  executables: []
103
+
74
104
  extensions: []
105
+
75
106
  extra_rdoc_files: []
76
- files:
107
+
108
+ files:
77
109
  - .gitignore
110
+ - .travis.yml
78
111
  - Gemfile
79
112
  - HISTORY
80
113
  - LICENCE
81
114
  - README.textile
82
115
  - Rakefile
83
- - app/controllers/pippin/ipns_controller.rb
84
116
  - config.ru
85
117
  - config/routes.rb
86
118
  - lib/pippin.rb
119
+ - lib/pippin/app.rb
87
120
  - lib/pippin/engine.rb
88
121
  - lib/pippin/ipn.rb
89
- - lib/pippin/version.rb
90
122
  - pippin.gemspec
91
- - spec/controllers/pippin/ipns_controller_spec.rb
92
123
  - spec/internal/log/.gitignore
93
124
  - spec/internal/public/favicon.ico
94
125
  - spec/pippin/ipn_spec.rb
95
126
  - spec/requests/ipn_submission_spec.rb
96
127
  - spec/spec_helper.rb
97
- homepage: ''
128
+ homepage: ""
98
129
  licenses: []
130
+
99
131
  post_install_message:
100
132
  rdoc_options: []
101
- require_paths:
133
+
134
+ require_paths:
102
135
  - lib
103
- required_ruby_version: !ruby/object:Gem::Requirement
136
+ required_ruby_version: !ruby/object:Gem::Requirement
104
137
  none: false
105
- requirements:
106
- - - ! '>='
107
- - !ruby/object:Gem::Version
108
- version: '0'
109
- required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ hash: 3
142
+ segments:
143
+ - 0
144
+ version: "0"
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
146
  none: false
111
- requirements:
112
- - - ! '>='
113
- - !ruby/object:Gem::Version
114
- version: '0'
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ hash: 3
151
+ segments:
152
+ - 0
153
+ version: "0"
115
154
  requirements: []
155
+
116
156
  rubyforge_project: pippin
117
- rubygems_version: 1.8.10
157
+ rubygems_version: 1.8.16
118
158
  signing_key:
119
159
  specification_version: 3
120
160
  summary: A PayPal Rails Engine that handles IPNs
121
- test_files:
122
- - spec/controllers/pippin/ipns_controller_spec.rb
161
+ test_files:
123
162
  - spec/internal/log/.gitignore
124
163
  - spec/internal/public/favicon.ico
125
164
  - spec/pippin/ipn_spec.rb
@@ -1,16 +0,0 @@
1
- class Pippin::IpnsController < ActionController::Base
2
- def create
3
- if ipn.valid?
4
- Pippin.listener.call ipn if Pippin.listener
5
- head :ok
6
- else
7
- head :bad_request
8
- end
9
- end
10
-
11
- private
12
-
13
- def ipn
14
- @ipn ||= Pippin::IPN.new params, request.body.read
15
- end
16
- end
@@ -1,3 +0,0 @@
1
- module Pippin
2
- VERSION = '0.1.1'
3
- end
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Pippin::IpnsController do
4
- describe '#create' do
5
- let(:ipn) { double('IPN') }
6
- let(:block) { double('block', :call => true) }
7
-
8
- before :each do
9
- Pippin::IPN.stub :new => ipn
10
- Pippin.stub :listener => block
11
- end
12
-
13
- context 'valid IPN' do
14
- before :each do
15
- ipn.stub :valid? => true
16
- end
17
-
18
- it "responds with 200" do
19
- post :create
20
-
21
- response.status.should == 200
22
- end
23
-
24
- it "calls the listener with the IPN object" do
25
- block.should_receive(:call).with(ipn)
26
-
27
- post :create
28
- end
29
- end
30
-
31
- context 'invalid IPN' do
32
- before :each do
33
- ipn.stub :valid? => false
34
- end
35
-
36
- it "responds with a 400" do
37
- post :create
38
-
39
- response.status.should == 400
40
- end
41
-
42
- it "does not call the listener" do
43
- block.should_not_receive(:call)
44
-
45
- post :create
46
- end
47
- end
48
- end
49
- end