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 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