sapnwrfc 0.23-i686-linux
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/ext/nwsaprfc/nwsaprfc.so +0 -0
- data/lib/sapnwrfc.rb +217 -0
- data/lib/sapnwrfc/base.rb +41 -0
- data/lib/sapnwrfc/config.rb +113 -0
- data/lib/sapnwrfc/connection.rb +153 -0
- data/lib/sapnwrfc/functions.rb +179 -0
- data/lib/sapnwrfc/parameters.rb +332 -0
- data/lib/sapnwrfc/server.rb +151 -0
- metadata +57 -0
Binary file
|
data/lib/sapnwrfc.rb
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# = SAPNWRFC - SAP Netweaver RFC support for Ruby
|
4
|
+
#
|
5
|
+
# Welcome to sapnwrfc !
|
6
|
+
#
|
7
|
+
# sapnwrfc is an RFC based connector to SAP specifically designed for use with the
|
8
|
+
# next generation RFC SDK supplied by SAP for NW2004+ .
|
9
|
+
#
|
10
|
+
# = Download and Documentation
|
11
|
+
#
|
12
|
+
# Documentation at: http://www.piersharding.com/download/ruby/sapnwrfc/doc/
|
13
|
+
#
|
14
|
+
# Project and Download at: http://raa.ruby-lang.org/project/sapnwrfc
|
15
|
+
#
|
16
|
+
# = Functionality
|
17
|
+
#
|
18
|
+
# The next generation RFCSDK from SAP provides a number of interesting new features. The two most
|
19
|
+
# important are:
|
20
|
+
# * UNICODE support
|
21
|
+
# * deep/nested structures
|
22
|
+
#
|
23
|
+
# The UNICODE support is built fundamentally into the core of the new SDK, and as a result this is reflected in
|
24
|
+
# sapnwrfc. sapnwrfc takes UTF-8 as its only input character set, and handles the translation of this to UTF-16
|
25
|
+
# as required by the RFCSDK.
|
26
|
+
#
|
27
|
+
# Deep and complex structures are now supported fully. Please see the test_deep.rb example in tests/ for
|
28
|
+
# an idea as to how it works.
|
29
|
+
#
|
30
|
+
# sapnwrfc is a departure to the way the original saprfc (http://raa.ruby-lang.org/project/saprfc) works.
|
31
|
+
# It aims to simplify the exchange of native Ruby data types between the user application and the
|
32
|
+
# connector. This means that the following general rules should be observered, when passing values
|
33
|
+
# to and from RFC interface parameters and tables:
|
34
|
+
#
|
35
|
+
# * Tables expect Arrays of Hashes.
|
36
|
+
# * Parameters with structures expect Hashes
|
37
|
+
# * CHAR, DATE, TIME, STRING, XSTRING, and BYTE type parameters expect String values.
|
38
|
+
# * all numeric parameter values must be Fixnum, Bignum or Float.
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# = Building and Installation
|
42
|
+
#
|
43
|
+
# After you have unpacked your kit, you should have all the files listed in the MANIFEST.
|
44
|
+
#
|
45
|
+
# In brief, the following should work on most systems: ruby setup.rb
|
46
|
+
#
|
47
|
+
# if your rfcsdk is not findable in the system search path, then use the command line switches
|
48
|
+
# for mkmf/setup.rb like so:
|
49
|
+
# ruby setup.rb config --with-nwrfcsdk-dir=/path/to/rfcsdk
|
50
|
+
# ruby setup.rb setup
|
51
|
+
# ruby setup.rb install
|
52
|
+
#
|
53
|
+
# You must otain the latest RFCSDK for Netweaver from SAP - this <b>MUST</b> be the next
|
54
|
+
# generation SDK, if you are to have any chance of succeeding.
|
55
|
+
#
|
56
|
+
#
|
57
|
+
# = WIN32 Support
|
58
|
+
#
|
59
|
+
# When Olivier (or anyone else offering) supplies prebuilt GEM files, I make them available
|
60
|
+
# on http://www.piersharding.com/download/ruby/sapnwrfc/ .
|
61
|
+
#
|
62
|
+
#
|
63
|
+
# = Support
|
64
|
+
#
|
65
|
+
# For both community based, and professional support, I can be contacted at Piers Harding <piers@ompka.net>.
|
66
|
+
#
|
67
|
+
# = Examples
|
68
|
+
#
|
69
|
+
# Please see the examples (test/*.rb) distributed with this packages (download source for this) for
|
70
|
+
# a comprehensive shake-down on what you can do.
|
71
|
+
#
|
72
|
+
# Here is a taster using the standard Flight demo BAPIs supplied by SAP:
|
73
|
+
#
|
74
|
+
# require 'sapnwrfc'
|
75
|
+
#
|
76
|
+
# TEST_FILE = 'ubuntu.yml'
|
77
|
+
#
|
78
|
+
# # specify the YAML config source and load
|
79
|
+
# SAPNW::Base.config_location = TEST_FILE
|
80
|
+
# SAPNW::Base.load_config
|
81
|
+
#
|
82
|
+
# # Connec to SAP
|
83
|
+
# conn = SAPNW::Base.rfc_connect
|
84
|
+
#
|
85
|
+
# # Inspect the connection attributes
|
86
|
+
# attrib = conn.connection_attributes
|
87
|
+
# $stderr.print "Connection Attributes: #{attrib.inspect}\n"
|
88
|
+
#
|
89
|
+
# # pull in your RFC definitions
|
90
|
+
# fld = conn.discover("BAPI_FLIGHT_GETLIST")
|
91
|
+
# flgd = conn.discover("BAPI_FLIGHT_GETDETAIL")
|
92
|
+
# fd = conn.discover("BAPI_FLBOOKING_CREATEFROMDATA")
|
93
|
+
#
|
94
|
+
# # get a new handle for each function call to be invoked
|
95
|
+
# fl = fld.new_function_call
|
96
|
+
#
|
97
|
+
# # set the parameters for the call
|
98
|
+
# fl.AIRLINE = "AA "
|
99
|
+
#
|
100
|
+
# # ivoke the call
|
101
|
+
# fl.invoke
|
102
|
+
#
|
103
|
+
# # interogate the results
|
104
|
+
# fl.FLIGHT_LIST.each do |row|
|
105
|
+
# $stderr.print "row: #{row.inspect}\n"
|
106
|
+
#
|
107
|
+
# # for each flight now do another RFC call to get the details
|
108
|
+
# flg = flgd.new_function_call
|
109
|
+
# flg.AIRLINEID = row['AIRLINEID']
|
110
|
+
# flg.CONNECTIONID = row['CONNECTID']
|
111
|
+
# flg.FLIGHTDATE = row['FLIGHTDATE']
|
112
|
+
# flg.invoke
|
113
|
+
# $stderr.print "\tflight data: #{flg.FLIGHT_DATA.inspect}\n"
|
114
|
+
# $stderr.print "\tadditional info: #{flg.ADDITIONAL_INFO.inspect}\n"
|
115
|
+
# $stderr.print "\tavailability: #{flg.AVAILIBILITY.inspect}\n"
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# # create a new booking
|
119
|
+
# fd.name == "BAPI_FLBOOKING_CREATEFROMDATA"
|
120
|
+
# f = fd.new_function_call
|
121
|
+
# f.BOOKING_DATA = { 'AIRLINEID' => "AA ", 'CONNECTID' => "0001", 'FLIGHTDATE' => "20070130",
|
122
|
+
# 'CLASS' => "F", 'CUSTOMERID' => "00000001", 'AGENCYNUM' => '00000093' }
|
123
|
+
#
|
124
|
+
# # trap Function Call exceptions
|
125
|
+
# begin
|
126
|
+
# f.invoke
|
127
|
+
# rescue SAPNW::RFC::FunctionCallException => e
|
128
|
+
# $stderr.print "FunctionCallException: #{e.error.inspect}\n"
|
129
|
+
# raise "gone"
|
130
|
+
# end
|
131
|
+
# f.RETURN.each do |row|
|
132
|
+
# $stderr.print "row: #{row.inspect}\n"
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# # use the standard COMMIT BAPI to commit the update
|
136
|
+
# cd = conn.discover("BAPI_TRANSACTION_COMMIT")
|
137
|
+
# c = cd.new_function_call
|
138
|
+
# c.WAIT = "X"
|
139
|
+
# c.invoke
|
140
|
+
#
|
141
|
+
# # close the RFC connection and destroy associated resources
|
142
|
+
# conn.close
|
143
|
+
#
|
144
|
+
#
|
145
|
+
#
|
146
|
+
# = A Closer Look At Handling Parameters
|
147
|
+
#
|
148
|
+
# Complex parameter types map naturally to Ruby data types.
|
149
|
+
# Parameters that require structures are represented by a Hash of field/value pairs eg:
|
150
|
+
# f.BOOKING_DATA = { 'AIRLINEID' => "AA ", 'CONNECTID' => "0001", 'FLIGHTDATE' => "20070130",
|
151
|
+
# 'CLASS' => "F", 'CUSTOMERID' => "00000001", 'AGENCYNUM' => '00000093' }
|
152
|
+
# Parameters that require tables are represented by an Array of Hashes of field/value pairs eg:
|
153
|
+
# ft.IMPORT_TAB = [{ 'I' => 123, 'C' => 'AbCdEf',
|
154
|
+
# 'STR' => 'The quick brown fox ...',
|
155
|
+
# 'XSTR' => ["deadbeef"].pack("H*") }]
|
156
|
+
#
|
157
|
+
#
|
158
|
+
# = Building your RFC Call
|
159
|
+
#
|
160
|
+
# When building a call for client-side RFC, you should always be inspecting the requirements
|
161
|
+
# of the RFC call by using transaction SE37 first. You should also be in the the habit of
|
162
|
+
# testing out your RFC calls first using SE37 too. YOu would be amazed how much this simple
|
163
|
+
# approach will save you (and me) time.
|
164
|
+
#
|
165
|
+
#
|
166
|
+
#
|
167
|
+
# = Thanks to:
|
168
|
+
#
|
169
|
+
# * Olivier Boudry - an Open Source guy
|
170
|
+
# * Craig Cmehil - SAP, and SDN
|
171
|
+
# * Ulrich Schmidt - SAP
|
172
|
+
# For their help in making this possible.
|
173
|
+
#
|
174
|
+
#
|
175
|
+
#
|
176
|
+
#
|
177
|
+
# Author:: Piers Harding <piers@ompka.net>
|
178
|
+
# License:: Copyright (c) 2006-2008 Piers Harding. sapnwrfc is free software, and may be redistributed under the terms specified in the README file of the Ruby distribution.
|
179
|
+
# Requires:: Ruby 1.9.0 or later
|
180
|
+
#
|
181
|
+
|
182
|
+
|
183
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
184
|
+
|
185
|
+
require 'yaml'
|
186
|
+
require 'fileutils'
|
187
|
+
require 'logger'
|
188
|
+
|
189
|
+
# setup defaults for logging
|
190
|
+
SAP_LOGGER = Logger.new(STDERR)
|
191
|
+
SAP_LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S"
|
192
|
+
SAP_LOGGER.level = Logger::WARN
|
193
|
+
|
194
|
+
require 'sapnwrfc/base'
|
195
|
+
|
196
|
+
# C extension
|
197
|
+
begin
|
198
|
+
require 'nwsaprfc'
|
199
|
+
rescue LoadError => e
|
200
|
+
SAP_LOGGER.error("Could not load nwsaprfc. Make sure nwrfcsdk libraries are properly installed: #{e.message}")
|
201
|
+
raise e
|
202
|
+
end
|
203
|
+
|
204
|
+
require 'sapnwrfc/config'
|
205
|
+
require 'sapnwrfc/connection'
|
206
|
+
require 'sapnwrfc/server'
|
207
|
+
require 'sapnwrfc/functions'
|
208
|
+
require 'sapnwrfc/parameters'
|
209
|
+
|
210
|
+
SAPNW::Base.class_eval do
|
211
|
+
include SAPNW::Config
|
212
|
+
include SAPNW::Connections
|
213
|
+
include SAPNW::Servers
|
214
|
+
include SAPNW::Functions
|
215
|
+
include SAPNW::Parameters
|
216
|
+
end
|
217
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# SAPNW is Copyright (c) 2006-2008 Piers Harding. It is free software, and
|
2
|
+
# may be redistributed under the terms specified in the README file of
|
3
|
+
# the Ruby distribution.
|
4
|
+
#
|
5
|
+
# Welcome to sapnwrfc !
|
6
|
+
#
|
7
|
+
# sapnwrfc is a RFC based connector to SAP specifically designed for use with the
|
8
|
+
# next generation RFC SDK supplied by SAP for NW2004+ .
|
9
|
+
#
|
10
|
+
#
|
11
|
+
#
|
12
|
+
|
13
|
+
module SAPNW
|
14
|
+
|
15
|
+
# Some doco in the SAPNW module
|
16
|
+
class Base
|
17
|
+
|
18
|
+
# some doco in Base
|
19
|
+
|
20
|
+
@@rfc_connections = {}
|
21
|
+
|
22
|
+
|
23
|
+
# def rfc_connection(*args)
|
24
|
+
# return SAPNW::RFC::Connection.connect(args)
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# def rfc_register(*args)
|
28
|
+
# return SAPNW::RFC::Server.connect(args)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def installFunction(*args)
|
32
|
+
# return SAPNW::RFC::Server.installFunction(args)
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
def Base.finalize(id)
|
36
|
+
SAP_LOGGER.debug("[#{self.class}] finalize called")
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# SAPNW is Copyright (c) 2006-2008 Piers Harding. It is free software, and
|
2
|
+
# may be redistributed under the terms specified in the README file of
|
3
|
+
# the Ruby distribution.
|
4
|
+
#
|
5
|
+
# Configuration for an RFC connection can be passed in two distinct ways, but those two methods
|
6
|
+
# can be combined together.
|
7
|
+
#
|
8
|
+
# (1) Config is loaded via a YAML based config file
|
9
|
+
# (2) Config is passed in the code directly into the connection
|
10
|
+
# (3) a combination of (1) and (2), where (2) overides (1) at run time.
|
11
|
+
#
|
12
|
+
# YAML config file format:
|
13
|
+
#
|
14
|
+
# ashost: ubuntu.local.net
|
15
|
+
# sysnr: "01"
|
16
|
+
# client: "001"
|
17
|
+
# user: developer
|
18
|
+
# passwd: developer
|
19
|
+
# lang: EN
|
20
|
+
# trace: 2
|
21
|
+
#
|
22
|
+
# At connection time, any valid parameters can be added or over ridden:
|
23
|
+
#
|
24
|
+
# conn = SAPNW::Base.rfc_connect(:user => 'developer', :passwd => 'developer')
|
25
|
+
#
|
26
|
+
# Valid connection parameters are:
|
27
|
+
# ashost
|
28
|
+
# dest - used in conjunction with sapnwrfc.ini file
|
29
|
+
# mshost
|
30
|
+
# group
|
31
|
+
# sysid
|
32
|
+
# msserv
|
33
|
+
# sysnr
|
34
|
+
# lang
|
35
|
+
# client
|
36
|
+
# user
|
37
|
+
# passwd
|
38
|
+
# trace
|
39
|
+
# tpname
|
40
|
+
# gwhost
|
41
|
+
# gwserv
|
42
|
+
# codepage
|
43
|
+
# x509cert
|
44
|
+
# extiddata
|
45
|
+
# extidtype
|
46
|
+
# mysapsso2
|
47
|
+
# mysapsso
|
48
|
+
# getsso2
|
49
|
+
# snc_mode
|
50
|
+
# snc_qop
|
51
|
+
# snc_myname
|
52
|
+
# snc_partnername
|
53
|
+
# snc_lib
|
54
|
+
#
|
55
|
+
#
|
56
|
+
#
|
57
|
+
# Author:: Piers Harding <piers@ompka.net>
|
58
|
+
# Requires:: Ruby 1.9 or later
|
59
|
+
#
|
60
|
+
|
61
|
+
Logger.class_eval do
|
62
|
+
def set_logdev(logfile, logfile_age = 0, logfile_size = 1048576)
|
63
|
+
@logdev = Logger::LogDevice.new(logfile, :shift_age => logfile_age, :shift_size => logfile_size)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module SAPNW
|
68
|
+
module Config
|
69
|
+
class << SAPNW::Base
|
70
|
+
|
71
|
+
@configuration = {}
|
72
|
+
|
73
|
+
attr_accessor :config_location, :config
|
74
|
+
|
75
|
+
def load_config
|
76
|
+
file = self.config_location || './sap.yml'
|
77
|
+
SAP_LOGGER.fatal("[#{self.name}] Configuration file not found: #{file}") unless FileTest.exists?(file)
|
78
|
+
self.config = File.open(file) { |f| YAML::load(f) }
|
79
|
+
SAP_LOGGER.debug("[#{self.name}] Configuration: " + self.config.inspect)
|
80
|
+
|
81
|
+
if self.config.key? 'logfile'
|
82
|
+
if /STDOUT/i.match(self.config['logfile'])
|
83
|
+
SAP_LOGGER.set_logdev(STDOUT)
|
84
|
+
else
|
85
|
+
SAP_LOGGER.set_logdev(self.config['logfile'],
|
86
|
+
self.config['logfile_age'] || 0,
|
87
|
+
self.config['logfile_size'] || 1048576)
|
88
|
+
SAP_LOGGER.datetime_format = "%Y-%m-%d %H:%M:%S"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
if self.config.key? 'loglevel'
|
92
|
+
case self.config['loglevel'].upcase
|
93
|
+
when 'FATAL'
|
94
|
+
SAP_LOGGER.level = Logger::FATAL
|
95
|
+
when 'ERROR'
|
96
|
+
SAP_LOGGER.level = Logger::ERROR
|
97
|
+
when 'WARN'
|
98
|
+
SAP_LOGGER.level = Logger::WARN
|
99
|
+
when 'INFO'
|
100
|
+
SAP_LOGGER.level = Logger::INFO
|
101
|
+
when 'DEBUG'
|
102
|
+
SAP_LOGGER.level = Logger::DEBUG
|
103
|
+
end
|
104
|
+
else
|
105
|
+
# set default
|
106
|
+
SAP_LOGGER.level = Logger::WARN
|
107
|
+
end
|
108
|
+
return self.config
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# SAPNW is Copyright (c) 2006-2008 Piers Harding. It is free software, and
|
2
|
+
# may be redistributed under the terms specified in the README file of
|
3
|
+
# the Ruby distribution.
|
4
|
+
#
|
5
|
+
# Author:: Piers Harding <piers@ompka.net>
|
6
|
+
# Requires:: Ruby 1.9 or later
|
7
|
+
#
|
8
|
+
|
9
|
+
module SAPNW
|
10
|
+
module Connections
|
11
|
+
|
12
|
+
class << SAPNW::Base
|
13
|
+
|
14
|
+
# Build a connection to an R/3 system (ABAP AS)
|
15
|
+
#
|
16
|
+
# Connection parameters can be passed in a variety of ways
|
17
|
+
# (a) via a YAML based configuration file
|
18
|
+
# (b) a Hash of parameter arguments
|
19
|
+
# (c) a combination of both
|
20
|
+
def rfc_connect(args = nil)
|
21
|
+
parms = {}
|
22
|
+
self.config = {} unless self.config
|
23
|
+
parms[:ashost] = self.config['ashost'] if self.config.key? 'ashost'
|
24
|
+
parms[:dest] = self.config['dest'] if self.config.key? 'dest'
|
25
|
+
parms[:mshost] = self.config['mshost'] if self.config.key? 'mshost'
|
26
|
+
parms[:group] = self.config['group'] if self.config.key? 'group'
|
27
|
+
parms[:sysid] = self.config['sysid'] if self.config.key? 'sysid'
|
28
|
+
parms[:msserv] = self.config['msserv'] if self.config.key? 'msserv'
|
29
|
+
parms[:sysnr] = self.config['sysnr'] if self.config.key? 'sysnr'
|
30
|
+
parms[:lang] = self.config['lang'] if self.config.key? 'lang'
|
31
|
+
parms[:client] = self.config['client'] if self.config.key? 'client'
|
32
|
+
parms[:user] = self.config['user'] if self.config.key? 'user'
|
33
|
+
parms[:passwd] = self.config['passwd'] if self.config.key? 'passwd'
|
34
|
+
parms[:trace] = self.config['trace'].to_i if self.config.key? 'trace'
|
35
|
+
parms[:codepage] = self.config['codepage'].to_i if self.config.key? 'codepage'
|
36
|
+
parms[:x509cert] = self.config['x509cert'] if self.config.key? 'x509cert'
|
37
|
+
parms[:extiddata] = self.config['extiddata'] if self.config.key? 'extiddata'
|
38
|
+
parms[:extidtype] = self.config['extidtype'] if self.config.key? 'extidtype'
|
39
|
+
parms[:mysapsso2] = self.config['mysapsso2'] if self.config.key? 'mysapsso2'
|
40
|
+
parms[:mysapsso] = self.config['mysapsso'] if self.config.key? 'mysapsso'
|
41
|
+
parms[:getsso2] = self.config['getsso2'].to_i if self.config.key? 'getsso2'
|
42
|
+
parms[:snc_mode] = self.config['snc_mode'] if self.config.key? 'snc_mode'
|
43
|
+
parms[:snc_qop] = self.config['snc_qop'] if self.config.key? 'snc_qop'
|
44
|
+
parms[:snc_myname] = self.config['snc_myname'] if self.config.key? 'snc_myname'
|
45
|
+
parms[:snc_partnername] = self.config['snc_partnername'] if self.config.key? 'snc_partnername'
|
46
|
+
parms[:snc_lib] = self.config['snc_lib'] if self.config.key? 'snc_lib'
|
47
|
+
SAP_LOGGER.debug("[" + self.name + "] base parameters to be passed are: " + parms.inspect)
|
48
|
+
|
49
|
+
case args
|
50
|
+
when nil
|
51
|
+
when Hash
|
52
|
+
parms[:ashost] = args[:ashost] if args.key? :ashost
|
53
|
+
parms[:dest] = args[:dest] if args.key? :dest
|
54
|
+
parms[:mshost] = args[:mshost] if args.key? :mshost
|
55
|
+
parms[:sysid] = args[:sysid] if args.key? :sysid
|
56
|
+
parms[:group] = args[:group] if args.key? :group
|
57
|
+
parms[:msserv] = args[:msserv] if args.key? :msserv
|
58
|
+
parms[:sysnr] = args[:sysnr] if args.key? :sysnr
|
59
|
+
parms[:lang] = args[:lang] if args.key? :lang
|
60
|
+
parms[:client] = args[:client] if args.key? :client
|
61
|
+
parms[:user] = args[:user] if args.key? :user
|
62
|
+
parms[:passwd] = args[:passwd] if args.key? :passwd
|
63
|
+
parms[:trace] = args[:trace].to_i if args.key? :trace
|
64
|
+
parms[:codepage] = args[:codepage].to_i if args.key? :codepage
|
65
|
+
parms[:x509cert] = args[:x509cert] if args.key? :x509cert
|
66
|
+
parms[:extiddata] = args[:extiddata] if args.key? :extiddata
|
67
|
+
parms[:extidtype] = args[:extidtype] if args.key? :extidtype
|
68
|
+
parms[:mysapsso2] = args[:mysapsso2] if args.key? :mysapsso2
|
69
|
+
parms[:mysapsso] = args[:mysapsso] if args.key? :mysapsso
|
70
|
+
parms[:getsso2] = args[:getsso2].to_i if args.key? :getsso2
|
71
|
+
parms[:snc_mode] = args[:snc_mode] if args.key? :snc_mode
|
72
|
+
parms[:snc_qop] = args[:snc_qop] if args.key? :snc_qop
|
73
|
+
parms[:snc_myname] = args[:snc_myname] if args.key? :snc_myname
|
74
|
+
parms[:snc_partnername] = args[:snc_partnername] if args.key? :snc_partnername
|
75
|
+
parms[:snc_lib] = args[:snc_lib] if args.key? :snc_lib
|
76
|
+
SAP_LOGGER.debug("[" + self.name + "] with EXTRA parameters to be passed are: " + parms.inspect)
|
77
|
+
else
|
78
|
+
raise "Wrong parameters for Connection - must pass a Hash\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
connection = SAPNW::RFC::Connection.new(parms)
|
83
|
+
SAP_LOGGER.debug("completed the connection (#{connection.handle.class}/#{connection.handle.object_id}) ...")
|
84
|
+
return connection
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module RFC
|
91
|
+
class Connection
|
92
|
+
attr_accessor :handle
|
93
|
+
attr_reader :connection_parameters, :functions
|
94
|
+
|
95
|
+
def initialize(args = nil)
|
96
|
+
@connection_parameters = []
|
97
|
+
case args
|
98
|
+
when nil
|
99
|
+
when Hash
|
100
|
+
args.each_pair { |key, val|
|
101
|
+
@connection_parameters << {'name' => key.to_s, 'value' => val.to_s}
|
102
|
+
}
|
103
|
+
else
|
104
|
+
raise "Wrong parameters for Connection - must pass a Hash\n"
|
105
|
+
end
|
106
|
+
SAP_LOGGER.debug("In #{self.class} initialize: #{@connection_parameters.inspect} ...")
|
107
|
+
@functions = {}
|
108
|
+
@handle = SAPNW::RFC::Handle.new(self)
|
109
|
+
end
|
110
|
+
|
111
|
+
def connection_attributes
|
112
|
+
SAP_LOGGER.debug("In #{self.class} connection_attributes ...")
|
113
|
+
return self.handle.connection_attributes()
|
114
|
+
end
|
115
|
+
|
116
|
+
# discover() looks up the dictionary definition of an RFC interface
|
117
|
+
# storing away the meta data of the associated Parameters/Tables and returns this
|
118
|
+
# as an instance of SAPNW::RFC::FunctionDescriptor. This is the MANDATORY starting
|
119
|
+
# point of all client side RFC.
|
120
|
+
def discover(func = nil)
|
121
|
+
SAP_LOGGER.debug("In #{self.class} discover (#{func}) ...")
|
122
|
+
case func
|
123
|
+
when nil
|
124
|
+
return nil
|
125
|
+
else
|
126
|
+
func_def = self.handle.function_lookup(SAPNW::RFC::FunctionDescriptor, SAPNW::RFC::Parameter, func.to_s)
|
127
|
+
@functions[func_def.name] = func_def
|
128
|
+
return func_def
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# terminate an established RFC connection. This will invalidate any currently in-scope
|
133
|
+
# FunctionDescriptors associated with this Connection.
|
134
|
+
def close
|
135
|
+
SAP_LOGGER.debug("In #{self.class} close ...")
|
136
|
+
return nil unless self.handle
|
137
|
+
SAP_LOGGER.debug("In #{self.class} handle: #{self.handle} ...")
|
138
|
+
res = self.handle.close()
|
139
|
+
self.handle = nil
|
140
|
+
# XXX Should destroy all cached functions and structures and types tied to handle ?
|
141
|
+
return true
|
142
|
+
end
|
143
|
+
|
144
|
+
# ping test a connection to see if it is still alive
|
145
|
+
def ping
|
146
|
+
SAP_LOGGER.debug("In #{self.class} ping ...")
|
147
|
+
return nil unless self.handle
|
148
|
+
SAP_LOGGER.debug("In #{self.class} handle: #{self.handle} ...")
|
149
|
+
return self.handle.ping()
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# SAPNW is Copyright (c) 2006-2008 Piers Harding. It is free software, and
|
2
|
+
# may be redistributed under the terms specified in the README file of
|
3
|
+
# the Ruby distribution.
|
4
|
+
#
|
5
|
+
# Author:: Piers Harding <piers@ompka.net>
|
6
|
+
# Requires:: Ruby 1.9 or later
|
7
|
+
#
|
8
|
+
|
9
|
+
module SAPNW
|
10
|
+
module Functions
|
11
|
+
class Base
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module RFC
|
17
|
+
|
18
|
+
# These are automatically created as a result of SAPNW::RFC::Connection#discover() -
|
19
|
+
# do not instantiate these yourself yourself!
|
20
|
+
#
|
21
|
+
class FunctionDescriptor
|
22
|
+
attr_reader :name, :parameters
|
23
|
+
attr_accessor :callback
|
24
|
+
|
25
|
+
# create a new SAPNW::RFC::FunctionCall object for this FunctionDescriptor.
|
26
|
+
#
|
27
|
+
# You must call this each time that you want to invoke() a new function call, as
|
28
|
+
# this creates a one shot container for the passing back and forth of interface parameters.
|
29
|
+
def new_function_call
|
30
|
+
return create_function_call(SAPNW::RFC::FunctionCall)
|
31
|
+
end
|
32
|
+
|
33
|
+
def make_empty_function_call
|
34
|
+
return SAPNW::RFC::FunctionCall.new(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def callback=(proc)
|
38
|
+
if proc.instance_of?(Proc)
|
39
|
+
@callback = proc
|
40
|
+
else
|
41
|
+
raise "Must pass in an instance of Proc for the callback"
|
42
|
+
end
|
43
|
+
return @callback
|
44
|
+
end
|
45
|
+
|
46
|
+
def handler(function)
|
47
|
+
begin
|
48
|
+
return @callback.call(function)
|
49
|
+
rescue SAPNW::RFC::ServerException => e
|
50
|
+
#$stderr.print "ServerException => #{e.error.inspect}\n"
|
51
|
+
return e
|
52
|
+
rescue StandardError => e
|
53
|
+
#$stderr.print "StandardError => #{e.inspect}/#{e.message}\n"
|
54
|
+
return SAPNW::RFC::ServerException.new({'code' => 3, 'key' => 'RUBY_RUNTIME', 'message' => e.message})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(methid, *rest)
|
59
|
+
meth = methid.id2name
|
60
|
+
if @parameters.has_key?(meth)
|
61
|
+
return @parameters[meth]
|
62
|
+
else
|
63
|
+
raise NoMethodError
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# internal method used to add parameters from within the C extension
|
68
|
+
#def addParameter(name = nil, direction = 0, type = 0, len = 0, ulen = 0, decimals = 0)
|
69
|
+
def addParameter(*parms)
|
70
|
+
parms = parms.first if parms.class == Array and (parms.first.class == Hash || parms.first.kind_of?(SAPNW::RFC::Parameter))
|
71
|
+
case parms
|
72
|
+
when Array
|
73
|
+
name, direction, type, len, ulen, decimals = parms
|
74
|
+
when Hash
|
75
|
+
name = parms.has_key?(:name) ? parms[:name] : nil
|
76
|
+
direction = parms.has_key?(:direction) ? parms[:direction] : nil
|
77
|
+
type = parms.has_key?(:type) ? parms[:type] : nil
|
78
|
+
len = parms.has_key?(:len) ? parms[:len] : nil
|
79
|
+
ulen = parms.has_key?(:ulen) ? parms[:ulen] : nil
|
80
|
+
decimals = parms.has_key?(:decimals) ? parms[:decimals] : nil
|
81
|
+
when SAPNW::RFC::Export, SAPNW::RFC::Import, SAPNW::RFC::Changing, SAPNW::RFC::Table
|
82
|
+
# this way happens when a function def is manually defined
|
83
|
+
self.add_parameter(parms)
|
84
|
+
@parameters[parms.name] = parms
|
85
|
+
return parms
|
86
|
+
else
|
87
|
+
raise "invalid SAPNW::RFC::FunctionDescriptor parameter supplied: #{parms.inspect}\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
#$stderr.print "parm: #{name} direction: #{direction} type: #{type} len: #{len} decimals: #{decimals}\n"
|
91
|
+
case direction
|
92
|
+
when SAPNW::RFC::IMPORT
|
93
|
+
if @parameters.has_key?(name) and @parameters[name].direction == SAPNW::RFC::EXPORT
|
94
|
+
p = SAPNW::RFC::Changing.new(self, name, type, len, ulen, decimals)
|
95
|
+
else
|
96
|
+
p = SAPNW::RFC::Import.new(self, name, type, len, ulen, decimals)
|
97
|
+
end
|
98
|
+
when SAPNW::RFC::EXPORT
|
99
|
+
if @parameters.has_key?(name) and @parameters[name].direction == SAPNW::RFC::IMPORT
|
100
|
+
p = SAPNW::RFC::Changing.new(self, name, type, len, ulen, decimals)
|
101
|
+
else
|
102
|
+
p = SAPNW::RFC::Export.new(self, name, type, len, ulen, decimals)
|
103
|
+
end
|
104
|
+
when SAPNW::RFC::CHANGING
|
105
|
+
p = SAPNW::RFC::Changing.new(self, name, type, len, ulen, decimals)
|
106
|
+
when SAPNW::RFC::TABLES
|
107
|
+
p = SAPNW::RFC::Table.new(self, name, type, len, ulen, decimals)
|
108
|
+
else
|
109
|
+
raise "unknown direction (#{name}): #{direction}\n"
|
110
|
+
end
|
111
|
+
@parameters[p.name] = p
|
112
|
+
return p
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
class FunctionCall
|
118
|
+
attr_reader :function_descriptor, :name, :parameters
|
119
|
+
|
120
|
+
# These are automatically created as a result of SAPNW::RFC::FunctionDescriptor#new_function_call() -
|
121
|
+
# do not instantiate these yourself!
|
122
|
+
#
|
123
|
+
# SAPNW::RFC::FunctionCall objects allow dynamic method calls of parameter, and table names
|
124
|
+
# for the setting and getting of interface values eg:
|
125
|
+
# fd = conn.discover("RFC_READ_TABLE")
|
126
|
+
# f = fd.new_function_call
|
127
|
+
# f.QUERY_TABLE = "T000" # <- QUERY_TABLE is a dynamic method serviced by method_missing
|
128
|
+
#def initialize(fd=nil)
|
129
|
+
def initialize(fd=nil)
|
130
|
+
@parameters = {}
|
131
|
+
if fd == nil
|
132
|
+
@function_descriptor.parameters.each_pair do |k, v|
|
133
|
+
@parameters[k] = v.clone
|
134
|
+
end
|
135
|
+
else
|
136
|
+
fd.parameters.each_pair do |k, v|
|
137
|
+
@parameters[k] = v.clone
|
138
|
+
end
|
139
|
+
end
|
140
|
+
@parameters_list = @parameters.values || []
|
141
|
+
end
|
142
|
+
|
143
|
+
# activate a parameter - parameters are active by default so it is unlikely that this
|
144
|
+
# would ever need to be called.
|
145
|
+
def activate(parm=nil)
|
146
|
+
raise "Parameter not found: #{parm}\n" unless @parameters.has_key?(parm)
|
147
|
+
return set_active(parm, 1)
|
148
|
+
end
|
149
|
+
|
150
|
+
# deactivate a parameter - parameters can be deactivated for a function call, to reduce the
|
151
|
+
# amount of RFC traffic on the wire. This is especially important for unrequired tables, or
|
152
|
+
# parameters that are similar sources of large data transfer.
|
153
|
+
def deactivate(parm=nil)
|
154
|
+
raise "Parameter not found: #{parm}\n" unless @parameters.has_key?(parm)
|
155
|
+
return set_active(parm, 0)
|
156
|
+
end
|
157
|
+
|
158
|
+
# dynamic method calls for parameters and tables
|
159
|
+
def method_missing(methid, *rest)
|
160
|
+
meth = methid.id2name
|
161
|
+
#$stderr.print "method_missing: #{meth}\n"
|
162
|
+
#$stderr.print "parameters: #{@parameters.keys.inspect}\n"
|
163
|
+
if @parameters.has_key?(meth)
|
164
|
+
#$stderr.print "return parm obj\n"
|
165
|
+
return @parameters[meth].value
|
166
|
+
elsif mat = /^(.*?)\=$/.match(meth)
|
167
|
+
#$stderr.print "return parm val\n"
|
168
|
+
if @parameters.has_key?(mat[1])
|
169
|
+
return @parameters[mat[1]].value = rest[0]
|
170
|
+
else
|
171
|
+
raise NoMethError
|
172
|
+
end
|
173
|
+
else
|
174
|
+
raise NoMethodError
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
# SAPNW is Copyright (c) 2006-2008 Piers Harding. It is free software, and
|
2
|
+
# may be redistributed under the terms specified in the README file of
|
3
|
+
# the Ruby distribution.
|
4
|
+
#
|
5
|
+
# Author:: Piers Harding <piers@ompka.net>
|
6
|
+
# Requires:: Ruby 1.9 or later
|
7
|
+
#
|
8
|
+
|
9
|
+
module SAPNW
|
10
|
+
module Parameters
|
11
|
+
|
12
|
+
# base class for all parameters
|
13
|
+
class Base
|
14
|
+
|
15
|
+
# they all have:
|
16
|
+
# a name
|
17
|
+
# may or may not have a structure = a type
|
18
|
+
# type may be complex or simple
|
19
|
+
#
|
20
|
+
#
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module RFC
|
26
|
+
|
27
|
+
# Parameter types
|
28
|
+
IMPORT = 1
|
29
|
+
EXPORT = 2
|
30
|
+
CHANGING = 3
|
31
|
+
TABLES = 7
|
32
|
+
|
33
|
+
# basic data types
|
34
|
+
CHAR = 0
|
35
|
+
DATE = 1
|
36
|
+
BCD = 2
|
37
|
+
TIME = 3
|
38
|
+
BYTE = 4
|
39
|
+
TABLE = 5
|
40
|
+
NUM = 6
|
41
|
+
FLOAT = 7
|
42
|
+
INT = 8
|
43
|
+
INT2 = 9
|
44
|
+
INT1 = 10
|
45
|
+
NULL = 14
|
46
|
+
STRUCTURE = 17
|
47
|
+
DECF16 = 23
|
48
|
+
DECF34 = 24
|
49
|
+
XMLDATA = 28
|
50
|
+
STRING = 29
|
51
|
+
XSTRING = 30
|
52
|
+
EXCEPTION = 98
|
53
|
+
|
54
|
+
# return codes
|
55
|
+
RFC_OK = 0
|
56
|
+
RFC_COMMUNICATION_FAILURE = 1
|
57
|
+
RFC_LOGON_FAILURE = 2
|
58
|
+
RFC_ABAP_RUNTIME_FAILURE = 3
|
59
|
+
RFC_ABAP_MESSAGE = 4
|
60
|
+
RFC_ABAP_EXCEPTION = 5
|
61
|
+
RFC_CLOSED = 6
|
62
|
+
RFC_CANCELED = 7
|
63
|
+
RFC_TIMEOUT = 8
|
64
|
+
RFC_MEMORY_INSUFFICIENT = 9
|
65
|
+
RFC_VERSION_MISMATCH = 10
|
66
|
+
RFC_INVALID_PROTOCOL = 11
|
67
|
+
RFC_SERIALIZATION_FAILURE = 12
|
68
|
+
RFC_INVALID_HANDLE = 13
|
69
|
+
RFC_RETRY = 14
|
70
|
+
RFC_EXTERNAL_FAILURE = 15
|
71
|
+
RFC_EXECUTED = 16
|
72
|
+
RFC_NOT_FOUND = 17
|
73
|
+
RFC_NOT_SUPPORTED = 18
|
74
|
+
RFC_ILLEGAL_STATE = 19
|
75
|
+
RFC_INVALID_PARAMETER = 20
|
76
|
+
RFC_CODEPAGE_CONVERSION_FAILURE = 21
|
77
|
+
RFC_CONVERSION_FAILURE = 22
|
78
|
+
RFC_BUFFER_TOO_SMALL = 23
|
79
|
+
RFC_TABLE_MOVE_BOF = 24
|
80
|
+
RFC_TABLE_MOVE_EOF = 25
|
81
|
+
|
82
|
+
|
83
|
+
# Base class for all Parameter types
|
84
|
+
class Parameter < SAPNW::Parameters::Base
|
85
|
+
attr_reader :name, :type, :typdef, :direction, :len, :ulen, :decimals, :value
|
86
|
+
|
87
|
+
# constructor called only from the SAPNW::RFC::Connector#discover process
|
88
|
+
#def initialize(funcdesc, name, type, len, ulen, decimals)
|
89
|
+
def initialize(*parms)
|
90
|
+
parms = parms.first if parms.class == Array and parms.first.class == Hash
|
91
|
+
case parms
|
92
|
+
when Array
|
93
|
+
# this way happens when the interface of the parameter has been discover()ed
|
94
|
+
funcdesc, name, type, len, ulen, decimals, typedef = parms
|
95
|
+
when Hash
|
96
|
+
# This way happens when a parameter is being manually constructed
|
97
|
+
raise "Missing parameter :name => #{parms.inspect}\n" unless parms.has_key?(:name)
|
98
|
+
raise "Missing parameter :type => #{parms.inspect}\n" unless parms.has_key?(:type)
|
99
|
+
case parms[:type]
|
100
|
+
when SAPNW::RFC::CHAR, SAPNW::RFC::DATE, SAPNW::RFC::BCD, SAPNW::RFC::TIME, SAPNW::RFC::BYTE, SAPNW::RFC::TABLE, SAPNW::RFC::NUM, SAPNW::RFC::FLOAT, SAPNW::RFC::INT, SAPNW::RFC::INT2, SAPNW::RFC::INT1, SAPNW::RFC::NULL, SAPNW::RFC::STRUCTURE, SAPNW::RFC::DECF16, SAPNW::RFC::DECF34, SAPNW::RFC::XMLDATA, SAPNW::RFC::STRING, SAPNW::RFC::XSTRING, SAPNW::RFC::EXCEPTION
|
101
|
+
else
|
102
|
+
if parms[:type].class == SAPNW::RFC::Type
|
103
|
+
parms[:typedef] = parms[:type]
|
104
|
+
parms[:type] = parms[:typedef].type
|
105
|
+
raise "Parameter type (#{self.class}) does not match Type type (#{parms[:typedef].inspect})\n" if self.class == SAPNW::RFC::Table and parms[:type] != SAPNW::RFC::TABLE
|
106
|
+
else
|
107
|
+
raise "Invalid SAPNW::RFC* type supplied (#{parms[:type]})\n"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
funcdesc = nil
|
111
|
+
len = 0
|
112
|
+
ulen = 0
|
113
|
+
decimals = 0
|
114
|
+
name = parms[:name]
|
115
|
+
type = parms[:type]
|
116
|
+
typedef = parms[:typedef] if parms.has_key?(:typedef)
|
117
|
+
len = parms[:len] if parms.has_key?(:len)
|
118
|
+
ulen = parms[:ulen] if parms.has_key?(:ulen)
|
119
|
+
decimals = parms[:decimals] if parms.has_key?(:decimals)
|
120
|
+
else
|
121
|
+
raise "invalid parameters: #{parms.inspect}\n"
|
122
|
+
end
|
123
|
+
@function_descriptor = funcdesc
|
124
|
+
@name = name
|
125
|
+
@type = type
|
126
|
+
@typedef = typedef
|
127
|
+
@len = len
|
128
|
+
@ulen = ulen
|
129
|
+
@decimals = decimals
|
130
|
+
@value = nil
|
131
|
+
#$stderr.print "initilised parameter(#{@name}): #{self.inspect}\n"
|
132
|
+
end
|
133
|
+
|
134
|
+
# method_missing is used to pass on any method call to a parameter
|
135
|
+
# to the underlying native Ruby data type
|
136
|
+
def method_missing(methid, *rest, &block)
|
137
|
+
meth = methid.id2name
|
138
|
+
#$stderr.print "method_missing: #{meth}\n"
|
139
|
+
#$stderr.print "parameters: #{@parameters.keys.inspect}\n"
|
140
|
+
if block
|
141
|
+
@value.send(meth, &block)
|
142
|
+
else
|
143
|
+
#$stderr.print "Export method_missing - no block: #{meth}\n"
|
144
|
+
@value.send(meth, *rest)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# value setting for parameters - does basic Type checking to preserve
|
149
|
+
# sanity for the underlying C extension
|
150
|
+
def value=(val=nil)
|
151
|
+
#$stderr.print "setting: #{@name} type: #{@type} value: #{val}/#{val.class}\n"
|
152
|
+
case @type
|
153
|
+
when SAPNW::RFC::INT, SAPNW::RFC::INT2, SAPNW::RFC::INT1
|
154
|
+
unless val.is_a?(Fixnum)
|
155
|
+
raise TypeError, "Must be Fixnum for INT, INT1, and INT2 (#{@name}/#{@type}/#{val.class})\n"
|
156
|
+
end
|
157
|
+
when SAPNW::RFC::NUM
|
158
|
+
unless val.is_a?(String)
|
159
|
+
raise TypeError, "Must be String for NUMC (#{@name}/#{@type}/#{val.class})\n"
|
160
|
+
end
|
161
|
+
when SAPNW::RFC::BCD
|
162
|
+
unless val.is_a?(Float) || val.is_a?(Fixnum) || val.is_a?(Bignum)
|
163
|
+
raise TypeError, "Must be FLoat or *NUM for BCD (#{@name}/#{@type}/#{val.class})\n"
|
164
|
+
end
|
165
|
+
val = val.to_s
|
166
|
+
when SAPNW::RFC::FLOAT
|
167
|
+
unless val.is_a?(Float)
|
168
|
+
raise TypeError, "Must be FLoat for FLOAT (#{@name}/#{@type}/#{val.class})\n"
|
169
|
+
end
|
170
|
+
when SAPNW::RFC::STRING, SAPNW::RFC::XSTRING
|
171
|
+
unless val.is_a?(String)
|
172
|
+
raise TypeError, "Must be String for STRING, and XSTRING (#{@name}/#{@type}/#{val.class})\n"
|
173
|
+
end
|
174
|
+
when SAPNW::RFC::BYTE
|
175
|
+
unless val.is_a?(String)
|
176
|
+
raise TypeError, "Must be String for BYTE (#{@name}/#{@type}/#{val.class})\n"
|
177
|
+
end
|
178
|
+
when SAPNW::RFC::CHAR, SAPNW::RFC::DATE, SAPNW::RFC::TIME
|
179
|
+
unless val.is_a?(String)
|
180
|
+
raise TypeError, "Must be String for CHAR, DATE, and TIME (#{@name}/#{@type}/#{val.class})\n"
|
181
|
+
end
|
182
|
+
when SAPNW::RFC::TABLE
|
183
|
+
unless val.is_a?(Array)
|
184
|
+
raise TypeError, "Must be Array for table value (#{@name}/#{val.class})\n"
|
185
|
+
end
|
186
|
+
cnt = 0
|
187
|
+
val.each do |row|
|
188
|
+
cnt += 1
|
189
|
+
unless row.is_a?(Hash)
|
190
|
+
raise TypeError, "Must be Hash for table row value (#{@name}/#{cnt}/#{row.class})\n"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
when SAPNW::RFC::STRUCTURE
|
194
|
+
unless val.is_a?(Hash)
|
195
|
+
raise TypeError, "Must be a Hash for a Structure Type (#{@name}/#{@type}/#{val.class})\n"
|
196
|
+
end
|
197
|
+
else # anything - barf
|
198
|
+
raise "unknown SAP data type (#{@name}/#{@type})\n"
|
199
|
+
end
|
200
|
+
@value = val
|
201
|
+
return val
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
# RFC Import Parameters
|
207
|
+
class Import < SAPNW::RFC::Parameter
|
208
|
+
def initialize(*args)
|
209
|
+
@direction = SAPNW::RFC::IMPORT
|
210
|
+
super
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# RFC Export Parameters
|
215
|
+
class Export < SAPNW::RFC::Parameter
|
216
|
+
def initialize(*args)
|
217
|
+
@direction = SAPNW::RFC::EXPORT
|
218
|
+
super
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# RFC Changing Parameters
|
223
|
+
class Changing < SAPNW::RFC::Parameter
|
224
|
+
def initialize(*args)
|
225
|
+
@direction = SAPNW::RFC::CHANGING
|
226
|
+
super
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# RFC Table type Parameters
|
231
|
+
class Table < SAPNW::RFC::Parameter
|
232
|
+
def initialize(*args)
|
233
|
+
@direction = SAPNW::RFC::TABLES
|
234
|
+
super
|
235
|
+
end
|
236
|
+
|
237
|
+
# returns the no. of rows currently in the table
|
238
|
+
def length
|
239
|
+
return @value.length
|
240
|
+
end
|
241
|
+
|
242
|
+
# assign an Array, of rows represented by Hashes to the value of
|
243
|
+
# the Table parameter.
|
244
|
+
def value=(val=[])
|
245
|
+
unless val.is_a?(Array)
|
246
|
+
raise TypeError, "Must be Array for table value (#{@name}/#{val.class})\n"
|
247
|
+
end
|
248
|
+
cnt = 0
|
249
|
+
val.each do |row|
|
250
|
+
cnt += 1
|
251
|
+
unless row.is_a?(Hash)
|
252
|
+
raise TypeError, "Must be Hash for table row value (#{@name}/#{cnt}/#{row.class})\n"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
@value = val
|
256
|
+
end
|
257
|
+
|
258
|
+
# Yields each row of the table to passed Proc
|
259
|
+
def each
|
260
|
+
return nil unless @value
|
261
|
+
@value.each do |row|
|
262
|
+
yield row
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class Type
|
268
|
+
|
269
|
+
attr_reader :name, :type, :len, :ulen, :decimals, :fields
|
270
|
+
|
271
|
+
def initialize(*args)
|
272
|
+
args = args.first if args.class == Array and args.first.class == Hash
|
273
|
+
case args
|
274
|
+
when Array
|
275
|
+
name, type, len, ulen, decimals, fields = args
|
276
|
+
when Hash
|
277
|
+
raise "Missing Type :name => #{args.inspect}\n" unless args.has_key?(:name)
|
278
|
+
raise "Missing Type :type => #{args.inspect}\n" unless args.has_key?(:type)
|
279
|
+
#raise "Missing Type :len => #{args.inspect}\n" unless args.has_key?(:len)
|
280
|
+
case args[:type]
|
281
|
+
when SAPNW::RFC::CHAR, SAPNW::RFC::DATE, SAPNW::RFC::BCD, SAPNW::RFC::TIME, SAPNW::RFC::BYTE, SAPNW::RFC::TABLE, SAPNW::RFC::NUM, SAPNW::RFC::FLOAT, SAPNW::RFC::INT, SAPNW::RFC::INT2, SAPNW::RFC::INT1, SAPNW::RFC::NULL, SAPNW::RFC::STRUCTURE, SAPNW::RFC::DECF16, SAPNW::RFC::DECF34, SAPNW::RFC::XMLDATA, SAPNW::RFC::STRING, SAPNW::RFC::XSTRING, SAPNW::RFC::EXCEPTION
|
282
|
+
else
|
283
|
+
raise "Invalid SAPNW::RFC* type supplied (#{args[:type]})\n"
|
284
|
+
end
|
285
|
+
len = 0
|
286
|
+
ulen = 0
|
287
|
+
decimals = 0
|
288
|
+
name = args[:name]
|
289
|
+
type = args[:type]
|
290
|
+
len = args[:len] if args.has_key?(:len)
|
291
|
+
ulen = 2 * len
|
292
|
+
ulen = args[:ulen] if args.has_key?(:ulen)
|
293
|
+
decimals = args[:decimals] if args.has_key?(:decimals)
|
294
|
+
fields = args[:fields] if args.has_key?(:fields)
|
295
|
+
else
|
296
|
+
raise "invalid parameters in SAPNW::RFC::Type: #{args.inspect}\n"
|
297
|
+
end
|
298
|
+
@name = name
|
299
|
+
@type = type
|
300
|
+
@len = len
|
301
|
+
@ulen = ulen
|
302
|
+
@decimals = decimals
|
303
|
+
if fields
|
304
|
+
raise "Fields must be an Array (#{fields.inspect})\n" unless fields.class == Array
|
305
|
+
slen = 0
|
306
|
+
sulen = 0
|
307
|
+
fields.each do |val|
|
308
|
+
raise "each field definition must be a Hash (#{val.inspect})\n" unless val.class == Hash
|
309
|
+
unless val.has_key?(:name) and
|
310
|
+
val.has_key?(:type) and
|
311
|
+
val.has_key?(:len)
|
312
|
+
raise "each field definition must have :name, :type, and :len (#{val.inspect})\n"
|
313
|
+
end
|
314
|
+
val[:ulen] = val[:len] * 2 unless val.has_key?(:ulen)
|
315
|
+
val[:decimals] = 0 unless val.has_key?(:decimals)
|
316
|
+
slen += val[:len]
|
317
|
+
sulen += val[:ulen]
|
318
|
+
# sort out nested types
|
319
|
+
if val[:type].class == SAPNW::RFC::Type
|
320
|
+
val[:typedef] = val[:type]
|
321
|
+
val[:type] = val[:typedef].type
|
322
|
+
end
|
323
|
+
end
|
324
|
+
@len = slen unless @len > 0
|
325
|
+
@ulen = sulen unless @ulen > 0
|
326
|
+
end
|
327
|
+
@fields = fields
|
328
|
+
#$stderr.print "initilised Type(#{name}): #{type} - #{@fields.inspect}\n"
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# SAPNW is Copyright (c) 2006-2008 Piers Harding. It is free software, and
|
2
|
+
# may be redistributed under the terms specified in the README file of
|
3
|
+
# the Ruby distribution.
|
4
|
+
#
|
5
|
+
# Author:: Piers Harding <piers@ompka.net>
|
6
|
+
# Requires:: Ruby 1.9 or later
|
7
|
+
#
|
8
|
+
|
9
|
+
module SAPNW
|
10
|
+
|
11
|
+
module Servers
|
12
|
+
|
13
|
+
class << SAPNW::Base
|
14
|
+
|
15
|
+
# registers with an R/3 systems gateway service. Uses parameters supplied
|
16
|
+
# in the YAML config file by default, but overrides these with any passed
|
17
|
+
# as a Hash. returns a SAPNW::RFC::Server object
|
18
|
+
def rfc_register(args = nil)
|
19
|
+
parms = {}
|
20
|
+
parms[:trace] = self.config['trace'].to_i if self.config.key? 'trace'
|
21
|
+
parms[:tpname] = self.config['tpname'] if self.config.key? 'tpname'
|
22
|
+
parms[:gwhost] = self.config['gwhost'] if self.config.key? 'gwhost'
|
23
|
+
parms[:gwserv] = self.config['gwserv'] if self.config.key? 'gwserv'
|
24
|
+
SAP_LOGGER.debug("[" + self.name + "] base parameters to be passed are: " + parms.inspect)
|
25
|
+
|
26
|
+
case args
|
27
|
+
when nil
|
28
|
+
when Hash
|
29
|
+
parms[:trace] = args[:trace].to_i if args.key? :trace
|
30
|
+
parms[:tpname] = args[:tpname] if args.key? :tpname
|
31
|
+
parms[:gwhost] = args[:gwhost] if args.key? :gwhost
|
32
|
+
parms[:gwserv] = args[:gwserv] if args.key? :gwserv
|
33
|
+
SAP_LOGGER.debug("[" + self.name + "] with EXTRA parameters to be passed are: " + parms.inspect)
|
34
|
+
else
|
35
|
+
raise "Wrong parameters for Connection - must pass a Hash\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
server = SAPNW::RFC::Server.new(parms)
|
39
|
+
SAP_LOGGER.debug("completed the server connection (#{server.handle.class}/#{server.handle.object_id}) ...")
|
40
|
+
return server
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
module RFC
|
47
|
+
class ServerException < Exception
|
48
|
+
def initialize(error=nil)
|
49
|
+
unless error.class == Hash
|
50
|
+
error = {'code' => 3, 'key' => 'RUNTIME', 'message' => error.to_s}
|
51
|
+
end
|
52
|
+
@error = error
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Server
|
57
|
+
attr_accessor :handle
|
58
|
+
attr_reader :connection_parameters, :functions
|
59
|
+
|
60
|
+
def initialize(args = nil)
|
61
|
+
@connection_parameters = []
|
62
|
+
case args
|
63
|
+
when nil
|
64
|
+
when Hash
|
65
|
+
args.each_pair { |key, val|
|
66
|
+
@connection_parameters << {'name' => key.to_s, 'value' => val.to_s}
|
67
|
+
}
|
68
|
+
else
|
69
|
+
raise "Wrong parameters for Server Connection - must pass a Hash\n"
|
70
|
+
end
|
71
|
+
SAP_LOGGER.debug("In #{self.class} initialize: #{@connection_parameters.inspect} ...")
|
72
|
+
@functions = {}
|
73
|
+
@attributes = nil
|
74
|
+
@handle = SAPNW::RFC::ServerHandle.new(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
# installs a SAPNW::RFC::FunctionDescriptor object, and optionally associates
|
78
|
+
# this with a particular SysId.
|
79
|
+
def installFunction(*args)
|
80
|
+
args = args.first if args.class == Array and args.first.class == Hash
|
81
|
+
case args
|
82
|
+
when Hash
|
83
|
+
raise "Must pass an instance of SAPNW::RFC::FunctionDescriptor to installFunction()\n" unless args.has_key?(:descriptor) and args[:descriptor].class == SAPNW::RFC::FunctionDescriptor
|
84
|
+
func = args[:descriptor]
|
85
|
+
sysid = args.has_key?(:sysid) ? args[:sysid] : ""
|
86
|
+
when Array
|
87
|
+
raise "Must pass an instance of SAPNW::RFC::FunctionDescriptor to installFunction()\n" unless args.first.class == SAPNW::RFC::FunctionDescriptor
|
88
|
+
func = args.first
|
89
|
+
sysid = args.length > 1 ? args[1] : ""
|
90
|
+
else
|
91
|
+
raise "Must pass an instance of SAPNW::RFC::FunctionDescriptor to installFunction()\n"
|
92
|
+
end
|
93
|
+
#$stderr.print "sysid: #{sysid}\n"
|
94
|
+
res = func.install(sysid)
|
95
|
+
@functions[func.name] = func
|
96
|
+
return res
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.handler(callback=nil, attributes=nil)
|
100
|
+
return if callback == nil
|
101
|
+
begin
|
102
|
+
return callback.call(attributes)
|
103
|
+
rescue SAPNW::RFC::ServerException => e
|
104
|
+
#$stderr.print "ServerException => #{e.error.inspect}\n"
|
105
|
+
return e
|
106
|
+
rescue StandardError => e
|
107
|
+
#$stderr.print "StandardError => #{e.inspect}/#{e.message}\n"
|
108
|
+
return SAPNW::RFC::ServerException.new({'code' => 3, 'key' => 'RUBY_RUNTIME', 'message' => e.message})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# fire the accept loop taking optionally a wait time for each loop, and a global
|
114
|
+
# callback to be triggered after each loop/loop timeout.
|
115
|
+
# Callback - if supplied - must be a Proc object, that takes a simgle parameter,
|
116
|
+
# which is a hash of the system connection attributes.
|
117
|
+
def accept(wait=120, callback=nil)
|
118
|
+
trap('INT', 'EXIT')
|
119
|
+
return @handle.accept_loop(wait, callback);
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# similar to the accept() loop only that it does a single RfcListenAndDispatch()
|
124
|
+
# on the SAPNW::RFC::Server connection handle. The result is the RFC return code
|
125
|
+
# as per the SAPNW::RFC::RFC_* return code constants.
|
126
|
+
# process() optionally takes asingle parameter which is the wait-time.
|
127
|
+
def process(wait=120)
|
128
|
+
trap('INT', 'EXIT')
|
129
|
+
return @handle.process_loop(wait);
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns a Hash of the connections attributes of this server connection.
|
133
|
+
def connection_attributes
|
134
|
+
SAP_LOGGER.debug("In #{self.class} connection_attributes ...")
|
135
|
+
return @attributes = self.handle.connection_attributes()
|
136
|
+
end
|
137
|
+
|
138
|
+
# terminate an established RFC connection. This will invalidate any currently in-scope
|
139
|
+
# FunctionDescriptors associated with this Connection.
|
140
|
+
def close
|
141
|
+
SAP_LOGGER.debug("In #{self.class} close ...")
|
142
|
+
return nil unless self.handle
|
143
|
+
SAP_LOGGER.debug("In #{self.class} handle: #{self.handle} ...")
|
144
|
+
res = self.handle.close()
|
145
|
+
self.handle = nil
|
146
|
+
# XXX Should destroy all cached functions and structures and types tied to handle ?
|
147
|
+
return true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sapnwrfc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.23'
|
5
|
+
prerelease:
|
6
|
+
platform: i686-linux
|
7
|
+
authors:
|
8
|
+
- Piers Harding
|
9
|
+
autorequire:
|
10
|
+
- sapnwrfc
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2011-07-27 00:00:00.000000000 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
description: ! " sapnwrfc is a ruby module for performing RFC functions and BAPI
|
17
|
+
calls on\n an SAP Netweaver system NW2004+\n"
|
18
|
+
email: piers@ompka.net
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/sapnwrfc/config.rb
|
24
|
+
- lib/sapnwrfc/base.rb
|
25
|
+
- lib/sapnwrfc/connection.rb
|
26
|
+
- lib/sapnwrfc/functions.rb
|
27
|
+
- lib/sapnwrfc/server.rb
|
28
|
+
- lib/sapnwrfc/parameters.rb
|
29
|
+
- lib/sapnwrfc.rb
|
30
|
+
- ext/nwsaprfc/nwsaprfc.so
|
31
|
+
has_rdoc: true
|
32
|
+
homepage: http://www.piersharding.com
|
33
|
+
licenses: []
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- ext/nwsaprfc
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.9.0
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.6.2
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: SAP Netweaver RFC connector for Ruby
|
57
|
+
test_files: []
|