nwrfc 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|