nwrfc 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +5 -0
- data/lib/dev_rfc.trc +7 -0
- data/lib/nwrfc.rb +61 -208
- data/lib/nwrfc/datacontainer.rb +235 -0
- data/lib/nwrfc/dev_rfc.trc +28 -0
- data/lib/nwrfc/nwrfclib.rb +29 -59
- metadata +6 -5
- data/lib/nwrfc.old.rb +0 -338
data/README.rdoc
CHANGED
@@ -59,6 +59,11 @@ On Windows
|
|
59
59
|
|
60
60
|
gem install nwrfc
|
61
61
|
|
62
|
+
== Documentation
|
63
|
+
|
64
|
+
Documentation is installed locally when you install the gem, but you can install it with `rdoc` or `yard` or whatever
|
65
|
+
if you have cloned the repository from GitHub.
|
66
|
+
|
62
67
|
== Running the tests
|
63
68
|
The test are located in the tests/ directory. The file `login_params.yaml` contains parameters that you will need
|
64
69
|
to customize to log on to your local system that you are testing with. The YAML file contains parameters for multiple
|
data/lib/dev_rfc.trc
CHANGED
@@ -131,3 +131,10 @@ ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
|
131
131
|
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
132
132
|
|
133
133
|
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
134
|
+
|
135
|
+
**** Trace file opened at 2012-02-17, 12:38:41 SAST
|
136
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
137
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
138
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
139
|
+
|
140
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
data/lib/nwrfc.rb
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
# No ownership over any of these is asserted by Martin Ceronio
|
7
7
|
|
8
8
|
require File.dirname(__FILE__)+'/nwrfc/nwrfclib'
|
9
|
+
require File.dirname(__FILE__)+'/nwrfc/datacontainer'
|
9
10
|
|
10
11
|
require 'date'
|
11
12
|
require 'time'
|
@@ -89,6 +90,45 @@ module NWRFC
|
|
89
90
|
|
90
91
|
end
|
91
92
|
|
93
|
+
def NWRFC.abap_bool(value)
|
94
|
+
return true if value == 'X'
|
95
|
+
return false if value == ' '
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def NWRFC.bool_abap(value)
|
100
|
+
return 'X' if value == true
|
101
|
+
return ' ' if value == false
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# Represents a function parameter
|
106
|
+
class Parameter
|
107
|
+
|
108
|
+
attr_accessor :handle
|
109
|
+
|
110
|
+
# Create a parameter by setting parameter attributes
|
111
|
+
def initialize(*args)
|
112
|
+
attr = args[0]
|
113
|
+
@handle = NWRFCLib::RFCFuncParam.new
|
114
|
+
@handle[:name] = attr[:name].cU if attr[:name]
|
115
|
+
@handle[:direction] = NWRFCLib::RFC_DIRECTION[attr[:direction]] if attr[:direction]
|
116
|
+
@handle[:type] = NWRFCLib::RFC_TYPE[attr[:type]] if attr[:type]
|
117
|
+
@handle[:ucLength] = attr[:length] * 2 if attr[:length]
|
118
|
+
@handle[:nucLength] = attr[:length] if attr[:length]
|
119
|
+
@handle[:decimals] = attr[:decimals] if attr[:decimals]
|
120
|
+
# TODO: Add support for type description
|
121
|
+
#@handle[:typeDescHandle]
|
122
|
+
@handle[:defaultValue] = attr[:defaultValue].cU if attr[:defaultValue]
|
123
|
+
@handle[:parameterText] = attr[:parameterText].cU if attr[:parameterText]
|
124
|
+
@handle[:optional] = abap_bool(attr[:optional]) if attr[:optional]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Type
|
129
|
+
|
130
|
+
end
|
131
|
+
|
92
132
|
# Represents a remote-enabled function module for RFC, can be instantiated either by the caller
|
93
133
|
# or by calling Connection#get_function. This only represents the description of the function;
|
94
134
|
# to call a function, an instance of a function call must be obtained with #get_function_call
|
@@ -96,12 +136,28 @@ module NWRFC
|
|
96
136
|
attr_reader :desc, :connection, :function_name
|
97
137
|
|
98
138
|
# Get a function module instance; can also be obtained by calling Connection#get_function
|
99
|
-
|
100
|
-
|
139
|
+
# Takes either: (connection, function_name) or (function_name)
|
140
|
+
# When passed only `function_name`, creates a new function description locally, instead of
|
141
|
+
# fetching it form the server pointed to by connection
|
142
|
+
def initialize(*args)#(connection, function_name)
|
143
|
+
raise("Must initialize function with 1 or 2 arguments") if args.size != 1 && args.size != 2
|
101
144
|
@error = NWRFCLib::RFCError.new
|
102
|
-
|
103
|
-
|
104
|
-
|
145
|
+
if args.size == 2
|
146
|
+
@function_name = args[1] #function_name
|
147
|
+
@desc = NWRFCLib.get_function_desc(args[0].handle, args[1].cU, @error.to_ptr)
|
148
|
+
NWRFC.check_error(@error)
|
149
|
+
@connection = args[0]
|
150
|
+
else
|
151
|
+
@function_name = args[0] #function_name
|
152
|
+
@desc = NWRFCLib::create_function_desc(args[0].cU, @error)
|
153
|
+
NWRFC.check_error(@error)
|
154
|
+
@connection = nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_parameter(parameter)
|
159
|
+
rc = NWRFCLib.add_parameter(@desc, parameter.handle, @error)
|
160
|
+
NWRFC.check_error(@error) if rc > 0
|
105
161
|
end
|
106
162
|
|
107
163
|
# Create and return a callable instance of this function module
|
@@ -119,209 +175,6 @@ module NWRFC
|
|
119
175
|
|
120
176
|
end
|
121
177
|
|
122
|
-
# Representation of a data container (function, structure or table)
|
123
|
-
class DataContainer
|
124
|
-
attr_reader :handle, :desc
|
125
|
-
|
126
|
-
def initialize(handle)
|
127
|
-
@error = NWRFCLib::RFCError.new
|
128
|
-
@handle = handle
|
129
|
-
@desc = NWRFCLib.describe_type(@handle, @error)
|
130
|
-
@member_metadata = {} #Cache of metadata for members
|
131
|
-
NWRFC.check_error(@error)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Return the member specified by string or symbol
|
135
|
-
def [](element)
|
136
|
-
member = element.to_s.upcase
|
137
|
-
metadata = member_metadata(element)
|
138
|
-
case metadata[:type]
|
139
|
-
when :RFCTYPE_CHAR
|
140
|
-
# TODO: Try use :string parameter in get_chars
|
141
|
-
return read_chars(metadata)
|
142
|
-
when :RFCTYPE_DATE
|
143
|
-
return Date.parse(read_chars(metadata))
|
144
|
-
#return Date.new(date[0..3].to_i, date[4..5].to_i, date[6..7].to_i)
|
145
|
-
when :RFCTYPE_BCD
|
146
|
-
size = metadata[:ucLength]
|
147
|
-
cb = FFI::MemoryPointer.new :char, size * 2
|
148
|
-
rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, size * 2, @error.to_ptr)
|
149
|
-
NWRFC.check_error(@error) if rc > 0
|
150
|
-
cb.read_string(size).uC
|
151
|
-
when :RFCTYPE_TIME
|
152
|
-
# TODO: See whether we can optimize this
|
153
|
-
timec = read_chars(metadata)
|
154
|
-
return Time.parse("#{timec[0..1]}:#{timec[2..3]}:#{timec[4..5]}")
|
155
|
-
when :RFCTYPE_BYTE
|
156
|
-
return read_chars(metadata)
|
157
|
-
when :RFCTYPE_TABLE
|
158
|
-
new_handle = NWRFCLib::RFCDataContainer.new
|
159
|
-
rc = NWRFCLib.get_table(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
|
160
|
-
NWRFC.check_error(@error) if rc > 0
|
161
|
-
# CAVEAT: Other calls using the handle require "handle" field
|
162
|
-
# of the RFC_DATA_CONTAINER struct for some reason.
|
163
|
-
new_handle = new_handle[:handle]
|
164
|
-
value = Table.new(new_handle)
|
165
|
-
when :RFCTYPE_NUM
|
166
|
-
return read_chars(metadata).to_i
|
167
|
-
when :RFCTYPE_FLOAT
|
168
|
-
double = FFI::MemoryPointer.new :double
|
169
|
-
rc = NWRFCLib.get_float(@handle, member.cU, double, @error)
|
170
|
-
NWRFC.check_error(@error) if rc > 0
|
171
|
-
return double.get_double(0)
|
172
|
-
when :RFCTYPE_INT
|
173
|
-
int = FFI::MemoryPointer.new :int
|
174
|
-
rc = NWRFCLib.get_int(@handle, member.cU, int, @error)
|
175
|
-
NWRFC.check_error(@error) if rc > 0
|
176
|
-
return int.get_int(0)
|
177
|
-
when :RFCTYPE_INT2
|
178
|
-
short = FFI::MemoryPointer.new :short
|
179
|
-
rc = NWRFCLib.get_int2(@handle, member.cU, short, @error)
|
180
|
-
NWRFC.check_error(@error) if rc > 0
|
181
|
-
return short.get_short(0)
|
182
|
-
when :RFCTYPE_INT1
|
183
|
-
int1 = FFI::MemoryPointer.new :uint8
|
184
|
-
rc = NWRFCLib.get_int1(@handle, member.cU, int1, @error)
|
185
|
-
NWRFC.check_error(@error) if rc > 0
|
186
|
-
return int1.get_uint8(0)
|
187
|
-
when :RFCTYPE_NULL
|
188
|
-
raise "Unsupported type RFCTYPE_NULL" #You should never run into this
|
189
|
-
when :RFCTYPE_STRUCTURE
|
190
|
-
new_handle = NWRFCLib::RFCDataContainer.new
|
191
|
-
rc = NWRFCLib.get_structure(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
|
192
|
-
NWRFC.check_error(@error) if rc > 0
|
193
|
-
new_handle = new_handle[:handle]
|
194
|
-
value = Structure.new(new_handle)
|
195
|
-
when :RFCTYPE_DECF16
|
196
|
-
return read_chars(metadata).to_f
|
197
|
-
when :RFCTYPE_DECF34
|
198
|
-
return read_chars(metadata).to_f
|
199
|
-
when :RFCTYPE_XMLDATA
|
200
|
-
raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
|
201
|
-
when :RFCTYPE_STRING
|
202
|
-
return read_string(metadata)
|
203
|
-
when :RFCTYPE_XSTRING
|
204
|
-
else
|
205
|
-
raise "Illegal member type #{metadata[:type]}"
|
206
|
-
end
|
207
|
-
NWRFC.check_error(@error)
|
208
|
-
value
|
209
|
-
end
|
210
|
-
|
211
|
-
def []=(element, value)
|
212
|
-
member = element.to_s.upcase
|
213
|
-
metadata = member_metadata(element)
|
214
|
-
case metadata[:type]
|
215
|
-
when :RFCTYPE_CHAR
|
216
|
-
NWRFCLib.set_chars(@handle, member.cU, value.cU, value.length, @error.to_ptr)
|
217
|
-
when :RFCTYPE_DATE
|
218
|
-
value = value_to_date(value)
|
219
|
-
NWRFCLib.set_date(@handle, member.cU, value.cU, @error.to_ptr)
|
220
|
-
when :RFCTYPE_BCD
|
221
|
-
when :RFCTYPE_TIME
|
222
|
-
value = value_to_time(value)
|
223
|
-
NWRFCLib.set_time(@handle, member.cU, value.cU, @error.to_ptr)
|
224
|
-
when :RFCTYPE_BYTE
|
225
|
-
when :RFCTYPE_TABLE
|
226
|
-
when :RFCTYPE_NUM
|
227
|
-
when :RFCTYPE_FLOAT
|
228
|
-
NWRFCLib.set_float(@handle, member.cU, value.to_f, @error.to_ptr)
|
229
|
-
#NWRFCLib.set_chars(@handle, member.cU, value.to_s.cU, value.to_s.length, @error.to_ptr)
|
230
|
-
when :RFCTYPE_INT
|
231
|
-
NWRFCLib.set_int(@handle, member.cU, value.to_i, @error.to_ptr)
|
232
|
-
when :RFCTYPE_INT2
|
233
|
-
NWRFCLib.set_int2(@handle, member.cU, value.to_i, @error.to_ptr)
|
234
|
-
when :RFCTYPE_INT1
|
235
|
-
NWRFCLib.set_int1(@handle, member.cU, value.to_i, @error.to_ptr)
|
236
|
-
when :RFCTYPE_NULL
|
237
|
-
raise "Unsupported type RFCTYPE_NULL" #You should never run into this
|
238
|
-
when :RFCTYPE_STRUCTURE
|
239
|
-
when :RFCTYPE_DECF16
|
240
|
-
when :RFCTYPE_DECF34
|
241
|
-
when :RFCTYPE_XMLDATA
|
242
|
-
when :RFCTYPE_STRING
|
243
|
-
when :RFCTYPE_XSTRING
|
244
|
-
else
|
245
|
-
raise "Illegal member type #{@members[:type]}"
|
246
|
-
end
|
247
|
-
NWRFC.check_error(@error)
|
248
|
-
end
|
249
|
-
|
250
|
-
def value_to_time(value)
|
251
|
-
return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
|
252
|
-
value.to_s
|
253
|
-
end
|
254
|
-
|
255
|
-
def value_to_date(value)
|
256
|
-
return value.strftime(NW_DATE_FORMAT) if value.respond_to? :strftime
|
257
|
-
# Force the resulting string into 8 characters otherwise
|
258
|
-
value = value.to_s
|
259
|
-
value << ' ' until value.size == 8 if value.size < 8
|
260
|
-
value = value[0..7] if value.size > 8
|
261
|
-
value
|
262
|
-
end
|
263
|
-
|
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
|
-
# Get the metadata of a member (function, structure or table)
|
274
|
-
def member_metadata(member_name)
|
275
|
-
member = member_name.to_s.upcase
|
276
|
-
if self.class == NWRFC::FunctionCall
|
277
|
-
fpar = NWRFCLib::RFCFuncParam.new
|
278
|
-
rc = NWRFCLib.get_parameter_desc_by_name(@desc, member.cU, fpar.to_ptr, @error.to_ptr)
|
279
|
-
NWRFC.check_error(@error) if rc > 0
|
280
|
-
member_to_hash(fpar)
|
281
|
-
elsif self.class == NWRFC::Table || self.class == NWRFC::Structure
|
282
|
-
fd = NWRFCLib::RFCFieldDesc.new
|
283
|
-
rc = NWRFCLib.get_field_desc_by_name(@desc, member.cU, fd.to_ptr, @error.to_ptr)
|
284
|
-
NWRFC.check_error(@error) if rc > 0
|
285
|
-
member_to_hash(fd)
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
private
|
290
|
-
# Returns the subset of metadata values common to both a function parameter
|
291
|
-
# and a type field
|
292
|
-
def member_to_hash(member)
|
293
|
-
{
|
294
|
-
:name => member[:name].get_str,
|
295
|
-
:type => NWRFCLib::RFCTYPE[member[:type]],
|
296
|
-
:nucLength => member[:nucLength],
|
297
|
-
:ucLength => member[:ucLength],
|
298
|
-
:decimals => member[:decimals],
|
299
|
-
:typeDescHandle => member[:typeDescHandle]
|
300
|
-
}
|
301
|
-
end
|
302
|
-
|
303
|
-
def read_chars(metadata)
|
304
|
-
size = metadata[:ucLength]
|
305
|
-
cb = FFI::MemoryPointer.new :char, size
|
306
|
-
rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, metadata[:nucLength], @error.to_ptr)
|
307
|
-
NWRFC.check_error(@error) if rc > 0
|
308
|
-
cb.read_string(size).uC
|
309
|
-
end
|
310
|
-
|
311
|
-
def read_string(metadata)
|
312
|
-
#size = metadata[:ucLength]
|
313
|
-
size = FFI::MemoryPointer.new(:uint)
|
314
|
-
rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
|
315
|
-
NWRFC.check_error(@error) if rc > 0
|
316
|
-
buf_len = size.read_uint + 1
|
317
|
-
sbuf = FFI::MemoryPointer.new :char, buf_len * NWRFCLib::B_SIZE
|
318
|
-
rc = NWRFCLib.get_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
|
319
|
-
NWRFC.check_error(@error) if rc > 0
|
320
|
-
sbuf.read_string(sbuf.size).uC
|
321
|
-
end
|
322
|
-
|
323
|
-
end
|
324
|
-
|
325
178
|
class FunctionCall < DataContainer
|
326
179
|
attr_reader :handle, :desc, :connection, :function
|
327
180
|
|
@@ -0,0 +1,235 @@
|
|
1
|
+
module NWRFC
|
2
|
+
|
3
|
+
# Representation of a data container (function, structure or table)
|
4
|
+
class DataContainer
|
5
|
+
attr_reader :handle, :desc
|
6
|
+
|
7
|
+
def initialize(handle)
|
8
|
+
@error = NWRFCLib::RFCError.new
|
9
|
+
@handle = handle
|
10
|
+
@desc = NWRFCLib.describe_type(@handle, @error)
|
11
|
+
@member_metadata = {} #Cache of metadata for members
|
12
|
+
NWRFC.check_error(@error)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return the member specified by string or symbol
|
16
|
+
def [](element)
|
17
|
+
member = element.to_s.upcase
|
18
|
+
metadata = member_metadata(element)
|
19
|
+
case metadata[:type]
|
20
|
+
when :RFCTYPE_CHAR
|
21
|
+
# TODO: Try use :string parameter in get_chars
|
22
|
+
return read_chars(metadata)
|
23
|
+
when :RFCTYPE_DATE
|
24
|
+
return Date.parse(read_chars(metadata))
|
25
|
+
#return Date.new(date[0..3].to_i, date[4..5].to_i, date[6..7].to_i)
|
26
|
+
when :RFCTYPE_BCD
|
27
|
+
size = metadata[:ucLength]
|
28
|
+
cb = FFI::MemoryPointer.new :char, size * 2
|
29
|
+
rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, size * 2, @error.to_ptr)
|
30
|
+
NWRFC.check_error(@error) if rc > 0
|
31
|
+
cb.read_string(size).uC
|
32
|
+
when :RFCTYPE_TIME
|
33
|
+
# TODO: See whether we can optimize this
|
34
|
+
timec = read_chars(metadata)
|
35
|
+
return Time.parse("#{timec[0..1]}:#{timec[2..3]}:#{timec[4..5]}")
|
36
|
+
when :RFCTYPE_BYTE
|
37
|
+
return read_chars(metadata)
|
38
|
+
when :RFCTYPE_TABLE
|
39
|
+
new_handle = NWRFCLib::RFCDataContainer.new
|
40
|
+
rc = NWRFCLib.get_table(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
|
41
|
+
NWRFC.check_error(@error) if rc > 0
|
42
|
+
# CAVEAT: Other calls using the handle require "handle" field
|
43
|
+
# of the RFC_DATA_CONTAINER struct for some reason.
|
44
|
+
new_handle = new_handle[:handle]
|
45
|
+
value = Table.new(new_handle)
|
46
|
+
when :RFCTYPE_NUM
|
47
|
+
return read_chars(metadata).to_i
|
48
|
+
when :RFCTYPE_FLOAT
|
49
|
+
double = FFI::MemoryPointer.new :double
|
50
|
+
rc = NWRFCLib.get_float(@handle, member.cU, double, @error)
|
51
|
+
NWRFC.check_error(@error) if rc > 0
|
52
|
+
return double.get_double(0)
|
53
|
+
when :RFCTYPE_INT
|
54
|
+
int = FFI::MemoryPointer.new :int
|
55
|
+
rc = NWRFCLib.get_int(@handle, member.cU, int, @error)
|
56
|
+
NWRFC.check_error(@error) if rc > 0
|
57
|
+
return int.get_int(0)
|
58
|
+
when :RFCTYPE_INT2
|
59
|
+
short = FFI::MemoryPointer.new :short
|
60
|
+
rc = NWRFCLib.get_int2(@handle, member.cU, short, @error)
|
61
|
+
NWRFC.check_error(@error) if rc > 0
|
62
|
+
return short.get_short(0)
|
63
|
+
when :RFCTYPE_INT1
|
64
|
+
int1 = FFI::MemoryPointer.new :uint8
|
65
|
+
rc = NWRFCLib.get_int1(@handle, member.cU, int1, @error)
|
66
|
+
NWRFC.check_error(@error) if rc > 0
|
67
|
+
return int1.get_uint8(0)
|
68
|
+
when :RFCTYPE_NULL
|
69
|
+
raise "Unsupported type RFCTYPE_NULL" #You should never run into this
|
70
|
+
when :RFCTYPE_STRUCTURE
|
71
|
+
new_handle = NWRFCLib::RFCDataContainer.new
|
72
|
+
rc = NWRFCLib.get_structure(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
|
73
|
+
NWRFC.check_error(@error) if rc > 0
|
74
|
+
new_handle = new_handle[:handle]
|
75
|
+
value = Structure.new(new_handle)
|
76
|
+
when :RFCTYPE_DECF16
|
77
|
+
double = FFI::MemoryPointer.new :double
|
78
|
+
rc = NWRFCLib.get_dec_f16(@handle, member.cU, double, @error)
|
79
|
+
NWRFC.check_error(@error) if rc > 0
|
80
|
+
return double.get_double(0)
|
81
|
+
when :RFCTYPE_DECF34
|
82
|
+
double = FFI::MemoryPointer.new :double, 2
|
83
|
+
rc = NWRFCLib.get_dec_f34(@handle, member.cU, double, @error)
|
84
|
+
NWRFC.check_error(@error) if rc > 0
|
85
|
+
return double.get_double(0)
|
86
|
+
when :RFCTYPE_XMLDATA
|
87
|
+
raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
|
88
|
+
when :RFCTYPE_STRING
|
89
|
+
return read_string(metadata)
|
90
|
+
when :RFCTYPE_XSTRING
|
91
|
+
size = FFI::MemoryPointer.new(:uint)
|
92
|
+
rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
|
93
|
+
NWRFC.check_error(@error) if rc > 0
|
94
|
+
buf_len = size.read_uint
|
95
|
+
sbuf = FFI::MemoryPointer.new :uchar, buf_len
|
96
|
+
rc = NWRFCLib.get_x_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
|
97
|
+
NWRFC.check_error(@error) if rc > 0
|
98
|
+
return sbuf.read_string(sbuf.size)
|
99
|
+
else
|
100
|
+
raise "Illegal member type #{metadata[:type]}"
|
101
|
+
end
|
102
|
+
NWRFC.check_error(@error)
|
103
|
+
value
|
104
|
+
end
|
105
|
+
|
106
|
+
def []=(element, value)
|
107
|
+
member = element.to_s.upcase
|
108
|
+
metadata = member_metadata(element)
|
109
|
+
case metadata[:type]
|
110
|
+
when :RFCTYPE_CHAR
|
111
|
+
NWRFCLib.set_chars(@handle, member.cU, value.cU, value.length, @error.to_ptr)
|
112
|
+
when :RFCTYPE_DATE
|
113
|
+
value = value_to_date(value)
|
114
|
+
NWRFCLib.set_date(@handle, member.cU, value.cU, @error.to_ptr)
|
115
|
+
when :RFCTYPE_BCD
|
116
|
+
when :RFCTYPE_TIME
|
117
|
+
value = value_to_time(value)
|
118
|
+
NWRFCLib.set_time(@handle, member.cU, value.cU, @error.to_ptr)
|
119
|
+
when :RFCTYPE_BYTE
|
120
|
+
when :RFCTYPE_TABLE
|
121
|
+
when :RFCTYPE_NUM
|
122
|
+
when :RFCTYPE_FLOAT
|
123
|
+
NWRFCLib.set_float(@handle, member.cU, value.to_f, @error.to_ptr)
|
124
|
+
#NWRFCLib.set_chars(@handle, member.cU, value.to_s.cU, value.to_s.length, @error.to_ptr)
|
125
|
+
when :RFCTYPE_INT
|
126
|
+
NWRFCLib.set_int(@handle, member.cU, value.to_i, @error.to_ptr)
|
127
|
+
when :RFCTYPE_INT2
|
128
|
+
NWRFCLib.set_int2(@handle, member.cU, value.to_i, @error.to_ptr)
|
129
|
+
when :RFCTYPE_INT1
|
130
|
+
NWRFCLib.set_int1(@handle, member.cU, value.to_i, @error.to_ptr)
|
131
|
+
when :RFCTYPE_NULL
|
132
|
+
raise "Unsupported type RFCTYPE_NULL" #You should never run into this
|
133
|
+
when :RFCTYPE_STRUCTURE
|
134
|
+
when :RFCTYPE_DECF16
|
135
|
+
raise "#{@members[:type]}: decfloat16 not supported yet"
|
136
|
+
double = NWRFCLib::RFC_DECF16.new #FFI::MemoryPointer.new :double
|
137
|
+
double[:align] = value.to_f
|
138
|
+
#double.put_double(0, value.to_f)
|
139
|
+
#double = FFI::Pointer.new 4
|
140
|
+
NWRFCLib.set_dec_f16(@handle, member.cU, double.pointer, @error)
|
141
|
+
when :RFCTYPE_DECF34
|
142
|
+
raise "#{@members[:type]}: decfloat34 not supported yet"
|
143
|
+
# double = FFI::MemoryPointer.new :double, 2
|
144
|
+
# double.put_double(0, value.to_f)
|
145
|
+
double = NWRFCLib::RFC_DECF34.new #FFI::MemoryPointer.new :double
|
146
|
+
double[:align] = value.to_f
|
147
|
+
NWRFCLib.set_dec_f34(@handle, member.cU, double, @error)
|
148
|
+
when :RFCTYPE_XMLDATA
|
149
|
+
raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
|
150
|
+
when :RFCTYPE_STRING
|
151
|
+
when :RFCTYPE_XSTRING
|
152
|
+
m = FFI::MemoryPointer.new value.size
|
153
|
+
m.put_bytes 0, value
|
154
|
+
NWRFCLib.set_x_string(@handle, member.cU, m, value.size, @error)
|
155
|
+
else
|
156
|
+
raise "Illegal member type #{@members[:type]}"
|
157
|
+
end
|
158
|
+
NWRFC.check_error(@error)
|
159
|
+
end
|
160
|
+
|
161
|
+
def value_to_time(value)
|
162
|
+
return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
|
163
|
+
value.to_s
|
164
|
+
end
|
165
|
+
|
166
|
+
def value_to_date(value)
|
167
|
+
return value.strftime(NW_DATE_FORMAT) if value.respond_to? :strftime
|
168
|
+
# Force the resulting string into 8 characters otherwise
|
169
|
+
value = value.to_s
|
170
|
+
value << ' ' until value.size == 8 if value.size < 8
|
171
|
+
value = value[0..7] if value.size > 8
|
172
|
+
value
|
173
|
+
end
|
174
|
+
|
175
|
+
def value_to_time(value)
|
176
|
+
return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
|
177
|
+
# Force the resulting string into 6 characters otherwise
|
178
|
+
value = value.to_s
|
179
|
+
value << ' ' until value.size == 6 if value.size < 6
|
180
|
+
value = value[0..6] if value.size > 6
|
181
|
+
value
|
182
|
+
end
|
183
|
+
|
184
|
+
# Get the metadata of a member (function, structure or table)
|
185
|
+
def member_metadata(member_name)
|
186
|
+
member = member_name.to_s.upcase
|
187
|
+
if self.class == NWRFC::FunctionCall
|
188
|
+
fpar = NWRFCLib::RFCFuncParam.new
|
189
|
+
rc = NWRFCLib.get_parameter_desc_by_name(@desc, member.cU, fpar.to_ptr, @error.to_ptr)
|
190
|
+
NWRFC.check_error(@error) if rc > 0
|
191
|
+
member_to_hash(fpar)
|
192
|
+
elsif self.class == NWRFC::Table || self.class == NWRFC::Structure
|
193
|
+
fd = NWRFCLib::RFCFieldDesc.new
|
194
|
+
rc = NWRFCLib.get_field_desc_by_name(@desc, member.cU, fd.to_ptr, @error.to_ptr)
|
195
|
+
NWRFC.check_error(@error) if rc > 0
|
196
|
+
member_to_hash(fd)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
# Returns the subset of metadata values common to both a function parameter
|
202
|
+
# and a type field
|
203
|
+
def member_to_hash(member)
|
204
|
+
{
|
205
|
+
:name => member[:name].get_str,
|
206
|
+
:type => NWRFCLib::RFC_TYPE[member[:type]],
|
207
|
+
:nucLength => member[:nucLength],
|
208
|
+
:ucLength => member[:ucLength],
|
209
|
+
:decimals => member[:decimals],
|
210
|
+
:typeDescHandle => member[:typeDescHandle]
|
211
|
+
}
|
212
|
+
end
|
213
|
+
|
214
|
+
def read_chars(metadata)
|
215
|
+
size = metadata[:ucLength]
|
216
|
+
cb = FFI::MemoryPointer.new :char, size
|
217
|
+
rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, metadata[:nucLength], @error.to_ptr)
|
218
|
+
NWRFC.check_error(@error) if rc > 0
|
219
|
+
cb.read_string(size).uC
|
220
|
+
end
|
221
|
+
|
222
|
+
def read_string(metadata)
|
223
|
+
size = FFI::MemoryPointer.new(:uint)
|
224
|
+
rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
|
225
|
+
NWRFC.check_error(@error) if rc > 0
|
226
|
+
buf_len = size.read_uint + 1
|
227
|
+
sbuf = FFI::MemoryPointer.new :char, buf_len * NWRFCLib::B_SIZE
|
228
|
+
rc = NWRFCLib.get_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
|
229
|
+
NWRFC.check_error(@error) if rc > 0
|
230
|
+
sbuf.read_string(sbuf.size).uC
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
**** Trace file opened at 2012-02-17, 14:29:25 SAST
|
3
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, 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-17, 14:35:20 SAST
|
10
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, 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-17, 14:50:20 SAST
|
17
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, 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-17, 15:04:13 SAST
|
24
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, 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
|
data/lib/nwrfc/nwrfclib.rb
CHANGED
@@ -15,6 +15,7 @@ RUBY_VERSION_18 = RUBY_VERSION[0..2] == "1.8"
|
|
15
15
|
# See http://stackoverflow.com/questions/9035661/ruby-ffi-memorypointer-read-int-present-in-1-9-but-not-1-8
|
16
16
|
# Probably not good to interpret an unsigned int as an int, but we don't expect to use it for big values
|
17
17
|
# that could result in sign conversion
|
18
|
+
# FIXME - This must go! Replace with calls to get_* defined in FFI::MemoryPointer
|
18
19
|
if RUBY_VERSION_18
|
19
20
|
FFI::MemoryPointer.class_eval{ alias :read_uint :read_int}
|
20
21
|
end
|
@@ -59,27 +60,24 @@ class String
|
|
59
60
|
end
|
60
61
|
|
61
62
|
end
|
62
|
-
#String.class_eval{define_method(:cU){ NWRFCLib::Cutf8_to_utf16le.iconv(self+"\0") }}
|
63
63
|
|
64
64
|
# Enhancement to FFI::StructLayout::CharArray to add a get_str method that changes the
|
65
65
|
# string value of the character array by enforcing encoding of UTF-16LE (as used in NW RFC SDK)
|
66
66
|
# and strips off blanks at the end to return a readable String
|
67
67
|
class FFI::StructLayout::CharArray
|
68
68
|
def get_str
|
69
|
-
#Iconv.conv("UTF8", "UTF-16LE", self.to_ptr.read_string(self.size)).strip
|
70
69
|
NWRFCLib::Cutf16le_to_utf8.iconv(self.to_ptr.read_string(self.size)).strip
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
74
73
|
# Library wrapper around NW RFC SDK shared library using RUBY-FFI
|
75
|
-
# FIXME: Make structs managed structs and deconstruct them on GC
|
76
74
|
module NWRFCLib
|
77
75
|
|
78
|
-
Cutf8_to_utf16le = Iconv.new("UTF-16LE", "
|
79
|
-
Cutf16le_to_utf8 = Iconv.new("
|
76
|
+
Cutf8_to_utf16le = Iconv.new("UTF-16LE", "UTF-8")
|
77
|
+
Cutf16le_to_utf8 = Iconv.new("UTF-8", "UTF-16LE")
|
80
78
|
|
81
79
|
extend FFI::Library
|
82
|
-
ffi_lib '
|
80
|
+
ffi_lib 'sapnwrfc'
|
83
81
|
|
84
82
|
# Multiplier for providing correct byte size for String passed to RFC library
|
85
83
|
#TODO: Make platform-dependent size based on RUBY_PLATFORM
|
@@ -132,7 +130,7 @@ module NWRFCLib
|
|
132
130
|
:RFC_TABLES, 7
|
133
131
|
)
|
134
132
|
|
135
|
-
|
133
|
+
RFC_TYPE = enum(
|
136
134
|
:RFCTYPE_CHAR , 0,
|
137
135
|
:RFCTYPE_DATE , 1,
|
138
136
|
:RFCTYPE_BCD , 2,
|
@@ -245,59 +243,29 @@ module NWRFCLib
|
|
245
243
|
layout :handle, :pointer
|
246
244
|
end
|
247
245
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
# #TODO: Do we want to include RfcDestroyFunction for releasing its memory?
|
265
|
-
# attach_function :get_param_count, :RfcGetParameterCount,
|
266
|
-
# [:pointer, :pointer, :pointer], :void #(fd_handle, uint, error)
|
267
|
-
# attach_function :get_parameter_desc_by_index, :RfcGetParameterDescByIndex,
|
268
|
-
# [:pointer, :uint, :pointer, :pointer], :void #(fd_handle, idx, param, error)
|
269
|
-
# # Field Handling
|
270
|
-
# # RFC_RC RfcGetChars(handle, name, buffer, length, error)
|
271
|
-
# attach_function :get_chars, :RfcGetChars,
|
272
|
-
# [:pointer, :pointer, :pointer, :uint, :pointer], :RFC_RC
|
273
|
-
# # RFC_RC RfcGetChars(handle, name, buffer, length, error)
|
274
|
-
# attach_function :get_num, :RfcGetNum,
|
275
|
-
# [:pointer, :pointer, :pointer, :uint, :pointer], :RFC_RC
|
276
|
-
# # RFC_RC RfcGetDate
|
277
|
-
# attach_function :get_date, :RfcGetDate,
|
278
|
-
# [:pointer, :pointer, [:char, 8], :pointer], :RFC_RC
|
279
|
-
# # RFC_RC RfcGetDate
|
280
|
-
# attach_function :get_time, :RfcGetTime,
|
281
|
-
# [:pointer, :pointer, [:char, 6], :pointer], :RFC_RC
|
282
|
-
# # RFC_RC RfcGetString
|
283
|
-
# # attach_function :get_string, :RfcGetString,
|
284
|
-
# # [:pointer, :pointer, :pointer], :RFC_RC
|
285
|
-
# # RFC RfcGetBytes
|
286
|
-
# attach_function :get_bytes, :RfcGetBytes,
|
287
|
-
# [:pointer, :pointer, :pointer, :uint, :pointer], :RFC_RC
|
288
|
-
# # RFC_RC RfcGetXString
|
289
|
-
# attach_function :get_xstring, :RfcGetXString,
|
290
|
-
# [:pointer, :pointer, :pointer, :uint, :pointer, :pointer], :RFC_RC
|
291
|
-
# # RFC_RC RfcGetStringLength(dataHandle, name, *length, error)
|
292
|
-
# attach_function :get_string_length, :RfcGetStringLength,
|
293
|
-
# [:pointer, :pointer, :pointer, :pointer], :int
|
294
|
-
# # void RfcSetChars(dataHandle, name, value, length, error)
|
295
|
-
# attach_function :set_chars, :RfcSetChars,
|
296
|
-
# [:pointer, :pointer, :pointer, :uint, :pointer], :void
|
297
|
-
#+++
|
246
|
+
class RFC_DECF16 < FFI::Union
|
247
|
+
layout :bytes, [:uchar, 8],
|
248
|
+
:align, :double
|
249
|
+
end
|
250
|
+
|
251
|
+
# class SAP_MAX_ALIGN_T < FFI::Union
|
252
|
+
# layout :align1, :long,
|
253
|
+
# :align2, :double,
|
254
|
+
# :align3, :pointer,
|
255
|
+
# :align4,
|
256
|
+
# end
|
257
|
+
|
258
|
+
# class RFC_DECF34 < FFI::Union
|
259
|
+
# layout :bytes, [:uchar, 16],
|
260
|
+
# :align, 16
|
261
|
+
# end
|
298
262
|
|
299
263
|
#############################################################################################################
|
300
264
|
# ATTACH FUNCTIONS
|
265
|
+
# The functions here were obtained by parsing content from the doxygen files from the documentation
|
266
|
+
# accompanying the NW RFC SDK, and were tweaked here and there afterward. Most of them are actually not
|
267
|
+
# yet used in our NWRFC library, so calling them may not work. For best results, consult sapnwrfc.h from
|
268
|
+
# the SDK
|
301
269
|
#############################################################################################################
|
302
270
|
[
|
303
271
|
[:add_exception, :RfcAddException, [:pointer, :pointer, :pointer], :int],
|
@@ -399,7 +367,8 @@ module NWRFCLib
|
|
399
367
|
[:set_bytes, :RfcSetBytes, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
400
368
|
[:set_chars, :RfcSetChars, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
401
369
|
[:set_date, :RfcSetDate, [:pointer, :pointer, :pointer, :pointer], :int],
|
402
|
-
[:set_dec_f16, :RfcSetDecF16, [:pointer, :pointer, :pointer, :pointer], :int],
|
370
|
+
#[:set_dec_f16, :RfcSetDecF16, [:pointer, :pointer, :pointer, :pointer], :int],
|
371
|
+
[:set_dec_f16, :RfcSetDecF16, [:pointer, :pointer, RFC_DECF16.by_value, :pointer], :int],
|
403
372
|
[:set_dec_f34, :RfcSetDecF34, [:pointer, :pointer, :pointer, :pointer], :int],
|
404
373
|
[:set_float, :RfcSetFloat, [:pointer, :pointer, :double, :pointer], :int],
|
405
374
|
[:set_ini_path, :RfcSetIniPath, [:pointer, :pointer], :int],
|
@@ -423,12 +392,13 @@ module NWRFCLib
|
|
423
392
|
[:submit_transaction, :RfcSubmitTransaction, [:pointer, :pointer], :int],
|
424
393
|
[:utf8_to_sapuc, :RfcUTF8ToSAPUC, [:pointer, :uint, :pointer, :pointer, :pointer, :pointer], :int]
|
425
394
|
].each{|funcsig|
|
426
|
-
# puts funcsig.to_s
|
427
395
|
attach_function(funcsig[0], funcsig[1], funcsig[2], funcsig[3])
|
428
396
|
}
|
429
397
|
|
430
398
|
# Take Hash of connection parameters and returns FFI pointer to an array
|
431
399
|
# for passing to connection
|
400
|
+
# ---
|
401
|
+
# TODO - Ideally, this method should live in nwrfc.rb
|
432
402
|
def NWRFCLib.make_conn_params(params) #https://github.com/ffi/ffi/wiki/Structs
|
433
403
|
par = FFI::MemoryPointer.new(RFCConnParam, params.length)
|
434
404
|
pars = params.length.times.collect do |i|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nwrfc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Martin Ceronio
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-02-20 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description: SAP Netweaver RFC Library Wrapper using Ruby-FFI
|
@@ -29,9 +29,10 @@ extra_rdoc_files:
|
|
29
29
|
files:
|
30
30
|
- README.rdoc
|
31
31
|
- Rakefile
|
32
|
-
- lib/nwrfc.old.rb
|
33
32
|
- lib/nwrfc.rb
|
33
|
+
- lib/nwrfc/datacontainer.rb
|
34
34
|
- lib/nwrfc/nwrfclib.rb
|
35
|
+
- lib/nwrfc/dev_rfc.trc
|
35
36
|
- lib/dev_rfc.trc
|
36
37
|
homepage: http://rubygems.org/gems/nwrfc
|
37
38
|
licenses: []
|
data/lib/nwrfc.old.rb
DELETED
@@ -1,338 +0,0 @@
|
|
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
|