nwrfc 0.0.0

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