scriptty 1.1.0-java → 1.2.0-java
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/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
|