aspera-cli 4.4.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2095 -1503
- data/bin/ascli +2 -1
- data/bin/asession +4 -5
- data/docs/test_env.conf +3 -0
- data/examples/aoc.rb +4 -3
- data/examples/faspex4.rb +25 -25
- data/examples/proxy.pac +1 -1
- data/examples/transfer.rb +17 -17
- data/lib/aspera/aoc.rb +238 -185
- data/lib/aspera/ascmd.rb +93 -83
- data/lib/aspera/ats_api.rb +11 -10
- data/lib/aspera/cli/basic_auth_plugin.rb +13 -14
- data/lib/aspera/cli/extended_value.rb +42 -33
- data/lib/aspera/cli/formater.rb +142 -108
- data/lib/aspera/cli/info.rb +17 -0
- data/lib/aspera/cli/listener/line_dump.rb +3 -2
- data/lib/aspera/cli/listener/logger.rb +2 -1
- data/lib/aspera/cli/listener/progress.rb +16 -18
- data/lib/aspera/cli/listener/progress_multi.rb +18 -21
- data/lib/aspera/cli/main.rb +173 -149
- data/lib/aspera/cli/manager.rb +163 -168
- data/lib/aspera/cli/plugin.rb +43 -31
- data/lib/aspera/cli/plugins/alee.rb +6 -6
- data/lib/aspera/cli/plugins/aoc.rb +405 -370
- data/lib/aspera/cli/plugins/ats.rb +86 -79
- data/lib/aspera/cli/plugins/bss.rb +14 -16
- data/lib/aspera/cli/plugins/config.rb +580 -362
- data/lib/aspera/cli/plugins/console.rb +23 -19
- data/lib/aspera/cli/plugins/cos.rb +18 -18
- data/lib/aspera/cli/plugins/faspex.rb +201 -158
- data/lib/aspera/cli/plugins/faspex5.rb +80 -57
- data/lib/aspera/cli/plugins/node.rb +183 -166
- data/lib/aspera/cli/plugins/orchestrator.rb +71 -67
- data/lib/aspera/cli/plugins/preview.rb +92 -96
- data/lib/aspera/cli/plugins/server.rb +79 -75
- data/lib/aspera/cli/plugins/shares.rb +35 -19
- data/lib/aspera/cli/plugins/sync.rb +20 -22
- data/lib/aspera/cli/transfer_agent.rb +76 -113
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +35 -27
- data/lib/aspera/command_line_builder.rb +48 -34
- data/lib/aspera/cos_node.rb +29 -21
- data/lib/aspera/data_repository.rb +3 -2
- data/lib/aspera/environment.rb +50 -45
- data/lib/aspera/fasp/{manager.rb → agent_base.rb} +28 -25
- data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +52 -43
- data/lib/aspera/fasp/{local.rb → agent_direct.rb} +58 -72
- data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +37 -43
- data/lib/aspera/fasp/{node.rb → agent_node.rb} +35 -16
- data/lib/aspera/fasp/agent_trsdk.rb +104 -0
- data/lib/aspera/fasp/error.rb +2 -1
- data/lib/aspera/fasp/error_info.rb +68 -52
- data/lib/aspera/fasp/installation.rb +152 -124
- data/lib/aspera/fasp/listener.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +87 -92
- data/lib/aspera/fasp/parameters.yaml +305 -249
- data/lib/aspera/fasp/resume_policy.rb +11 -14
- data/lib/aspera/fasp/transfer_spec.rb +26 -0
- data/lib/aspera/fasp/uri.rb +22 -21
- data/lib/aspera/faspex_gw.rb +55 -89
- data/lib/aspera/hash_ext.rb +4 -3
- data/lib/aspera/id_generator.rb +8 -7
- data/lib/aspera/keychain/encrypted_hash.rb +121 -0
- data/lib/aspera/keychain/macos_security.rb +90 -0
- data/lib/aspera/log.rb +55 -37
- data/lib/aspera/nagios.rb +13 -12
- data/lib/aspera/node.rb +30 -25
- data/lib/aspera/oauth.rb +175 -226
- data/lib/aspera/open_application.rb +4 -3
- data/lib/aspera/persistency_action_once.rb +6 -6
- data/lib/aspera/persistency_folder.rb +5 -9
- data/lib/aspera/preview/file_types.rb +6 -5
- data/lib/aspera/preview/generator.rb +25 -24
- data/lib/aspera/preview/options.rb +16 -14
- data/lib/aspera/preview/utils.rb +98 -98
- data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
- data/lib/aspera/proxy_auto_config.rb +111 -20
- data/lib/aspera/rest.rb +154 -135
- data/lib/aspera/rest_call_error.rb +2 -2
- data/lib/aspera/rest_error_analyzer.rb +23 -25
- data/lib/aspera/rest_errors_aspera.rb +15 -14
- data/lib/aspera/ssh.rb +12 -10
- data/lib/aspera/sync.rb +42 -41
- data/lib/aspera/temp_file_manager.rb +18 -14
- data/lib/aspera/timer_limiter.rb +2 -1
- data/lib/aspera/uri_reader.rb +7 -5
- data/lib/aspera/web_auth.rb +79 -76
- metadata +116 -29
- data/docs/Makefile +0 -66
- data/docs/README.erb.md +0 -3973
- data/docs/README.md +0 -13
- data/docs/diagrams.txt +0 -49
- data/docs/doc_tools.rb +0 -58
- data/lib/aspera/api_detector.rb +0 -60
- data/lib/aspera/cli/plugins/shares2.rb +0 -114
- data/lib/aspera/secrets.rb +0 -20
data/lib/aspera/ascmd.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'aspera/log'
|
|
2
3
|
|
|
3
4
|
module Aspera
|
|
@@ -7,7 +8,7 @@ module Aspera
|
|
|
7
8
|
# Note: "ls" can take filters: as_ls -f *.txt -f *.bin /
|
|
8
9
|
class AsCmd
|
|
9
10
|
# list of supported actions
|
|
10
|
-
OPERATIONS=[:ls,:rm,:mv,:du,:info,:mkdir,:cp,:df,:md5sum]
|
|
11
|
+
OPERATIONS=[:ls,:rm,:mv,:du,:info,:mkdir,:cp,:df,:md5sum].freeze
|
|
11
12
|
|
|
12
13
|
# @param command_executor [Object] provides the "execute" method, taking a command to execute, and stdin to feed to it, typically: ssh or local
|
|
13
14
|
def initialize(command_executor)
|
|
@@ -25,7 +26,7 @@ module Aspera
|
|
|
25
26
|
bytebuffer=@command_executor.execute('ascmd',stdin_input).unpack('C*')
|
|
26
27
|
# get hash or table result
|
|
27
28
|
result=self.class.parse(bytebuffer,:result)
|
|
28
|
-
raise
|
|
29
|
+
raise 'ERROR: unparsed bytes remaining' unless bytebuffer.empty?
|
|
29
30
|
# get and delete info,always present in results
|
|
30
31
|
system_info=result[:info]
|
|
31
32
|
result.delete(:info)
|
|
@@ -36,116 +37,125 @@ module Aspera
|
|
|
36
37
|
result[:dir].each do |file|
|
|
37
38
|
if file.has_key?(:smode)
|
|
38
39
|
# Converts the first character of the file mode (see 'man ls') into a type.
|
|
39
|
-
file[:type]=case file[:smode][0,1];when'd'
|
|
40
|
+
file[:type]=case file[:smode][0,1];when 'd' then:directory;when '-' then:file;when 'l' then:link;else;:other;end
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
# for info, second overrides first, so restore it
|
|
44
|
-
case result.keys.length;when 0
|
|
45
|
+
case result.keys.length;when 0 then result=system_info;when 1 then result=result[result.keys.first];else raise 'error';end
|
|
45
46
|
# raise error as exception
|
|
46
|
-
raise Error.new(result[:errno],result[:errstr],action_sym,args) if result.is_a?(Hash)
|
|
47
|
+
raise Error.new(result[:errno],result[:errstr],action_sym,args) if result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{|i|i[:name]}.sort)
|
|
47
48
|
return result
|
|
48
49
|
end # execute_single
|
|
49
50
|
|
|
50
51
|
# This exception is raised when +ascmd+ returns an error.
|
|
51
52
|
class Error < StandardError
|
|
52
53
|
attr_reader :errno, :errstr, :command, :args
|
|
53
|
-
def initialize(errno,errstr,cmd,args);@errno=errno;@errstr=errstr;@command=cmd;@args=args;end
|
|
54
|
+
def initialize(errno,errstr,cmd,args);super();@errno=errno;@errstr=errstr;@command=cmd;@args=args;end
|
|
54
55
|
|
|
55
56
|
def message; "ascmd: (#{errno}) #{errstr}"; end
|
|
56
57
|
|
|
57
58
|
def extended_message; "ascmd: errno=#{errno} errstr=\"#{errstr}\" command=\"#{command}\" args=#{args}"; end
|
|
58
59
|
end # Error
|
|
59
60
|
|
|
60
|
-
private
|
|
61
|
-
|
|
62
61
|
# description of result structures (see ascmdtypes.h). Base types are big endian
|
|
63
62
|
# key = name of type
|
|
64
63
|
TYPES_DESCR={
|
|
65
|
-
:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
:
|
|
75
|
-
|
|
76
|
-
:
|
|
77
|
-
:
|
|
78
|
-
|
|
64
|
+
result: {decode: :field_list,
|
|
65
|
+
fields: [{name: :file,is_a: :stat},{name: :dir,is_a: :stat,special: :substruct},{name: :size,is_a: :size},{name: :error,is_a: :error},{name: :info,is_a: :info},
|
|
66
|
+
{name: :success,is_a: nil,special: :return_true},{name: :exit,is_a: nil},{name: :df,is_a: :mnt,special: :restart_on_first},{name: :md5sum,is_a: :md5sum}]},
|
|
67
|
+
stat: {decode: :field_list,
|
|
68
|
+
fields: [{name: :name,is_a: :zstr},{name: :size,is_a: :int64},{name: :mode,is_a: :int32,check: nil},{name: :zmode,is_a: :zstr},{name: :uid,is_a: :int32,check: nil},
|
|
69
|
+
{name: :zuid,is_a: :zstr},{name: :gid,is_a: :int32,check: nil},{name: :zgid,is_a: :zstr},{name: :ctime,is_a: :epoch},{name: :zctime,is_a: :zstr},{name: :mtime,is_a: :epoch},{name: :zmtime,is_a: :zstr},{name: :atime,is_a: :epoch},{name: :zatime,is_a: :zstr},{name: :symlink,is_a: :zstr},{name: :errno,is_a: :int32},{name: :errstr,is_a: :zstr}]},
|
|
70
|
+
info: {decode: :field_list,
|
|
71
|
+
fields: [{name: :platform,is_a: :zstr},{name: :version,is_a: :zstr},{name: :lang,is_a: :zstr},{name: :territory,is_a: :zstr},{name: :codeset,is_a: :zstr},
|
|
72
|
+
{name: :lc_ctype,is_a: :zstr},{name: :lc_numeric,is_a: :zstr},{name: :lc_time,is_a: :zstr},{name: :lc_all,is_a: :zstr},{name: :dev,is_a: :zstr,special: :multiple},{name: :browse_caps,is_a: :zstr},{name: :protocol,is_a: :zstr}]},
|
|
73
|
+
size: {decode: :field_list,
|
|
74
|
+
fields: [{name: :size,is_a: :int64},{name: :fcount,is_a: :int32},{name: :dcount,is_a: :int32},{name: :failed_fcount,is_a: :int32},{name: :failed_dcount,is_a: :int32}]},
|
|
75
|
+
error: {decode: :field_list,fields: [{name: :errno,is_a: :int32},{name: :errstr,is_a: :zstr}]},
|
|
76
|
+
mnt: {decode: :field_list,
|
|
77
|
+
fields: [{name: :fs,is_a: :zstr},{name: :dir,is_a: :zstr},{name: :is_a,is_a: :zstr},{name: :total,is_a: :int64},{name: :used,is_a: :int64},{name: :free,is_a: :int64},
|
|
78
|
+
{name: :fcount,is_a: :int64},{name: :errno,is_a: :int32},{name: :errstr,is_a: :zstr}]},
|
|
79
|
+
md5sum: {decode: :field_list,fields: [{name: :md5sum,is_a: :zstr}]},
|
|
80
|
+
int8: {decode: :base,unpack: 'C',size: 1},
|
|
81
|
+
int32: {decode: :base,unpack: 'L>',size: 4},
|
|
82
|
+
int64: {decode: :base,unpack: 'Q>',size: 8},
|
|
83
|
+
epoch: {decode: :base,unpack: 'Q>',size: 8},
|
|
84
|
+
zstr: {decode: :base,unpack: 'Z*'},
|
|
85
|
+
blist: {decode: :buffer_list}
|
|
86
|
+
}.freeze
|
|
79
87
|
|
|
80
88
|
# protocol enum start at one, but array index start at zero
|
|
81
89
|
ENUM_START=1
|
|
82
90
|
|
|
83
91
|
private_constant :TYPES_DESCR,:ENUM_START
|
|
84
92
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
class << self
|
|
94
|
+
# get description of structure's field, @param struct_name, @param typed_buffer provides field name
|
|
95
|
+
def field_description(struct_name,typed_buffer)
|
|
96
|
+
result=TYPES_DESCR[struct_name][:fields][typed_buffer[:btype]-ENUM_START]
|
|
97
|
+
raise "Unrecognized field for #{struct_name}: #{typed_buffer[:btype]}\n#{typed_buffer[:buffer]}" if result.nil?
|
|
98
|
+
return result
|
|
99
|
+
end
|
|
91
100
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
101
|
+
# decodes the provided buffer as provided type name
|
|
102
|
+
# @return a decoded type.
|
|
103
|
+
# :base : value, :buffer_list : an array of {btype,buffer}, :field_list : a hash, or array
|
|
104
|
+
def parse(buffer,type_name,indent_level=nil)
|
|
105
|
+
indent_level=(indent_level||-1)+1
|
|
106
|
+
type_descr=TYPES_DESCR[type_name]
|
|
107
|
+
raise "Unexpected type #{type_name}" if type_descr.nil?
|
|
108
|
+
Log.log.debug("#{' .'*indent_level}parse:#{type_name}:#{type_descr[:decode]}:#{buffer[0,16]}...".red)
|
|
109
|
+
result=nil
|
|
110
|
+
case type_descr[:decode]
|
|
111
|
+
when :base
|
|
112
|
+
num_bytes=type_name.eql?(:zstr) ? buffer.length : type_descr[:size]
|
|
113
|
+
raise 'ERROR:not enough bytes' if buffer.length < num_bytes
|
|
114
|
+
byte_array=buffer.shift(num_bytes);byte_array=[byte_array] unless byte_array.is_a?(Array)
|
|
115
|
+
result=byte_array.pack('C*').unpack1(type_descr[:unpack])
|
|
116
|
+
Log.log.debug("#{' .'*indent_level}-> base:#{byte_array} -> #{result}")
|
|
117
|
+
result=Time.at(result) if type_name.eql?(:epoch)
|
|
118
|
+
when :buffer_list
|
|
119
|
+
result = []
|
|
120
|
+
while !buffer.empty?
|
|
121
|
+
btype=parse(buffer,:int8,indent_level)
|
|
122
|
+
length=parse(buffer,:int32,indent_level)
|
|
123
|
+
raise 'ERROR:not enough bytes' if buffer.length < length
|
|
124
|
+
value=buffer.shift(length)
|
|
125
|
+
result.push({btype: btype,buffer: value})
|
|
126
|
+
Log.log.debug("#{' .'*indent_level}:buffer_list[#{result.length-1}] #{result.last}")
|
|
127
|
+
end
|
|
128
|
+
when :field_list
|
|
129
|
+
# by default the result is one struct
|
|
130
|
+
result = {}
|
|
131
|
+
# get individual binary fields
|
|
132
|
+
parse(buffer,:blist,indent_level).each do |typed_buffer|
|
|
133
|
+
# what type of field is it ?
|
|
134
|
+
field_info=field_description(type_name,typed_buffer)
|
|
135
|
+
Log.log.debug("#{' .'*indent_level}+ field(special=#{field_info[:special]})=#{field_info[:name]}".green)
|
|
136
|
+
case field_info[:special]
|
|
137
|
+
when nil
|
|
138
|
+
result[field_info[:name]]=parse(typed_buffer[:buffer],field_info[:is_a],indent_level)
|
|
139
|
+
when :return_true
|
|
140
|
+
result[field_info[:name]]=true
|
|
141
|
+
when :substruct
|
|
142
|
+
result[field_info[:name]]=parse(typed_buffer[:buffer],:blist,indent_level).map{|r|parse(r[:buffer],field_info[:is_a],indent_level)}
|
|
143
|
+
when :multiple
|
|
144
|
+
result[field_info[:name]]||=[]
|
|
145
|
+
result[field_info[:name]].push(parse(typed_buffer[:buffer],field_info[:is_a],indent_level))
|
|
146
|
+
when :restart_on_first
|
|
147
|
+
fl=result[field_info[:name]]=[]
|
|
148
|
+
parse(typed_buffer[:buffer],:blist,indent_level).map do |tb|
|
|
149
|
+
fl.push({}) if tb[:btype].eql?(ENUM_START)
|
|
150
|
+
fi=field_description(field_info[:is_a],tb)
|
|
151
|
+
fl.last[fi[:name]]=parse(tb[:buffer],fi[:is_a],indent_level)
|
|
152
|
+
end
|
|
143
153
|
end
|
|
144
154
|
end
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
155
|
+
else raise "error: unknown decode:#{type_descr[:decode]}"
|
|
156
|
+
end # is_a
|
|
157
|
+
return result
|
|
158
|
+
end
|
|
149
159
|
end
|
|
150
160
|
end
|
|
151
161
|
end
|
data/lib/aspera/ats_api.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'aspera/log'
|
|
2
3
|
require 'aspera/rest'
|
|
3
4
|
|
|
@@ -6,24 +7,24 @@ module Aspera
|
|
|
6
7
|
# currently supported clouds
|
|
7
8
|
# Note to Aspera: shall be an API call
|
|
8
9
|
CLOUD_NAME={
|
|
9
|
-
:
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
13
|
-
:
|
|
14
|
-
:
|
|
10
|
+
aws: 'Amazon Web Services',
|
|
11
|
+
azure: 'Microsoft Azure',
|
|
12
|
+
google: 'Google Cloud',
|
|
13
|
+
limelight: 'Limelight',
|
|
14
|
+
rackspace: 'Rackspace',
|
|
15
|
+
softlayer: 'IBM Cloud'
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
private_constant :CLOUD_NAME
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
def self.base_url;'https://ats.aspera.io';end
|
|
20
21
|
|
|
21
22
|
def initialize
|
|
22
|
-
super({:
|
|
23
|
+
super({base_url: AtsApi.base_url+'/pub/v1'})
|
|
23
24
|
# cache of server data
|
|
24
25
|
@all_servers_cache=nil
|
|
25
26
|
end
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
def cloud_names;CLOUD_NAME;end
|
|
28
29
|
|
|
29
30
|
# all available ATS servers
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'aspera/rest'
|
|
2
3
|
require 'aspera/cli/plugin'
|
|
3
4
|
|
|
@@ -7,32 +8,30 @@ module Aspera
|
|
|
7
8
|
class BasicAuthPlugin < Plugin
|
|
8
9
|
def initialize(env)
|
|
9
10
|
super(env)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
11
|
+
return if env[:skip_basic_auth_options]
|
|
12
|
+
options.add_opt_simple(:url,'URL of application, e.g. https://org.asperafiles.com')
|
|
13
|
+
options.add_opt_simple(:username,'username to log in')
|
|
14
|
+
options.add_opt_simple(:password,"user's password")
|
|
15
|
+
options.parse_options!
|
|
16
16
|
end
|
|
17
17
|
ACTIONS=[]
|
|
18
18
|
|
|
19
19
|
def execute_action
|
|
20
|
-
raise
|
|
20
|
+
raise 'do not execute action on this generic plugin'
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# returns a Rest object with basic auth
|
|
24
24
|
def basic_auth_api(subpath=nil)
|
|
25
|
-
api_url=
|
|
25
|
+
api_url=options.get_option(:url,:mandatory)
|
|
26
26
|
api_url=api_url+'/'+subpath unless subpath.nil?
|
|
27
27
|
return Rest.new({
|
|
28
|
-
:
|
|
29
|
-
:
|
|
30
|
-
|
|
31
|
-
:
|
|
32
|
-
:
|
|
28
|
+
base_url: api_url,
|
|
29
|
+
auth: {
|
|
30
|
+
type: :basic,
|
|
31
|
+
username: options.get_option(:username,:mandatory),
|
|
32
|
+
password: options.get_option(:password,:mandatory)
|
|
33
33
|
}})
|
|
34
34
|
end
|
|
35
|
-
|
|
36
35
|
end # BasicAuthPlugin
|
|
37
36
|
end # Cli
|
|
38
37
|
end # Aspera
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'aspera/cli/plugins/config'
|
|
3
|
+
require 'aspera/uri_reader'
|
|
2
4
|
require 'json'
|
|
3
5
|
require 'base64'
|
|
4
6
|
require 'zlib'
|
|
@@ -10,54 +12,61 @@ module Aspera
|
|
|
10
12
|
# command line extended values
|
|
11
13
|
class ExtendedValue
|
|
12
14
|
include Singleton
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
col_titles
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
|
|
16
|
+
class<<self
|
|
17
|
+
# decode comma separated table text
|
|
18
|
+
def self.decode_csvt(value)
|
|
19
|
+
col_titles=nil
|
|
20
|
+
hasharray=[]
|
|
21
|
+
CSV.parse(value).each do |values|
|
|
22
|
+
next if values.empty?
|
|
23
|
+
if col_titles.nil?
|
|
24
|
+
col_titles=values
|
|
25
|
+
else
|
|
26
|
+
entry={}
|
|
27
|
+
col_titles.each{|title|entry[title]=values.shift}
|
|
28
|
+
hasharray.push(entry)
|
|
29
|
+
end
|
|
26
30
|
end
|
|
31
|
+
return hasharray
|
|
27
32
|
end
|
|
28
|
-
value=hasharray
|
|
29
33
|
end
|
|
30
34
|
|
|
35
|
+
private
|
|
36
|
+
|
|
31
37
|
def initialize
|
|
32
38
|
@handlers={
|
|
33
|
-
:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
decoder: {
|
|
40
|
+
base64: lambda{|v|Base64.decode64(v)},
|
|
41
|
+
json: lambda{|v|JSON.parse(v)},
|
|
42
|
+
zlib: lambda{|v|Zlib::Inflate.inflate(v)},
|
|
43
|
+
ruby: lambda{|v|eval(v)},
|
|
44
|
+
csvt: lambda{|v|ExtendedValue.decode_csvt(v)},
|
|
45
|
+
lines: lambda{|v|v.split("\n")},
|
|
46
|
+
list: lambda{|v|v[1..-1].split(v[0])}
|
|
41
47
|
},
|
|
42
|
-
:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
reader: {
|
|
49
|
+
val: lambda{|v|v},
|
|
50
|
+
file: lambda{|v|File.read(File.expand_path(v))},
|
|
51
|
+
path: lambda{|v|File.expand_path(v)},
|
|
52
|
+
env: lambda{|v|ENV[v]},
|
|
53
|
+
uri: lambda{|v|UriReader.read(v)},
|
|
54
|
+
stdin: lambda{|v|raise 'no value allowed for stdin' unless v.empty?;$stdin.read}
|
|
48
55
|
}
|
|
49
56
|
# other handlers can be set using set_handler, e.g. preset is reader in config plugin
|
|
50
57
|
}
|
|
51
58
|
end
|
|
59
|
+
|
|
52
60
|
public
|
|
53
61
|
|
|
54
|
-
def modifiers;@handlers.keys.map{|i|@handlers[i].keys}.flatten;end
|
|
62
|
+
def modifiers;@handlers.keys.map{|i|@handlers[i].keys}.flatten.map(&:to_s);end
|
|
55
63
|
|
|
56
64
|
# add a new :reader or :decoder
|
|
57
65
|
# decoder can be chained, reader is last one on right
|
|
58
66
|
def set_handler(name,type,method)
|
|
59
|
-
raise "type must be one of #{@handlers.keys}" unless @handlers.keys.include?(type)
|
|
60
67
|
Log.log.debug("setting #{type} handler for #{name}")
|
|
68
|
+
raise 'name must be Symbol' unless name.is_a?(Symbol)
|
|
69
|
+
raise "type #{type} must be one of #{@handlers.keys}" unless @handlers.keys.include?(type)
|
|
61
70
|
@handlers[type][name]=method
|
|
62
71
|
end
|
|
63
72
|
|
|
@@ -67,13 +76,13 @@ module Aspera
|
|
|
67
76
|
return value if !value.is_a?(String)
|
|
68
77
|
# first determine decoders, in reversed order
|
|
69
78
|
decoders_reversed=[]
|
|
70
|
-
while (m=value.match(/^@([^:]+):(.*)/))
|
|
71
|
-
decoders_reversed.unshift(m[1])
|
|
79
|
+
while (m=value.match(/^@([^:]+):(.*)/)) && @handlers[:decoder].include?(m[1].to_sym)
|
|
80
|
+
decoders_reversed.unshift(m[1].to_sym)
|
|
72
81
|
value=m[2]
|
|
73
82
|
end
|
|
74
83
|
# then read value
|
|
75
84
|
@handlers[:reader].each do |reader,method|
|
|
76
|
-
if m=value.match(/^@#{reader}:(.*)/)
|
|
85
|
+
if (m=value.match(/^@#{reader}:(.*)/))
|
|
77
86
|
value=method.call(m[1])
|
|
78
87
|
break
|
|
79
88
|
end
|