trustcommerce 0.5.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.
- data/MIT-LICENSE +20 -0
- data/README +147 -0
- data/Rakefile +198 -0
- data/lib/trustcommerce.rb +217 -0
- data/lib/version.rb +9 -0
- data/support/rdoc/code_info.rb +211 -0
- data/test/test_helper.rb +69 -0
- data/test/trustcommerce_test.rb +149 -0
- metadata +58 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 Zack Chandler <zackchandler@depixelate.com>
|
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
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
= TrustCommerce Subscription Library
|
2
|
+
|
3
|
+
[TrustCommerce](http://www.trustcommerce.com) is a payment gateway providing credit card
|
4
|
+
processing and recurring / subscription billing services.
|
5
|
+
|
6
|
+
This library provides a simple interface to create, edit, delete, and query subscriptions
|
7
|
+
using TrustCommerce.
|
8
|
+
|
9
|
+
== Background
|
10
|
+
|
11
|
+
TrustCommerce's recurring / subscription billing solution is implemented through a service
|
12
|
+
called [Citadel](http://www.trustcommerce.com/citadel.php). A Citadel-enabled account is
|
13
|
+
required to use the subscription-based features implemented by this library.
|
14
|
+
|
15
|
+
=== Citadel Basics
|
16
|
+
* Citadel stores customer profiles which can include credit card information and billing frequency.
|
17
|
+
* Citadel will automatically bill customers on their respective schedules.
|
18
|
+
* Citadel identifies each customer by a Billing ID (six-character alphanumeric string).
|
19
|
+
* A customer's profile, credit card, and billing frequency can be modified using the Billing ID.
|
20
|
+
|
21
|
+
== Installation
|
22
|
+
|
23
|
+
The simple way:
|
24
|
+
$ sudo gem install trustcommerce
|
25
|
+
|
26
|
+
Directly from repository:
|
27
|
+
$ svn co svn://rubyforge.org/var/svn/trustcommerce/trunk trustcommerce
|
28
|
+
|
29
|
+
It is highly recommended to download and install the
|
30
|
+
[TCLink ruby extension](http://www.trustcommerce.com/tclink.php).
|
31
|
+
This extension provides failover capability and enhanced security features.
|
32
|
+
If this library is not installed, standard POST over SSL will be used.
|
33
|
+
|
34
|
+
== Configuration
|
35
|
+
|
36
|
+
When you signup for a TrustCommerce account you are issued a custid and a password.
|
37
|
+
These are your credentials when using the TrustCommerce API.
|
38
|
+
|
39
|
+
TrustCommerce.custid = '123456'
|
40
|
+
TrustCommerce.password = 'topsecret'
|
41
|
+
|
42
|
+
# optional - sets Vault password for use in query() calls
|
43
|
+
TrustCommerce.vault_password = 'supersecure'
|
44
|
+
|
45
|
+
The password that TrustCommerce issues never changes or expires when used through the TCLink
|
46
|
+
extension. However if you choose to use SSL over HTTP instead (the fallback option if the TCLink
|
47
|
+
library is not installed), be aware that you need to set the password to your Vault password.
|
48
|
+
Likewise, if your application uses the query() method you must set the vault_password.
|
49
|
+
The reason is that TrustCommerce currently routes these query() calls through the vault
|
50
|
+
and therefore your password must be set accordingly. To make matters more complicated,
|
51
|
+
TrustCommerce currently forces you to change the Vault password every 90 days.
|
52
|
+
|
53
|
+
|
54
|
+
== Examples
|
55
|
+
|
56
|
+
=== Creating a subscription
|
57
|
+
|
58
|
+
# Bill Jennifer $12.00 monthly
|
59
|
+
response = TrustCommerce::Subscription.create(
|
60
|
+
:cc => '4111111111111111',
|
61
|
+
:exp => '0412',
|
62
|
+
:name => 'Jennifer Smith',
|
63
|
+
:amount => 1200,
|
64
|
+
:cycle => '1m'
|
65
|
+
)
|
66
|
+
|
67
|
+
if response['status'] == 'approved'
|
68
|
+
puts "Subscription created with Billing ID: #{response['billingid']}"
|
69
|
+
else
|
70
|
+
puts "An error occurred: #{response['error']}"
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
=== Update a subscription
|
75
|
+
|
76
|
+
# Update subscription to use new credit card
|
77
|
+
response = TrustCommerce::Subscription.update(
|
78
|
+
:billingid => 'ABC123',
|
79
|
+
:cc => '5411111111111115',
|
80
|
+
:exp => '0412'
|
81
|
+
)
|
82
|
+
|
83
|
+
if response['status'] == 'accepted'
|
84
|
+
puts 'Subscription updated.'
|
85
|
+
else
|
86
|
+
puts "An error occurred: #{response['error']}"
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
=== Delete a subscription
|
91
|
+
|
92
|
+
# Delete subscription
|
93
|
+
response = TrustCommerce::Subscription.delete(
|
94
|
+
:billingid => 'ABC123'
|
95
|
+
)
|
96
|
+
|
97
|
+
if response['status'] == 'accepted'
|
98
|
+
puts 'Subscription removed from active use.'
|
99
|
+
else
|
100
|
+
puts 'An error occurred.'
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
=== Query a subscription
|
105
|
+
|
106
|
+
# Get all sale transactions for a subscription in CSV format
|
107
|
+
response = TrustCommerce::Subscription.query(
|
108
|
+
:querytype => 'transaction',
|
109
|
+
:action => 'sale',
|
110
|
+
:billingid => 'ABC123'
|
111
|
+
)
|
112
|
+
|
113
|
+
|
114
|
+
=== Process a one-time charge
|
115
|
+
|
116
|
+
# Process one-time sale against existing subscription
|
117
|
+
response = TrustCommerce::Subscription.charge(
|
118
|
+
:billingid => 'ABC123',
|
119
|
+
:amount => 1995
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
=== Credit a transaction
|
124
|
+
|
125
|
+
# Process one-time credit against existing transaction
|
126
|
+
response = TrustCommerce::Subscription.credit(
|
127
|
+
:transid => '001-0000111101',
|
128
|
+
:amount => 1995
|
129
|
+
)
|
130
|
+
|
131
|
+
|
132
|
+
== Running the tests
|
133
|
+
|
134
|
+
The following special environment variables must be set up prior to running tests:
|
135
|
+
|
136
|
+
$ export TC_USERNAME=123456
|
137
|
+
$ export TC_PASSWORD=password
|
138
|
+
$ export TC_VAULT_PASSWORD=password
|
139
|
+
|
140
|
+
Run tests via rake:
|
141
|
+
|
142
|
+
$ rake test
|
143
|
+
|
144
|
+
Run tests via ruby:
|
145
|
+
|
146
|
+
$ ruby test/trustcommerce_test.rb
|
147
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'erb'
|
8
|
+
|
9
|
+
require File.dirname(__FILE__) + '/lib/trustcommerce'
|
10
|
+
require File.dirname(__FILE__) + '/lib/version'
|
11
|
+
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
Rake::TestTask.new(:test) { |t|
|
15
|
+
t.libs << 'test'
|
16
|
+
t.test_files = Dir.glob('test/*_test.rb')
|
17
|
+
t.verbose = true
|
18
|
+
}
|
19
|
+
|
20
|
+
namespace :doc do
|
21
|
+
|
22
|
+
Rake::RDocTask.new do |rdoc|
|
23
|
+
rdoc.rdoc_dir = 'doc'
|
24
|
+
rdoc.title = "TrustCommerce Subscription Library"
|
25
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
26
|
+
rdoc.rdoc_files.include('README')
|
27
|
+
rdoc.rdoc_files.include('MIT-LICENSE')
|
28
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
29
|
+
end
|
30
|
+
|
31
|
+
task :rdoc => 'doc:readme'
|
32
|
+
|
33
|
+
task :refresh => :rerdoc do
|
34
|
+
system 'open doc/index.html'
|
35
|
+
end
|
36
|
+
|
37
|
+
task :readme do
|
38
|
+
require 'support/rdoc/code_info'
|
39
|
+
RDoc::CodeInfo.parse('(lib|test)/*.rb')
|
40
|
+
|
41
|
+
strip_comments = lambda {|comment| comment.gsub(/^# ?/, '')}
|
42
|
+
docs_for = lambda do |location|
|
43
|
+
info = RDoc::CodeInfo.for(location)
|
44
|
+
raise RuntimeError, "Couldn't find documentation for `#{location}'" unless info
|
45
|
+
strip_comments[info.comment]
|
46
|
+
end
|
47
|
+
|
48
|
+
open('README', 'w') do |file|
|
49
|
+
file.write ERB.new(IO.read('README.erb')).result(binding)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
task :deploy => :rerdoc do
|
54
|
+
sh %(scp -r doc zackchandler@rubyforge.org:/var/www/gforge-projects/trustcommerce/)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
namespace :site do
|
60
|
+
require 'rdoc/markup/simple_markup'
|
61
|
+
require 'rdoc/markup/simple_markup/to_html'
|
62
|
+
|
63
|
+
readme = lambda { IO.read('README') }
|
64
|
+
|
65
|
+
readme_to_html = lambda do
|
66
|
+
handler = SM::ToHtml.new
|
67
|
+
handler.instance_eval do
|
68
|
+
require 'syntax'
|
69
|
+
require 'syntax/convertors/html'
|
70
|
+
def accept_verbatim(am, fragment)
|
71
|
+
syntax = Syntax::Convertors::HTML.for_syntax('ruby')
|
72
|
+
@res << %(<div class="ruby">#{syntax.convert(fragment.txt, true)}</div>)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
html = SM::SimpleMarkup.new.convert(readme.call, handler)
|
76
|
+
html.gsub(%r{\[([\w\s]+)\]\(([\w:/.]+)\)}, '<a href="\2">\1</a>')
|
77
|
+
end
|
78
|
+
|
79
|
+
desc 'Regenerate the public website page'
|
80
|
+
task :build => 'doc:readme' do
|
81
|
+
open('site/public/index.html', 'w') do |file|
|
82
|
+
erb_data = {}
|
83
|
+
erb_data[:readme] = readme_to_html.call
|
84
|
+
file.write ERB.new(IO.read('site/index.erb')).result(binding)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
task :refresh => :build do
|
89
|
+
system 'open site/public/index.html'
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'Update the live website'
|
93
|
+
task :deploy => :build do
|
94
|
+
site_files = FileList['site/public/*']
|
95
|
+
sh %(scp #{site_files.join ' '} zackchandler@rubyforge.org:/var/www/gforge-projects/trustcommerce/)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
namespace :dist do
|
100
|
+
|
101
|
+
spec = Gem::Specification.new do |s|
|
102
|
+
s.name = 'trustcommerce'
|
103
|
+
s.version = Gem::Version.new(TrustCommerce::Version)
|
104
|
+
s.summary = 'TrustCommerce Subscription Library'
|
105
|
+
s.description = s.summary
|
106
|
+
s.email = 'zackchandler@depixelate.com'
|
107
|
+
s.author = 'Zack Chandler'
|
108
|
+
s.has_rdoc = true
|
109
|
+
s.extra_rdoc_files = %w(README MIT-LICENSE)
|
110
|
+
s.homepage = 'http://trustcommerce.rubyforge.org'
|
111
|
+
s.rubyforge_project = 'trustcommerce'
|
112
|
+
s.files = FileList['Rakefile', 'lib/*.rb', 'support/**/*.rb']
|
113
|
+
s.test_files = Dir['test/*']
|
114
|
+
|
115
|
+
s.rdoc_options = ['--title', 'TrustCommerce Subscription Library',
|
116
|
+
'--main', 'README',
|
117
|
+
'--line-numbers', '--inline-source']
|
118
|
+
end
|
119
|
+
|
120
|
+
# Regenerate README before packaging
|
121
|
+
task :package => 'doc:readme'
|
122
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
123
|
+
pkg.need_tar_gz = true
|
124
|
+
pkg.package_files.include('{lib,test,support}/**/*')
|
125
|
+
pkg.package_files.include('README')
|
126
|
+
pkg.package_files.include('MIT-LICENSE')
|
127
|
+
pkg.package_files.include('Rakefile')
|
128
|
+
end
|
129
|
+
|
130
|
+
desc 'Install with gems'
|
131
|
+
task :install => :repackage do
|
132
|
+
sh "sudo gem i pkg/#{spec.name}-#{spec.version}.gem"
|
133
|
+
end
|
134
|
+
|
135
|
+
desc 'Uninstall gem'
|
136
|
+
task :uninstall do
|
137
|
+
sh "sudo gem uninstall #{spec.name}"
|
138
|
+
end
|
139
|
+
|
140
|
+
desc 'Reinstall gem'
|
141
|
+
task :reinstall => [:uninstall, :install]
|
142
|
+
|
143
|
+
task :confirm_release do
|
144
|
+
print "Releasing version #{spec.version}. Are you sure you want to proceed? [Yn] "
|
145
|
+
abort if STDIN.getc == ?n
|
146
|
+
end
|
147
|
+
|
148
|
+
desc 'Tag release'
|
149
|
+
task :tag do
|
150
|
+
svn_root = 'svn+ssh://zackchandler@rubyforge.org/var/svn/trustcommerce'
|
151
|
+
sh %(svn cp #{svn_root}/trunk #{svn_root}/tags/rel-#{spec.version} -m "Tag #{spec.name} release #{spec.version}")
|
152
|
+
end
|
153
|
+
|
154
|
+
desc 'Update changelog to include a release marker'
|
155
|
+
task :add_release_marker_to_changelog do
|
156
|
+
changelog = IO.read('CHANGELOG')
|
157
|
+
changelog.sub!(/^trunk:/, "#{spec.version}:")
|
158
|
+
|
159
|
+
open('CHANGELOG', 'w') do |file|
|
160
|
+
file.write "trunk:\n\n#{changelog}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
task :commit_changelog do
|
165
|
+
sh %(svn ci CHANGELOG -m "Bump changelog version marker for release")
|
166
|
+
end
|
167
|
+
|
168
|
+
package_name = lambda {|specification| File.join('pkg', "#{specification.name}-#{specification.version}")}
|
169
|
+
|
170
|
+
desc 'Push a release to rubyforge'
|
171
|
+
task :release => [:confirm_release, :clean, :add_release_marker_to_changelog, :package, :commit_changelog, :tag] do
|
172
|
+
require 'rubyforge'
|
173
|
+
package = package_name[spec]
|
174
|
+
|
175
|
+
rubyforge = RubyForge.new
|
176
|
+
rubyforge.login
|
177
|
+
|
178
|
+
version_already_released = lambda do
|
179
|
+
releases = rubyforge.config['rubyforge']['release_ids']
|
180
|
+
releases.has_key?(spec.name) && releases[spec.name][spec.version]
|
181
|
+
end
|
182
|
+
|
183
|
+
abort("Release #{spec.version} already exists!") if version_already_released.call
|
184
|
+
|
185
|
+
if release_id = rubyforge.add_release(spec.rubyforge_project, spec.name, spec.version, "#{package}.tar.gz")
|
186
|
+
rubyforge.add_file(spec.rubyforge_project, spec.name, release_id, "#{package}.gem")
|
187
|
+
else
|
188
|
+
puts 'Release failed!'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
task :spec do
|
193
|
+
puts spec.to_ruby
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
task :clean => ['dist:clobber_package', 'doc:clobber_rdoc']
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
# [TrustCommerce](http://www.trustcommerce.com) is a payment gateway providing credit card
|
6
|
+
# processing and recurring / subscription billing services.
|
7
|
+
#
|
8
|
+
# This library provides a simple interface to create, edit, delete, and query subscriptions
|
9
|
+
# using TrustCommerce.
|
10
|
+
#
|
11
|
+
# == Background
|
12
|
+
#
|
13
|
+
# TrustCommerce's recurring / subscription billing solution is implemented through a service
|
14
|
+
# called [Citadel](http://www.trustcommerce.com/citadel.php). A Citadel-enabled account is
|
15
|
+
# required to use the subscription-based features implemented by this library.
|
16
|
+
#
|
17
|
+
# === Citadel Basics
|
18
|
+
# * Citadel stores customer profiles which can include credit card information and billing frequency.
|
19
|
+
# * Citadel will automatically bill customers on their respective schedules.
|
20
|
+
# * Citadel identifies each customer by a Billing ID (six-character alphanumeric string).
|
21
|
+
# * A customer's profile, credit card, and billing frequency can be modified using the Billing ID.
|
22
|
+
#
|
23
|
+
# == Installation
|
24
|
+
#
|
25
|
+
# The simple way:
|
26
|
+
# $ sudo gem install trustcommerce
|
27
|
+
#
|
28
|
+
# Directly from repository:
|
29
|
+
# $ svn co svn://rubyforge.org/var/svn/trustcommerce/trunk trustcommerce
|
30
|
+
#
|
31
|
+
# It is highly recommended to download and install the
|
32
|
+
# [TCLink ruby extension](http://www.trustcommerce.com/tclink.php).
|
33
|
+
# This extension provides failover capability and enhanced security features.
|
34
|
+
# If this library is not installed, standard POST over SSL will be used.
|
35
|
+
#
|
36
|
+
# == Configuration
|
37
|
+
#
|
38
|
+
# When you signup for a TrustCommerce account you are issued a custid and a password.
|
39
|
+
# These are your credentials when using the TrustCommerce API.
|
40
|
+
#
|
41
|
+
# TrustCommerce.custid = '123456'
|
42
|
+
# TrustCommerce.password = 'topsecret'
|
43
|
+
#
|
44
|
+
# # optional - sets Vault password for use in query() calls
|
45
|
+
# TrustCommerce.vault_password = 'supersecure'
|
46
|
+
#
|
47
|
+
# The password that TrustCommerce issues never changes or expires when used through the TCLink
|
48
|
+
# extension. However if you choose to use SSL over HTTP instead (the fallback option if the TCLink
|
49
|
+
# library is not installed), be aware that you need to set the password to your Vault password.
|
50
|
+
# Likewise, if your application uses the query() method you must set the vault_password.
|
51
|
+
# The reason is that TrustCommerce currently routes these query() calls through the vault
|
52
|
+
# and therefore your password must be set accordingly. To make matters more complicated,
|
53
|
+
# TrustCommerce currently forces you to change the Vault password every 90 days.
|
54
|
+
class TrustCommerce
|
55
|
+
|
56
|
+
class << self
|
57
|
+
attr_accessor :custid
|
58
|
+
attr_accessor :password
|
59
|
+
attr_accessor :vault_password
|
60
|
+
|
61
|
+
# Returns Vault password.
|
62
|
+
def vault_password
|
63
|
+
@vault_password || password
|
64
|
+
end
|
65
|
+
end
|
66
|
+
self.custid = 'TestMerchant'
|
67
|
+
self.password = 'password'
|
68
|
+
|
69
|
+
# Settings for standard POST over SSL
|
70
|
+
# Only used if TCLink library is not installed
|
71
|
+
API_SETTINGS = {
|
72
|
+
:domain => 'vault.trustcommerce.com',
|
73
|
+
:query_path => '/query/',
|
74
|
+
:trans_path => '/trans/',
|
75
|
+
:port => 443
|
76
|
+
}
|
77
|
+
|
78
|
+
class Subscription
|
79
|
+
|
80
|
+
# # Bill Jennifer $12.00 monthly
|
81
|
+
# response = TrustCommerce::Subscription.create(
|
82
|
+
# :cc => '4111111111111111',
|
83
|
+
# :exp => '0412',
|
84
|
+
# :name => 'Jennifer Smith',
|
85
|
+
# :amount => 1200,
|
86
|
+
# :cycle => '1m'
|
87
|
+
# )
|
88
|
+
#
|
89
|
+
# if response['status'] == 'approved'
|
90
|
+
# puts "Subscription created with Billing ID: #{response['billingid']}"
|
91
|
+
# else
|
92
|
+
# puts "An error occurred: #{response['error']}"
|
93
|
+
# end
|
94
|
+
def self.create(options)
|
95
|
+
return TrustCommerce.send_request(options.merge(:action => 'store'))
|
96
|
+
end
|
97
|
+
|
98
|
+
# # Update subscription to use new credit card
|
99
|
+
# response = TrustCommerce::Subscription.update(
|
100
|
+
# :billingid => 'ABC123',
|
101
|
+
# :cc => '5411111111111115',
|
102
|
+
# :exp => '0412'
|
103
|
+
# )
|
104
|
+
#
|
105
|
+
# if response['status'] == 'accepted'
|
106
|
+
# puts 'Subscription updated.'
|
107
|
+
# else
|
108
|
+
# puts "An error occurred: #{response['error']}"
|
109
|
+
# end
|
110
|
+
def self.update(options)
|
111
|
+
return TrustCommerce.send_request(options.merge(:action => 'store'))
|
112
|
+
end
|
113
|
+
|
114
|
+
# # Delete subscription
|
115
|
+
# response = TrustCommerce::Subscription.delete(
|
116
|
+
# :billingid => 'ABC123'
|
117
|
+
# )
|
118
|
+
#
|
119
|
+
# if response['status'] == 'accepted'
|
120
|
+
# puts 'Subscription removed from active use.'
|
121
|
+
# else
|
122
|
+
# puts 'An error occurred.'
|
123
|
+
# end
|
124
|
+
def self.delete(options)
|
125
|
+
return TrustCommerce.send_request(options.merge(:action => 'unstore'))
|
126
|
+
end
|
127
|
+
|
128
|
+
# # Process one-time sale against existing subscription
|
129
|
+
# response = TrustCommerce::Subscription.charge(
|
130
|
+
# :billingid => 'ABC123',
|
131
|
+
# :amount => 1995
|
132
|
+
# )
|
133
|
+
def self.charge(options)
|
134
|
+
return TrustCommerce.send_request(options.merge(:action => 'sale'))
|
135
|
+
end
|
136
|
+
|
137
|
+
# # Process one-time credit against existing transaction
|
138
|
+
# response = TrustCommerce::Subscription.credit(
|
139
|
+
# :transid => '001-0000111101',
|
140
|
+
# :amount => 1995
|
141
|
+
# )
|
142
|
+
def self.credit(options)
|
143
|
+
return TrustCommerce.send_request(options.merge(:action => 'credit'))
|
144
|
+
end
|
145
|
+
|
146
|
+
# # Get all sale transactions for a subscription in CSV format
|
147
|
+
# response = TrustCommerce::Subscription.query(
|
148
|
+
# :querytype => 'transaction',
|
149
|
+
# :action => 'sale',
|
150
|
+
# :billingid => 'ABC123'
|
151
|
+
# )
|
152
|
+
def self.query(options)
|
153
|
+
return TrustCommerce.send_query(options)
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
# It is highly recommended to download and install the
|
159
|
+
# [TCLink ruby extension](http://www.trustcommerce.com/tclink.php).
|
160
|
+
# This extension provides failover capability and enhanced security features.
|
161
|
+
# If this library is not installed, standard POST over SSL will be used.
|
162
|
+
def self.tclink?
|
163
|
+
begin
|
164
|
+
require 'tclink'
|
165
|
+
true
|
166
|
+
rescue LoadError
|
167
|
+
false
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def self.stringify_hash(hash)
|
174
|
+
hash.inject({}) { |h,(k,v)| h[k.to_s] = v.to_s; h }
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.symbolize_hash(hash)
|
178
|
+
hash.inject({}) { |h,(k,v)| h[k.to_sym] = v.to_s; h }
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.send_request(options)
|
182
|
+
options[:custid] = self.custid
|
183
|
+
options[:password] = self.password
|
184
|
+
options.update(:demo => 'y') if ENV['RAILS_ENV'] != 'production'
|
185
|
+
parameters = stringify_hash(options)
|
186
|
+
if tclink? # use TCLink extension if installed
|
187
|
+
return symbolize_hash(TCLink.send(parameters))
|
188
|
+
else # TCLink library not installed - use https post
|
189
|
+
parameters[:password] = self.vault_password.to_s
|
190
|
+
response = send_https_request(API_SETTINGS[:trans_path], parameters)
|
191
|
+
|
192
|
+
# parse response
|
193
|
+
results = {}
|
194
|
+
response.body.split("\n").each do |line|
|
195
|
+
k, v = line.split('=')
|
196
|
+
results[k.to_sym] = v
|
197
|
+
end
|
198
|
+
results
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.send_query(options)
|
203
|
+
options[:custid] = self.custid
|
204
|
+
options[:password] = self.vault_password.to_s
|
205
|
+
response = send_https_request(API_SETTINGS[:query_path], stringify_hash(options))
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.send_https_request(path, parameters)
|
209
|
+
http = Net::HTTP.new(API_SETTINGS[:domain], API_SETTINGS[:port])
|
210
|
+
http.use_ssl = true
|
211
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # avoid ssl cert warning
|
212
|
+
request = Net::HTTP::Post.new(path)
|
213
|
+
request.form_data = parameters
|
214
|
+
http.request(request)
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rdoc/rdoc'
|
4
|
+
|
5
|
+
module RDoc
|
6
|
+
class CodeInfo
|
7
|
+
class << self
|
8
|
+
def parse(wildcard_pattern = nil)
|
9
|
+
@info_for_corpus = parse_files(wildcard_pattern)
|
10
|
+
end
|
11
|
+
|
12
|
+
def for(constant)
|
13
|
+
new(constant).info
|
14
|
+
end
|
15
|
+
|
16
|
+
def info_for_corpus
|
17
|
+
raise RuntimeError, "You must first generate a corpus to search by using RDoc::CodeInfo.parse" unless @info_for_corpus
|
18
|
+
@info_for_corpus
|
19
|
+
end
|
20
|
+
|
21
|
+
def parsed_files
|
22
|
+
info_for_corpus.map {|info| info.file_absolute_name}
|
23
|
+
end
|
24
|
+
|
25
|
+
def files_to_parse
|
26
|
+
@files_to_parse ||= Rake::FileList.new
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def parse_files(pattern)
|
31
|
+
files = pattern ? Rake::FileList[pattern] : files_to_parse
|
32
|
+
options = Options.instance
|
33
|
+
options.parse(files << '-q', RDoc::GENERATORS)
|
34
|
+
rdoc.send(:parse_files, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def rdoc
|
38
|
+
TopLevel.reset
|
39
|
+
rdoc = RDoc.new
|
40
|
+
stats = Stats.new
|
41
|
+
# We don't want any output so we'll override the print method
|
42
|
+
stats.instance_eval { def print; nil end }
|
43
|
+
rdoc.instance_variable_set(:@stats, stats)
|
44
|
+
rdoc
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :info
|
49
|
+
def initialize(location)
|
50
|
+
@location = CodeLocation.new(location)
|
51
|
+
find_constant
|
52
|
+
find_method if @location.has_method?
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
attr_reader :location
|
57
|
+
attr_writer :info
|
58
|
+
def find_constant
|
59
|
+
parts = location.namespace_parts
|
60
|
+
self.class.info_for_corpus.each do |file_info|
|
61
|
+
@info = parts.inject(file_info) do |result, const_part|
|
62
|
+
(result.find_module_named(const_part) || result.find_class_named(const_part)) || break
|
63
|
+
end
|
64
|
+
return if info
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_method
|
69
|
+
return unless info
|
70
|
+
self.info = info.method_list.detect do |method_info|
|
71
|
+
next unless method_info.name == location.method_name
|
72
|
+
if location.class_method?
|
73
|
+
method_info.singleton
|
74
|
+
elsif location.instance_method?
|
75
|
+
!method_info.singleton
|
76
|
+
else
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class CodeLocation
|
84
|
+
attr_reader :location
|
85
|
+
|
86
|
+
def initialize(location)
|
87
|
+
@location = location
|
88
|
+
end
|
89
|
+
|
90
|
+
def parts
|
91
|
+
location.split(/::|\.|#/)
|
92
|
+
end
|
93
|
+
|
94
|
+
def namespace_parts
|
95
|
+
has_method? ? parts[0...-1] : parts
|
96
|
+
end
|
97
|
+
|
98
|
+
def has_method?
|
99
|
+
('a'..'z').include?(parts.last[0, 1])
|
100
|
+
end
|
101
|
+
|
102
|
+
def instance_method?
|
103
|
+
!location['#'].nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
def class_method?
|
107
|
+
has_method? && !location[/#|\./]
|
108
|
+
end
|
109
|
+
|
110
|
+
def method_name
|
111
|
+
parts.last if has_method?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if __FILE__ == $0
|
117
|
+
require 'test/unit'
|
118
|
+
class CodeInfoTest < Test::Unit::TestCase
|
119
|
+
def setup
|
120
|
+
RDoc::CodeInfo.parse(__FILE__)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_constant_lookup
|
124
|
+
assert RDoc::CodeInfo.for('RDoc')
|
125
|
+
|
126
|
+
info = RDoc::CodeInfo.for('RDoc::CodeInfo')
|
127
|
+
assert_equal 'CodeInfo', info.name
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_method_lookup
|
131
|
+
{'RDoc::CodeInfo.parse' => true,
|
132
|
+
'RDoc::CodeInfo::parse' => true,
|
133
|
+
'RDoc::CodeInfo#parse' => false,
|
134
|
+
'RDoc::CodeInfo.find_method' => true,
|
135
|
+
'RDoc::CodeInfo::find_method' => false,
|
136
|
+
'RDoc::CodeInfo#find_method' => true,
|
137
|
+
'RDoc::CodeInfo#no_such_method' => false,
|
138
|
+
'RDoc::NoSuchConst#foo' => false}.each do |location, result_of_lookup|
|
139
|
+
assert_equal result_of_lookup, !RDoc::CodeInfo.for(location).nil?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class CodeLocationTest < Test::Unit::TestCase
|
145
|
+
def test_parts
|
146
|
+
{'Foo' => %w(Foo),
|
147
|
+
'Foo::Bar' => %w(Foo Bar),
|
148
|
+
'Foo::Bar#baz' => %w(Foo Bar baz),
|
149
|
+
'Foo::Bar.baz' => %w(Foo Bar baz),
|
150
|
+
'Foo::Bar::baz' => %w(Foo Bar baz),
|
151
|
+
'Foo::Bar::Baz' => %w(Foo Bar Baz)}.each do |location, parts|
|
152
|
+
assert_equal parts, RDoc::CodeLocation.new(location).parts
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_namespace_parts
|
157
|
+
{'Foo' => %w(Foo),
|
158
|
+
'Foo::Bar' => %w(Foo Bar),
|
159
|
+
'Foo::Bar#baz' => %w(Foo Bar),
|
160
|
+
'Foo::Bar.baz' => %w(Foo Bar),
|
161
|
+
'Foo::Bar::baz' => %w(Foo Bar),
|
162
|
+
'Foo::Bar::Baz' => %w(Foo Bar Baz)}.each do |location, namespace_parts|
|
163
|
+
assert_equal namespace_parts, RDoc::CodeLocation.new(location).namespace_parts
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_has_method?
|
168
|
+
{'Foo' => false,
|
169
|
+
'Foo::Bar' => false,
|
170
|
+
'Foo::Bar#baz' => true,
|
171
|
+
'Foo::Bar.baz' => true,
|
172
|
+
'Foo::Bar::baz' => true,
|
173
|
+
'Foo::Bar::Baz' => false}.each do |location, has_method_result|
|
174
|
+
assert_equal has_method_result, RDoc::CodeLocation.new(location).has_method?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_instance_method?
|
179
|
+
{'Foo' => false,
|
180
|
+
'Foo::Bar' => false,
|
181
|
+
'Foo::Bar#baz' => true,
|
182
|
+
'Foo::Bar.baz' => false,
|
183
|
+
'Foo::Bar::baz' => false,
|
184
|
+
'Foo::Bar::Baz' => false}.each do |location, is_instance_method|
|
185
|
+
assert_equal is_instance_method, RDoc::CodeLocation.new(location).instance_method?
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_class_method?
|
190
|
+
{'Foo' => false,
|
191
|
+
'Foo::Bar' => false,
|
192
|
+
'Foo::Bar#baz' => false,
|
193
|
+
'Foo::Bar.baz' => false,
|
194
|
+
'Foo::Bar::baz' => true,
|
195
|
+
'Foo::Bar::Baz' => false}.each do |location, is_class_method|
|
196
|
+
assert_equal is_class_method, RDoc::CodeLocation.new(location).class_method?
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_method_name
|
201
|
+
{'Foo' => nil,
|
202
|
+
'Foo::Bar' => nil,
|
203
|
+
'Foo::Bar#baz' => 'baz',
|
204
|
+
'Foo::Bar.baz' => 'baz',
|
205
|
+
'Foo::Bar::baz' => 'baz',
|
206
|
+
'Foo::Bar::Baz' => nil}.each do |location, method_name|
|
207
|
+
assert_equal method_name, RDoc::CodeLocation.new(location).method_name
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require File.dirname(__FILE__) + '/../lib/trustcommerce'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'mocha'
|
7
|
+
rescue LoadError
|
8
|
+
puts "Mocha required to run tests. `gem install mocha` and try again."
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
TCLINK_LIB_INSTALLED = TrustCommerce.tclink?
|
13
|
+
|
14
|
+
def via_tclink_and_https
|
15
|
+
# test via TCLink
|
16
|
+
if !TCLINK_LIB_INSTALLED
|
17
|
+
puts 'TCLink library not installed - skipping tests via TCLink'
|
18
|
+
else
|
19
|
+
TrustCommerce.stubs(:tclink?).returns(true)
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
|
23
|
+
# test via https
|
24
|
+
TrustCommerce.stubs(:tclink?).returns(false)
|
25
|
+
yield
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_subscription!(name)
|
29
|
+
response = TrustCommerce::Subscription.create(
|
30
|
+
:cc => CARDS[:visa][:cc],
|
31
|
+
:exp => CARDS[:visa][:exp],
|
32
|
+
:address1 => CARDS[:visa][:address],
|
33
|
+
:zip => CARDS[:visa][:zip],
|
34
|
+
:avs => 'y',
|
35
|
+
:name => name,
|
36
|
+
:amount => 1200,
|
37
|
+
:cycle => '1m',
|
38
|
+
:demo => 'y'
|
39
|
+
)
|
40
|
+
assert_equal 'approved', response[:status]
|
41
|
+
assert response.keys.include?(:billingid)
|
42
|
+
response[:billingid]
|
43
|
+
end
|
44
|
+
|
45
|
+
# --- [ TrustCommerce test data ] ---
|
46
|
+
# reference: https://vault.trustcommerce.com/downloads/TCDevGuide.html#testdata
|
47
|
+
CARDS = {
|
48
|
+
:visa => { :cc => '4111111111111111',
|
49
|
+
:exp => '0412',
|
50
|
+
:cvv => 123,
|
51
|
+
:address => '123 Test St.',
|
52
|
+
:city => 'Somewhere',
|
53
|
+
:state => 'CA',
|
54
|
+
:zip => 90001 },
|
55
|
+
:mastercard => { :cc => '5411111111111115',
|
56
|
+
:exp => '0412',
|
57
|
+
:cvv => 777,
|
58
|
+
:address => '4000 Main St.',
|
59
|
+
:city => 'Anytown',
|
60
|
+
:state => 'MA',
|
61
|
+
:zip => 85001 },
|
62
|
+
:amex => { :cc => '341111111111111',
|
63
|
+
:exp => '0412',
|
64
|
+
:cvv => 4000,
|
65
|
+
:address => '12 Colorado Blvd.',
|
66
|
+
:city => 'Elsewhere',
|
67
|
+
:state => 'IL',
|
68
|
+
:zip => 54321 }
|
69
|
+
}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
# The following special environment variables must be set up prior to running tests:
|
4
|
+
#
|
5
|
+
# $ export TC_USERNAME=123456
|
6
|
+
# $ export TC_PASSWORD=password
|
7
|
+
# $ export TC_VAULT_PASSWORD=password
|
8
|
+
#
|
9
|
+
# Run tests via rake:
|
10
|
+
#
|
11
|
+
# $ rake test
|
12
|
+
#
|
13
|
+
# Run tests via ruby:
|
14
|
+
#
|
15
|
+
# $ ruby test/trustcommerce_test.rb
|
16
|
+
class TrustCommerceSubscriptionTest < Test::Unit::TestCase
|
17
|
+
|
18
|
+
def setup
|
19
|
+
if ENV['TC_USERNAME'].nil? || ENV['TC_PASSWORD'].nil?
|
20
|
+
puts 'TC_USERNAME and TC_PASSWORD must be set.'
|
21
|
+
puts 'Usage: TC_USERNAME=username TC_PASSWORD=password TC_VAULT_PASSWORD=password ruby test/trustcommerce_test.rb'
|
22
|
+
exit 1
|
23
|
+
else
|
24
|
+
TrustCommerce.custid = ENV['TC_USERNAME']
|
25
|
+
TrustCommerce.password = ENV['TC_PASSWORD']
|
26
|
+
TrustCommerce.vault_password = ENV['TC_VAULT_PASSWORD'] if ENV['TC_VAULT_PASSWORD']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_subscription_create
|
31
|
+
via_tclink_and_https do
|
32
|
+
response = TrustCommerce::Subscription.create(
|
33
|
+
:cc => CARDS[:visa][:cc],
|
34
|
+
:exp => CARDS[:visa][:exp],
|
35
|
+
:address1 => CARDS[:visa][:address],
|
36
|
+
:zip => CARDS[:visa][:zip],
|
37
|
+
:avs => 'y',
|
38
|
+
:name => 'Jennifer Smith - create() test',
|
39
|
+
:amount => 1200,
|
40
|
+
:cycle => '1m',
|
41
|
+
:demo => 'y'
|
42
|
+
)
|
43
|
+
assert_equal Hash, response.class
|
44
|
+
assert_not_nil response[:billingid]
|
45
|
+
assert response.keys.include?(:transid)
|
46
|
+
assert_equal 'approved', response[:status]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_subscription_update
|
51
|
+
via_tclink_and_https do
|
52
|
+
billing_id = create_subscription!('update() test')
|
53
|
+
response = TrustCommerce::Subscription.update(
|
54
|
+
:billingid => billing_id,
|
55
|
+
:cc => CARDS[:mastercard][:cc],
|
56
|
+
:exp => CARDS[:mastercard][:exp],
|
57
|
+
:address1 => CARDS[:mastercard][:address],
|
58
|
+
:zip => CARDS[:mastercard][:zip],
|
59
|
+
:avs => 'y'
|
60
|
+
)
|
61
|
+
assert_equal 'accepted', response[:status]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_subscription_delete
|
66
|
+
via_tclink_and_https do
|
67
|
+
billing_id = create_subscription!('delete() test')
|
68
|
+
response = TrustCommerce::Subscription.delete(
|
69
|
+
:billingid => billing_id
|
70
|
+
)
|
71
|
+
assert response.keys.include?(:transid)
|
72
|
+
assert_equal 'accepted', response[:status]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_subscription_charge_and_credit
|
77
|
+
via_tclink_and_https do
|
78
|
+
billing_id = create_subscription!('charge and credit() test')
|
79
|
+
|
80
|
+
# charge
|
81
|
+
charge_response = TrustCommerce::Subscription.charge(
|
82
|
+
:billingid => billing_id,
|
83
|
+
:amount => 1995,
|
84
|
+
:demo => 'y'
|
85
|
+
)
|
86
|
+
assert charge_response.keys.include?(:transid)
|
87
|
+
assert_equal 'approved', charge_response[:status]
|
88
|
+
|
89
|
+
# credit
|
90
|
+
credit_response = TrustCommerce::Subscription.credit(
|
91
|
+
:transid => charge_response[:transid],
|
92
|
+
:amount => 995,
|
93
|
+
:demo => 'y'
|
94
|
+
)
|
95
|
+
assert credit_response.keys.include?(:transid)
|
96
|
+
assert_equal 'accepted', credit_response[:status]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_subscription_query
|
101
|
+
puts "\n"
|
102
|
+
puts "---------------------------------------------------------------------------"
|
103
|
+
puts "IMPORTANT: This query test will likely take between 1 and 2 minutes!"
|
104
|
+
puts "Make sure TC_VAULT_PASSWORD is set if it differs from your TCLink password."
|
105
|
+
puts "---------------------------------------------------------------------------"
|
106
|
+
|
107
|
+
# create subscription
|
108
|
+
billing_id = create_subscription!('query() test')
|
109
|
+
|
110
|
+
# query for charges
|
111
|
+
options = { :querytype => 'transaction', :action => 'sale', :billingid => billing_id }
|
112
|
+
while (query_response = TrustCommerce::Subscription.query(options))
|
113
|
+
if query_response.body =~ /error/i
|
114
|
+
fail(query_response.body)
|
115
|
+
break
|
116
|
+
elsif query_response.body.split("\n").size < 2
|
117
|
+
puts 'Transaction has not yet showed up... will try again in 15 seconds.'
|
118
|
+
sleep(15)
|
119
|
+
else
|
120
|
+
puts 'Transaction found.'
|
121
|
+
|
122
|
+
# setup index hash
|
123
|
+
field_names = query_response.body.split("\n")[0].split(',')
|
124
|
+
date_line_1 = query_response.body.split("\n")[1].split(',')
|
125
|
+
indexes = field_names.inject({}) {|h, field| h[field.to_sym] = field_names.index(field); h }
|
126
|
+
|
127
|
+
# check transaction data
|
128
|
+
assert_equal '1111', date_line_1[indexes[:cc]]
|
129
|
+
assert_equal '1200', date_line_1[indexes[:amount]]
|
130
|
+
assert_equal 'query() test', date_line_1[indexes[:name]]
|
131
|
+
break
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# test private helpers
|
137
|
+
def test_stringify_hash
|
138
|
+
assert_equal ({ 'a' => '1', 'b' => '2' }), TrustCommerce.stringify_hash(:a => '1', :b => '2')
|
139
|
+
assert_equal ({ 'a' => '1', 'b' => '2' }), TrustCommerce.stringify_hash(:a => 1, :b => 2)
|
140
|
+
assert_equal ({ 'a' => '2' }), TrustCommerce.stringify_hash(:a => 1, :a => 2)
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_symbolize_hash
|
144
|
+
assert_equal ({ :a => '1', :b => '2' }), TrustCommerce.symbolize_hash('a' => '1', 'b' => '2')
|
145
|
+
assert_equal ({ :a => '1', :b => '2' }), TrustCommerce.symbolize_hash(:a => 1, 'b' => 2)
|
146
|
+
assert_equal ({ :a => '1' }), TrustCommerce.symbolize_hash(:a => 1, 'a' => 2)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: trustcommerce
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2007-02-28 00:00:00 -08:00
|
8
|
+
summary: TrustCommerce Subscription Library
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: zackchandler@depixelate.com
|
12
|
+
homepage: http://trustcommerce.rubyforge.org
|
13
|
+
rubyforge_project: trustcommerce
|
14
|
+
description: TrustCommerce Subscription Library
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Zack Chandler
|
31
|
+
files:
|
32
|
+
- Rakefile
|
33
|
+
- lib/trustcommerce.rb
|
34
|
+
- lib/version.rb
|
35
|
+
- support/rdoc/code_info.rb
|
36
|
+
- README
|
37
|
+
- MIT-LICENSE
|
38
|
+
test_files:
|
39
|
+
- test/test_helper.rb
|
40
|
+
- test/trustcommerce_test.rb
|
41
|
+
rdoc_options:
|
42
|
+
- --title
|
43
|
+
- TrustCommerce Subscription Library
|
44
|
+
- --main
|
45
|
+
- README
|
46
|
+
- --line-numbers
|
47
|
+
- --inline-source
|
48
|
+
extra_rdoc_files:
|
49
|
+
- README
|
50
|
+
- MIT-LICENSE
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
dependencies: []
|
58
|
+
|