wvanbergen-adyen 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.
- data/LICENSE +20 -0
- data/README.rdoc +100 -0
- data/Rakefile +3 -0
- data/init.rb +1 -0
- data/lib/adyen.rb +31 -0
- data/lib/adyen/form.rb +58 -0
- data/lib/adyen/matchers.rb +105 -0
- data/spec/form_spec.rb +88 -0
- data/spec/spec_helper.rb +11 -0
- data/tasks/github-gem.rake +255 -0
- metadata +84 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Willem van Bergen and Michel Barbosa
|
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,100 @@
|
|
1
|
+
= Adyen
|
2
|
+
|
3
|
+
Package to simplify including the Adyen payments services into a Ruby on Rails application.
|
4
|
+
Currently, the package contains functions to easily generate the required hidden fields and
|
5
|
+
matchers to easily check your views using rspec.
|
6
|
+
|
7
|
+
* For more information about Adyen, see http://www.adyen.com
|
8
|
+
* For more information about integrating Adyen, see their manuals at
|
9
|
+
http://support.adyen.com/links/documentation
|
10
|
+
|
11
|
+
== Skins
|
12
|
+
|
13
|
+
Adyen using the notion of "skins" to determine what payment methods should be available
|
14
|
+
and what the payment environment should look like. At least one skin is required, which
|
15
|
+
can be created in the merchant area of Adyen. For every key, a shared secret is generated
|
16
|
+
that is required to sign payment forms. You will need to provide this secret as the
|
17
|
+
:shared_secret field to the hidden_fields method (see below).
|
18
|
+
|
19
|
+
== Building payment forms
|
20
|
+
|
21
|
+
<% form_tag(:url => Adyen::Form.url) do %>
|
22
|
+
<%= Adyen::Form.hidden_fields(:merchant_account => 'myaccount', ... ,
|
23
|
+
:skin_code => 'myperfectskin', :shared_secret => 'youllneverguess')
|
24
|
+
...
|
25
|
+
<%= submit_tag('pay') %>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
Please refer to the Adyen integration manual for all the
|
29
|
+
|
30
|
+
<tt>Adyen::Form.url</tt> will return the URL to the live environment of Adyen in production
|
31
|
+
mode, otherwise it will return the testing environment. To override this behavior, use:
|
32
|
+
|
33
|
+
<% form_tag(:url => Adyen::Form.url('live')) do %>
|
34
|
+
...
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<tt>Adyen::Form.hidden_fields</tt> will generate the hidden fields for the key/value pairs
|
38
|
+
you provide to the function. The keys will be camelized automatically. Some notes:
|
39
|
+
|
40
|
+
* <tt>:recurring => true</tt> will be translated to <tt>:recurringContract => 'DEFAULT'</tt>.
|
41
|
+
* <tt>:order_data</tt> will be encoded using gzip/base64.
|
42
|
+
* <tt>:shared_secret</tt> must be provided to calculate the merchant signature.
|
43
|
+
* <tt>:merchant_sig</tt> will be computed automatically using this secret.
|
44
|
+
|
45
|
+
== Testing payment forms using rspec matchers
|
46
|
+
|
47
|
+
First, make sure that the Adyen matchers are available in your view specs:
|
48
|
+
|
49
|
+
Spec::Runner.configure do |config|
|
50
|
+
...
|
51
|
+
config.include Adyen::Matchers, :type => :views
|
52
|
+
...
|
53
|
+
end
|
54
|
+
|
55
|
+
To check the response in a view spec, use the <tt>have_adyen_payment_form</tt>,
|
56
|
+
<tt>have_adyen_recurrent_payment_form</tt> and <tt>have_adyen_single_payment_form matchers</tt>.
|
57
|
+
By passing a hash, you can check the values of the hidden fields. By passing :anything
|
58
|
+
as value, the matcher will simply check if the hidden field exists and ignore its value.
|
59
|
+
|
60
|
+
Some example specs:
|
61
|
+
|
62
|
+
before(:each) do
|
63
|
+
render 'payments/new.html.erb'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should contain an Adyen payment form" do
|
67
|
+
# either single or recurring
|
68
|
+
response.should have_adyen_payment_form(:currency_code => 'EUR', :payment_amount => 1000)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should contain an Adyen recurrent payment form" do
|
72
|
+
response.should have_adyen_recurrent_payment_form
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should contain an Adyen recurrent payment form" do
|
76
|
+
response.should have_adyen_single_payment_form(:merchant_reference => :anything)
|
77
|
+
end
|
78
|
+
|
79
|
+
== Testing payment forms using assertions
|
80
|
+
|
81
|
+
To use the assertions in unit tests, first include the matchers module in your test class:
|
82
|
+
|
83
|
+
class PaymentControllerTest < Test::Unit
|
84
|
+
include Adyen::Matchers
|
85
|
+
...
|
86
|
+
|
87
|
+
Use the assertion methods <tt>assert_adyen_payment_form</tt>, <tt>assert_adyen_single_payment_form</tt>
|
88
|
+
and <tt>assert_adyen_recurring_payment_form</tt>. They work similarly to the RSpec matcher methods
|
89
|
+
described above. An example:
|
90
|
+
|
91
|
+
def test_payment_form
|
92
|
+
get new_payment_path
|
93
|
+
assert_adyen_payment_form(@response, :currency_code => 'EUR', :payment_amount => 1000)
|
94
|
+
end
|
95
|
+
|
96
|
+
== About
|
97
|
+
|
98
|
+
This package is written by Michel Barbosa and Willem van Bergen for Floorplanner.com,
|
99
|
+
and made public under the MIT license (see LICENSE). It comes without warranty of any kind,
|
100
|
+
so use at your own risk.
|
data/Rakefile
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'adyen'
|
data/lib/adyen.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Adyen
|
2
|
+
LIVE_RAILS_ENVIRONMENTS = ['production']
|
3
|
+
|
4
|
+
# Setter voor the current Adyen environment.
|
5
|
+
# Must be either 'test' or 'live'
|
6
|
+
def self.environment=(env)
|
7
|
+
@environment = env
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the current Adyen environment.
|
11
|
+
# Returns either 'test' or 'live'.
|
12
|
+
def self.environment(override = nil)
|
13
|
+
override || @environment || Adyen.autodetect_environment
|
14
|
+
end
|
15
|
+
|
16
|
+
# Autodetects the Adyen environment based on the RAILS_ENV constant
|
17
|
+
def self.autodetect_environment
|
18
|
+
(defined?(RAILS_ENV) && Adyen::LIVE_RAILS_ENVIRONMENTS.include?(RAILS_ENV.to_s.downcase)) ? 'live' : 'test'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Loads submodules on demand, so that dependencies are not required.
|
22
|
+
def self.const_missing(sym)
|
23
|
+
require "adyen/#{sym.to_s.downcase}"
|
24
|
+
return Adyen.const_get(sym)
|
25
|
+
rescue
|
26
|
+
super(sym)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'adyen/encoding'
|
31
|
+
require 'adyen/formatter'
|
data/lib/adyen/form.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
|
3
|
+
module Adyen
|
4
|
+
module Form
|
5
|
+
|
6
|
+
extend ActionView::Helpers::TagHelper
|
7
|
+
|
8
|
+
ACTION_URL = "https://%s.adyen.com/hpp/select.shtml"
|
9
|
+
|
10
|
+
def self.url(environment = nil)
|
11
|
+
environment ||= Adyen.environment(environment)
|
12
|
+
Adyen::Form::ACTION_URL % environment.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.calculate_signature_string(attributes)
|
16
|
+
merchant_sig_string = ""
|
17
|
+
merchant_sig_string << attributes[:payment_amount].to_s << attributes[:currency_code].to_s <<
|
18
|
+
attributes[:ship_before_date].to_s << attributes[:merchant_reference].to_s <<
|
19
|
+
attributes[:skin_code].to_s << attributes[:merchant_account].to_s <<
|
20
|
+
attributes[:session_validity].to_s << attributes[:shopper_email].to_s <<
|
21
|
+
attributes[:shopper_reference].to_s << attributes[:recurring_contract].to_s <<
|
22
|
+
attributes[:allowed_methods].to_s << attributes[:blocked_methods].to_s <<
|
23
|
+
attributes[:shopper_statement].to_s << attributes[:billing_address_type].to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.calculate_signature(attributes)
|
27
|
+
Adyen::Encoding.hmac_base64(attributes.delete(:shared_secret), calculate_signature_string(attributes))
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.do_attribute_transformations!(attributes = {})
|
31
|
+
raise "YENs are not yet supported!" if attributes[:currency_code] == 'JPY' # TODO: fixme
|
32
|
+
|
33
|
+
attributes[:recurring_contract] = 'DEFAULT' if attributes.delete(:recurring) == true
|
34
|
+
attributes[:order_data] = Adyen::Encoding.gzip_base64(attributes.delete(:order_data_raw)) if attributes[:order_data_raw]
|
35
|
+
attributes[:ship_before_date] = Adyen::Formatter::DateTime.fmt_date(attributes[:ship_before_date])
|
36
|
+
attributes[:session_validity] = Adyen::Formatter::DateTime.fmt_time(attributes[:session_validity])
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.hidden_fields(attributes = {})
|
40
|
+
do_attribute_transformations!(attributes)
|
41
|
+
|
42
|
+
raise "Cannot generate form: :currency code attribute not found!" unless attributes[:currency_code]
|
43
|
+
raise "Cannot generate form: :payment_amount code attribute not found!" unless attributes[:payment_amount]
|
44
|
+
raise "Cannot generate form: :merchant_account attribute not found!" unless attributes[:merchant_account]
|
45
|
+
raise "Cannot generate form: :skin_code attribute not found!" unless attributes[:skin_code]
|
46
|
+
raise "Cannot generate form: :shared_secret signing secret not provided!" unless attributes[:shared_secret]
|
47
|
+
|
48
|
+
# Merchant signature
|
49
|
+
attributes[:merchant_sig] = calculate_signature(attributes)
|
50
|
+
|
51
|
+
# Generate hidden input tags
|
52
|
+
attributes.map { |key, value|
|
53
|
+
self.tag(:input, :type => 'hidden', :name => key.to_s.camelize(:lower), :value => value)
|
54
|
+
}.join("\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'xml'
|
2
|
+
|
3
|
+
module Adyen
|
4
|
+
module Matchers
|
5
|
+
|
6
|
+
module XPathPaymentFormCheck
|
7
|
+
|
8
|
+
def self.build_xpath_query(checks)
|
9
|
+
# Start by finding the check for the Adyen form tag
|
10
|
+
xpath_query = "//form[@action='#{Adyen::Form.url}']"
|
11
|
+
|
12
|
+
# Add recurring/single check if specified
|
13
|
+
recurring = checks.delete(:recurring)
|
14
|
+
unless recurring.nil?
|
15
|
+
if recurring
|
16
|
+
xpath_query << "[descendant::input[@type='hidden'][@name='recurringContract']]"
|
17
|
+
else
|
18
|
+
xpath_query << "[not(descendant::input[@type='hidden'][@name='recurringContract'])]"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add a check for all the other fields specified
|
23
|
+
checks.each do |key, value|
|
24
|
+
condition = "descendant::input[@type='hidden'][@name='#{key.to_s.camelize(:lower)}']"
|
25
|
+
condition << "[@value='#{value}']" unless value == :anything
|
26
|
+
xpath_query << "[#{condition}]"
|
27
|
+
end
|
28
|
+
|
29
|
+
return xpath_query
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.document(subject)
|
33
|
+
if String === subject
|
34
|
+
XML::HTMLParser.string(subject).parse
|
35
|
+
elsif subject.respond_to?(:body)
|
36
|
+
XML::HTMLParser.string(subject.body).parse
|
37
|
+
elsif XML::Node === subject
|
38
|
+
subject
|
39
|
+
elsif XML::Document === subject
|
40
|
+
subject
|
41
|
+
else
|
42
|
+
raise "Cannot handle this XML input type"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.check(subject, checks = {})
|
47
|
+
document(subject).find_first(build_xpath_query(checks))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class HaveAdyenPaymentForm
|
52
|
+
|
53
|
+
def initialize(checks)
|
54
|
+
@checks = checks
|
55
|
+
end
|
56
|
+
|
57
|
+
def matches?(document)
|
58
|
+
Adyen::Matchers::XPathPaymentFormCheck.check(document, @checks)
|
59
|
+
end
|
60
|
+
|
61
|
+
def description
|
62
|
+
"have an adyen payment form"
|
63
|
+
end
|
64
|
+
|
65
|
+
def failure_message
|
66
|
+
"expected to find a valid Adyen form on this page"
|
67
|
+
end
|
68
|
+
|
69
|
+
def negative_failure_message
|
70
|
+
"expected not to find a valid Adyen form on this page"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def have_adyen_payment_form(checks = {})
|
75
|
+
default_checks = {:merchant_sig => :anything, :payment_amount => :anything, :currency_code => :anything, :skin_code => :anything }
|
76
|
+
HaveAdyenPaymentForm.new(default_checks.merge(checks))
|
77
|
+
end
|
78
|
+
|
79
|
+
def have_adyen_recurring_payment_form(checks = {})
|
80
|
+
recurring_checks = { :recurring => true, :shopper_email => :anything, :shopper_reference => :anything }
|
81
|
+
have_adyen_payment_form(recurring_checks.merge(checks))
|
82
|
+
end
|
83
|
+
|
84
|
+
def have_adyen_single_payment_form(checks = {})
|
85
|
+
recurring_checks = { :recurring => false }
|
86
|
+
have_adyen_payment_form(recurring_checks.merge(checks))
|
87
|
+
end
|
88
|
+
|
89
|
+
def assert_adyen_payment_form(subject, checks = {})
|
90
|
+
default_checks = {:merchant_sig => :anything, :payment_amount => :anything, :currency_code => :anything, :skin_code => :anything }
|
91
|
+
assert Adyen::Matchers::XPathPaymentFormCheck.check(subject, default_checks.merge(checks)), 'No Adyen payment form found'
|
92
|
+
end
|
93
|
+
|
94
|
+
def assert_adyen_recurring_payment_form(subject, checks = {})
|
95
|
+
recurring_checks = { :recurring => true, :shopper_email => :anything, :shopper_reference => :anything }
|
96
|
+
assert_adyen_payment_form(subject, recurring_checks.merge(checks))
|
97
|
+
end
|
98
|
+
|
99
|
+
def assert_adyen_single_payment_form(subject, checks = {})
|
100
|
+
recurring_checks = { :recurring => false }
|
101
|
+
assert_adyen_payment_form(subject, recurring_checks.merge(checks))
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
data/spec/form_spec.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/spec_helper.rb"
|
2
|
+
|
3
|
+
describe Adyen::Form do
|
4
|
+
|
5
|
+
describe 'Action URLs' do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
# Use autodetection for the environment unless otherwise specified
|
9
|
+
Adyen.environment = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should generate correct the testing url" do
|
13
|
+
Adyen::Form.url.should eql('https://test.adyen.com/hpp/select.shtml')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should generate a live url if the environemtn is set top live" do
|
17
|
+
Adyen.environment = :live
|
18
|
+
Adyen::Form.url.should eql('https://live.adyen.com/hpp/select.shtml')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should generate correct live url in a production environment" do
|
22
|
+
Adyen.stub!(:autodetect_environment).and_return('live')
|
23
|
+
Adyen::Form.url.should eql('https://live.adyen.com/hpp/select.shtml')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should generate correct live url if explicitely asked for" do
|
27
|
+
Adyen::Form.url(:live).should eql('https://live.adyen.com/hpp/select.shtml')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'hidden fields generation' do
|
32
|
+
|
33
|
+
include ActionView::Helpers::TagHelper
|
34
|
+
|
35
|
+
before(:each) do
|
36
|
+
@attributes = { :currency_code => 'GBP', :payment_amount => 10000, :ship_before_date => Date.today,
|
37
|
+
:merchant_reference => 'Internet Order 12345', :skin_code => '4aD37dJA',
|
38
|
+
:merchant_account => 'TestMerchant', :session_validity => 1.hour.from_now }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should generate a valid payment form" do
|
42
|
+
content_tag(:form, Adyen::Form.hidden_fields(@attributes.merge(:shared_secret => 'secret')),
|
43
|
+
:action => Adyen::Form.url, :method => :post).should have_adyen_payment_form
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'signature calculation' do
|
48
|
+
|
49
|
+
# This example is taken from the Adyen integration manual
|
50
|
+
|
51
|
+
before(:each) do
|
52
|
+
@attributes = { :currency_code => 'GBP', :payment_amount => 10000,
|
53
|
+
:ship_before_date => '2007-10-20', :merchant_reference => 'Internet Order 12345',
|
54
|
+
:skin_code => '4aD37dJA', :merchant_account => 'TestMerchant',
|
55
|
+
:session_validity => '2007-10-11T11:00:00Z' }
|
56
|
+
|
57
|
+
Adyen::Form.do_attribute_transformations!(@attributes)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should construct the signature string correctly" do
|
61
|
+
signature_string = Adyen::Form.calculate_signature_string(@attributes)
|
62
|
+
signature_string.should eql("10000GBP2007-10-20Internet Order 123454aD37dJATestMerchant2007-10-11T11:00:00Z")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should calculate the signature correctly" do
|
66
|
+
signature = Adyen::Form.calculate_signature(@attributes.merge(:shared_secret => 'Kah942*$7sdp0)'))
|
67
|
+
signature.should eql('x58ZcRVL1H6y+XSeBGrySJ9ACVo=')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should calculate the signature correctly for a recurring payment" do
|
71
|
+
# Add the required recurrent payment attributes
|
72
|
+
@attributes.merge!(:recurring_contract => 'DEFAULT', :shopper_reference => 'grasshopper52', :shopper_email => 'gras.shopper@somewhere.org')
|
73
|
+
|
74
|
+
signature_string = Adyen::Form.calculate_signature_string(@attributes)
|
75
|
+
signature_string.should eql("10000GBP2007-10-20Internet Order 123454aD37dJATestMerchant2007-10-11T11:00:00Zgras.shopper@somewhere.orggrasshopper52DEFAULT")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should calculate the signature correctly for a recurring payment" do
|
79
|
+
# Add the required recurrent payment attributes
|
80
|
+
@attributes.merge!(:recurring_contract => 'DEFAULT', :shopper_reference => 'grasshopper52', :shopper_email => 'gras.shopper@somewhere.org')
|
81
|
+
|
82
|
+
signature = Adyen::Form.calculate_signature(@attributes.merge(:shared_secret => 'Kah942*$7sdp0)'))
|
83
|
+
signature.should eql('F2BQEYbE+EUhiRGuPtcD16Gm7JY=')
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubyforge'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/tasklib'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
module Rake
|
8
|
+
|
9
|
+
class GithubGem < TaskLib
|
10
|
+
|
11
|
+
attr_accessor :name
|
12
|
+
attr_accessor :specification
|
13
|
+
|
14
|
+
def self.define_tasks!
|
15
|
+
gem_task_builder = Rake::GithubGem.new
|
16
|
+
gem_task_builder.register_all_tasks!
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
reload_gemspec!
|
22
|
+
end
|
23
|
+
|
24
|
+
def register_all_tasks!
|
25
|
+
namespace(:gem) do
|
26
|
+
desc "Updates the file lists for this gem"
|
27
|
+
task(:manifest) { manifest_task }
|
28
|
+
|
29
|
+
desc "Releases a new version of #{@name}"
|
30
|
+
task(:build => [:manifest]) { build_task }
|
31
|
+
|
32
|
+
|
33
|
+
release_dependencies = [:check_clean_master_branch, :version, :build, :create_tag]
|
34
|
+
release_dependencies.push 'doc:publish' if generate_rdoc?
|
35
|
+
release_dependencies.unshift 'test' if has_tests?
|
36
|
+
release_dependencies.unshift 'spec' if has_specs?
|
37
|
+
|
38
|
+
desc "Releases a new version of #{@name}"
|
39
|
+
task(:release => release_dependencies) { release_task }
|
40
|
+
|
41
|
+
# helper task for releasing
|
42
|
+
task(:check_clean_master_branch) { verify_clean_status('master') }
|
43
|
+
task(:check_version) { verify_version(ENV['VERSION'] || @specification.version) }
|
44
|
+
task(:version => [:check_version]) { set_gem_version! }
|
45
|
+
task(:create_tag) { create_version_tag! }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Register RDoc tasks
|
49
|
+
if generate_rdoc?
|
50
|
+
require 'rake/rdoctask'
|
51
|
+
|
52
|
+
namespace(:doc) do
|
53
|
+
desc 'Generate documentation for request-log-analyzer'
|
54
|
+
Rake::RDocTask.new(:compile) do |rdoc|
|
55
|
+
rdoc.rdoc_dir = 'doc'
|
56
|
+
rdoc.title = @name
|
57
|
+
rdoc.options += @specification.rdoc_options
|
58
|
+
rdoc.rdoc_files.include(@specification.extra_rdoc_files)
|
59
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Publish RDoc files for #{@name} to Github"
|
63
|
+
task(:publish => :compile) do
|
64
|
+
sh 'git checkout gh-pages'
|
65
|
+
sh 'git pull origin gh-pages'
|
66
|
+
sh 'cp -rf doc/* .'
|
67
|
+
sh "git commit -am \"Publishing newest RDoc documentation for #{@name}\""
|
68
|
+
sh "git push origin gh-pages"
|
69
|
+
sh "git checkout master"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Setup :spec task if RSpec files exist
|
75
|
+
if has_specs?
|
76
|
+
require 'spec/rake/spectask'
|
77
|
+
|
78
|
+
desc "Run all specs for #{@name}"
|
79
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
80
|
+
t.spec_files = FileList[File.dirname(__FILE__) + '/../spec/**/*_spec.rb']
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Setup :test task if unit test files exist
|
85
|
+
if has_tests?
|
86
|
+
require 'rake/testtask'
|
87
|
+
|
88
|
+
desc "Run all unit tests for #{@name}"
|
89
|
+
Rake::TestTask.new(:test) do |t|
|
90
|
+
t.pattern = File.dirname(__FILE__) + '/../test/**/*_test.rb'
|
91
|
+
t.verbose = true
|
92
|
+
t.libs << 'test'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
def generate_rdoc?
|
100
|
+
git_branch_exists?('gh-pages')
|
101
|
+
end
|
102
|
+
|
103
|
+
def has_specs?
|
104
|
+
Dir[File.dirname(__FILE__) + '/../spec/**/*_spec.rb'].any?
|
105
|
+
end
|
106
|
+
|
107
|
+
def has_tests?
|
108
|
+
Dir[File.dirname(__FILE__) + '/../test/**/*_test.rb'].any?
|
109
|
+
end
|
110
|
+
|
111
|
+
def reload_gemspec!
|
112
|
+
raise "No gemspec file found!" if gemspec_file.nil?
|
113
|
+
spec = File.read(gemspec_file)
|
114
|
+
@specification = eval(spec)
|
115
|
+
@name = specification.name
|
116
|
+
end
|
117
|
+
|
118
|
+
def run_command(command)
|
119
|
+
lines = []
|
120
|
+
IO.popen(command) { |f| lines = f.readlines }
|
121
|
+
return lines
|
122
|
+
end
|
123
|
+
|
124
|
+
def git_modified?(file)
|
125
|
+
return !run_command('git status').detect { |line| Regexp.new(Regexp.quote(file)) =~ line }.nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
def git_commit_file(file, message, branch = nil)
|
129
|
+
verify_current_branch(branch) unless branch.nil?
|
130
|
+
if git_modified?(file)
|
131
|
+
sh "git add #{file}"
|
132
|
+
sh "git commit -m \"#{message}\""
|
133
|
+
else
|
134
|
+
raise "#{file} is not modified and cannot be committed!"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def git_create_tag(tag_name, message)
|
139
|
+
sh "git tag -a \"#{tag_name}\" -m \"#{message}\""
|
140
|
+
end
|
141
|
+
|
142
|
+
def git_push(remote = 'origin', branch = 'master', options = [])
|
143
|
+
verify_clean_status(branch)
|
144
|
+
options_str = options.map { |o| "--#{o}"}.join(' ')
|
145
|
+
sh "git push #{options_str} #{remote} #{branch}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def gemspec_version=(new_version)
|
149
|
+
spec = File.read(gemspec_file)
|
150
|
+
spec.gsub!(/^(\s*s\.version\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{new_version}'#{$5}" }
|
151
|
+
spec.gsub!(/^(\s*s\.date\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{Date.today.strftime('%Y-%m-%d')}'#{$5}" }
|
152
|
+
File.open(gemspec_file, 'w') { |f| f << spec }
|
153
|
+
reload_gemspec!
|
154
|
+
end
|
155
|
+
|
156
|
+
def gemspec_date=(new_date)
|
157
|
+
spec = File.read(gemspec_file)
|
158
|
+
spec.gsub!(/^(\s*s\.date\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{new_date.strftime('%Y-%m-%d')}'#{$5}" }
|
159
|
+
File.open(gemspec_file, 'w') { |f| f << spec }
|
160
|
+
reload_gemspec!
|
161
|
+
end
|
162
|
+
|
163
|
+
def gemspec_file
|
164
|
+
@gemspec_file ||= Dir[File.dirname(__FILE__) + '/../*.gemspec'].first
|
165
|
+
end
|
166
|
+
|
167
|
+
def git_branch_exists?(branch_name)
|
168
|
+
branches = run_command('git branch').map { |line| /^\*?\s+(\w+)/ =~ line; $1 }
|
169
|
+
branches.include?(branch_name.to_s)
|
170
|
+
end
|
171
|
+
|
172
|
+
def verify_current_branch(branch)
|
173
|
+
run_command('git branch').detect { |line| /^\* (.+)/ =~ line }
|
174
|
+
raise "You are currently not working in the master branch!" unless branch == $1
|
175
|
+
end
|
176
|
+
|
177
|
+
def verify_clean_status(on_branch = nil)
|
178
|
+
sh "git fetch"
|
179
|
+
lines = run_command('git status')
|
180
|
+
raise "You don't have the most recent version available. Run git pull first." if /^\# Your branch is behind/ =~ lines[1]
|
181
|
+
raise "You are currently not working in the #{on_branch} branch!" unless on_branch.nil? || (/^\# On branch (.+)/ =~ lines.first && $1 == on_branch)
|
182
|
+
raise "Your master branch contains modifications!" unless /^nothing to commit \(working directory clean\)/ =~ lines.last
|
183
|
+
end
|
184
|
+
|
185
|
+
def verify_version(new_version)
|
186
|
+
newest_version = run_command('git tag').map { |tag| tag.split(name + '-').last }.compact.map { |v| Gem::Version.new(v) }.max
|
187
|
+
raise "This version number (#{new_version}) is not higher than the highest tagged version (#{newest_version})" if !newest_version.nil? && newest_version >= Gem::Version.new(new_version.to_s)
|
188
|
+
end
|
189
|
+
|
190
|
+
def set_gem_version!
|
191
|
+
# update gemspec file
|
192
|
+
self.gemspec_version = ENV['VERSION'] if Gem::Version.correct?(ENV['VERSION'])
|
193
|
+
self.gemspec_date = Date.today
|
194
|
+
end
|
195
|
+
|
196
|
+
def manifest_task
|
197
|
+
verify_current_branch('master')
|
198
|
+
|
199
|
+
list = Dir['**/*'].sort
|
200
|
+
list -= [gemspec_file]
|
201
|
+
|
202
|
+
if File.exist?('.gitignore')
|
203
|
+
File.read('.gitignore').each_line do |glob|
|
204
|
+
glob = glob.chomp.sub(/^\//, '')
|
205
|
+
list -= Dir[glob]
|
206
|
+
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# update the spec file
|
211
|
+
spec = File.read(gemspec_file)
|
212
|
+
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
|
213
|
+
assignment = $1
|
214
|
+
bunch = $2 ? list.grep(/^(test.*_test\.rb|spec.*_spec.rb)$/) : list
|
215
|
+
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
216
|
+
end
|
217
|
+
|
218
|
+
File.open(gemspec_file, 'w') { |f| f << spec }
|
219
|
+
reload_gemspec!
|
220
|
+
end
|
221
|
+
|
222
|
+
def build_task
|
223
|
+
sh "gem build #{gemspec_file}"
|
224
|
+
Dir.mkdir('pkg') unless File.exist?('pkg')
|
225
|
+
sh "mv #{name}-#{specification.version}.gem pkg/#{name}-#{specification.version}.gem"
|
226
|
+
end
|
227
|
+
|
228
|
+
def install_task
|
229
|
+
raise "#{name} .gem file not found" unless File.exist?("pkg/#{name}-#{specification.version}.gem")
|
230
|
+
sh "gem install pkg/#{name}-#{specification.version}.gem"
|
231
|
+
end
|
232
|
+
|
233
|
+
def uninstall_task
|
234
|
+
raise "#{name} .gem file not found" unless File.exist?("pkg/#{name}-#{specification.version}.gem")
|
235
|
+
sh "gem uninstall #{name}"
|
236
|
+
end
|
237
|
+
|
238
|
+
def create_version_tag!
|
239
|
+
# commit the gemspec file
|
240
|
+
git_commit_file(gemspec_file, "Updated #{gemspec_file} for release of version #{@specification.version}") if git_modified?(gemspec_file)
|
241
|
+
|
242
|
+
# create tag and push changes
|
243
|
+
git_create_tag("#{@name}-#{@specification.version}", "Tagged version #{@specification.version}")
|
244
|
+
git_push('origin', 'master', [:tags])
|
245
|
+
end
|
246
|
+
|
247
|
+
def release_task
|
248
|
+
puts
|
249
|
+
puts '------------------------------------------------------------'
|
250
|
+
puts "Released #{@name} - version #{@specification.version}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
Rake::GithubGem.define_tasks!
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wvanbergen-adyen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Willem van Bergen
|
8
|
+
- Michel Barbosa
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-08-06 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
description: Package to simplify including the Adyen payments services into a Ruby on Rails application.
|
27
|
+
email:
|
28
|
+
- willem@vanbergen.org
|
29
|
+
- cicaboo@gmail.com
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files:
|
35
|
+
- README.rdoc
|
36
|
+
files:
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- init.rb
|
41
|
+
- lib
|
42
|
+
- lib/adyen
|
43
|
+
- lib/adyen.rb
|
44
|
+
- lib/adyen/form.rb
|
45
|
+
- lib/adyen/matchers.rb
|
46
|
+
- spec
|
47
|
+
- spec/form_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
- tasks
|
50
|
+
- tasks/github-gem.rake
|
51
|
+
has_rdoc: false
|
52
|
+
homepage: http://www.adyen.com
|
53
|
+
licenses:
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --title
|
57
|
+
- adyen
|
58
|
+
- --main
|
59
|
+
- README.rdoc
|
60
|
+
- --line-numbers
|
61
|
+
- --inline-source
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 2
|
82
|
+
summary: Integrate Adyen payment services in you Ruby on Rails application
|
83
|
+
test_files:
|
84
|
+
- spec/form_spec.rb
|