scriptty 1.1.0-java → 1.2.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/scriptty/expect.rb +12 -14
- data/lib/scriptty/request_dispatcher.rb +72 -21
- data/lib/scriptty/screen_pattern/parser.rb +7 -5
- data/lib/scriptty/screen_pattern.rb +2 -2
- data/test/request_dispatcher_test.rb +76 -0
- metadata +5 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/lib/scriptty/expect.rb
CHANGED
@@ -29,13 +29,17 @@ module ScripTTY
|
|
29
29
|
class Expect
|
30
30
|
|
31
31
|
# Methods to export to Evaluator
|
32
|
-
EXPORTED_METHODS = Set.new [:basedir, :init_term, :term, :connect, :screen, :expect, :on, :wait, :send, :send_password, :capture, :match, :push_patterns, :pop_patterns, :exit, :eval_script_file, :eval_script_inline, :sleep, :set_basedir, :set_timeout, :load_screens, :print, :puts, :p, :pp ]
|
32
|
+
EXPORTED_METHODS = Set.new [:basedir, :init_term, :term, :connect, :screen, :expect, :on, :wait, :send, :send_password, :capture, :match, :push_patterns, :pop_patterns, :exit, :eval_script_file, :eval_script_inline, :sleep, :set_basedir, :set_timeout, :load_screens, :matched_screen, :matched_pattern, :print, :puts, :p, :pp ]
|
33
33
|
|
34
34
|
attr_reader :term # The terminal emulation object
|
35
35
|
|
36
36
|
attr_reader :capture # The last non-background captured fields. For a ScreenPattern match, this is a Hash of fields. For a String or Regexp match, this is a MatchData object.
|
37
37
|
alias match capture # "match" is the deprecated name for "capture"
|
38
38
|
|
39
|
+
attr_accessor :matched_pattern # The last non-background pattern that matched (might be a RegExp or a ScreenPattern)
|
40
|
+
|
41
|
+
attr_accessor :matched_screen # The last non-background ScreenPattern that matched.
|
42
|
+
|
39
43
|
attr_accessor :transcript_writer # Set this to an instance of ScripTTY::Util::Transcript::Writer
|
40
44
|
attr_accessor :transcript_writer_autoclose # Set this to false to disable closing transcript_writer on exit
|
41
45
|
|
@@ -58,6 +62,8 @@ module ScripTTY
|
|
58
62
|
@transcript_writer_autoclose = options[:transcript_writer_autoclose].nil? ? true : options[:transcript_writer_autoclose]
|
59
63
|
@screen_patterns = {}
|
60
64
|
@basedir = "."
|
65
|
+
@matched_pattern = nil
|
66
|
+
@matched_screen = nil
|
61
67
|
end
|
62
68
|
|
63
69
|
# Get instance variable from the Evaluator
|
@@ -87,20 +93,10 @@ module ScripTTY
|
|
87
93
|
nil
|
88
94
|
end
|
89
95
|
|
90
|
-
# Return the expanded path relative to basedir
|
91
|
-
def expand_path(path)
|
92
|
-
base = Pathname.new(@basedir)
|
93
|
-
p = Pathname.new(path)
|
94
|
-
unless p.absolute?
|
95
|
-
p = base.join(p)
|
96
|
-
end
|
97
|
-
p.expand_path.to_s
|
98
|
-
end
|
99
|
-
|
100
96
|
# Load and evaluate a script from a file.
|
101
97
|
def eval_script_file(path)
|
102
98
|
fail_unexpected_block if block_given?
|
103
|
-
path = expand_path(path)
|
99
|
+
path = File.expand_path(path, @basedir)
|
104
100
|
eval_script_inline(File.read(path), path)
|
105
101
|
end
|
106
102
|
|
@@ -200,7 +196,7 @@ module ScripTTY
|
|
200
196
|
fail_unexpected_block if block_given?
|
201
197
|
filenames_or_glob = filenames_or_glob.to_s if filenames_or_glob.is_a?(Pathname)
|
202
198
|
if filenames_or_glob.is_a?(String)
|
203
|
-
filenames_or_glob = expand_path(filenames_or_glob)
|
199
|
+
filenames_or_glob = File.expand_path(filenames_or_glob, @basedir)
|
204
200
|
filenames = Dir.glob(filenames_or_glob)
|
205
201
|
elsif filenames_or_glob.is_a?(Array)
|
206
202
|
filenames = filenames_or_glob
|
@@ -208,7 +204,7 @@ module ScripTTY
|
|
208
204
|
raise ArgumentError.new("load_screens takes a string(glob) or an array, not #{filenames.class.name}")
|
209
205
|
end
|
210
206
|
filenames.each do |filename|
|
211
|
-
ScreenPattern.parse(File.read(filename)).each do |pattern|
|
207
|
+
ScreenPattern.parse(File.read(filename), filename).each do |pattern|
|
212
208
|
@screen_patterns[pattern.name.to_sym] = pattern
|
213
209
|
end
|
214
210
|
end
|
@@ -441,6 +437,8 @@ module ScripTTY
|
|
441
437
|
|
442
438
|
# Make the next wait() call return
|
443
439
|
unless ph.background?
|
440
|
+
@matched_pattern = ph.pattern
|
441
|
+
@matched_screen = ph.pattern if ph.pattern.is_a?(ScreenPattern)
|
444
442
|
@capture = m
|
445
443
|
@wait_finished = true
|
446
444
|
@net.suspend
|
@@ -33,6 +33,9 @@ module ScripTTY
|
|
33
33
|
# RequestDispatcher can be used, for example, to provide an HTTP interface to
|
34
34
|
# functions of a screen-scraped terminal.
|
35
35
|
class RequestDispatcher
|
36
|
+
|
37
|
+
class RequestCancelled < StandardError; end
|
38
|
+
|
36
39
|
def initialize
|
37
40
|
# Graceful shutdown flag
|
38
41
|
@finishing_lock = Mutex.new
|
@@ -117,18 +120,27 @@ module ScripTTY
|
|
117
120
|
def main
|
118
121
|
loop do
|
119
122
|
break if finishing?
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
wrap_in_exception_handler(
|
124
|
+
:body => Proc.new {
|
125
|
+
handle_one_request
|
126
|
+
},
|
127
|
+
:rescue => Proc.new { |exc|
|
128
|
+
# Log & swallow exception
|
129
|
+
show_exception(exc)
|
130
|
+
close_expect rescue nil # Ignore errors while closing the connection
|
131
|
+
sleep 0.5 # Delay just a tiny bit to keep an exception loop from consuming all available resources.
|
132
|
+
}
|
133
|
+
)
|
128
134
|
end
|
129
135
|
execute_hooks(:before_finish)
|
130
136
|
ensure
|
131
137
|
close_expect
|
138
|
+
|
139
|
+
# Clean up any remaining requests.
|
140
|
+
while (request = dequeue)
|
141
|
+
request[:exception] = RequestCancelled.new("main loop exited; request not executed")
|
142
|
+
signal_request_done(request)
|
143
|
+
end
|
132
144
|
end
|
133
145
|
|
134
146
|
def handle_one_request
|
@@ -140,22 +152,36 @@ module ScripTTY
|
|
140
152
|
|
141
153
|
# Run the before_each_request hooks. If an exception is raised,
|
142
154
|
# put the request back on the queue before re-raising the error.
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
155
|
+
wrap_in_exception_handler(
|
156
|
+
:body => Proc.new {
|
157
|
+
execute_hooks(:before_each_request)
|
158
|
+
},
|
159
|
+
:rescue => Proc.new { |exc|
|
160
|
+
requeue(request)
|
161
|
+
},
|
162
|
+
:reraise => true
|
163
|
+
)
|
149
164
|
|
150
165
|
# Execute the request
|
166
|
+
success = false
|
167
|
+
exception_caught = false
|
151
168
|
begin
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
169
|
+
wrap_in_exception_handler(
|
170
|
+
:body => Proc.new {
|
171
|
+
request[:result] = block_eval(request[:block_how], &request[:block])
|
172
|
+
success = true
|
173
|
+
},
|
174
|
+
:rescue => Proc.new { |exc|
|
175
|
+
show_exception(exc, "in request")
|
176
|
+
request[:exception] = exc
|
177
|
+
close_expect rescue nil
|
178
|
+
exception_caught = true
|
179
|
+
}
|
180
|
+
)
|
181
|
+
ensure
|
182
|
+
signal_request_done(request)
|
183
|
+
raise "RUBY BUG: No success and no exception caught: #{$ERROR_INFO.inspect}" unless success or exception_caught
|
157
184
|
end
|
158
|
-
request[:cv_mutex].synchronize { request[:cv].signal }
|
159
185
|
|
160
186
|
# Execute the after_each_request hooks.
|
161
187
|
execute_hooks(:after_each_request)
|
@@ -235,7 +261,32 @@ module ScripTTY
|
|
235
261
|
output = ["Exception #{context || "in #{self}"}: #{exc} (#{exc.class.name})"]
|
236
262
|
output += exc.backtrace.map { |line| " #{line}" }
|
237
263
|
$stderr.puts output.join("\n")
|
238
|
-
|
264
|
+
end
|
265
|
+
|
266
|
+
def signal_request_done(request)
|
267
|
+
request[:cv_mutex].synchronize { request[:cv].signal }
|
268
|
+
end
|
269
|
+
|
270
|
+
# XXX - Workaround for JRuby bug.
|
271
|
+
#
|
272
|
+
# Apparently, if we do this:
|
273
|
+
#
|
274
|
+
# begin
|
275
|
+
# # some code here
|
276
|
+
# rescue Exception => exc
|
277
|
+
# # handle
|
278
|
+
# end
|
279
|
+
#
|
280
|
+
# Then other errors like SyntaxError and ArgumentError don't get caught
|
281
|
+
# when we do our block-passing-over-threads magic. However, if we
|
282
|
+
# enumerate a bunch of exception classes, it seems to work.
|
283
|
+
def wrap_in_exception_handler(options={})
|
284
|
+
begin
|
285
|
+
options[:body].call
|
286
|
+
rescue Exception, ScriptError, SystemStackError, StandardError => exc
|
287
|
+
options[:rescue].call(exc) if options[:rescue]
|
288
|
+
raise if options[:reraise]
|
289
|
+
end
|
239
290
|
end
|
240
291
|
end
|
241
292
|
end
|
@@ -59,19 +59,20 @@ module ScripTTY
|
|
59
59
|
RECOGNIZED_PROPERTIES = SINGLE_CHAR_PROPERTIES + TWO_TUPLE_PROPERTIES + %w( rectangle fields text )
|
60
60
|
|
61
61
|
class <<self
|
62
|
-
def parse(s, &block)
|
63
|
-
new(s, &block).parse
|
62
|
+
def parse(s, filename=nil, lineno=nil, &block)
|
63
|
+
new(s, filename, lineno, &block).parse
|
64
64
|
nil
|
65
65
|
end
|
66
66
|
protected :new # Users should not instantiate this object directly
|
67
67
|
end
|
68
68
|
|
69
|
-
def initialize(s, &block)
|
69
|
+
def initialize(s, filename=nil, lineno=nil, &block)
|
70
70
|
raise ArgumentError.new("no block given") unless block
|
71
71
|
@block = block
|
72
72
|
@lines = preprocess(s).split("\n").map{|line| "#{line}\n"}
|
73
73
|
@line = nil
|
74
|
-
@
|
74
|
+
@filename = filename
|
75
|
+
@lineno = (lineno||1) - 1
|
75
76
|
@state = :start
|
76
77
|
end
|
77
78
|
|
@@ -529,7 +530,8 @@ module ScripTTY
|
|
529
530
|
|
530
531
|
def parse_fail(message=nil, line=nil)
|
531
532
|
line ||= @lineno
|
532
|
-
|
533
|
+
f = @filename ? "#{@filename}:" : "line "
|
534
|
+
raise ArgumentError.new("error:#{f}#{line}: #{message || 'parse error'}")
|
533
535
|
end
|
534
536
|
|
535
537
|
# Pre-process an input string.
|
@@ -20,9 +20,9 @@ module ScripTTY
|
|
20
20
|
class ScreenPattern
|
21
21
|
class <<self
|
22
22
|
# Parse a pattern file and return an array of ScreenPattern objects
|
23
|
-
def parse(s)
|
23
|
+
def parse(s, filename=nil, lineno=nil)
|
24
24
|
retval = []
|
25
|
-
Parser.parse(s) do |spec|
|
25
|
+
Parser.parse(s, filename, lineno) do |spec|
|
26
26
|
retval << new(spec[:name], spec[:properties])
|
27
27
|
end
|
28
28
|
retval
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# = Tests for ScripTTY::RequestDispatcher
|
2
|
+
# Copyright (C) 2010 Infonium Inc.
|
3
|
+
#
|
4
|
+
# This file is part of ScripTTY.
|
5
|
+
#
|
6
|
+
# ScripTTY is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ScripTTY is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
require File.dirname(__FILE__) + "/test_helper.rb"
|
19
|
+
require 'scriptty/request_dispatcher'
|
20
|
+
|
21
|
+
class RequestDispatcherTest < Test::Unit::TestCase
|
22
|
+
|
23
|
+
if !defined?(Java)
|
24
|
+
# This test gets executed when JRuby is not detected
|
25
|
+
def test_dummy_no_jruby
|
26
|
+
raise LoadError.new("Cannot test ScripTTY::RequestDispatcher: Not running under JRuby")
|
27
|
+
end
|
28
|
+
|
29
|
+
else # defined?(Java)
|
30
|
+
|
31
|
+
def setup
|
32
|
+
@r = ScripTTY::RequestDispatcher.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown
|
36
|
+
@r.finish
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_after_init
|
40
|
+
after_init_ran = false
|
41
|
+
@r.after_init { after_init_ran = true }
|
42
|
+
@r.start
|
43
|
+
sleep 0.2 # XXX - race condition
|
44
|
+
assert after_init_ran, "after_init hook should run"
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_request
|
48
|
+
@r.start
|
49
|
+
assert_equal :success, @r.request { init_term "dg410"; :success }
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_standard_exception_in_request
|
53
|
+
@r.instance_eval do
|
54
|
+
def show_exception(*args) end # Silence exceptions
|
55
|
+
end
|
56
|
+
|
57
|
+
@r.start
|
58
|
+
assert_raise ArgumentError do
|
59
|
+
@r.request { raise ArgumentError.new("foo") }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_base_exception_in_request
|
64
|
+
@r.instance_eval do
|
65
|
+
def show_exception(*args) end # Silence exceptions
|
66
|
+
end
|
67
|
+
|
68
|
+
@r.start
|
69
|
+
assert_raise SyntaxError do
|
70
|
+
@r.request { eval("*") }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 1.
|
9
|
+
version: 1.2.0
|
10
10
|
platform: java
|
11
11
|
authors:
|
12
12
|
- Dwayne Litzenberger
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-15 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -119,6 +119,7 @@ files:
|
|
119
119
|
- test/fsm_test.rb
|
120
120
|
- test/multiline_buffer_test.rb
|
121
121
|
- test/net/event_loop_test.rb
|
122
|
+
- test/request_dispatcher_test.rb
|
122
123
|
- test/screen_pattern/generator_test.rb
|
123
124
|
- test/screen_pattern/parser_test.rb
|
124
125
|
- test/screen_pattern/parser_test/explicit_cursor_pattern.txt
|
@@ -172,6 +173,7 @@ test_files:
|
|
172
173
|
- test/fsm_definition_parser_test.rb
|
173
174
|
- test/fsm_test.rb
|
174
175
|
- test/multiline_buffer_test.rb
|
176
|
+
- test/request_dispatcher_test.rb
|
175
177
|
- test/test_helper.rb
|
176
178
|
- test/apps/capture_app_test.rb
|
177
179
|
- test/apps/transcript_parse_app_test.rb
|