ruby_psigate 0.7
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/CHANGELOG +2 -0
- data/Gemfile +12 -0
- data/LICENSE +0 -0
- data/Manifest +45 -0
- data/README.markdown +99 -0
- data/Rakefile +28 -0
- data/lib/certs/cacert.pem +2633 -0
- data/lib/ruby_psigate/account.rb +152 -0
- data/lib/ruby_psigate/account_manager_api.rb +137 -0
- data/lib/ruby_psigate/account_methods.rb +5 -0
- data/lib/ruby_psigate/address.rb +54 -0
- data/lib/ruby_psigate/connection.rb +96 -0
- data/lib/ruby_psigate/credit_card.rb +104 -0
- data/lib/ruby_psigate/credit_card_methods.rb +12 -0
- data/lib/ruby_psigate/error.rb +33 -0
- data/lib/ruby_psigate/gateway.rb +126 -0
- data/lib/ruby_psigate/hash_variables.rb +58 -0
- data/lib/ruby_psigate/item.rb +65 -0
- data/lib/ruby_psigate/item_option.rb +10 -0
- data/lib/ruby_psigate/number_validation_methods.rb +12 -0
- data/lib/ruby_psigate/order.rb +120 -0
- data/lib/ruby_psigate/recurring_charge.rb +53 -0
- data/lib/ruby_psigate/recurring_item.rb +26 -0
- data/lib/ruby_psigate/response.rb +109 -0
- data/lib/ruby_psigate/serializer.rb +59 -0
- data/lib/ruby_psigate/transaction_methods.rb +21 -0
- data/lib/ruby_psigate/utils.rb +18 -0
- data/lib/ruby_psigate.rb +57 -0
- data/ruby_psigate.gemspec +31 -0
- data/test/remote/remote_account_test.rb +33 -0
- data/test/remote/remote_gateway_test.rb +32 -0
- data/test/test_helper.rb +144 -0
- data/test/unit/account_manager_api_test.rb +96 -0
- data/test/unit/account_test.rb +388 -0
- data/test/unit/address_test.rb +99 -0
- data/test/unit/connection_test.rb +153 -0
- data/test/unit/credit_card_test.rb +152 -0
- data/test/unit/gateway_test.rb +112 -0
- data/test/unit/item_option_test.rb +19 -0
- data/test/unit/item_test.rb +106 -0
- data/test/unit/order_test.rb +491 -0
- data/test/unit/recurring_charge_test.rb +89 -0
- data/test/unit/recurring_item_test.rb +62 -0
- data/test/unit/response_test.rb +110 -0
- data/test/unit/serializer_test.rb +89 -0
- data/test/unit/xml_api_test.rb +25 -0
- metadata +154 -0
data/lib/ruby_psigate.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010 Simon Chiu
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
$:.unshift File.dirname(__FILE__)
|
25
|
+
|
26
|
+
require 'cgi'
|
27
|
+
require 'builder'
|
28
|
+
require 'crack'
|
29
|
+
require 'creditcard'
|
30
|
+
require 'money'
|
31
|
+
require 'net/https'
|
32
|
+
|
33
|
+
require 'ruby_psigate/error'
|
34
|
+
require 'ruby_psigate/utils'
|
35
|
+
require 'ruby_psigate/number_validation_methods'
|
36
|
+
require 'ruby_psigate/hash_variables'
|
37
|
+
require 'ruby_psigate/credit_card_methods'
|
38
|
+
|
39
|
+
require 'ruby_psigate/connection'
|
40
|
+
require 'ruby_psigate/transaction_methods'
|
41
|
+
require 'ruby_psigate/account_methods'
|
42
|
+
require 'ruby_psigate/gateway'
|
43
|
+
require 'ruby_psigate/response'
|
44
|
+
require 'ruby_psigate/serializer'
|
45
|
+
|
46
|
+
require 'ruby_psigate/order'
|
47
|
+
require 'ruby_psigate/account'
|
48
|
+
require 'ruby_psigate/address'
|
49
|
+
require 'ruby_psigate/credit_card'
|
50
|
+
require 'ruby_psigate/item'
|
51
|
+
require 'ruby_psigate/item_option'
|
52
|
+
require 'ruby_psigate/recurring_charge'
|
53
|
+
require 'ruby_psigate/recurring_item'
|
54
|
+
|
55
|
+
module RubyPsigate
|
56
|
+
# => For future use
|
57
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{ruby_psigate}
|
5
|
+
s.version = "0.7"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Simon Chiu"]
|
9
|
+
s.date = %q{2010-09-21}
|
10
|
+
s.description = %q{RubyPsigate parses and packages XML messages to/from Psigate's servers for transactions and recurring billing management.}
|
11
|
+
s.email = %q{skhchiu@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.markdown", "lib/certs/cacert.pem", "lib/ruby_psigate.rb", "lib/ruby_psigate/account.rb", "lib/ruby_psigate/account_manager_api.rb", "lib/ruby_psigate/account_methods.rb", "lib/ruby_psigate/address.rb", "lib/ruby_psigate/connection.rb", "lib/ruby_psigate/credit_card.rb", "lib/ruby_psigate/credit_card_methods.rb", "lib/ruby_psigate/error.rb", "lib/ruby_psigate/gateway.rb", "lib/ruby_psigate/hash_variables.rb", "lib/ruby_psigate/item.rb", "lib/ruby_psigate/item_option.rb", "lib/ruby_psigate/number_validation_methods.rb", "lib/ruby_psigate/order.rb", "lib/ruby_psigate/recurring_charge.rb", "lib/ruby_psigate/recurring_item.rb", "lib/ruby_psigate/response.rb", "lib/ruby_psigate/serializer.rb", "lib/ruby_psigate/transaction_methods.rb", "lib/ruby_psigate/utils.rb"]
|
13
|
+
s.files = ["CHANGELOG", "Gemfile", "LICENSE", "Manifest", "README.markdown", "Rakefile", "lib/certs/cacert.pem", "lib/ruby_psigate.rb", "lib/ruby_psigate/account.rb", "lib/ruby_psigate/account_manager_api.rb", "lib/ruby_psigate/account_methods.rb", "lib/ruby_psigate/address.rb", "lib/ruby_psigate/connection.rb", "lib/ruby_psigate/credit_card.rb", "lib/ruby_psigate/credit_card_methods.rb", "lib/ruby_psigate/error.rb", "lib/ruby_psigate/gateway.rb", "lib/ruby_psigate/hash_variables.rb", "lib/ruby_psigate/item.rb", "lib/ruby_psigate/item_option.rb", "lib/ruby_psigate/number_validation_methods.rb", "lib/ruby_psigate/order.rb", "lib/ruby_psigate/recurring_charge.rb", "lib/ruby_psigate/recurring_item.rb", "lib/ruby_psigate/response.rb", "lib/ruby_psigate/serializer.rb", "lib/ruby_psigate/transaction_methods.rb", "lib/ruby_psigate/utils.rb", "test/remote/remote_account_test.rb", "test/remote/remote_gateway_test.rb", "test/test_helper.rb", "test/unit/account_manager_api_test.rb", "test/unit/account_test.rb", "test/unit/address_test.rb", "test/unit/connection_test.rb", "test/unit/credit_card_test.rb", "test/unit/gateway_test.rb", "test/unit/item_option_test.rb", "test/unit/item_test.rb", "test/unit/order_test.rb", "test/unit/recurring_charge_test.rb", "test/unit/recurring_item_test.rb", "test/unit/response_test.rb", "test/unit/serializer_test.rb", "test/unit/xml_api_test.rb", "ruby_psigate.gemspec"]
|
14
|
+
s.homepage = %q{http://oss.simonchiu.com}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ruby_psigate", "--main", "README.markdown"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{ruby_psigate}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{A library to connect with the XML and Account interfaces of Psigate's servers}
|
20
|
+
s.test_files = ["test/remote/remote_account_test.rb", "test/remote/remote_gateway_test.rb", "test/test_helper.rb", "test/unit/account_manager_api_test.rb", "test/unit/account_test.rb", "test/unit/address_test.rb", "test/unit/connection_test.rb", "test/unit/credit_card_test.rb", "test/unit/gateway_test.rb", "test/unit/item_option_test.rb", "test/unit/item_test.rb", "test/unit/order_test.rb", "test/unit/recurring_charge_test.rb", "test/unit/recurring_item_test.rb", "test/unit/response_test.rb", "test/unit/serializer_test.rb", "test/unit/xml_api_test.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
else
|
28
|
+
end
|
29
|
+
else
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module RubyPsigate
|
4
|
+
class RemoteAccountTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@gateway = RubyPsigate::Gateway.new(:cid => 1000001, :user_id => "teststore", :password => "testpass")
|
8
|
+
end
|
9
|
+
|
10
|
+
should "create a new billing account" do
|
11
|
+
@account = RubyPsigate::Account.new
|
12
|
+
@account.action = { :account => :register }
|
13
|
+
|
14
|
+
@credit_card = RubyPsigate::CreditCard.new(
|
15
|
+
:number => "4111111111111111",
|
16
|
+
:month => "12",
|
17
|
+
:year => "2011",
|
18
|
+
:verification_value => "123",
|
19
|
+
:name => "Bob John"
|
20
|
+
)
|
21
|
+
|
22
|
+
@account.email = "bob@gmail.com"
|
23
|
+
@account.cc = @credit_card
|
24
|
+
# @account.address = valid_address
|
25
|
+
|
26
|
+
@gateway.account = @account
|
27
|
+
|
28
|
+
response = @gateway.commit!
|
29
|
+
assert response.success?
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module RubyPsigate
|
4
|
+
class RemoteGatewayTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@gateway = RubyPsigate::Gateway.new(:store_id => "teststore", :passphrase => "psigate1234")
|
8
|
+
end
|
9
|
+
|
10
|
+
should "commit! successfully" do
|
11
|
+
@order = RubyPsigate::Order.new
|
12
|
+
@order.amount = 10.00
|
13
|
+
@order.action = :sale
|
14
|
+
@order.email = "bob@gmail.com"
|
15
|
+
|
16
|
+
@credit_card = RubyPsigate::CreditCard.new(
|
17
|
+
:number => "4111111111111111",
|
18
|
+
:month => "12",
|
19
|
+
:year => "2011",
|
20
|
+
:verification_value => "123",
|
21
|
+
:name => "Bob John"
|
22
|
+
)
|
23
|
+
|
24
|
+
@order.cc = @credit_card
|
25
|
+
|
26
|
+
@gateway.order = @order
|
27
|
+
|
28
|
+
assert @gateway.commit!.success?
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'mocha'
|
6
|
+
require 'shoulda'
|
7
|
+
require 'ruby_psigate'
|
8
|
+
|
9
|
+
module RubyPsigate
|
10
|
+
# => Borrowed heavily from ActiveMerchant
|
11
|
+
module Assertions
|
12
|
+
AssertionClass = RUBY_VERSION > '1.9' ? MiniTest::Assertion : Test::Unit::AssertionFailedError
|
13
|
+
|
14
|
+
def assert_success(response)
|
15
|
+
clean_backtrace do
|
16
|
+
assert response.success?, "Response failed: #{response.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def clean_backtrace(&block)
|
23
|
+
yield
|
24
|
+
rescue AssertionClass => e
|
25
|
+
path = File.expand_path(__FILE__)
|
26
|
+
raise AssertionClass, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
module ValidModelInstances
|
32
|
+
def valid_address
|
33
|
+
@address = Address.new
|
34
|
+
@address.firstname = "Bob"
|
35
|
+
@address.lastname = "Parsons"
|
36
|
+
@address.line1 = "1234 West Street"
|
37
|
+
@address.line2 = "Apt 100"
|
38
|
+
@address.city = "Toronto"
|
39
|
+
@address.province = "Ontario"
|
40
|
+
@address.country = "CA"
|
41
|
+
@address.postalcode = "L3N9J2"
|
42
|
+
@address.telephone = "416-333-3333"
|
43
|
+
@address.fax = "416-322-2222"
|
44
|
+
@address.company = "Bob Parson's Co."
|
45
|
+
@address
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid_address_hash
|
49
|
+
{
|
50
|
+
:Bname => "Bob Parsons",
|
51
|
+
:Bcompany => "Bob Parson's Co.",
|
52
|
+
:Baddress1 => "1234 West Street",
|
53
|
+
:Baddress2 => "Apt 100",
|
54
|
+
:Bcity => "Toronto",
|
55
|
+
:Bprovince => "Ontario",
|
56
|
+
:Bcountry => "CA",
|
57
|
+
:Bpostalcode => "L3N9J2",
|
58
|
+
:Phone => "416-333-3333",
|
59
|
+
:Fax => "416-322-2222"
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid_credit_card
|
64
|
+
@valid_attributes = {
|
65
|
+
:number => "4111111111111111",
|
66
|
+
:month => "12",
|
67
|
+
:year => "2020",
|
68
|
+
:verification_value => "123",
|
69
|
+
:name => "Bob John"
|
70
|
+
}
|
71
|
+
@cc = CreditCard.new(@valid_attributes)
|
72
|
+
@cc
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid_credit_card_hash(type = nil)
|
76
|
+
@cc = valid_credit_card
|
77
|
+
@cc.to_hash(type)
|
78
|
+
end
|
79
|
+
|
80
|
+
def valid_item
|
81
|
+
@item1 = Item.new
|
82
|
+
@item1.unique_id = "PSI-BOOK"
|
83
|
+
@item1.desc = "XML Interface Doc"
|
84
|
+
@item1.qty = 2
|
85
|
+
@item1.price = 10.00
|
86
|
+
@item1
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_coupon
|
90
|
+
@item2 = Item.new
|
91
|
+
@item2.unique_id = "COUPON"
|
92
|
+
@item2.desc = "10% discount"
|
93
|
+
@item2.qty = 1
|
94
|
+
@item2.price = -2.00
|
95
|
+
@item2
|
96
|
+
end
|
97
|
+
|
98
|
+
def valid_option(option_hash)
|
99
|
+
@option = ItemOption.new(option_hash)
|
100
|
+
@option
|
101
|
+
end
|
102
|
+
|
103
|
+
def valid_recurring_charge
|
104
|
+
@valid_attributes = {
|
105
|
+
:rbcid => "9999999999",
|
106
|
+
:rbname => "Charge123",
|
107
|
+
:interval => "M",
|
108
|
+
:rbtrigger => "25", # Trigger date
|
109
|
+
:status => "Active",
|
110
|
+
:starttime => "2010.08.01",
|
111
|
+
:endtime => "2015.12.31",
|
112
|
+
:processtype => "A"
|
113
|
+
}
|
114
|
+
@recurring_charge = RecurringCharge.new(@valid_attributes)
|
115
|
+
end
|
116
|
+
|
117
|
+
def valid_recurring_charge_hash
|
118
|
+
@recurring_charge = valid_recurring_charge
|
119
|
+
@recurring_charge.to_hash
|
120
|
+
end
|
121
|
+
|
122
|
+
def valid_recurring_item
|
123
|
+
@recurring_item = RecurringItem.new(
|
124
|
+
:product_id => "Newspaper",
|
125
|
+
:description => "Toronto Star",
|
126
|
+
:quantity => "1",
|
127
|
+
:price => "25.00",
|
128
|
+
:tax1 => "2.00",
|
129
|
+
:tax2 => "1.25"
|
130
|
+
)
|
131
|
+
@recurring_item
|
132
|
+
end
|
133
|
+
|
134
|
+
def valid_recurring_item_hash
|
135
|
+
valid_recurring_item.to_hash
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
Test::Unit::TestCase.class_eval do
|
142
|
+
include RubyPsigate::Assertions
|
143
|
+
include RubyPsigate::ValidModelInstances
|
144
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# require 'test_helper'
|
2
|
+
#
|
3
|
+
# module RubyPsigate
|
4
|
+
# class AccountManagerApiTest < Test::Unit::TestCase
|
5
|
+
#
|
6
|
+
# def setup
|
7
|
+
# @valid_instance_opts = {
|
8
|
+
# :cid => '1000001', :login => 'teststore', :password => 'testpass'
|
9
|
+
# }
|
10
|
+
# @am = AccountManagerApi.new(@valid_instance_opts)
|
11
|
+
#
|
12
|
+
# @valid_register_opts = {
|
13
|
+
# :name => "Bob Parsons",
|
14
|
+
# :company => "Scaler Apps",
|
15
|
+
# :address1 => "1234 10th Avenue",
|
16
|
+
# :address2 => "Unit 123",
|
17
|
+
# :city => "Toronto",
|
18
|
+
# :province => "ON",
|
19
|
+
# :country => "Canada",
|
20
|
+
# :postal_code => "M2N9J9",
|
21
|
+
# :phone => "416-123-1234",
|
22
|
+
# :fax => "416-321-4321",
|
23
|
+
# :email => "hello@world.com",
|
24
|
+
# :comments => "No comments here. Move on.",
|
25
|
+
# :card_holder => "Bob Parsons",
|
26
|
+
# :card_number => "4111111111111111",
|
27
|
+
# :card_exp_month => (Time.now.month).to_s,
|
28
|
+
# :card_exp_year => (Time.now.year+1).to_s
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# context "constants" do
|
34
|
+
# def test_test_url
|
35
|
+
# assert_equal AccountManagerApi::TEST_URL, 'https://dev.psigate.com:8645/Messenger/AMMessenger'
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# context "new instance" do
|
40
|
+
# %w( cid login password ).each do |p|
|
41
|
+
# should "raise error when instantiating a new instance without the parameter #{p}" do
|
42
|
+
# @valid_instance_opts.delete(p.to_sym)
|
43
|
+
# assert_raises(ArgumentError) { AccountManagerApi.new(@valid_instance_opts) }
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# context "registering an account" do
|
49
|
+
# %w( name address1 city province country postal_code phone email card_holder card_number card_exp_month card_exp_year ).each do |p|
|
50
|
+
# should "raise error if #{p} is not included" do
|
51
|
+
# @valid_register_opts.delete(p.to_sym)
|
52
|
+
# assert_raises(ArgumentError) { @am.register(@valid_register_opts) }
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# %w( company address2 fax comments ).each do |p|
|
57
|
+
# should "not raise error if #{p} is not included" do
|
58
|
+
# @valid_register_opts.delete(p.to_sym)
|
59
|
+
# assert_nothing_raised { @am.register(@valid_register_opts) }
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# # should "form a valid request" do
|
64
|
+
# # valid_request = <<-EOF
|
65
|
+
# # <Request>
|
66
|
+
# # <CID>1000001</CID>
|
67
|
+
# # <UserID>teststore</UserID>
|
68
|
+
# # <Password>testpass</Password>
|
69
|
+
# # <Action>AMA00</Action>
|
70
|
+
# # <Condition>
|
71
|
+
# # <AccountID>1234567890</AccountID>
|
72
|
+
# # </Condition>
|
73
|
+
# # </Request>
|
74
|
+
# # EOF
|
75
|
+
# #
|
76
|
+
# # end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# context "retrieving an account summary" do
|
80
|
+
# should "raise error if account_id is not included" do
|
81
|
+
# assert_raises(ArgumentError) { @am.retrieve_summary }
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# should "form a valid request" do
|
85
|
+
# # => Move this to remote test
|
86
|
+
# # response = @am.retrieve_summary(:account_id => "1234567890")
|
87
|
+
# # assert_success response
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
#
|
92
|
+
#
|
93
|
+
#
|
94
|
+
#
|
95
|
+
# end
|
96
|
+
# end
|