transcriptic 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/transcriptic +12 -0
- data/lib/transcriptic.rb +3 -0
- data/lib/transcriptic/api.rb +73 -0
- data/lib/transcriptic/api/errors.rb +24 -0
- data/lib/transcriptic/api/sequences.rb +33 -0
- data/lib/transcriptic/api/version.rb +5 -0
- data/lib/transcriptic/auth.rb +223 -0
- data/lib/transcriptic/cli.rb +12 -0
- data/lib/transcriptic/client.rb +332 -0
- data/lib/transcriptic/command.rb +233 -0
- data/lib/transcriptic/command/analyze.rb +20 -0
- data/lib/transcriptic/command/base.rb +195 -0
- data/lib/transcriptic/command/console.rb +10 -0
- data/lib/transcriptic/command/data.rb +8 -0
- data/lib/transcriptic/command/help.rb +124 -0
- data/lib/transcriptic/command/history.rb +8 -0
- data/lib/transcriptic/command/login.rb +35 -0
- data/lib/transcriptic/command/run.rb +60 -0
- data/lib/transcriptic/command/sequences.rb +45 -0
- data/lib/transcriptic/command/status.rb +17 -0
- data/lib/transcriptic/helpers.rb +311 -0
- data/lib/transcriptic/version.rb +3 -0
- data/lib/vendor/transcriptic/okjson.rb +557 -0
- metadata +161 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
module Transcriptic::Command
|
2
|
+
|
3
|
+
# list commands and display help
|
4
|
+
#
|
5
|
+
class Help < Base
|
6
|
+
PRIMARY_NAMESPACES = %w( analyze data history run status sequences)
|
7
|
+
|
8
|
+
# help [COMMAND]
|
9
|
+
#
|
10
|
+
# list available commands or display help for a specific command
|
11
|
+
#
|
12
|
+
def index
|
13
|
+
if command = args.shift
|
14
|
+
help_for_command(command)
|
15
|
+
else
|
16
|
+
help_for_root
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_command "-h", "help"
|
21
|
+
alias_command "--help", "help"
|
22
|
+
|
23
|
+
def self.usage_for_command(command)
|
24
|
+
command = new.send(:commands)[command]
|
25
|
+
"Usage: transcriptic #{command[:banner]}" if command
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def commands_for_namespace(name)
|
31
|
+
Transcriptic::Command.commands.values.select do |command|
|
32
|
+
command[:namespace] == name && command[:command] != name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def namespaces
|
37
|
+
namespaces = Transcriptic::Command.namespaces
|
38
|
+
namespaces.delete("app")
|
39
|
+
namespaces
|
40
|
+
end
|
41
|
+
|
42
|
+
def commands
|
43
|
+
Transcriptic::Command.commands
|
44
|
+
end
|
45
|
+
|
46
|
+
def legacy_help_for_namespace(namespace)
|
47
|
+
instance = Transcriptic::Command::Help.groups.map do |group|
|
48
|
+
[ group.title, group.select { |c| c.first =~ /^#{namespace}/ }.length ]
|
49
|
+
end.sort_by { |l| l.last }.last
|
50
|
+
return nil unless instance
|
51
|
+
return nil if instance.last.zero?
|
52
|
+
instance.first
|
53
|
+
end
|
54
|
+
|
55
|
+
def primary_namespaces
|
56
|
+
PRIMARY_NAMESPACES.map { |name| namespaces[name] }.compact
|
57
|
+
end
|
58
|
+
|
59
|
+
def additional_namespaces
|
60
|
+
(namespaces.values - primary_namespaces)
|
61
|
+
end
|
62
|
+
|
63
|
+
def summary_for_namespaces(namespaces)
|
64
|
+
size = longest(namespaces.map { |n| n[:name] })
|
65
|
+
namespaces.sort_by {|namespace| namespace[:name]}.each do |namespace|
|
66
|
+
name = namespace[:name]
|
67
|
+
namespace[:description]
|
68
|
+
puts " %-#{size}s # %s" % [ name, namespace[:description] ]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def help_for_root
|
73
|
+
puts "Usage: transcriptic COMMAND [command-specific-options]"
|
74
|
+
puts
|
75
|
+
puts "Primary help topics, type \"transcriptic help TOPIC\" for more details:"
|
76
|
+
puts
|
77
|
+
summary_for_namespaces(primary_namespaces)
|
78
|
+
puts
|
79
|
+
puts "Additional topics:"
|
80
|
+
puts
|
81
|
+
summary_for_namespaces(additional_namespaces)
|
82
|
+
puts
|
83
|
+
end
|
84
|
+
|
85
|
+
def help_for_namespace(name)
|
86
|
+
namespace_commands = commands_for_namespace(name)
|
87
|
+
|
88
|
+
unless namespace_commands.empty?
|
89
|
+
size = longest(namespace_commands.map { |c| c[:banner] })
|
90
|
+
namespace_commands.sort_by { |c| c[:banner].to_s }.each do |command|
|
91
|
+
next if command[:help] =~ /DEPRECATED/
|
92
|
+
command[:summary]
|
93
|
+
puts " %-#{size}s # %s" % [ command[:banner], command[:summary] ]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def help_for_command(name)
|
99
|
+
if command_alias = Transcriptic::Command.command_aliases[name]
|
100
|
+
display("Alias: #{name} redirects to #{command_alias}")
|
101
|
+
name = command_alias
|
102
|
+
end
|
103
|
+
if command = commands[name]
|
104
|
+
puts "Usage: transcriptic #{command[:banner]}"
|
105
|
+
|
106
|
+
if command[:help].strip.length > 0
|
107
|
+
puts command[:help].split("\n")[1..-1].join("\n")
|
108
|
+
end
|
109
|
+
puts
|
110
|
+
end
|
111
|
+
|
112
|
+
if commands_for_namespace(name).size > 0
|
113
|
+
puts "Additional commands, type \"transcriptic help COMMAND\" for more details:"
|
114
|
+
puts
|
115
|
+
help_for_namespace(name)
|
116
|
+
puts
|
117
|
+
elsif command.nil?
|
118
|
+
error "#{name} is not a transcriptic command. See `transcriptic help`."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# login, logout, and view your api token
|
2
|
+
#
|
3
|
+
class Transcriptic::Command::Auth < Transcriptic::Command::Base
|
4
|
+
|
5
|
+
# auth:login
|
6
|
+
#
|
7
|
+
# log in with your transcriptic account
|
8
|
+
#
|
9
|
+
def login
|
10
|
+
Transcriptic::Auth.login
|
11
|
+
display "Authentication successful."
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_command "login", "auth:login"
|
15
|
+
|
16
|
+
# auth:logout
|
17
|
+
#
|
18
|
+
# clear local authentication credentials
|
19
|
+
#
|
20
|
+
def logout
|
21
|
+
Transcriptic::Auth.logout
|
22
|
+
display "Local credentials cleared."
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_command "logout", "auth:logout"
|
26
|
+
|
27
|
+
# auth:token
|
28
|
+
#
|
29
|
+
# display your api token
|
30
|
+
#
|
31
|
+
def token
|
32
|
+
display Transcriptic::Auth.api_key
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'zipruby'
|
2
|
+
require 'net/http/post/multipart'
|
3
|
+
|
4
|
+
module Transcriptic::Command
|
5
|
+
|
6
|
+
# upload and run a protocol
|
7
|
+
#
|
8
|
+
class Run < Base
|
9
|
+
|
10
|
+
# transcriptic run [FILENAME|DIRECTORY]
|
11
|
+
#
|
12
|
+
# upload FILENAME or DIRECTORY and launch it
|
13
|
+
#
|
14
|
+
def index
|
15
|
+
path = args.shift
|
16
|
+
upload_protocol(path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def upload_protocol(path)
|
20
|
+
begin
|
21
|
+
stat = File.stat(path)
|
22
|
+
rescue
|
23
|
+
puts "No such path: #{path}"
|
24
|
+
return
|
25
|
+
end
|
26
|
+
if stat.directory?
|
27
|
+
files = Pathname.glob("#{path}/**/**")
|
28
|
+
file_count = files.reject(&:directory?).length
|
29
|
+
dir_count = files.reject(&:file?).length
|
30
|
+
puts "Package detected, compressing #{file_count} files (#{dir_count} directories)..."
|
31
|
+
upfile = Tempfile.new('protocol')
|
32
|
+
Zip::Archive.open(upfile.path, Zip::CREATE) do |ar|
|
33
|
+
ar.add_dir(path)
|
34
|
+
Dir.glob("#{path}/**/**").each do |path|
|
35
|
+
if File.directory?(path)
|
36
|
+
ar.add_dir(path)
|
37
|
+
else
|
38
|
+
ar.add_file(path, path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
else
|
43
|
+
upfile = path
|
44
|
+
end
|
45
|
+
puts "Uploading `#{path}` to Transcriptic..."
|
46
|
+
url = URI.parse('http://client.transcriptic.com/receive')
|
47
|
+
File.open(upfile) do |zf|
|
48
|
+
req = Net::HTTP::Post::Multipart.new url.path,
|
49
|
+
"file" => UploadIO.new(zf, "application/zip", "protocol.zip")
|
50
|
+
res = Net::HTTP.start(url.host, url.port) do |http|
|
51
|
+
http.request(req)
|
52
|
+
end
|
53
|
+
if res.code != 200
|
54
|
+
puts "HTTP Error: #{res.message} (#{res.code})"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Transcriptic::Command
|
2
|
+
|
3
|
+
# manage sequence data
|
4
|
+
#
|
5
|
+
class Sequences < Base
|
6
|
+
|
7
|
+
# sequences
|
8
|
+
#
|
9
|
+
# list all sequence entries
|
10
|
+
#
|
11
|
+
#Examples:
|
12
|
+
#
|
13
|
+
# $ transcriptic sequences
|
14
|
+
# ...
|
15
|
+
#
|
16
|
+
def index
|
17
|
+
validate_arguments!
|
18
|
+
end
|
19
|
+
|
20
|
+
# sequences:add FILENAME.EXT
|
21
|
+
#
|
22
|
+
# upload new sequence data file. EXT must be .gb or .fasta
|
23
|
+
#
|
24
|
+
#Examples:
|
25
|
+
#
|
26
|
+
# $ transcriptic sequences:add myvector.fasta
|
27
|
+
# ...
|
28
|
+
#
|
29
|
+
def add
|
30
|
+
end
|
31
|
+
|
32
|
+
# sequences:delete NAME
|
33
|
+
#
|
34
|
+
# delete sequence by name NAME
|
35
|
+
#
|
36
|
+
#Examples:
|
37
|
+
#
|
38
|
+
# $ transcriptic sequences:delete myvector20110204
|
39
|
+
# ...
|
40
|
+
#
|
41
|
+
def remove
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Transcriptic::Command
|
2
|
+
|
3
|
+
# view the status of your runs
|
4
|
+
#
|
5
|
+
class Status < Base
|
6
|
+
|
7
|
+
# transcriptic status [RUNID]
|
8
|
+
#
|
9
|
+
# list active runs, or if RUNID supplied, show details of that run
|
10
|
+
#
|
11
|
+
def index
|
12
|
+
run_id = args.shift
|
13
|
+
puts run_id
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,311 @@
|
|
1
|
+
require "vendor/transcriptic/okjson"
|
2
|
+
|
3
|
+
module Transcriptic
|
4
|
+
module Helpers
|
5
|
+
def home_directory
|
6
|
+
running_on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
|
7
|
+
end
|
8
|
+
|
9
|
+
def running_on_windows?
|
10
|
+
RUBY_PLATFORM =~ /mswin32|mingw32/
|
11
|
+
end
|
12
|
+
|
13
|
+
def running_on_a_mac?
|
14
|
+
RUBY_PLATFORM =~ /-darwin\d/
|
15
|
+
end
|
16
|
+
|
17
|
+
def display(msg="", newline=true)
|
18
|
+
if newline
|
19
|
+
puts(msg)
|
20
|
+
else
|
21
|
+
print(msg)
|
22
|
+
STDOUT.flush
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def redisplay(line, line_break = false)
|
27
|
+
display("\r\e[0K#{line}", line_break)
|
28
|
+
end
|
29
|
+
|
30
|
+
def deprecate(version)
|
31
|
+
display "!!! DEPRECATION WARNING: This command will be removed in version #{version}"
|
32
|
+
display
|
33
|
+
end
|
34
|
+
|
35
|
+
def error(msg)
|
36
|
+
STDERR.puts(msg)
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def confirm_billing
|
41
|
+
display
|
42
|
+
display "This action will cause your account to be billed at the end of the month"
|
43
|
+
display "For more information, see http://devcenter.heroku.com/articles/billing"
|
44
|
+
display "Are you sure you want to do this? (y/n) ", false
|
45
|
+
if ask.downcase == 'y'
|
46
|
+
heroku.confirm_billing
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def confirm(message = "Are you sure you wish to continue? (y/n)?")
|
52
|
+
display("#{message} ", false)
|
53
|
+
ask.downcase == 'y'
|
54
|
+
end
|
55
|
+
|
56
|
+
def confirm_command(app = app)
|
57
|
+
raise(Transcriptic::Command::CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") unless app
|
58
|
+
|
59
|
+
confirmed_app = extract_option('--confirm', false)
|
60
|
+
if confirmed_app
|
61
|
+
unless confirmed_app == app
|
62
|
+
raise(Transcriptic::Command::CommandFailed, "Confirmed app #{confirmed_app} did not match the selected app #{app}.")
|
63
|
+
end
|
64
|
+
return true
|
65
|
+
else
|
66
|
+
display
|
67
|
+
display " ! WARNING: Potentially Destructive Action"
|
68
|
+
display " ! This command will affect the app: #{app}"
|
69
|
+
display " ! To proceed, type \"#{app}\" or re-run this command with --confirm #{app}"
|
70
|
+
display
|
71
|
+
display "> ", false
|
72
|
+
if ask.downcase != app
|
73
|
+
display " ! Input did not match #{app}. Aborted."
|
74
|
+
false
|
75
|
+
else
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def format_date(date)
|
82
|
+
date = Time.parse(date) if date.is_a?(String)
|
83
|
+
date.strftime("%Y-%m-%d %H:%M %Z")
|
84
|
+
end
|
85
|
+
|
86
|
+
def ask
|
87
|
+
gets.strip
|
88
|
+
end
|
89
|
+
|
90
|
+
def shell(cmd)
|
91
|
+
FileUtils.cd(Dir.pwd) {|d| return `#{cmd}`}
|
92
|
+
end
|
93
|
+
|
94
|
+
def run_command(command, args=[])
|
95
|
+
Transcriptic::Command.run(command, args)
|
96
|
+
end
|
97
|
+
|
98
|
+
def retry_on_exception(*exceptions)
|
99
|
+
retry_count = 0
|
100
|
+
begin
|
101
|
+
yield
|
102
|
+
rescue *exceptions => ex
|
103
|
+
raise ex if retry_count >= 3
|
104
|
+
sleep 3
|
105
|
+
retry_count += 1
|
106
|
+
retry
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def has_git?
|
111
|
+
%x{ git --version }
|
112
|
+
$?.success?
|
113
|
+
end
|
114
|
+
|
115
|
+
def git(args)
|
116
|
+
return "" unless has_git?
|
117
|
+
flattened_args = [args].flatten.compact.join(" ")
|
118
|
+
%x{ git #{flattened_args} 2>&1 }.strip
|
119
|
+
end
|
120
|
+
|
121
|
+
def time_ago(elapsed)
|
122
|
+
if elapsed < 60
|
123
|
+
"#{elapsed.floor}s ago"
|
124
|
+
elsif elapsed < (60 * 60)
|
125
|
+
"#{(elapsed / 60).floor}m ago"
|
126
|
+
else
|
127
|
+
"#{(elapsed / 60 / 60).floor}h ago"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def truncate(text, length)
|
132
|
+
if text.size > length
|
133
|
+
text[0, length - 2] + '..'
|
134
|
+
else
|
135
|
+
text
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
@@kb = 1024
|
140
|
+
@@mb = 1024 * @@kb
|
141
|
+
@@gb = 1024 * @@mb
|
142
|
+
def format_bytes(amount)
|
143
|
+
amount = amount.to_i
|
144
|
+
return '(empty)' if amount == 0
|
145
|
+
return amount if amount < @@kb
|
146
|
+
return "#{(amount / @@kb).round}k" if amount < @@mb
|
147
|
+
return "#{(amount / @@mb).round}M" if amount < @@gb
|
148
|
+
return "#{(amount / @@gb).round}G"
|
149
|
+
end
|
150
|
+
|
151
|
+
def quantify(string, num)
|
152
|
+
"%d %s" % [ num, num.to_i == 1 ? string : "#{string}s" ]
|
153
|
+
end
|
154
|
+
|
155
|
+
def create_git_remote(app, remote)
|
156
|
+
return unless has_git?
|
157
|
+
return if git('remote').split("\n").include?(remote)
|
158
|
+
return unless File.exists?(".git")
|
159
|
+
git "remote add #{remote} git@#{heroku.host}:#{app}.git"
|
160
|
+
display "Git remote #{remote} added"
|
161
|
+
end
|
162
|
+
|
163
|
+
def app_urls(name)
|
164
|
+
"http://#{name}.heroku.com/ | git@heroku.com:#{name}.git"
|
165
|
+
end
|
166
|
+
|
167
|
+
def longest(items)
|
168
|
+
items.map { |i| i.to_s.length }.sort.last
|
169
|
+
end
|
170
|
+
|
171
|
+
def display_table(objects, columns, headers)
|
172
|
+
lengths = []
|
173
|
+
columns.each_with_index do |column, index|
|
174
|
+
header = headers[index]
|
175
|
+
lengths << longest([header].concat(objects.map { |o| o[column].to_s }))
|
176
|
+
end
|
177
|
+
display_row headers, lengths
|
178
|
+
display_row headers.map { |header| "-" * header.length }, lengths
|
179
|
+
objects.each do |row|
|
180
|
+
display_row columns.map { |column| row[column] }, lengths
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def display_row(row, lengths)
|
185
|
+
row.zip(lengths).each do |column, length|
|
186
|
+
format = column.is_a?(Fixnum) ? "%#{length}s " : "%-#{length}s "
|
187
|
+
display format % column, false
|
188
|
+
end
|
189
|
+
display
|
190
|
+
end
|
191
|
+
|
192
|
+
def json_encode(object)
|
193
|
+
Transcriptic::OkJson.encode(object)
|
194
|
+
rescue Transcriptic::OkJson::ParserError
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
|
198
|
+
def json_decode(json)
|
199
|
+
Transcriptic::OkJson.decode(json)
|
200
|
+
rescue Transcriptic::OkJson::ParserError
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
|
204
|
+
def set_buffer(enable)
|
205
|
+
return unless $stdin.tty?
|
206
|
+
|
207
|
+
if enable
|
208
|
+
`stty icanon echo`
|
209
|
+
else
|
210
|
+
`stty -icanon -echo`
|
211
|
+
end
|
212
|
+
rescue
|
213
|
+
# fails on windows
|
214
|
+
end
|
215
|
+
|
216
|
+
def get_terminal_environment
|
217
|
+
{ "TERM" => ENV["TERM"], "COLUMNS" => `tput cols`, "LINES" => `tput lines` }
|
218
|
+
rescue
|
219
|
+
{ "TERM" => ENV["TERM"] }
|
220
|
+
end
|
221
|
+
|
222
|
+
def fail(message)
|
223
|
+
raise Transcriptic::Command::CommandFailed, message
|
224
|
+
end
|
225
|
+
|
226
|
+
## DISPLAY HELPERS
|
227
|
+
|
228
|
+
def arrow(message)
|
229
|
+
"-----> #{message}"
|
230
|
+
end
|
231
|
+
|
232
|
+
def action(message)
|
233
|
+
display "#{arrow(message)}... ", false
|
234
|
+
Transcriptic::Helpers.enable_error_capture
|
235
|
+
yield
|
236
|
+
Transcriptic::Helpers.disable_error_capture
|
237
|
+
display "done", false
|
238
|
+
display(", #{@status}", false) if @status
|
239
|
+
display
|
240
|
+
end
|
241
|
+
|
242
|
+
def status(message)
|
243
|
+
@status = message
|
244
|
+
end
|
245
|
+
|
246
|
+
def output(message="")
|
247
|
+
return if message.to_s.strip == ""
|
248
|
+
display " " + message.split("\n").join("\n ")
|
249
|
+
end
|
250
|
+
|
251
|
+
def output_with_arrow(message="")
|
252
|
+
return if message.to_s.strip == ""
|
253
|
+
display "-----> " + message.split("\n").join("\n ")
|
254
|
+
end
|
255
|
+
|
256
|
+
def error_with_failure(message)
|
257
|
+
display "failed"
|
258
|
+
message.gsub!(/^ +! */, '')
|
259
|
+
display message.split("\n").map { |line| " ! #{line}" }.join("\n")
|
260
|
+
exit 1
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.included_into
|
264
|
+
@@included_into ||= []
|
265
|
+
end
|
266
|
+
|
267
|
+
def self.extended_into
|
268
|
+
@@extended_into ||= []
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.included(base)
|
272
|
+
included_into << base
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.extended(base)
|
276
|
+
extended_into << base
|
277
|
+
end
|
278
|
+
|
279
|
+
def self.enable_error_capture
|
280
|
+
included_into.each do |base|
|
281
|
+
base.send(:alias_method, :error_without_failure, :error)
|
282
|
+
base.send(:alias_method, :error, :error_with_failure)
|
283
|
+
end
|
284
|
+
extended_into.each do |base|
|
285
|
+
class << base
|
286
|
+
alias_method :error_without_failure, :error
|
287
|
+
alias_method :error, :error_with_failure
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def self.disable_error_capture
|
293
|
+
included_into.each do |base|
|
294
|
+
base.send(:alias_method, :error, :error_without_failure)
|
295
|
+
end
|
296
|
+
extended_into.each do |base|
|
297
|
+
class << base
|
298
|
+
alias_method :error, :error_without_failure
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
unless String.method_defined?(:shellescape)
|
306
|
+
class String
|
307
|
+
def shellescape
|
308
|
+
empty? ? "''" : gsub(/([^A-Za-z0-9_\-.,:\/@\n])/n, '\\\\\\1').gsub(/\n/, "'\n'")
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|