nwrfc 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,85 @@
1
+ = NWRFC - Wrapper for SAP Netweaver RFC SDK using Ruby-FFI
2
+
3
+ This library provides a wrapper around the sapnwrfc shared library provided in the SAP Netweaver RFC SDK using Ruby-FFI.
4
+
5
+ The NW RFC SDK allows you to call remote-enabled function modules on an ABAP server (referred to as RFC,
6
+ which stands for "Remote Function Call").
7
+
8
+ To use this library, you must have the nwrfcsdk (libsapnwrfc.so / sapnwrfc.dll) library
9
+ and related libraries somewhere in your path.
10
+
11
+ I am developing the library using the 7.20 patch level 2 version of the NW RFC SDK. I have used 7.11, but found for instance
12
+ that it suffers from the bug described in note 1058327, where RfcGetStringLength() returns the incorrect length
13
+ of the string if it is longer than 255 characters (supposed to have been fixed in 7.10 patch 2).
14
+
15
+ == Issues
16
+
17
+ Ruby-FFI does not seem to be able to take string encoding into consideration, so for example, calling RfcGetVersion()
18
+ is problematic, because the returned string pointer has (like all NW RFC SDK functions) UTF-16LE encoding,
19
+ and FFI does not seem to be able to work with this. So far this is not too problematic, but let's see.
20
+
21
+ == Obtaining the Netweaver RFC shared library
22
+ The Netweaver RFC SDK libraries are available from SAP. You cannot, unfortunately, obtain these as a public user; you need to have access via a customer account to download them
23
+ from SAP Service Marketplace (http://service.sap.com)
24
+
25
+ Alternatively, you can download and install one of the Netweaver Trial Editions from SDN (requires signup): http://www.sdn.sap.com/irj/scn/downloads
26
+
27
+ After installation, the files are available in /usr/sap/<sysid>/exe
28
+
29
+ == Usage
30
+
31
+ Connecting to the SAP system:
32
+
33
+ require 'rubygems'
34
+
35
+ gem 'nwrfc'
36
+ include NWRFC
37
+
38
+ c = Connection.new {"user" => "martin", "passwd" => "secret",
39
+ "client" => "100", "ashost" => "ajax.domain.com", "sysnr" => "00"}
40
+
41
+ Calling a function:
42
+
43
+ f = c.get_function("STFC_STRUCTURE") #Obtain description of a function module
44
+ fc = f.get_function_call
45
+ f.invoke
46
+
47
+ Setting and getting parameters and fields:
48
+
49
+ import_struc = fc[:IMPORTSTRUC]
50
+ import_struc[:RFCCHAR1] = 'a'
51
+
52
+ == Installation
53
+
54
+ On Linux:
55
+
56
+ sudo gem install nwrfc
57
+
58
+ On Windows
59
+
60
+ gem install nwrfc
61
+
62
+ == Running the tests
63
+ The test are located in the tests/ directory. The file `login_params.yaml` contains parameters that you will need
64
+ to customize to log on to your local system that you are testing with. The YAML file contains parameters for multiple
65
+ systems, so if you want to switch to a different system, change the following line in `test_nwrfc.rb`:
66
+
67
+ $login_params = YAML.load_file(File.dirname(__FILE__) + "/login_params.yaml")["system2"]
68
+
69
+ so that ["system2"] at the end points to whatever label you gave it in the YAML file, then
70
+
71
+ rake test
72
+
73
+ == Release Notes
74
+
75
+ === What's new in 0.0.0
76
+
77
+ * Able to call functions
78
+ * Most types work,
79
+ * Getting list of fields from a structure causes a SEGFAULT
80
+
81
+ == Contributing
82
+
83
+ Improvements to the source code are welcome. Email me at martin dot ceronio at infosize dot co dot za.
84
+
85
+ Also let me know, please on which platforms you have tested the gem.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.test_files = FileList['test/**/*.rb']
8
+ end
data/lib/dev_rfc.trc ADDED
@@ -0,0 +1,133 @@
1
+
2
+ **** Trace file opened at 2012-02-15, 13:50:29 SAST
3
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
4
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
5
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
6
+
7
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
8
+
9
+ **** Trace file opened at 2012-02-15, 13:51:02 SAST
10
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
11
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
12
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
13
+
14
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
15
+
16
+ **** Trace file opened at 2012-02-15, 14:07:53 SAST
17
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
18
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
19
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
20
+
21
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
22
+
23
+ **** Trace file opened at 2012-02-15, 14:08:23 SAST
24
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
25
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
26
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
27
+
28
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
29
+
30
+ **** Trace file opened at 2012-02-15, 14:09:49 SAST
31
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
32
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
33
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
34
+
35
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
36
+
37
+ **** Trace file opened at 2012-02-15, 14:40:33 SAST
38
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb1
39
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
40
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
41
+
42
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
43
+
44
+ **** Trace file opened at 2012-02-15, 14:42:34 SAST
45
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
46
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
47
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
48
+
49
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
50
+
51
+ **** Trace file opened at 2012-02-15, 14:50:05 SAST
52
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
53
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
54
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
55
+
56
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
57
+
58
+ **** Trace file opened at 2012-02-15, 15:19:16 SAST
59
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
60
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
61
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
62
+
63
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
64
+
65
+ **** Trace file opened at 2012-02-15, 15:19:42 SAST
66
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
67
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
68
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
69
+
70
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
71
+
72
+ **** Trace file opened at 2012-02-15, 15:20:37 SAST
73
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
74
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
75
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
76
+
77
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
78
+
79
+ **** Trace file opened at 2012-02-15, 15:21:59 SAST
80
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
81
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
82
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
83
+
84
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
85
+
86
+ **** Trace file opened at 2012-02-15, 15:23:05 SAST
87
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
88
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
89
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
90
+
91
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
92
+
93
+ **** Trace file opened at 2012-02-15, 15:25:42 SAST
94
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
95
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
96
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
97
+
98
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
99
+
100
+ **** Trace file opened at 2012-02-15, 15:26:25 SAST
101
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
102
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
103
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
104
+
105
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
106
+
107
+ **** Trace file opened at 2012-02-15, 22:23:25 SAST
108
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
109
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
110
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
111
+
112
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
113
+
114
+ **** Trace file opened at 2012-02-15, 22:23:59 SAST
115
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
116
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
117
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
118
+
119
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
120
+
121
+ **** Trace file opened at 2012-02-15, 22:27:38 SAST
122
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
123
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
124
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
125
+
126
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
127
+
128
+ **** Trace file opened at 2012-02-15, 22:28:08 SAST
129
+ RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
130
+ Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
131
+ Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
132
+
133
+ ERROR pfuuid_init returns error -> UUIDs cannot be generated
data/lib/nwrfc.old.rb ADDED
@@ -0,0 +1,338 @@
1
+ require File.dirname(__FILE__)+'/nwrfc/nwrfclib'
2
+
3
+ # This library provides a way to call the functions of the SAP Netweaver RFC
4
+ # SDK, i.e. opening a connection to an ABAP system, calling functions etc., as
5
+ # well as running an RFC service
6
+ #---
7
+ # *TODO*: Create an error class that wraps the SAP error struct, so it can
8
+ # be raised and the caller can get all the information from there
9
+ #+++
10
+
11
+ module NWRFC
12
+
13
+ def inspect
14
+ self.to_s
15
+ end
16
+
17
+ def NWRFC.check_error(error_handle)
18
+ raise "Error code #{error_handle[:code]} group #{error_handle[:group]} message #{error_handle[:message].get_str}" \
19
+ if error_handle[:code] > 0
20
+ end
21
+
22
+ # Representation of a data container (function, structure or table)
23
+ module DataContainer
24
+
25
+ def initialize(handle)
26
+ @handle = handle
27
+ @error = NWRFCLib::RFCError.new
28
+ end
29
+
30
+ # Return the member specified by string or symbol
31
+ def [](element)
32
+ element = element.upcase
33
+ metadata = member_metadata(element)
34
+ case metadata[:type]
35
+ when :RFCTYPE_CHAR
36
+ # TODO: Try use :string parameter in get_chars
37
+ size = metadata[:ucLength]
38
+ cb = FFI::MemoryPointer.new :char, size
39
+ NWRFCLib.get_chars(@handle, element.cU, cb, metadata[:nucLength], @error.to_ptr)
40
+ value = cb.read_string(size).uC
41
+ when :RFCTYPE_DATE
42
+ when :RFCTYPE_BCD
43
+ when :RFCTYPE_TIME
44
+ when :RFCTYPE_BYTE
45
+ when :RFCTYPE_TABLE
46
+ value = get_data_container(element, :table)
47
+ when :RFCTYPE_NUM
48
+ when :RFCTYPE_FLOAT
49
+ when :RFCTYPE_INT
50
+ when :RFCTYPE_INT2
51
+ when :RFCTYPE_INT1
52
+ when :RFCTYPE_NULL
53
+ when :RFCTYPE_STRUCTURE
54
+ value = get_data_container(element, :structure)
55
+ when :RFCTYPE_DECF16
56
+ when :RFCTYPE_DECF34
57
+ when :RFCTYPE_XMLDATA
58
+ when :RFCTYPE_STRING
59
+ when :RFCTYPE_XSTRING
60
+ else
61
+ raise "Illegal member type #{metadata[:type]}"
62
+ end
63
+ NWRFC.check_error(@error)
64
+ value
65
+ end
66
+
67
+ def []=(element, value)
68
+ metadata = member_metadata(element)
69
+ case member_metadata[element][:type]
70
+ when :RFCTYPE_CHAR
71
+ NWRFCLib.set_chars(@handle, element.cU, value.cU, value.length, @error.to_ptr)
72
+ when :RFCTYPE_DATE
73
+ when :RFCTYPE_BCD
74
+ when :RFCTYPE_TIME
75
+ when :RFCTYPE_BYTE
76
+ when :RFCTYPE_TABLE
77
+ when :RFCTYPE_NUM
78
+ when :RFCTYPE_FLOAT
79
+ when :RFCTYPE_INT
80
+ when :RFCTYPE_INT2
81
+ when :RFCTYPE_INT1
82
+ when :RFCTYPE_NULL
83
+ when :RFCTYPE_STRUCTURE
84
+ when :RFCTYPE_DECF16
85
+ when :RFCTYPE_DECF34
86
+ when :RFCTYPE_XMLDATA
87
+ when :RFCTYPE_STRING
88
+ when :RFCTYPE_XSTRING
89
+ else
90
+ raise "Illegal member type #{@members[:type]}"
91
+ end
92
+ NWRFC.check_error(@error)
93
+ end
94
+
95
+ # Get the metadata of a member (function, structure or table)
96
+ def member_metadata(member)
97
+ if self.class == NWRFC::FunctionCall
98
+ self.function_description.member_metadata(member)
99
+ elsif self.class.instance_of?(Table) || self.class.instance_of?(Structure)
100
+ NWRFCLib.get_field_desc_by_name(@handle, member.uC, )
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ # Returns the subset of metadata values common to both a function parameter
107
+ # and a type field
108
+ def member_to_hash(member)
109
+ {
110
+ :type => NWRFCLib::RFCTYPE[member[:type]],
111
+ :nucLength => member[:nucLength],
112
+ :ucLength => member[:ucLength],
113
+ :decimals => member[:decimals],
114
+ :typeDescHandle => member[:typeDescHandle]
115
+ }
116
+ end
117
+
118
+ # Return either table or structure representing the element given
119
+ # from the current data container
120
+ def get_data_container(element, type)
121
+ datac = FFI::MemoryPointer.new NWRFCLib::RFCDataContainer
122
+ if type == :table
123
+ NWRFCLib.get_table(@handle, element.cU, datac, @error.to_ptr)
124
+ @components[element] = Table.new(datac)
125
+ end
126
+ if type == :structure
127
+ NWRFCLib.get_structure(@handle, element.cU, datac, @error.to_ptr)
128
+ @components[element] = Structure.new(datac)
129
+ end
130
+ NWRFC.check_error(@error)
131
+ end
132
+
133
+ end
134
+
135
+ # Represents a connection to a SAP system that can be used to invoke
136
+ # remote-enabled functions
137
+ class Connection
138
+
139
+ attr_reader :handle, :error
140
+
141
+ # Opens a connection to the SAP system with the given connection parameters
142
+ # (described in the NW RFC SDK document), passed in the form of a Hash, e.g.
143
+ # Connection.new { 'ashost' :=> 'ajax.domain.com', ... }
144
+ def initialize(conn_params)
145
+ conn_params.untaint #For params loaded from file, e.g.
146
+ raise "Connection parameters must be a Hash" unless conn_params.instance_of? Hash
147
+ #NWRFCLib.init
148
+ @cparams = NWRFCLib.make_conn_params(conn_params)
149
+ raise "Could not create valid pointer from parameters" unless @cparams.instance_of? FFI::MemoryPointer
150
+ #@errp = FFI::MemoryPointer.new(NWRFCLib::RFCError)
151
+ @error = NWRFCLib::RFCError.new #@errp
152
+ @handle = NWRFCLib.open_connection(@cparams, conn_params.length, @error.to_ptr)
153
+ NWRFC.check_error(@error)
154
+ self
155
+ end
156
+
157
+ # Call the NW RFC SDK's RfcCloseConnection() function with the current
158
+ # connection; this (should - *TODO* - check) invalidate the connection handle
159
+ # and cause an error on any subsequent use of this connection
160
+ def disconnect
161
+ NWRFCLib.close_connection(@handle, @error.to_ptr)
162
+ NWRFC.check_error(@error)
163
+ end
164
+
165
+ # Return details about the current connection and the system
166
+ def connection_info
167
+ return @get_connection_attributes if @get_connection_attributes
168
+ conn_info = NWRFCLib::RFCConnection.new
169
+ NWRFCLib.get_connection_attributes(@handle, conn_info.to_ptr, @error.to_ptr)
170
+ check_error
171
+ @get_connection_attributes = conn_info.members.inject({}) {|hash, member|
172
+ hash[member] = conn_info[member].get_str #get_str, own definition in nwrfclib.rb, FFI::StructLayout::CharArray#get_str
173
+ hash
174
+ }
175
+ end
176
+
177
+ def get_function(func_name)
178
+ Function.new(self, func_name)
179
+ end
180
+
181
+ # Check the status of the error structure; raise an exception if it
182
+ # contains an error
183
+ # *TODO* - Raise a more meaningful error
184
+ def check_error
185
+ raise "Error code #{@error[:code]} group #{@error[:group]}" if @error[:code] > 0
186
+ end
187
+
188
+ end
189
+
190
+ # Represents a remote-enabled function module for RFC, can be instantiated either by the caller
191
+ # or by calling Connection#get_function. This only represents the description of the function;
192
+ # to call a function, an instance of a function call must be obtained with #get_function_call
193
+ #---
194
+ # FIXME: We are using a shared error object in the connection, so what happens in a
195
+ # multi-threaded situation? Functions should each have their own error handle
196
+ #+++
197
+ class Function
198
+ attr_reader :func_desc, :connection, :function_name, :members
199
+
200
+ # Get a function module instance; can also be obtained by calling Connection#get_function
201
+ def initialize(connection, func_name)
202
+ @connection = connection
203
+ @func_desc = NWRFCLib.get_function_desc(@connection.handle, func_name.cU, @connection.error.to_ptr)
204
+ NWRFC.check_error(@connection.error)
205
+ @function_name = func_name
206
+ @error = NWRFCLib::RFCError.new
207
+ @members = self_describe # Load the metadata of the function description
208
+ end
209
+
210
+ # Create and return a callable instance of this function module
211
+ def get_function_call
212
+ FunctionCall.new(self)
213
+ end
214
+
215
+ def parameter_count
216
+ pcount = FFI::MemoryPointer.new(:uint)
217
+ NWRFCLib.get_parameter_count(@func_desc, pcount, @error.to_ptr)
218
+ NWRFC.check_error(@error)
219
+ pcount.read_uint
220
+ end
221
+
222
+ # Returns the definitions of parameters associated with this function module (lazy loading)
223
+ def parameters
224
+ @members
225
+ end
226
+
227
+ def [](param)
228
+ member_metadata(param)
229
+ end
230
+
231
+ # Look up parameter by method name given, returns parameter instance
232
+ # if it refers to a valid parameter
233
+ def method_missing(method_sym, *args, &block)
234
+ param_name = method_sym.to_s.upcase
235
+ fpar = NWRFCLib::RFCFuncParam.new
236
+ rc = NWRFCLib.get_parameter_desc_by_name(@func_desc, param_name.cU, fpar.to_ptr, @error.to_ptr)
237
+ NWRFC.check_error(@error) if rc > 0
238
+ parameter_to_hash(fpar)
239
+ end
240
+
241
+ def member_metadata(param)
242
+ param_name = param.to_s.upcase
243
+ fpar = NWRFCLib::RFCFuncParam.new
244
+ rc = NWRFCLib.get_parameter_desc_by_name(@func_desc, param_name.cU, fpar.to_ptr, @error.to_ptr)
245
+ NWRFC.check_error(@error) if rc > 0
246
+ parameter_to_hash(fpar)
247
+ end
248
+
249
+ private
250
+
251
+ def parameter_to_hash(param)
252
+ {
253
+ :type => NWRFCLib::RFCTYPE[param[:type]],
254
+ :direction => NWRFCLib::RFC_DIRECTION[param[:direction]],
255
+ :nucLength => param[:nucLength],
256
+ :ucLength => param[:ucLength],
257
+ :decimals => param[:decimals],
258
+ :typeDescHandle => param[:typeDescHandle],
259
+ :defaultValue => param[:defaultValue].get_str,
260
+ :parameterText => param[:parameterText].get_str,
261
+ :optional => param[:optional]
262
+ }
263
+ end
264
+
265
+ # Load the metadata of the function description (only called during init)
266
+ def self_describe
267
+ parameter_count.times.inject({}) do |params, index|
268
+ param = NWRFCLib::RFCFuncParam.new
269
+ NWRFCLib.get_parameter_desc_by_index(@func_desc, index, param.to_ptr, @error.to_ptr)
270
+ params[param[:name].get_str] = {
271
+ :type => NWRFCLib::RFCTYPE[param[:type]],
272
+ :direction => NWRFCLib::RFC_DIRECTION[param[:direction]],
273
+ :nucLength => param[:nucLength],
274
+ :ucLength => param[:ucLength],
275
+ :decimals => param[:decimals],
276
+ :typeDescHandle => param[:typeDescHandle],
277
+ :defaultValue => param[:defaultValue].get_str,
278
+ :parameterText => param[:parameterText].get_str,
279
+ :optional => param[:optional]
280
+ }
281
+ params
282
+ end
283
+ end
284
+
285
+ end
286
+
287
+ # Represents a callable instance of a function module, using a function module description
288
+ # obtained previously. Can be obtained by passing either a function description or
289
+ # calling Function#get_function_call
290
+ class FunctionCall
291
+ include DataContainer
292
+ attr_reader :function_description, :handle, :connection
293
+
294
+ def initialize(function_description)
295
+ @error = NWRFCLib::RFCError.new
296
+ @function_description = function_description
297
+ @connection = function_description.connection
298
+ @handle = NWRFCLib.create_function(@function_description.func_desc, @error.to_ptr)
299
+ @members = function_description
300
+ NWRFC.check_error(@error)
301
+ end
302
+
303
+ # Execute the function call on the SAP server
304
+ def invoke
305
+ rc = NWRFCLib.invoke(@connection.handle, @handle, @connection.error.to_ptr)
306
+ NWRFC.check_error(@error) if rc > 0
307
+ end
308
+
309
+ def method_missing(method_sym, *args, &block)
310
+ param_name = method_sym.to_s.upcase
311
+ # fpar = NWRFCLib::RFCFuncParam.new
312
+ # rc = NWRFCLib.get_parameter_desc_by_name(@function_description, param_name.cU, fpar.to_ptr, @error.to_ptr)
313
+ # NWRFC.check_error(@error) if rc > 0
314
+ if param_name[-1..-1] == "="
315
+ self[:param_name] = *args[0]
316
+ else
317
+ self[:param_name]
318
+ end
319
+ end
320
+
321
+ end
322
+
323
+ # Represents a structure
324
+ class Structure
325
+ include DataContainer
326
+ end
327
+
328
+ # Represents a table
329
+ class Table
330
+ include DataContainer
331
+ end
332
+
333
+ # Wrapper around the RFC Server functionality
334
+ class Server
335
+
336
+ end
337
+
338
+ end