ass_launcher 0.1.1.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.simplecov +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +122 -0
- data/Rakefile +10 -0
- data/ass_launcher.gemspec +32 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/ass_launcher/enterprise/binary_wrapper.rb +367 -0
- data/lib/ass_launcher/enterprise/cli/arguments_builder.rb +41 -0
- data/lib/ass_launcher/enterprise/cli/cli.spec +256 -0
- data/lib/ass_launcher/enterprise/cli/parameters.rb +289 -0
- data/lib/ass_launcher/enterprise/cli/spec_dsl/dsl_helpers.rb +66 -0
- data/lib/ass_launcher/enterprise/cli/spec_dsl.rb +209 -0
- data/lib/ass_launcher/enterprise/cli.rb +131 -0
- data/lib/ass_launcher/enterprise/ole/ole_binaries.rb +249 -0
- data/lib/ass_launcher/enterprise/ole/win32ole.rb +92 -0
- data/lib/ass_launcher/enterprise/ole.rb +188 -0
- data/lib/ass_launcher/enterprise/web_clients.rb +59 -0
- data/lib/ass_launcher/enterprise.rb +111 -0
- data/lib/ass_launcher/support/connection_string.rb +422 -0
- data/lib/ass_launcher/support/platforms.rb +232 -0
- data/lib/ass_launcher/support/shell/process_holder.rb +212 -0
- data/lib/ass_launcher/support/shell.rb +374 -0
- data/lib/ass_launcher/support/v8i_file.rb +66 -0
- data/lib/ass_launcher/support/v8i_section.rb +70 -0
- data/lib/ass_launcher/support.rb +9 -0
- data/lib/ass_launcher/version.rb +3 -0
- data/lib/ass_launcher.rb +6 -0
- metadata +202 -0
@@ -0,0 +1,422 @@
|
|
1
|
+
module AssLauncher
|
2
|
+
module Support
|
3
|
+
# Implement 1C connection string
|
4
|
+
# Mixin for connection string classes
|
5
|
+
# @note All connection string class have methods for get and set values
|
6
|
+
# of defined fields. Methods have name as fields but in downcase
|
7
|
+
# All fields defined for connection string class retutn {#fields}
|
8
|
+
# @example
|
9
|
+
# cs = AssLauncher::Support::\
|
10
|
+
# ConnectionString.new('File="\\fileserver\accounting.ib"')
|
11
|
+
# cs.is #-> :file
|
12
|
+
# cs.is? :file #-> true
|
13
|
+
# cs.usr = 'username'
|
14
|
+
# cs.pwd = 'password'
|
15
|
+
# cmd = "1civ8.exe enterprise #{cs.to_cmd}"
|
16
|
+
# run_result = AssLauncher::Support::Shell.run_ass(cmd)
|
17
|
+
module ConnectionString
|
18
|
+
class Error < StandardError; end
|
19
|
+
class ParseError < StandardError; end
|
20
|
+
# Commonn connection string fields
|
21
|
+
COMMON_FIELDS = %w(Usr Pwd LicDstr prmod Locale)
|
22
|
+
# Fields for server-infobase
|
23
|
+
SERVER_FIELDS = %w(Srvr Ref)
|
24
|
+
# Fields for file-infobase
|
25
|
+
FILE_FIELDS = %w(File)
|
26
|
+
# Fields for infobase published on http server
|
27
|
+
HTTP_FIELDS = %w(Ws)
|
28
|
+
HTTP_WEB_AUTH_FIELDS = %w(Wsn Wsp)
|
29
|
+
# Proxy fields for accsess to infobase published on http server via proxy
|
30
|
+
PROXY_FIELDS = %w(WspAuto WspSrv WspPort WspUser WspPwd)
|
31
|
+
# Fields for makes server-infobase
|
32
|
+
IB_MAKER_FIELDS = %w(DBMS DBSrvr DB
|
33
|
+
DBUID DBPwd SQLYOffs
|
34
|
+
CrSQLDB SchJobDn SUsr SPwd)
|
35
|
+
# Values for DBMS field
|
36
|
+
DBMS_VALUES = %w(MSSQLServer PostgreSQL IBMDB2 OracleDatabase)
|
37
|
+
|
38
|
+
# Analyzes connect string and build suitable class
|
39
|
+
# @param connstr (see parse)
|
40
|
+
# @return [Server | File | Http] instanse
|
41
|
+
def self.new(connstr)
|
42
|
+
case connstr
|
43
|
+
when /(\W|\A)File\s*=\s*"/i then File.new(parse(connstr))
|
44
|
+
when /(\W|\A)Srvr\s*=\s*"/i then Server.new(parse(connstr))
|
45
|
+
when /(\W|\A)Ws\s*=\s*"/i then Http.new(parse(connstr))
|
46
|
+
else
|
47
|
+
fail ParseError, "Uncknown connstr `#{connstr}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Parse connect string into hash.
|
52
|
+
# Connect string have format:
|
53
|
+
# 'Field1="Value";Field2="Value";'
|
54
|
+
# Quotes ' " ' in value of field escape as doble quote ' "" '.
|
55
|
+
# Fields name convert to downcase [Symbol]
|
56
|
+
# @example
|
57
|
+
# parse 'Field="""Value"""' -> {field: '"Value"'}
|
58
|
+
# @param connstr [String]
|
59
|
+
# @return [Hash]
|
60
|
+
def self.parse(connstr)
|
61
|
+
res = {}
|
62
|
+
connstr.split(';').each do |str|
|
63
|
+
str.strip!
|
64
|
+
res.merge!(parse_key_value str) unless str.empty?
|
65
|
+
end
|
66
|
+
res
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.parse_key_value(str)
|
70
|
+
fail ParseError, "Invalid string #{str}" unless\
|
71
|
+
/\A\s*(?<field>\w+)\s*=\s*"(?<value>.*)"\s*\z/i =~ str
|
72
|
+
{ field.downcase.to_sym => value.gsub('""', '"') }
|
73
|
+
end
|
74
|
+
private_class_method :parse_key_value
|
75
|
+
|
76
|
+
# Return type of connection string
|
77
|
+
# :file, :server, :http
|
78
|
+
# @return [Symbol]
|
79
|
+
def is
|
80
|
+
self.class.name.split('::').last.downcase.to_sym
|
81
|
+
end
|
82
|
+
|
83
|
+
# Check connection string for type :file, :server, :http
|
84
|
+
# @param symbol [Symvol]
|
85
|
+
# @example
|
86
|
+
# if cs.is? :file
|
87
|
+
# #do for connect to the file infobase
|
88
|
+
# else
|
89
|
+
# raise "#{cs.is} unsupport
|
90
|
+
# end
|
91
|
+
def is?(symbol)
|
92
|
+
is == symbol
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_hash
|
96
|
+
result = {}
|
97
|
+
fields.each do |f|
|
98
|
+
result[f.downcase.to_sym] = get_property(f)
|
99
|
+
end
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_s(only_fields = nil)
|
104
|
+
only_fields ||= fields
|
105
|
+
result = ''
|
106
|
+
only_fields.each do |f|
|
107
|
+
result << "#{prop_to_s(f)};" unless get_property(f).to_s.empty?
|
108
|
+
end
|
109
|
+
result
|
110
|
+
end
|
111
|
+
|
112
|
+
# Convert connection string to array of 1C:Enterprise parameters.
|
113
|
+
# @return [Array] of 1C:Enterprise CLI parameters.
|
114
|
+
def to_args
|
115
|
+
to_args_common + to_args_private
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_args_common
|
119
|
+
r = []
|
120
|
+
r += ['/N', usr] if usr
|
121
|
+
r += ['/P', pwd] if pwd
|
122
|
+
r += ['/UsePrivilegedMode', ''] if prmod.to_s == '1'
|
123
|
+
r += ['/L', locale] if locale
|
124
|
+
r
|
125
|
+
end
|
126
|
+
private :to_args_common
|
127
|
+
|
128
|
+
# Convert connection string to string of 1C:Enterprise parameters
|
129
|
+
# like /N"usr" /P"pwd" etc. See {#to_args}
|
130
|
+
# @return [String]
|
131
|
+
def to_cmd
|
132
|
+
r = ''
|
133
|
+
args = to_args
|
134
|
+
args.each_with_index do |v, i|
|
135
|
+
next unless i.even?
|
136
|
+
r << v
|
137
|
+
r << "\"#{args[i + 1]}\"" unless args[i + 1].to_s.empty?
|
138
|
+
r << ' '
|
139
|
+
end
|
140
|
+
r
|
141
|
+
end
|
142
|
+
|
143
|
+
# Fields required for new instance of connection string
|
144
|
+
def required_fields
|
145
|
+
self.class.required_fields
|
146
|
+
end
|
147
|
+
|
148
|
+
# All fields defined for connection string
|
149
|
+
def fields
|
150
|
+
self.class.fields
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.included(base)
|
154
|
+
base.fields.each do |f|
|
155
|
+
base.send(:attr_accessor, f.downcase.to_sym)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def _set_properties(hash)
|
162
|
+
hash.each do |key, value|
|
163
|
+
set_property(key, value)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def set_property(prop, value)
|
168
|
+
send("#{prop.downcase}=".to_sym, value)
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_property(prop)
|
172
|
+
send(prop.downcase.to_sym)
|
173
|
+
end
|
174
|
+
|
175
|
+
def prop_to_s(prop)
|
176
|
+
"#{fields_to_hash[prop.downcase.to_sym]}="\
|
177
|
+
"\"#{get_property(prop).to_s.gsub('"', '""')}\""
|
178
|
+
end
|
179
|
+
|
180
|
+
def fields_to_hash
|
181
|
+
res = {}
|
182
|
+
fields.each do |f|
|
183
|
+
res[f.downcase.to_sym] = f
|
184
|
+
end
|
185
|
+
res
|
186
|
+
end
|
187
|
+
|
188
|
+
def required_fields_received?(received_fields)
|
189
|
+
(required_fields.map { |f| f.downcase.to_sym }\
|
190
|
+
& received_fields.keys.map { |k| k.downcase.to_sym }) == \
|
191
|
+
required_fields.map { |f| f.downcase.to_sym }
|
192
|
+
end
|
193
|
+
|
194
|
+
# Connection string for server-infobases
|
195
|
+
# @note (see ConnectionString)
|
196
|
+
class Server
|
197
|
+
# Simple class host:port
|
198
|
+
class ServerDescr
|
199
|
+
attr_reader :host, :port
|
200
|
+
|
201
|
+
# @param host [String] hostname
|
202
|
+
# @param port [String] port number
|
203
|
+
def initialize(host, port = nil)
|
204
|
+
@host = host.strip
|
205
|
+
@port = port.to_s.strip
|
206
|
+
end
|
207
|
+
|
208
|
+
# Parse sting <srv_string>
|
209
|
+
# @param srv_str [String] string like 'host:port,host:port'
|
210
|
+
# @return [Arry<ServerDescr>]
|
211
|
+
def self.parse(srv_str)
|
212
|
+
r = []
|
213
|
+
srv_str.split(',').each do |srv|
|
214
|
+
srv.strip!
|
215
|
+
r << new(* srv.chomp.split(':')) unless srv.empty?
|
216
|
+
end
|
217
|
+
r
|
218
|
+
end
|
219
|
+
|
220
|
+
# @return [String] formated 'host:port'
|
221
|
+
def to_s
|
222
|
+
"#{host}" + (port.empty? ? '' : ":#{port}")
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.fields
|
227
|
+
required_fields | COMMON_FIELDS | IB_MAKER_FIELDS
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.required_fields
|
231
|
+
SERVER_FIELDS
|
232
|
+
end
|
233
|
+
|
234
|
+
include ConnectionString
|
235
|
+
|
236
|
+
def initialize(hash)
|
237
|
+
fail ConnectionString::Error unless required_fields_received?(hash)
|
238
|
+
_set_properties(hash)
|
239
|
+
end
|
240
|
+
|
241
|
+
# @return [Array<ServerDescr>]
|
242
|
+
def servers
|
243
|
+
@servers ||= []
|
244
|
+
end
|
245
|
+
|
246
|
+
def srvr=(str)
|
247
|
+
fail ArgumentError if str.empty?
|
248
|
+
@servers = ServerDescr.parse(str)
|
249
|
+
@srvr = str
|
250
|
+
end
|
251
|
+
|
252
|
+
def ref=(str)
|
253
|
+
fail ArgumentError if str.empty?
|
254
|
+
@ref = str
|
255
|
+
end
|
256
|
+
|
257
|
+
def srvr
|
258
|
+
servers.join(',')
|
259
|
+
end
|
260
|
+
|
261
|
+
def srvr_raw
|
262
|
+
@srvr
|
263
|
+
end
|
264
|
+
|
265
|
+
# Build string suitable for
|
266
|
+
# :createinfibase runmode
|
267
|
+
# @todo validte createinfibase params
|
268
|
+
def createinfobase_cmd
|
269
|
+
to_s
|
270
|
+
end
|
271
|
+
|
272
|
+
# Build string suitable for Ole objects connecting.
|
273
|
+
def to_ole_string
|
274
|
+
"#{to_s(fields - IB_MAKER_FIELDS)}"
|
275
|
+
end
|
276
|
+
|
277
|
+
# Build args array suitable for
|
278
|
+
# :createinfibase runmode
|
279
|
+
def createinfobase_args
|
280
|
+
[createinfobase_cmd]
|
281
|
+
end
|
282
|
+
|
283
|
+
# (see DBMS_VALUES)
|
284
|
+
def dbms=(value)
|
285
|
+
@dbms = valid_value(value, DBMS_VALUES)
|
286
|
+
end
|
287
|
+
|
288
|
+
def crsqldb=(value)
|
289
|
+
@crsqldb = yes_or_not(value)
|
290
|
+
end
|
291
|
+
|
292
|
+
def schjobdn=(value)
|
293
|
+
@schjobdn = yes_or_not(value)
|
294
|
+
end
|
295
|
+
|
296
|
+
def yes_or_not(value)
|
297
|
+
valid_value(value, %w(Y N))
|
298
|
+
end
|
299
|
+
private :yes_or_not
|
300
|
+
|
301
|
+
def valid_value(v, av)
|
302
|
+
fail ArgumentError, "Bad value #{v}. Accepted values are `#{av}'"\
|
303
|
+
unless av.map(&:downcase).include?(v.to_s.downcase)
|
304
|
+
v
|
305
|
+
end
|
306
|
+
private :valid_value
|
307
|
+
|
308
|
+
def to_args_private
|
309
|
+
['/S', "#{srvr}/#{ref}"]
|
310
|
+
end
|
311
|
+
private :to_args_private
|
312
|
+
end
|
313
|
+
|
314
|
+
# Connection string for file-infobases
|
315
|
+
# @note (see ConnectionString)
|
316
|
+
class File
|
317
|
+
def self.required_fields
|
318
|
+
FILE_FIELDS
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.fields
|
322
|
+
required_fields | COMMON_FIELDS
|
323
|
+
end
|
324
|
+
|
325
|
+
include ConnectionString
|
326
|
+
|
327
|
+
def initialize(hash)
|
328
|
+
fail ConnectionString::Error unless required_fields_received?(hash)
|
329
|
+
_set_properties(hash)
|
330
|
+
end
|
331
|
+
|
332
|
+
def file=(str)
|
333
|
+
fail ArgumentError if str.empty?
|
334
|
+
@file = str
|
335
|
+
end
|
336
|
+
|
337
|
+
# Build string suitable for
|
338
|
+
# :createinfibase runmode
|
339
|
+
def createinfobase_cmd
|
340
|
+
"File=\"#{path.realdirpath.win_string}\""
|
341
|
+
end
|
342
|
+
|
343
|
+
# Build string suitable for Ole objects connecting.
|
344
|
+
def to_ole_string
|
345
|
+
"#{createinfobase_cmd};#{to_s(fields - ['File'])}"
|
346
|
+
end
|
347
|
+
|
348
|
+
# Build args array suitable for
|
349
|
+
# :createinfibase runmode
|
350
|
+
# Fucking 1C:
|
351
|
+
# - File="path" not work but work running as script
|
352
|
+
# - File='path' work correct
|
353
|
+
def createinfobase_args
|
354
|
+
["File='#{path.realdirpath.win_string}'"]
|
355
|
+
end
|
356
|
+
|
357
|
+
def path
|
358
|
+
AssLauncher::Support::Platforms.path(file)
|
359
|
+
end
|
360
|
+
|
361
|
+
# Convert connection string to array of 1C:Enterprise parameters.
|
362
|
+
# @return [Array] of 1C:Enterprise CLI parameters.
|
363
|
+
def to_args_private
|
364
|
+
['/F', path.realpath.to_s]
|
365
|
+
end
|
366
|
+
private :to_args_private
|
367
|
+
end
|
368
|
+
|
369
|
+
# Connection string for infobases published on http server
|
370
|
+
# @note (see ConnectionString)
|
371
|
+
class Http
|
372
|
+
def self.required_fields
|
373
|
+
HTTP_FIELDS
|
374
|
+
end
|
375
|
+
|
376
|
+
def self.fields
|
377
|
+
required_fields | COMMON_FIELDS | HTTP_WEB_AUTH_FIELDS | PROXY_FIELDS
|
378
|
+
end
|
379
|
+
|
380
|
+
include ConnectionString
|
381
|
+
|
382
|
+
def initialize(hash)
|
383
|
+
fail ConnectionString::Error unless required_fields_received?(hash)
|
384
|
+
_set_properties(hash)
|
385
|
+
end
|
386
|
+
|
387
|
+
def ws=(str)
|
388
|
+
fail ArgumentError if str.empty?
|
389
|
+
@ws = str
|
390
|
+
end
|
391
|
+
|
392
|
+
def uri
|
393
|
+
require 'uri'
|
394
|
+
uri = URI(ws)
|
395
|
+
uri.user = wsn
|
396
|
+
uri.password = wsp
|
397
|
+
uri
|
398
|
+
end
|
399
|
+
|
400
|
+
# Convert connection string to array of 1C:Enterprise parameters.
|
401
|
+
# @return [Array] of 1C:Enterprise CLI parameters.
|
402
|
+
def to_args_private
|
403
|
+
r = []
|
404
|
+
r += ['/WS', ws] if ws
|
405
|
+
r += ['/WSN', wsn] if wsn
|
406
|
+
r += ['/WSP', wsp] if wsp
|
407
|
+
to_args_private_proxy(r)
|
408
|
+
end
|
409
|
+
private :to_args_private
|
410
|
+
|
411
|
+
def to_args_private_proxy(r)
|
412
|
+
return r unless !wspauto && wspsrv
|
413
|
+
r += ['/Proxy', '', '-Psrv', wspsrv]
|
414
|
+
r += ['-PPort', wspport.to_s] if wspport
|
415
|
+
r += ['-PUser', wspuser] if wspuser
|
416
|
+
r += ['-PPwd', wsppwd] if wsppwd
|
417
|
+
r
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
# Monkey patch of [FFI::Platform]
|
7
|
+
module Platform
|
8
|
+
IS_CYGWIN = is_os('cygwin')
|
9
|
+
|
10
|
+
def self.cygwin?
|
11
|
+
IS_CYGWIN
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.linux?
|
15
|
+
IS_LINUX
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module AssLauncher
|
21
|
+
module Support
|
22
|
+
# OS-specific things
|
23
|
+
# Mixin module help work with things as paths and env in other plases
|
24
|
+
# @example
|
25
|
+
# include AssLauncher::Support::Platforms
|
26
|
+
#
|
27
|
+
# if cigwin?
|
28
|
+
# #do if run in Cygwin
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # Find env value on regex
|
32
|
+
# pf = platform.env[/program\s*files/i]
|
33
|
+
# return if pf.size == 0
|
34
|
+
#
|
35
|
+
# # Use #path
|
36
|
+
# p = platform.path(pf[0])
|
37
|
+
# p.exists?
|
38
|
+
#
|
39
|
+
# # Use #path_class
|
40
|
+
# platform.path_class.glob('C:/*').each do |path|
|
41
|
+
# path.exists?
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # Use #glob directly
|
45
|
+
# platform.glob('C:/*').each do |path|
|
46
|
+
# path.exists?
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
#
|
50
|
+
module Platforms
|
51
|
+
# True if run in Cygwin
|
52
|
+
def cygwin?
|
53
|
+
FFI::Platform.cygwin?
|
54
|
+
end
|
55
|
+
module_function :cygwin?
|
56
|
+
|
57
|
+
# True if run in MinGW
|
58
|
+
def windows?
|
59
|
+
FFI::Platform.windows?
|
60
|
+
end
|
61
|
+
module_function :windows?
|
62
|
+
|
63
|
+
# True if run in Linux
|
64
|
+
def linux?
|
65
|
+
FFI::Platform.linux?
|
66
|
+
end
|
67
|
+
module_function :linux?
|
68
|
+
|
69
|
+
# Return module [Platforms] as helper
|
70
|
+
# @return [Platforms]
|
71
|
+
def platform
|
72
|
+
AssLauncher::Support::Platforms
|
73
|
+
end
|
74
|
+
private :platform
|
75
|
+
|
76
|
+
require 'pathname'
|
77
|
+
|
78
|
+
# Return suitable class
|
79
|
+
# @return [UnixPath | WinPath | CygPath]
|
80
|
+
def self.path_class
|
81
|
+
if cygwin?
|
82
|
+
PathnameExt::CygPath
|
83
|
+
elsif windows?
|
84
|
+
PathnameExt::WinPath
|
85
|
+
else
|
86
|
+
PathnameExt::UnixPath
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return suitable class instance
|
91
|
+
# @return [UnixPath | WinPath | CygPath]
|
92
|
+
def self.path(string)
|
93
|
+
path_class.new(string)
|
94
|
+
end
|
95
|
+
|
96
|
+
# (see PathnameExt.glob)
|
97
|
+
def self.glob(p1, *args)
|
98
|
+
path_class.glob(p1, *args)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Parent for OS-specific *Path classes
|
102
|
+
# @todo TRANSLATE THIS:
|
103
|
+
#
|
104
|
+
# rubocop:disable AsciiComments
|
105
|
+
# @note
|
106
|
+
# Класс предназначен для унификации работы с путями ФС в различных
|
107
|
+
# ОС.
|
108
|
+
# ОС зависимые методы будут переопределены в классах потомках
|
109
|
+
# [UnixPath | WinPath | CygPath].
|
110
|
+
#
|
111
|
+
# Пути могут приходить из следующих источников:
|
112
|
+
# - из консоли - при этом в Cygwin путь вида '/cygdrive/c' будет
|
113
|
+
# непонятен за пределами Cygwin
|
114
|
+
# - из ENV - при этом путь \\\\host\\share будет непонятен в Unix
|
115
|
+
#
|
116
|
+
# Общая мысль в следующем:
|
117
|
+
# - пути приводятся к mixed_path - /cygwin/c -> C:/, C:\\ -> C:/,
|
118
|
+
# \\\\host\\share -> //host/share
|
119
|
+
# - переопределяется метод glob класса [Pathname] при этом метод в
|
120
|
+
# Cygwin будет иметь свою реализацию т.к. в cygwin
|
121
|
+
# Dirname.glob('C:/') вернет пустой массив,
|
122
|
+
# а Dirname.glob('/cygdrive/c') отработает правильно.
|
123
|
+
# rubocop:enable AsciiComments
|
124
|
+
class PathnameExt < Pathname
|
125
|
+
# Override constructor for lead path to (#mixed_path)
|
126
|
+
# @param string [String] - string of path
|
127
|
+
def initialize(string)
|
128
|
+
@raw = string.to_s.strip
|
129
|
+
super mixed_path(@raw)
|
130
|
+
end
|
131
|
+
|
132
|
+
# This is fix (bug or featere)? of [Pathname] method. Called in
|
133
|
+
# chiled clesses returns not childe class instance but returns
|
134
|
+
# [Pathname] instance
|
135
|
+
def +(other)
|
136
|
+
self.class.new(super(other).to_s)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return mixed_path where delimiter is '/'
|
140
|
+
# @return [String]
|
141
|
+
def mixed_path(string)
|
142
|
+
string.tr('\\', '/')
|
143
|
+
end
|
144
|
+
private :mixed_path
|
145
|
+
|
146
|
+
# Return path suitable for windows apps. In Unix this method overridden
|
147
|
+
# @return [String]
|
148
|
+
def win_string
|
149
|
+
to_s.tr('/', '\\')
|
150
|
+
end
|
151
|
+
|
152
|
+
# Override (Pathname.glob) method for correct work with windows paths
|
153
|
+
# like a '\\\\host\\share', 'C:\\' and Cygwin paths like a '/cygdrive/c'
|
154
|
+
# @param (see Pathname.glob)
|
155
|
+
# @return [Array<PathnameExt>]
|
156
|
+
def self.glob(p1, *args)
|
157
|
+
super p1.tr('\\', '/'), *args
|
158
|
+
end
|
159
|
+
|
160
|
+
# Class for MinGW Ruby
|
161
|
+
class WinPath < PathnameExt; end
|
162
|
+
|
163
|
+
# Class for Unix Ruby
|
164
|
+
class UnixPath < PathnameExt
|
165
|
+
# (see PathnameExt#win_string)
|
166
|
+
def win_string
|
167
|
+
to_s
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Class for Cygwin Ruby
|
172
|
+
class CygPath < PathnameExt
|
173
|
+
# (cee PathnameExt#mixed_path)
|
174
|
+
def mixed_path(string)
|
175
|
+
cygpath(string, :m)
|
176
|
+
end
|
177
|
+
|
178
|
+
# (see PathnameExt.glob)
|
179
|
+
def self.glob(p1, *args)
|
180
|
+
super cygpath(p1, :u), *args
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.cygpath(p1, flag)
|
184
|
+
fail ArgumentError, 'Only accepts :w | :m | :u flags'\
|
185
|
+
unless %w(w m u).include? flag.to_s
|
186
|
+
# TODO, extract shell call into Shell module
|
187
|
+
out = `cygpath -#{flag} #{p1.escape} 2>&1`.chomp
|
188
|
+
fail Shell::RunError, out unless exitstatus == 0
|
189
|
+
out
|
190
|
+
end
|
191
|
+
|
192
|
+
# TODO, extract shell call into Shell module
|
193
|
+
def self.exitstatus
|
194
|
+
# rubocop:disable all
|
195
|
+
fail Shell::Error, 'Unexpected $?.nil?' if $?.nil?
|
196
|
+
$?.exitstatus
|
197
|
+
# rubocop:enable all
|
198
|
+
end
|
199
|
+
private_class_method :exitstatus
|
200
|
+
|
201
|
+
def cygpath(p1, flag)
|
202
|
+
self.class.cygpath(p1, flag)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Return suitable class
|
208
|
+
# @return [UnixEnv | WinEnv | CygEnv]
|
209
|
+
def self.env
|
210
|
+
if cygwin?
|
211
|
+
CygEnv
|
212
|
+
elsif windows?
|
213
|
+
WinEnv
|
214
|
+
else
|
215
|
+
UnixEnv
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Wrapper for ENV in Unix Ruby
|
220
|
+
class UnixEnv
|
221
|
+
# Return values ENV on regex
|
222
|
+
def self.[](regex)
|
223
|
+
ENV.map { |k, v| v if k =~ regex }.compact
|
224
|
+
end
|
225
|
+
end
|
226
|
+
# Wrapper for ENV in Cygwin Ruby
|
227
|
+
class CygEnv < UnixEnv; end
|
228
|
+
# Wrapper for ENV in MinGw Ruby
|
229
|
+
class WinEnv < UnixEnv; end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|