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 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,9 @@
1
+ class TrustCommerce
2
+ module VERSION #:nodoc:
3
+ MAJOR = '0'
4
+ MINOR = '5'
5
+ TINY = '0'
6
+ end
7
+
8
+ Version = [VERSION::MAJOR, VERSION::MINOR, VERSION::TINY].compact * '.'
9
+ end
@@ -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
@@ -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
+