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 +7 -0
- data/README.rdoc +189 -0
- data/Rakefile +8 -0
- data/lib/nwrfc/datacontainer.rb +346 -0
- data/lib/nwrfc/nwerror.rb +40 -0
- data/lib/nwrfc/nwrfclib.rb +474 -0
- data/lib/nwrfc/server.rb +48 -0
- data/lib/nwrfc.rb +495 -0
- metadata +109 -0
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,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
|