sapnwrfc 0.23-i686-linux
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|