nwrfc 0.0.2 → 0.0.3
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/README.rdoc +11 -0
- data/lib/nwrfc.rb +68 -22
- data/lib/nwrfc/datacontainer.rb +2 -0
- data/lib/nwrfc/nwrfclib.rb +10 -1
- data/lib/nwrfc/server.rb +15 -5
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -51,6 +51,13 @@ Setting and getting parameters and fields:
|
|
51
51
|
|
52
52
|
== Installation
|
53
53
|
|
54
|
+
In order to install and run nwrfc, you need to install {http://github.com/ffi/ffi Ruby-FFI} which requires compilation.
|
55
|
+
On Windows, you should be running the one-click {http://rubyinstaller.org/downloads/ Ruby Installer} and install the
|
56
|
+
{http://github.com/oneclick/rubyinstaller/wiki/Development-Kit DevKit} which is really the easiest way to compile
|
57
|
+
it on Windows.
|
58
|
+
|
59
|
+
Then install the nwrfc gem:
|
60
|
+
|
54
61
|
On Linux:
|
55
62
|
|
56
63
|
sudo gem install nwrfc
|
@@ -77,6 +84,10 @@ so that ["system2"] at the end points to whatever label you gave it in the YAML
|
|
77
84
|
|
78
85
|
== Release Notes
|
79
86
|
|
87
|
+
=== What's new in 0.0.3
|
88
|
+
|
89
|
+
* Basic RFC server functionality
|
90
|
+
|
80
91
|
=== What's new in 0.0.2
|
81
92
|
|
82
93
|
* More comprehensive type support
|
data/lib/nwrfc.rb
CHANGED
@@ -16,20 +16,19 @@ require 'time'
|
|
16
16
|
# This library provides a way to call the functions of the SAP Netweaver RFC
|
17
17
|
# SDK, i.e. opening a connection to an ABAP system, calling functions etc., as
|
18
18
|
# well as running an RFC service
|
19
|
-
#---
|
20
|
-
# *TODO*: Create an error class that wraps the SAP error struct, so it can
|
21
|
-
# be raised and the caller can get all the information from there
|
22
|
-
#+++
|
23
19
|
|
24
20
|
module NWRFC
|
25
21
|
|
22
|
+
# ABAP Time Format ("HHMMSS")
|
26
23
|
NW_TIME_FORMAT = "%H%M%S"
|
24
|
+
# ABAP Date Format ("YYYYMMDD")
|
27
25
|
NW_DATE_FORMAT = "%Y%m%d"
|
28
26
|
|
29
27
|
def inspect
|
30
28
|
self.to_s
|
31
29
|
end
|
32
30
|
|
31
|
+
# Return the version of the NW RFC SDK library
|
33
32
|
def NWRFC.get_version
|
34
33
|
# See custom method FFI::Pointer#read_string_dn in nwrfclib.rb
|
35
34
|
# http://stackoverflow.com/questions/9293307/ruby-ffi-ruby-1-8-reading-utf-16le-encoded-strings
|
@@ -41,7 +40,7 @@ module NWRFC
|
|
41
40
|
end
|
42
41
|
|
43
42
|
# Take Hash of connection parameters and returns FFI pointer to an array
|
44
|
-
# for
|
43
|
+
# for setting up a connection
|
45
44
|
def NWRFC.make_conn_params(params) #https://github.com/ffi/ffi/wiki/Structs
|
46
45
|
par = FFI::MemoryPointer.new(NWRFCLib::RFCConnParam, params.length)
|
47
46
|
pars = params.length.times.collect do |i|
|
@@ -55,6 +54,7 @@ module NWRFC
|
|
55
54
|
par
|
56
55
|
end
|
57
56
|
|
57
|
+
# Check for an error using error handle (used internally)
|
58
58
|
def NWRFC.check_error(error_handle)
|
59
59
|
raise NWError, error_handle \
|
60
60
|
if error_handle[:code] > 0
|
@@ -62,7 +62,7 @@ module NWRFC
|
|
62
62
|
|
63
63
|
end
|
64
64
|
|
65
|
-
# Represents a connection to a SAP system that can be used to invoke
|
65
|
+
# Represents a client connection to a SAP system that can be used to invoke
|
66
66
|
# remote-enabled functions
|
67
67
|
class Connection
|
68
68
|
attr_reader :handle
|
@@ -82,18 +82,22 @@ module NWRFC
|
|
82
82
|
end
|
83
83
|
|
84
84
|
# Call the NW RFC SDK's RfcCloseConnection() function with the current
|
85
|
-
# connection; this
|
85
|
+
# connection; this *should* invalidate the connection handle
|
86
86
|
# and cause an error on any subsequent use of this connection
|
87
|
+
#@todo Write test to check that handle is invalidated and causes subsequent calls to fail
|
87
88
|
def disconnect
|
88
89
|
NWRFCLib.close_connection(@handle, @error.to_ptr)
|
89
90
|
NWRFC.check_error(@error)
|
90
91
|
end
|
91
|
-
|
92
|
+
|
93
|
+
# Get the description of a given function module from the system to which we are connected
|
94
|
+
# @return [Function] function module description
|
92
95
|
def get_function(function_name)
|
93
96
|
Function.new(self, function_name)
|
94
97
|
end
|
95
98
|
|
96
99
|
# Return details about the current connection and the system
|
100
|
+
# @return [Hash] information about the current connection
|
97
101
|
def connection_info
|
98
102
|
return @get_connection_attributes if @get_connection_attributes
|
99
103
|
conn_info = NWRFCLib::RFCConnection.new
|
@@ -103,35 +107,43 @@ module NWRFC
|
|
103
107
|
hash[member] = conn_info[member].get_str #get_str, own definition in nwrfclib.rb, FFI::StructLayout::CharArray#get_str
|
104
108
|
hash
|
105
109
|
}
|
106
|
-
end
|
110
|
+
end
|
111
|
+
|
112
|
+
alias :close :disconnect
|
107
113
|
|
108
114
|
end
|
109
115
|
|
116
|
+
# Converts ABAP true/false into Ruby true/false
|
117
|
+
# @return True for 'X', False for ' ' or nil otherwise
|
110
118
|
def NWRFC.abap_bool(value)
|
111
119
|
return true if value == 'X'
|
112
120
|
return false if value == ' '
|
113
121
|
nil
|
114
122
|
end
|
115
123
|
|
124
|
+
# Converts Ruby true/false into ABAP true/false
|
125
|
+
# @return 'X' for true,, ' ' for false or nil otherwise
|
116
126
|
def NWRFC.bool_abap(value)
|
117
127
|
return 'X' if value == true
|
118
128
|
return ' ' if value == false
|
119
129
|
nil
|
120
130
|
end
|
121
131
|
|
122
|
-
# Represents a function parameter
|
132
|
+
# Represents the metadata of a function parameter
|
123
133
|
class Parameter
|
124
134
|
|
125
135
|
attr_accessor :handle
|
126
136
|
|
127
137
|
# Create a parameter by setting parameter attributes
|
138
|
+
#@todo For certain types, e.g. :RFCTYPE_BCD, a length specification is
|
139
|
+
# required, otherwise a segfault is the result later down the line.
|
140
|
+
# Find and implement all the types where this is required
|
128
141
|
def initialize(*args)
|
129
142
|
|
130
143
|
attr = args[0]
|
131
144
|
|
132
|
-
|
133
|
-
|
134
|
-
# Find and implement all the types where this is required
|
145
|
+
|
146
|
+
|
135
147
|
raise "RFCTYPE_BCD requires a length" if attr[:type] == :RFCTYPE_BCD && !(attr[:length])
|
136
148
|
|
137
149
|
@handle = NWRFCLib::RFCFuncParam.new
|
@@ -160,11 +172,19 @@ module NWRFC
|
|
160
172
|
attr_reader :desc, :function_name
|
161
173
|
attr_accessor :connection
|
162
174
|
|
163
|
-
# TODO: Update doc to reflect different calling options
|
164
175
|
# Get a function module instance; can also be obtained by calling Connection#get_function
|
165
176
|
# Takes either: (connection, function_name) or (function_name)
|
166
177
|
# When passed only `function_name`, creates a new function description locally, instead of
|
167
178
|
# fetching it form the server pointed to by connection
|
179
|
+
#@overload new(connection, function_name)
|
180
|
+
# Fetches a function definition from the server pointed to by the connection
|
181
|
+
# @param [Connection] connection Connection to SAP ABAP system
|
182
|
+
# @param [String] function_name Name of the function module on the connected system
|
183
|
+
#
|
184
|
+
#@overload new(function_name)
|
185
|
+
# Returns a new function descriptor. This is ideally used in the case of establishing a
|
186
|
+
# server function. In this case, the function cannot be used to make a remote function call.
|
187
|
+
# @param [String] function_name Name of the new function module
|
168
188
|
def initialize(*args)#(connection, function_name)
|
169
189
|
raise("Must initialize function with 1 or 2 arguments") if args.size != 1 && args.size != 2
|
170
190
|
@error = NWRFCLib::RFCError.new
|
@@ -181,6 +201,9 @@ module NWRFC
|
|
181
201
|
end
|
182
202
|
end
|
183
203
|
|
204
|
+
# Add a parameter to a function module. Ideally to be used in the case where a function definition is built
|
205
|
+
# up in the client code, rather than fetching it from the server for a remote call
|
206
|
+
# @param [Parameter] Definition of a function module parameter
|
184
207
|
def add_parameter(parameter)
|
185
208
|
rc = NWRFCLib.add_parameter(@desc, parameter.handle, @error)
|
186
209
|
NWRFC.check_error(@error) if rc > 0
|
@@ -225,16 +248,28 @@ module NWRFC
|
|
225
248
|
class FunctionCall < DataContainer
|
226
249
|
attr_reader :handle, :desc, :connection, :function
|
227
250
|
|
228
|
-
# TODO: Update doc to reflect different calling options
|
229
251
|
# Call with either Function or Connection and Function Call instance (handle)
|
252
|
+
#@overload new(function)
|
253
|
+
# Get a function call instance from the function description
|
254
|
+
# @param [Function] function Function Description
|
255
|
+
#@overload new(function_handle)
|
256
|
+
# Used in the case of server functions; instantiate a function call instance from the connection
|
257
|
+
# and function description handles received when function is invoked on our side from a remote
|
258
|
+
# system; in this case, there is no handle to the connection, and we take advantage only of the
|
259
|
+
# data container capabilities
|
260
|
+
# @param [FFI::Pointer] function_handle Pointer to the function handle (RFC_FUNCTION_HANDLE)
|
230
261
|
def initialize(*args)
|
231
|
-
raise("Must initialize function with 1 or 2 arguments") if args.size != 1 && args.size != 2
|
232
262
|
@error = NWRFCLib::RFCError.new
|
233
|
-
if args.
|
234
|
-
@handle = args[
|
235
|
-
@connection =
|
236
|
-
|
237
|
-
|
263
|
+
if args[0].class == FFI::Pointer
|
264
|
+
@handle = args[0]
|
265
|
+
@connection = nil
|
266
|
+
@function = nil
|
267
|
+
# @connection = args[0].connection
|
268
|
+
@desc = NWRFCLib.describe_function(@handle, @error)
|
269
|
+
#@todo Investigate having a referenced Function object as well in the server case; does it have practical applications?
|
270
|
+
# Doing this would require an extra way of handling the constructor of Function
|
271
|
+
# @function = Function.new
|
272
|
+
elsif args[0].class == Function
|
238
273
|
@function = args[0] #function
|
239
274
|
@connection = args[0].connection
|
240
275
|
@handle = NWRFCLib.create_function(@function.desc, @error.to_ptr)
|
@@ -243,8 +278,12 @@ module NWRFC
|
|
243
278
|
NWRFC.check_error(@error)
|
244
279
|
end
|
245
280
|
|
281
|
+
# Execute the function on the connected ABAP system
|
282
|
+
#@raise NWRFC::NWError
|
246
283
|
def invoke
|
284
|
+
raise "Not a callable function" unless @connection
|
247
285
|
rc = NWRFCLib.invoke(@connection.handle, @handle, @error.to_ptr)
|
286
|
+
#@todo Handle function exceptions by checking for :RFC_ABAP_EXCEPTION (5)
|
248
287
|
NWRFC.check_error(@error) if rc > 0
|
249
288
|
end
|
250
289
|
end
|
@@ -297,10 +336,17 @@ module NWRFC
|
|
297
336
|
end
|
298
337
|
|
299
338
|
# Add new (empty) row and return the structure handle
|
339
|
+
# or yield it to a passed block
|
340
|
+
# @return Structure
|
300
341
|
def new_row
|
301
342
|
s_handle = NWRFCLib.append_new_row(@handle, @error)
|
302
343
|
NWRFC.check_error(@error)
|
303
|
-
Structure.new(s_handle)
|
344
|
+
s = Structure.new(s_handle)
|
345
|
+
if block_given?
|
346
|
+
yield s
|
347
|
+
else
|
348
|
+
s
|
349
|
+
end
|
304
350
|
end
|
305
351
|
|
306
352
|
end #class Table
|
data/lib/nwrfc/datacontainer.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module NWRFC
|
2
2
|
|
3
3
|
# Representation of a data container (function, structure or table)
|
4
|
+
# Implements common functions for data containers, such as setting and getting values, tables, structures and
|
5
|
+
# takes care of type conversion and calling correct SDK functions to set or get values
|
4
6
|
class DataContainer
|
5
7
|
attr_reader :handle, :desc
|
6
8
|
|
data/lib/nwrfc/nwrfclib.rb
CHANGED
@@ -51,10 +51,13 @@ end
|
|
51
51
|
# architectures, though the plan is not to support them)
|
52
52
|
#String.class_eval{define_method(:cU){ Iconv.conv("UTF-16LE", "UTF8", self+"\0") }}
|
53
53
|
class String
|
54
|
+
|
55
|
+
# Convert string from UTF-8 to doudble-null terminated UTF-16LE string
|
54
56
|
def cU
|
55
57
|
NWRFCLib::Cutf8_to_utf16le.iconv(self+"\0")
|
56
58
|
end
|
57
59
|
|
60
|
+
# Convert string from UTF-16LE to UTF-8 and trim trailing whitespace
|
58
61
|
def uC
|
59
62
|
NWRFCLib::Cutf16le_to_utf8.iconv(self).strip
|
60
63
|
end
|
@@ -80,7 +83,7 @@ module NWRFCLib
|
|
80
83
|
ffi_lib 'sapnwrfc'
|
81
84
|
|
82
85
|
# Multiplier for providing correct byte size for String passed to RFC library
|
83
|
-
|
86
|
+
#@todo Make platform-dependent size based on RUBY_PLATFORM
|
84
87
|
B_SIZE = 2
|
85
88
|
|
86
89
|
RFC_RC = enum(
|
@@ -281,7 +284,13 @@ module NWRFCLib
|
|
281
284
|
[:add_parameter, :RfcAddParameter, [:pointer, :pointer, :pointer], :int],
|
282
285
|
[:add_type_desc, :RfcAddTypeDesc, [:pointer, :pointer, :pointer], :int],
|
283
286
|
[:add_type_field, :RfcAddTypeField, [:pointer, :pointer, :pointer], :int],
|
287
|
+
# @method append_new_row(table_handle, error_handle)
|
288
|
+
# @returns FFI::Pointer pointer to structure of new row
|
289
|
+
# calls RfcAppendNewRow()
|
284
290
|
[:append_new_row, :RfcAppendNewRow, [:pointer, :pointer], :pointer],
|
291
|
+
# @method append_row(table_handle, structure, error_handle)
|
292
|
+
# @returns Integer RC
|
293
|
+
# calls RfcAppendRow()
|
285
294
|
[:append_row, :RfcAppendRow, [:pointer, :pointer, :pointer], :int],
|
286
295
|
[:clone_structure, :RfcCloneStructure, [:pointer, :pointer], :pointer],
|
287
296
|
[:clone_table, :RfcCloneTable, [:pointer, :pointer], :pointer],
|
data/lib/nwrfc/server.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
module NWRFC
|
2
2
|
|
3
3
|
# Implementation of a server to host RFC functions to be called from an ABAP system
|
4
|
-
# or from another RFC implementation
|
5
4
|
class Server
|
6
5
|
|
7
|
-
TIMEOUT =
|
6
|
+
TIMEOUT = 0
|
8
7
|
|
9
8
|
# Register a server with the given gateway and program ID
|
10
9
|
def initialize(params)
|
@@ -16,21 +15,32 @@ module NWRFC
|
|
16
15
|
NWRFC.check_error(@error)
|
17
16
|
end
|
18
17
|
|
18
|
+
|
19
|
+
# Start serving an RFC function, given the definition and the block,
|
20
|
+
# to which the connection and functions are yielded
|
19
21
|
def serve(function, &block)
|
20
22
|
# Establish callback handler
|
21
|
-
callback = Proc.new do |connection,
|
22
|
-
|
23
|
-
debugger
|
23
|
+
callback = Proc.new do |connection, function_handle, error|
|
24
|
+
function_call = FunctionCall.new(function_handle)
|
24
25
|
yield(function_call)
|
25
26
|
end
|
26
27
|
rc = NWRFCLib.install_server_function(nil, function.desc, callback, @error)
|
27
28
|
NWRFC.check_error(@error) if rc > 0
|
29
|
+
|
28
30
|
# Server loop
|
29
31
|
while (rc==NWRFCLib::RFC_RC[:RFC_OK] || rc==NWRFCLib::RFC_RC[:RFC_RETRY] || rc==NWRFCLib::RFC_RC[:RFC_ABAP_EXCEPTION])
|
30
32
|
rc = NWRFCLib.listen_and_dispatch(@handle, TIMEOUT, @error)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
36
|
+
# Disconnect from the server
|
37
|
+
def disconnect
|
38
|
+
NWRFCLib.close_connection(@handle, @error)
|
39
|
+
NWRFC.check_error(@error)
|
40
|
+
end
|
41
|
+
|
42
|
+
alias :close :disconnect
|
43
|
+
|
34
44
|
end
|
35
45
|
|
36
46
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nwrfc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Martin Ceronio
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-02-
|
18
|
+
date: 2012-02-23 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description: SAP Netweaver RFC Library Wrapper using Ruby-FFI
|