nwrfc-aphid 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ed5e67139740dc21e73cad3b3d3a5366851fa436394daac1b17b660bcdc20e01
4
+ data.tar.gz: e4d8d6aef9fac386235529d6d2503dd0c0a4f7bdf3b9272fa3edb8058c2a1dc8
5
+ SHA512:
6
+ metadata.gz: 689b4474fd486532d19efb12e6087a23befb2d0ca3f2b95bd9cb62f605a6e79d15875dd857e3590d910fbdb805f3f33b32ce639db6ae559b4c6fc75b80e85d7a
7
+ data.tar.gz: c11fdfd60fec5ce470102196e9ea445147e85a57ca6cfd072305838d45edd74eabde57a29482de8766c21c7438bdaf1b8f0755dd681340bba1de7ffad078c078
data/README.rdoc ADDED
@@ -0,0 +1,189 @@
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-aphid'
36
+ require 'nwrfc'
37
+ include NWRFC
38
+
39
+ c = Connection.new {"user" => "martin", "passwd" => "secret",
40
+ "client" => "100", "ashost" => "ajax.domain.com", "sysnr" => "00"}
41
+
42
+ Calling a function:
43
+
44
+ f = c.get_function("STFC_STRUCTURE") #Obtain description of a function module
45
+ f.with_function_call do |fc|
46
+ fc.invoke
47
+ end
48
+
49
+ Or close the function call explicitly. Closing it also releases all nested table and structure data:
50
+
51
+ fc = f.get_function_call
52
+ begin
53
+ fc.invoke
54
+ ensure
55
+ fc.close
56
+ end
57
+
58
+ Function descriptions fetched through a connection are cached and borrowed from the SDK, so +f.close+
59
+ never destroys them. A locally defined function owns its description and should be closed after all calls
60
+ created from it have been closed:
61
+
62
+ local_function = Function.new("MY_FUNCTION")
63
+ begin
64
+ # add parameters and use local_function
65
+ ensure
66
+ local_function.close
67
+ end
68
+
69
+ Transactions retain their handle when submit or confirm fails so +commit+ can retry the appropriate step.
70
+ Close an abandoned transaction explicitly; +close+ and +destroy+ are idempotent:
71
+
72
+ transaction = c.start_transaction
73
+ begin
74
+ fc.invoke(transaction)
75
+ transaction.commit
76
+ ensure
77
+ transaction.close
78
+ end
79
+
80
+ Connections and servers also have idempotent +close+ methods. Function calls received by server callbacks,
81
+ and table or structure handles obtained from a function call, are borrowed and are never destroyed directly.
82
+
83
+ Setting and getting parameters and fields:
84
+
85
+ import_struc = fc[:IMPORTSTRUC]
86
+ import_struc[:RFCCHAR1] = 'a'
87
+
88
+ Close function calls before their connection:
89
+
90
+ fc.close
91
+ c.close
92
+
93
+ == Installation
94
+
95
+ In order to install and run nwrfc-aphid, you need to install {http://github.com/ffi/ffi Ruby-FFI} which may require compilation.
96
+ On Windows, you should be running the one-click {http://rubyinstaller.org/downloads/ Ruby Installer} and install the
97
+ {http://github.com/oneclick/rubyinstaller/wiki/Development-Kit DevKit} which is really the easiest way to compile
98
+ it on Windows.
99
+
100
+ Then install the nwrfc-aphid gem:
101
+
102
+ In a Bundler Gemfile:
103
+
104
+ gem 'nwrfc-aphid', require: 'nwrfc'
105
+
106
+ On Linux:
107
+
108
+ sudo gem install nwrfc-aphid
109
+
110
+ On Windows
111
+
112
+ gem install nwrfc-aphid
113
+
114
+ == Documentation
115
+
116
+ Documentation is installed locally when you install the gem, but you can install it with `rdoc` or `yard` or whatever
117
+ if you have cloned the repository from GitHub.
118
+
119
+ == Running the tests
120
+ The test are located in the tests/ directory. The file `login_params.yaml` contains parameters that you will need
121
+ to customize to log on to your local system that you are testing with. The YAML file contains parameters for multiple
122
+ systems, so if you want to switch to a different system, change the following line in `test_nwrfc.rb`:
123
+
124
+ $login_params = YAML.load_file(File.dirname(__FILE__) + "/login_params.yaml")["system2"]
125
+
126
+ so that ["system2"] at the end points to whatever label you gave it in the YAML file, then
127
+
128
+ rake test
129
+
130
+ == Release Notes
131
+
132
+ === Available in 0.1.0 (nwrfc-aphid)
133
+
134
+ * Maintained fork packaged as nwrfc-aphid
135
+ * Ruby 4 and Bundler 4 dependency compatibility
136
+
137
+ === Available in 0.0.9
138
+
139
+ * Support for qRFC/tRFC
140
+
141
+ === Available in 0.0.8
142
+
143
+ * BigDecimal support added for BCD values (BCD is also returned as BigDecimal to cater for very big numbers like timestamps)
144
+ * Support for String::encode in Ruby 1.9 and above instead of iconv (thanks Meatballs1)
145
+
146
+ === What's new in 0.0.7
147
+
148
+ * Add support for JRuby by extending FFI::StructLayout::CharArrayProxy
149
+ * Fix #7 (error when reading timestamp value / BCD field)
150
+
151
+ === What's new in 0.0.6
152
+
153
+ * Add :blocking => true in attach_function to allow other threads to continue running (thanks Meatballs1[https://github.com/Meatballs1])
154
+
155
+ === What's new in 0.0.5
156
+
157
+ * Bug fix for Table#each
158
+
159
+ === What's new in 0.0.4
160
+
161
+ * Add parameter activation/deactivation functionality
162
+ * Fix/add metadata retrieval for DataContainer
163
+
164
+ === What's new in 0.0.3
165
+
166
+ * Basic RFC server functionality
167
+
168
+ === What's new in 0.0.2
169
+
170
+ * More comprehensive type support
171
+ * More table operations
172
+
173
+ === What's new in 0.0.1
174
+
175
+ * Fix loading sapnw library
176
+ * Fix UTF-8 conversion for Windows
177
+ * Enhanced type support
178
+
179
+ === What's new in 0.0.0
180
+
181
+ * Able to call functions
182
+ * Most types work,
183
+ * Getting list of fields from a structure causes a SEGFAULT
184
+
185
+ == Contributing
186
+
187
+ * Improvements to the source code are welcome
188
+ * Indicate on which platforms you have tested the gem
189
+ * Suggestions for improving the API / Hints for idiomatic Ruby
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/**/test*.rb']
8
+ end
@@ -0,0 +1,346 @@
1
+ require 'bigdecimal'
2
+
3
+ module NWRFC
4
+
5
+ # Representation of a data container (function, structure or table)
6
+ # Implements common functions for data containers, such as setting and getting values, tables, structures and
7
+ # takes care of type conversion and calling correct SDK functions to set or get values
8
+ #
9
+ # == Type Conversions
10
+ # To ensure that data is passed correctly to the NW RFC SDK functions, certain conversions are applied to values
11
+ # passed, depending on the type of the field. ABAP supports a number of elementary types, which are listed in the
12
+ #
13
+ # DECFLOAT16 and DECFLOAT34 types are not yet supported.
14
+ #
15
+ # === Inbound
16
+ # For character and string
17
+ class DataContainer
18
+ attr_reader :handle, :desc
19
+
20
+ def initialize(handle)
21
+ @error = NWRFCLib::RFCError.new
22
+ @handle = handle
23
+ @desc = NWRFCLib.describe_type(@handle, @error)
24
+ @member_metadata = {} #Cache of metadata for members
25
+ NWRFC.check_error(@error)
26
+ end
27
+
28
+ #--
29
+ # VALUE RETRIEVAL
30
+ #++
31
+
32
+ # Get value from a data container (structure, function instance or table)
33
+ def [](element)
34
+ member = element.to_s.upcase
35
+ metadata = member_metadata(element)
36
+ case metadata[:type]
37
+
38
+ when :RFCTYPE_CHAR
39
+ # TODO: Try use :string parameter in get_chars
40
+ return read_chars(metadata)
41
+
42
+ when :RFCTYPE_DATE
43
+ res = read_chars(metadata)
44
+ if res == "00000000" # empty date in SAP, Date.parse will throw an error on this
45
+ return nil
46
+ else
47
+ return Date.parse(res)
48
+ end
49
+ #return Date.new(date[0..3].to_i, date[4..5].to_i, date[6..7].to_i)
50
+
51
+ when :RFCTYPE_BCD
52
+ size = metadata[:nucLength] + (metadata[:decimals] || 0)
53
+ size += 10 # Temporary fix for issue https://github.com/mydoghasworms/nwrfc/issues/7
54
+ buf = FFI::MemoryPointer.new(:uchar, size*2)
55
+ rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, buf, size, @error.to_ptr)
56
+ NWRFC.check_error(@error) if rc > 0
57
+ str = buf.get_bytes(0, size*2).uC.strip
58
+ return str.empty? ? BigDecimal(0) : BigDecimal(str)
59
+
60
+ when :RFCTYPE_TIME
61
+ # TODO: See whether we can optimize this
62
+ timec = read_chars(metadata)
63
+ return Time.parse("#{timec[0..1]}:#{timec[2..3]}:#{timec[4..5]}")
64
+
65
+ when :RFCTYPE_BYTE
66
+ size = metadata[:ucLength]
67
+ buf = FFI::MemoryPointer.new(:uchar, size)
68
+ rc = NWRFCLib.get_bytes(@handle, metadata[:name].cU, buf, size, @error.to_ptr)
69
+ NWRFC.check_error(@error) if rc > 0
70
+ return buf.get_bytes(0, size)
71
+
72
+ when :RFCTYPE_TABLE
73
+ # TODO Cache instances of table members and return those where available
74
+ new_handle = NWRFCLib::RFCDataContainer.new
75
+ rc = NWRFCLib.get_table(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
76
+ NWRFC.check_error(@error) if rc > 0
77
+ # CAVEAT: Other calls using the handle require "handle" field
78
+ # of the RFC_DATA_CONTAINER struct for some reason.
79
+ new_handle = new_handle[:handle]
80
+ return Table.new(new_handle)
81
+
82
+ when :RFCTYPE_NUM
83
+ return read_chars(metadata)
84
+
85
+ when :RFCTYPE_FLOAT
86
+ double = FFI::MemoryPointer.new :double
87
+ rc = NWRFCLib.get_float(@handle, member.cU, double, @error)
88
+ NWRFC.check_error(@error) if rc > 0
89
+ return double.get_double(0)
90
+
91
+ when :RFCTYPE_INT
92
+ int = FFI::MemoryPointer.new :int
93
+ rc = NWRFCLib.get_int(@handle, member.cU, int, @error)
94
+ NWRFC.check_error(@error) if rc > 0
95
+ return int.get_int(0)
96
+
97
+ when :RFCTYPE_INT2
98
+ short = FFI::MemoryPointer.new :short
99
+ rc = NWRFCLib.get_int2(@handle, member.cU, short, @error)
100
+ NWRFC.check_error(@error) if rc > 0
101
+ return short.get_short(0)
102
+
103
+ when :RFCTYPE_INT1
104
+ int1 = FFI::MemoryPointer.new :uint8
105
+ rc = NWRFCLib.get_int1(@handle, member.cU, int1, @error)
106
+ NWRFC.check_error(@error) if rc > 0
107
+ return int1.get_uint8(0)
108
+
109
+ when :RFCTYPE_NULL
110
+ raise "Unsupported type RFCTYPE_NULL" #You should never run into this
111
+
112
+ when :RFCTYPE_STRUCTURE
113
+ # TODO Cache instances of structure members and return those where available
114
+ new_handle = NWRFCLib::RFCDataContainer.new
115
+ rc = NWRFCLib.get_structure(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
116
+ NWRFC.check_error(@error) if rc > 0
117
+ new_handle = new_handle[:handle]
118
+ return Structure.new(new_handle)
119
+
120
+ when :RFCTYPE_DECF16
121
+ double = FFI::MemoryPointer.new :double
122
+ rc = NWRFCLib.get_dec_f16(@handle, member.cU, double, @error)
123
+ NWRFC.check_error(@error) if rc > 0
124
+ return double.get_double(0)
125
+
126
+ when :RFCTYPE_DECF34
127
+ double = FFI::MemoryPointer.new :double, 2
128
+ rc = NWRFCLib.get_dec_f34(@handle, member.cU, double, @error)
129
+ NWRFC.check_error(@error) if rc > 0
130
+ return double.get_double(0)
131
+
132
+ when :RFCTYPE_XMLDATA
133
+ raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
134
+
135
+ when :RFCTYPE_STRING
136
+ return read_string(metadata)
137
+
138
+ when :RFCTYPE_XSTRING
139
+ size = FFI::MemoryPointer.new(:uint)
140
+ rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
141
+ NWRFC.check_error(@error) if rc > 0
142
+ buf_len = size.read_uint
143
+ sbuf = FFI::MemoryPointer.new :uchar, buf_len
144
+ rc = NWRFCLib.get_x_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
145
+ NWRFC.check_error(@error) if rc > 0
146
+ return sbuf.read_string(sbuf.size)
147
+
148
+ else
149
+ raise "Illegal member type #{metadata[:type]}"
150
+ end
151
+
152
+ end
153
+
154
+ #--
155
+ # VALUE STORAGE
156
+ #++
157
+
158
+ # Set value on a data container (structure, function instance or table)
159
+ def []=(element, value)
160
+ member = element.to_s.upcase
161
+ metadata = member_metadata(element)
162
+ case metadata[:type]
163
+
164
+ when :RFCTYPE_CHAR
165
+ value = value.to_s
166
+ NWRFCLib.set_chars(@handle, member.cU, value.cU, value.length, @error.to_ptr)
167
+
168
+ when :RFCTYPE_DATE
169
+ value = value_to_date(value)
170
+ NWRFCLib.set_date(@handle, member.cU, value.cU, @error.to_ptr)
171
+
172
+ when :RFCTYPE_BCD
173
+ if value.class == BigDecimal
174
+ stval = value.to_s('F')
175
+ else
176
+ stval = value.to_s
177
+ end
178
+ m = FFI::MemoryPointer.from_string stval.cU
179
+ NWRFCLib.set_string(@handle, member.cU, m, stval.size, @error)
180
+
181
+ when :RFCTYPE_TIME
182
+ value = value_to_time(value)
183
+ NWRFCLib.set_time(@handle, member.cU, value.cU, @error.to_ptr)
184
+
185
+ when :RFCTYPE_BYTE
186
+ m = FFI::MemoryPointer.from_string value.to_s
187
+ NWRFCLib.set_bytes(@handle, member.cU, m, value.to_s.size, @error.to_ptr)
188
+
189
+ when :RFCTYPE_TABLE
190
+ raise "Value must be of type table" unless value.class == NWRFC::Table
191
+ NWRFCLib.set_table(@handle, member.cU, value.handle, @error)
192
+
193
+ when :RFCTYPE_NUM
194
+ value = value.to_s
195
+ NWRFCLib.set_num(@handle, member.cU, value.cU, value.length, @error.to_ptr)
196
+
197
+ when :RFCTYPE_FLOAT
198
+ NWRFCLib.set_float(@handle, member.cU, value.to_f, @error.to_ptr)
199
+
200
+ when :RFCTYPE_INT
201
+ NWRFCLib.set_int(@handle, member.cU, value.to_i, @error.to_ptr)
202
+
203
+ when :RFCTYPE_INT2
204
+ NWRFCLib.set_int2(@handle, member.cU, value.to_i, @error.to_ptr)
205
+
206
+ when :RFCTYPE_INT1
207
+ NWRFCLib.set_int1(@handle, member.cU, value.to_i, @error.to_ptr)
208
+
209
+ when :RFCTYPE_NULL
210
+ raise "Unsupported type RFCTYPE_NULL" #You should never run into this
211
+
212
+ when :RFCTYPE_STRUCTURE
213
+ raise "Value must be of type table" unless value.class == NWRFC::Structure
214
+ NWRFCLib.set_structure(@handle, member.cU, value.handle, @error)
215
+
216
+ when :RFCTYPE_DECF16
217
+ raise "#{@members[:type]}: decfloat16 not supported yet"
218
+ double = NWRFCLib::RFC_DECF16.new #FFI::MemoryPointer.new :double
219
+ double[:align] = value.to_f
220
+ #double.put_double(0, value.to_f)
221
+ #double = FFI::Pointer.new 4
222
+ NWRFCLib.set_dec_f16(@handle, member.cU, double.pointer, @error)
223
+
224
+ when :RFCTYPE_DECF34
225
+ raise "#{@members[:type]}: decfloat34 not supported yet"
226
+ # double = FFI::MemoryPointer.new :double, 2
227
+ # double.put_double(0, value.to_f)
228
+ double = NWRFCLib::RFC_DECF34.new #FFI::MemoryPointer.new :double
229
+ double[:align] = value.to_f
230
+ NWRFCLib.set_dec_f34(@handle, member.cU, double, @error)
231
+
232
+ when :RFCTYPE_XMLDATA
233
+ raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
234
+
235
+ when :RFCTYPE_STRING
236
+ stval = value.cU
237
+ m = FFI::MemoryPointer.from_string stval
238
+ NWRFCLib.set_string(@handle, member.cU, m, value.size, @error)
239
+
240
+ when :RFCTYPE_XSTRING
241
+ m = FFI::MemoryPointer.new value.size
242
+ m.put_bytes 0, value
243
+ NWRFCLib.set_x_string(@handle, member.cU, m, value.size, @error)
244
+
245
+ else
246
+ raise "Illegal member type #{@members[:type]}"
247
+ end
248
+ NWRFC.check_error(@error)
249
+ end
250
+
251
+ # Return value as a SAP-formatted date ("YYYYMMDD"). Force value to fit into 8 chars by
252
+ # truncating or padding with spaces
253
+ def value_to_date(value)
254
+ return value.strftime(NW_DATE_FORMAT) if value.respond_to? :strftime
255
+ # Force the resulting string into 8 characters otherwise
256
+ value = value.to_s
257
+ value << ' ' until value.size == 8 if value.size < 8
258
+ value = value[0..7] if value.size > 8
259
+ value
260
+ end
261
+
262
+ # Return value as a SAP-formatted time ("HHMMSS"). Force value to fit into 6 chars by
263
+ # truncating or padding with spaces
264
+ def value_to_time(value)
265
+ return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
266
+ # Force the resulting string into 6 characters otherwise
267
+ value = value.to_s
268
+ value << ' ' until value.size == 6 if value.size < 6
269
+ value = value[0..6] if value.size > 6
270
+ value
271
+ end
272
+
273
+ # Return a list (array) of symbols representing the names of the fields (or parameters, in the case of a function)
274
+ # of this data container
275
+ def fields
276
+ fc = FFI::MemoryPointer.new(:uint)
277
+ rc = NWRFCLib.get_field_count(@desc, fc, @error)
278
+ NWRFC.check_error(@error) if rc > 0
279
+ fc = fc.read_uint
280
+ fd = NWRFCLib::RFCFieldDesc.new
281
+ # Make a list of field names
282
+ fc.times.inject([]) {|array, index|
283
+ rc = NWRFCLib.get_field_desc_by_index(@desc, index, fd.to_ptr, @error.to_ptr)
284
+ NWRFC.check_error(@error) if rc > 0
285
+ #@todo WARNING! our get_str method did not handle getting the name of the RESPTEXT parameter in STFC_DEEP_TABLE correctly
286
+ # As a workaround, we use our read_string_dn method; do we need to use this elsewhere?
287
+ #array << fd[:name].get_str.to_sym #<-The code with good intentions
288
+ array << fd[:name].to_ptr.read_string_dn.uC.to_sym #<- Workaround; the way of the future?
289
+ }
290
+ end
291
+
292
+ # Get the metadata of a member (function, structure or table)
293
+ def member_metadata(member_name)
294
+ # TODO: Cache metadata definitions; will it be quicker than making a hash of metadata for a given member each time?
295
+ member = member_name.to_s.upcase
296
+ if self.class == NWRFC::FunctionCall
297
+ fpar = NWRFCLib::RFCFuncParam.new
298
+ rc = NWRFCLib.get_parameter_desc_by_name(@desc, member.cU, fpar.to_ptr, @error.to_ptr)
299
+ NWRFC.check_error(@error) if rc > 0
300
+ member_to_hash(fpar)
301
+ elsif self.class == NWRFC::Table || self.class == NWRFC::Structure
302
+ fd = NWRFCLib::RFCFieldDesc.new
303
+ rc = NWRFCLib.get_field_desc_by_name(@desc, member.cU, fd.to_ptr, @error.to_ptr)
304
+ NWRFC.check_error(@error) if rc > 0
305
+ member_to_hash(fd)
306
+ end
307
+ end
308
+
309
+
310
+
311
+ private
312
+ # Returns the subset of metadata values common to both a function parameter
313
+ # and a type field
314
+ def member_to_hash(member)
315
+ {
316
+ :name => member[:name].get_str,
317
+ :type => NWRFCLib::RFC_TYPE[member[:type]],
318
+ :nucLength => member[:nucLength],
319
+ :ucLength => member[:ucLength],
320
+ :decimals => member[:decimals],
321
+ :typeDescHandle => member[:typeDescHandle]
322
+ }
323
+ end
324
+
325
+ def read_chars(metadata)
326
+ size = metadata[:ucLength]
327
+ cb = FFI::MemoryPointer.new :char, size
328
+ rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, metadata[:nucLength], @error.to_ptr)
329
+ NWRFC.check_error(@error) if rc > 0
330
+ cb.read_string(size).uC
331
+ end
332
+
333
+ def read_string(metadata)
334
+ size = FFI::MemoryPointer.new(:uint)
335
+ rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
336
+ NWRFC.check_error(@error) if rc > 0
337
+ buf_len = size.read_uint + 1
338
+ sbuf = FFI::MemoryPointer.new :char, buf_len * NWRFCLib::B_SIZE
339
+ rc = NWRFCLib.get_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
340
+ NWRFC.check_error(@error) if rc > 0
341
+ sbuf.read_string(sbuf.size).uC
342
+ end
343
+
344
+ end
345
+
346
+ end
@@ -0,0 +1,40 @@
1
+ module NWRFC
2
+
3
+ class NWError < Exception
4
+
5
+ attr_reader :code, :group, :message, :class, :type, :number
6
+
7
+ # Instantiate Error object with a handle to an FFI::MemoryPointer
8
+ # to an NWRFCLib::RFCError object. The error object is analyzed so that
9
+ # when the caller intercepts it with Rescue, all the error details are
10
+ # available
11
+ def initialize(error)
12
+ @code = NWRFCLib::RFC_RC[error[:code]]
13
+ # In the event that the called function raised an exception, we must create a more specific
14
+ # error
15
+ raise(NWABAPException, error[:key].get_str) if @code == :RFC_ABAP_EXCEPTION
16
+ @group = NWRFCLib::RFC_ERROR_GROUP[error[:group]]
17
+ @message = error[:message].get_str
18
+ @type = error[:abapMsgType].get_str
19
+ @number = error[:abapMsgNumber].get_str
20
+ end
21
+
22
+ def inspect
23
+ "#{@message} (code #{@code}, group #{@group}, type #{@type}, number #{@number})"
24
+ end
25
+
26
+ end
27
+
28
+ class NWABAPException < NWError
29
+ attr_reader :exception
30
+ def initialize(exception)
31
+ @exception = exception
32
+ end
33
+
34
+ def to_s
35
+ "Function exception #{@exception}"
36
+ end
37
+
38
+ end
39
+
40
+ end