nwrfc 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +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
|