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 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 passing to connection
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 (should - *TODO* - check) invalidate the connection handle
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
- #TODO: For certain types, e.g. :RFCTYPE_BCD, a length specification is
133
- # required, otherwise a segfault is the result later down the line.
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.size == 2
234
- @handle = args[1]
235
- @connection = args[0].connection
236
- # TODO: Get function description from instance
237
- else
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
@@ -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
 
@@ -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
- #TODO: Make platform-dependent size based on RUBY_PLATFORM
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 = 200
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, function_call, error|
22
- fc =
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: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
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-22 00:00:00 Z
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