stella 0.8.5.002 → 0.8.6.001
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +9 -2
- data/Rakefile +3 -3
- data/VERSION.yml +2 -2
- data/lib/stella.rb +22 -27
- data/lib/stella/cli.rb +6 -8
- data/lib/stella/client.rb +16 -3
- data/lib/stella/data/http.rb +5 -5
- data/lib/stella/engine.rb +5 -4
- data/lib/stella/engine/functional.rb +2 -2
- data/lib/stella/engine/load.rb +1 -0
- data/lib/stella/logger.rb +5 -0
- data/lib/stella/testplan.rb +4 -0
- data/stella.gemspec +10 -11
- data/vendor/httpclient-2.1.5.2/httpclient/http.rb +4 -1
- data/vendor/httpclient-2.1.5.2/httpclient/session.rb +4 -3
- metadata +4 -5
- data/lib/proc_source.rb +0 -470
data/CHANGES.txt
CHANGED
@@ -10,10 +10,17 @@ STELLA, CHANGES
|
|
10
10
|
* TODO: request block conditions.
|
11
11
|
* TODO: process templates for calls to set in get blocks
|
12
12
|
|
13
|
-
#### 0.8.6 (2010-04
|
13
|
+
#### 0.8.6 (2010-04-14) ###############################
|
14
14
|
|
15
|
-
*
|
15
|
+
* FIXED: Wait option and hosts on CLI were not being passed correctly
|
16
16
|
* CHANGE: Stella::Testplan#userid now used in digest calculation.
|
17
|
+
* CHANGE: Moved ProcSource to Storable
|
18
|
+
* CHANGE: Don't auto freeze testrun.
|
19
|
+
* CHANGE: Store Stella::Data::HTTP::Request#wait as a Range
|
20
|
+
* ADDED: Stella::Testplan#userid
|
21
|
+
* ADDED: Stella.post
|
22
|
+
* ADDED: Stella::Logger.debug
|
23
|
+
* ADDED: Print wait time for each request at verbose level 3 (-vvv)
|
17
24
|
|
18
25
|
|
19
26
|
#### 0.8.5 (2010-03-27) ###############################
|
data/Rakefile
CHANGED
@@ -25,11 +25,11 @@ begin
|
|
25
25
|
gem.email = "delano@solutious.com"
|
26
26
|
gem.homepage = "http://blamestella.com/"
|
27
27
|
gem.authors = ["Delano Mandelbaum"]
|
28
|
-
gem.add_dependency("gibbler", ">= 0.
|
28
|
+
gem.add_dependency("gibbler", ">= 0.8.1")
|
29
29
|
gem.add_dependency("drydock", ">= 0.6.9")
|
30
|
-
gem.add_dependency("benelux", ">= 0.5.
|
30
|
+
gem.add_dependency("benelux", ">= 0.5.15")
|
31
31
|
gem.add_dependency('sysinfo', '>= 0.7.3')
|
32
|
-
gem.add_dependency('storable', '>= 0.
|
32
|
+
gem.add_dependency('storable', '>= 0.7.3')
|
33
33
|
gem.add_dependency("nokogiri")
|
34
34
|
|
35
35
|
#gem.add_development_dependency("rspec", ">= 1.2.9")
|
data/VERSION.yml
CHANGED
data/lib/stella.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
|
2
2
|
STELLA_LIB_HOME = File.expand_path File.dirname(__FILE__) unless defined?(STELLA_LIB_HOME)
|
3
3
|
|
4
|
-
|
5
|
-
$:.unshift File.join(STELLA_LIB_HOME, '..', '..', dir, 'lib')
|
6
|
-
end
|
4
|
+
#%w{attic hexoid storable sysinfo gibbler benelux}.each do |dir|
|
5
|
+
# $:.unshift File.join(STELLA_LIB_HOME, '..', '..', dir, 'lib')
|
6
|
+
#end
|
7
7
|
|
8
8
|
autoload :SysInfo, 'sysinfo'
|
9
9
|
autoload :Drydock, 'drydock'
|
@@ -17,6 +17,8 @@ require 'gibbler/aliases' # important for run time digests and freezes
|
|
17
17
|
require 'benelux'
|
18
18
|
require 'proc_source'
|
19
19
|
|
20
|
+
class OpenStruct; include Gibbler::Object; end
|
21
|
+
|
20
22
|
module Stella
|
21
23
|
module VERSION
|
22
24
|
def self.to_s
|
@@ -128,10 +130,9 @@ module Stella
|
|
128
130
|
autoload :Client, 'stella/client'
|
129
131
|
autoload :Service, 'stella/service'
|
130
132
|
|
131
|
-
def get(uri,
|
132
|
-
|
133
|
-
|
134
|
-
res = http_client.get(uri, query)
|
133
|
+
def get(uri, params={}, headers={}, &blk)
|
134
|
+
http_client = HTTPClient.new :agent_name => Stella.agent
|
135
|
+
res = http_client.get(uri, params, headers)
|
135
136
|
if blk.nil?
|
136
137
|
res.body.content
|
137
138
|
else
|
@@ -143,29 +144,23 @@ module Stella
|
|
143
144
|
nil
|
144
145
|
end
|
145
146
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
class Storable
|
154
|
-
# These methods are used by Storable objects.
|
155
|
-
# See Stella::Testplan
|
156
|
-
module DefaultProcessors
|
157
|
-
# If the object already has a value for +@id+
|
158
|
-
# use it, otherwise return the current digest.
|
159
|
-
#
|
160
|
-
# This allows an object to have a preset ID.
|
161
|
-
#
|
162
|
-
def gibbler_id_processor
|
163
|
-
Proc.new do |val|
|
164
|
-
@id || self.gibbler
|
165
|
-
end
|
147
|
+
def post(uri, params={}, headers={}, &blk)
|
148
|
+
http_client = HTTPClient.new :agent_name => Stella.agent
|
149
|
+
res = http_client.post(uri, params, headers)
|
150
|
+
if blk.nil?
|
151
|
+
res.body.content
|
152
|
+
else
|
153
|
+
blk.call res
|
166
154
|
end
|
155
|
+
rescue => ex
|
156
|
+
STDERR.puts ex.message
|
157
|
+
STDERR.puts ex.backtrace if Stella.debug?
|
158
|
+
nil
|
167
159
|
end
|
160
|
+
|
168
161
|
end
|
169
162
|
|
163
|
+
Stella.stdout.lev = Stella.quiet? ? 0 : 1
|
164
|
+
Stella.stdout.autoflush!
|
170
165
|
|
171
166
|
|
data/lib/stella/cli.rb
CHANGED
@@ -22,7 +22,7 @@ class Stella::CLI < Drydock::Command
|
|
22
22
|
def run(mode)
|
23
23
|
opts = { :mode => mode }
|
24
24
|
opts[:hosts] = @hosts
|
25
|
-
[:nowait, :clients, :repetitions, :duration, :arrival, :granularity].each do |opt|
|
25
|
+
[:nowait, :clients, :repetitions, :duration, :arrival, :granularity, :'wait'].each do |opt|
|
26
26
|
opts[opt] = @option.send(opt) unless @option.send(opt).nil?
|
27
27
|
end
|
28
28
|
[:'notemplates', :'nostats', :'noheader', :'noparam'].each do |opt|
|
@@ -87,16 +87,14 @@ class Stella::CLI < Drydock::Command
|
|
87
87
|
raise "Bad variable format: #{var}" if n.nil? || !n.match(/[a-z]+/i)
|
88
88
|
Stella::Testplan.global(n.to_sym, v)
|
89
89
|
end
|
90
|
+
@hosts = @argv.collect { |uri|;
|
91
|
+
uri = 'http://' << uri unless uri.match /^https?:\/\//i
|
92
|
+
URI.parse uri;
|
93
|
+
}
|
90
94
|
if @option.testplan
|
91
|
-
@hosts = @argv.collect { |uri|;
|
92
|
-
uri = 'http://' << uri unless uri.match /^https?:\/\//i
|
93
|
-
URI.parse uri;
|
94
|
-
}
|
95
95
|
@testplan = Stella::Testplan.load_file @option.testplan
|
96
96
|
else
|
97
|
-
|
98
|
-
opts[:wait] = @option.wait if @option.wait
|
99
|
-
@testplan = Stella::Testplan.new(@argv)
|
97
|
+
@testplan = Stella::Testplan.new(@hosts)
|
100
98
|
end
|
101
99
|
@testplan.check! # raise errors, update usecase ratios
|
102
100
|
@testplan.freeze # cascades through usecases and requests
|
data/lib/stella/client.rb
CHANGED
@@ -154,8 +154,15 @@ module Stella
|
|
154
154
|
next
|
155
155
|
end
|
156
156
|
|
157
|
-
|
157
|
+
if (req.wait.is_a?(::Range) && req.wait.last > 0) ||
|
158
|
+
(Numeric === req.wait && req.wait > 0)
|
159
|
+
wait = req.wait
|
160
|
+
else
|
161
|
+
wait = @opts[:wait]
|
162
|
+
end
|
163
|
+
|
158
164
|
run_sleeper(wait, asset_duration) unless nowait?
|
165
|
+
|
159
166
|
|
160
167
|
# TODO: consider throw/catch
|
161
168
|
case ret.class.to_s
|
@@ -212,7 +219,7 @@ module Stella
|
|
212
219
|
end
|
213
220
|
|
214
221
|
def run_sleeper(wait, already_waited=0)
|
215
|
-
return if wait == 0
|
222
|
+
return if (wait.is_a?(::Range) && wait.last == 0) || wait == 0
|
216
223
|
# The time it took to download the assets can
|
217
224
|
# be removed from the specified wait time.
|
218
225
|
if wait.is_a?(::Range)
|
@@ -221,6 +228,7 @@ module Stella
|
|
221
228
|
else
|
222
229
|
ms = wait * 1000
|
223
230
|
end
|
231
|
+
Stella.stdout.info3 " WAIT: #{ms}ms"
|
224
232
|
sec = ms / 1000
|
225
233
|
Stella.ld "WAIT ADJUSTED FROM %.1f TO: %.1f" % [sec, (sec - already_waited)]
|
226
234
|
sleep (sec - already_waited) if (sec - already_waited) > 0
|
@@ -297,7 +305,12 @@ module Stella
|
|
297
305
|
# $ stella verify -p plan.rb http://localhost/basicauth
|
298
306
|
if URI::Generic === base_uri && base_uri.path
|
299
307
|
if uri.path.nil? || uri.path.empty?
|
300
|
-
|
308
|
+
begin
|
309
|
+
uri.path = base_uri.path
|
310
|
+
rescue => ex
|
311
|
+
Stella.stdout.info "#{ex.class}: #{self.base_uri}"
|
312
|
+
exit
|
313
|
+
end
|
301
314
|
else
|
302
315
|
a = base_uri.path.gsub(/\/$/, '')
|
303
316
|
b = uri.path.gsub(/^\//, '')
|
data/lib/stella/data/http.rb
CHANGED
@@ -21,16 +21,16 @@ module Stella::Data::HTTP
|
|
21
21
|
attic :description
|
22
22
|
field :description
|
23
23
|
|
24
|
-
field :header
|
24
|
+
field :header => Hash
|
25
25
|
field :uri
|
26
|
-
field :wait
|
27
|
-
field :params
|
26
|
+
field :wait => Range
|
27
|
+
field :params => Hash
|
28
28
|
field :body
|
29
29
|
field :http_method
|
30
30
|
field :http_version
|
31
31
|
field :content_type
|
32
32
|
field :http_auth
|
33
|
-
field :timeout
|
33
|
+
field :timeout => Float
|
34
34
|
|
35
35
|
field :autofollow # boolean. Was this an auto generated follow request.
|
36
36
|
|
@@ -49,7 +49,7 @@ module Stella::Data::HTTP
|
|
49
49
|
@header, @params, @response_handler = {}, {}, {}
|
50
50
|
@resources = {}
|
51
51
|
@autofollow = false
|
52
|
-
@wait = 0
|
52
|
+
@wait = 0..0
|
53
53
|
self.description = "Request"
|
54
54
|
instance_eval &definition unless definition.nil?
|
55
55
|
end
|
data/lib/stella/engine.rb
CHANGED
@@ -95,7 +95,7 @@ class Stella::Testrun < Storable
|
|
95
95
|
field :duration => Integer
|
96
96
|
field :arrival => Float
|
97
97
|
field :repetitions => Integer
|
98
|
-
field :wait =>
|
98
|
+
field :wait => Range
|
99
99
|
field :nowait => TrueClass
|
100
100
|
field :logsize => Integer
|
101
101
|
field :granularity => Integer
|
@@ -184,6 +184,7 @@ class Stella::Testrun < Storable
|
|
184
184
|
end
|
185
185
|
|
186
186
|
@start_time ||= Time.now.to_i
|
187
|
+
@end_time ||= 0
|
187
188
|
|
188
189
|
@event_probes.collect! { |event| event.to_s }
|
189
190
|
|
@@ -194,7 +195,6 @@ class Stella::Testrun < Storable
|
|
194
195
|
@duration &&= @duration.to_i
|
195
196
|
@arrival &&= @arrival.to_f
|
196
197
|
@repetitions &&= @repetitions.to_i
|
197
|
-
@wait &&= @wait.to_f
|
198
198
|
|
199
199
|
@mode &&= @mode.to_sym
|
200
200
|
|
@@ -269,7 +269,7 @@ class Stella::Testrun < Storable
|
|
269
269
|
end
|
270
270
|
engine.run self
|
271
271
|
@status = "done"
|
272
|
-
self.freeze
|
272
|
+
#self.freeze
|
273
273
|
self
|
274
274
|
end
|
275
275
|
|
@@ -417,7 +417,8 @@ class Stella::Testrun < Storable
|
|
417
417
|
me.stats.each_pair { |ucid,uchash|
|
418
418
|
uchash.each_pair { |reqid,reqhash|
|
419
419
|
if me.stats[ucid][reqid].has_key? 'status'
|
420
|
-
me.stats[ucid][reqid]['status'].
|
420
|
+
me.stats[ucid][reqid]['status'].keys do |status|
|
421
|
+
value = me.stats[ucid][reqid]['status'][status]
|
421
422
|
me.stats[ucid][reqid]['status'].delete status
|
422
423
|
me.stats[ucid][reqid]['status'][status.to_i] = value.to_i
|
423
424
|
end
|
@@ -53,8 +53,8 @@ module Stella::Engine
|
|
53
53
|
req.http_method, container.status, uri.to_s,
|
54
54
|
params, container.response.request.header.dump,
|
55
55
|
container.response.header.dump,
|
56
|
-
container.response.body.
|
57
|
-
|
56
|
+
container.response.body.content
|
57
|
+
|
58
58
|
Benelux.thread_timeline.add_message log, :status => container.status, :kind => :log
|
59
59
|
|
60
60
|
msg = ' %-6s %-53s ' % [req.http_method, uri]
|
data/lib/stella/engine/load.rb
CHANGED
@@ -49,6 +49,7 @@ module Stella::Engine
|
|
49
49
|
Stella.stdout.head 'Hosts', testrun.hosts.join(', ')
|
50
50
|
Stella.stdout.head 'Clients', counts[:total]
|
51
51
|
Stella.stdout.head 'Limit', timing
|
52
|
+
Stella.stdout.head 'Wait', testrun.wait
|
52
53
|
Stella.stdout.head 'Arrival', testrun.arrival if testrun.arrival
|
53
54
|
|
54
55
|
@dumper.start
|
data/lib/stella/logger.rb
CHANGED
data/lib/stella/testplan.rb
CHANGED
data/stella.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{stella}
|
8
|
-
s.version = "0.8.
|
8
|
+
s.version = "0.8.6.001"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Delano Mandelbaum"]
|
@@ -38,7 +38,6 @@ Gem::Specification.new do |s|
|
|
38
38
|
"examples/exceptions/plan.rb",
|
39
39
|
"examples/timeout/plan.rb",
|
40
40
|
"examples/variables/plan.rb",
|
41
|
-
"lib/proc_source.rb",
|
42
41
|
"lib/stella.rb",
|
43
42
|
"lib/stella/cli.rb",
|
44
43
|
"lib/stella/client.rb",
|
@@ -98,26 +97,26 @@ Gem::Specification.new do |s|
|
|
98
97
|
s.specification_version = 3
|
99
98
|
|
100
99
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
101
|
-
s.add_runtime_dependency(%q<gibbler>, [">= 0.
|
100
|
+
s.add_runtime_dependency(%q<gibbler>, [">= 0.8.1"])
|
102
101
|
s.add_runtime_dependency(%q<drydock>, [">= 0.6.9"])
|
103
|
-
s.add_runtime_dependency(%q<benelux>, [">= 0.5.
|
102
|
+
s.add_runtime_dependency(%q<benelux>, [">= 0.5.15"])
|
104
103
|
s.add_runtime_dependency(%q<sysinfo>, [">= 0.7.3"])
|
105
|
-
s.add_runtime_dependency(%q<storable>, [">= 0.
|
104
|
+
s.add_runtime_dependency(%q<storable>, [">= 0.7.3"])
|
106
105
|
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
107
106
|
else
|
108
|
-
s.add_dependency(%q<gibbler>, [">= 0.
|
107
|
+
s.add_dependency(%q<gibbler>, [">= 0.8.1"])
|
109
108
|
s.add_dependency(%q<drydock>, [">= 0.6.9"])
|
110
|
-
s.add_dependency(%q<benelux>, [">= 0.5.
|
109
|
+
s.add_dependency(%q<benelux>, [">= 0.5.15"])
|
111
110
|
s.add_dependency(%q<sysinfo>, [">= 0.7.3"])
|
112
|
-
s.add_dependency(%q<storable>, [">= 0.
|
111
|
+
s.add_dependency(%q<storable>, [">= 0.7.3"])
|
113
112
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
114
113
|
end
|
115
114
|
else
|
116
|
-
s.add_dependency(%q<gibbler>, [">= 0.
|
115
|
+
s.add_dependency(%q<gibbler>, [">= 0.8.1"])
|
117
116
|
s.add_dependency(%q<drydock>, [">= 0.6.9"])
|
118
|
-
s.add_dependency(%q<benelux>, [">= 0.5.
|
117
|
+
s.add_dependency(%q<benelux>, [">= 0.5.15"])
|
119
118
|
s.add_dependency(%q<sysinfo>, [">= 0.7.3"])
|
120
|
-
s.add_dependency(%q<storable>, [">= 0.
|
119
|
+
s.add_dependency(%q<storable>, [">= 0.7.3"])
|
121
120
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
122
121
|
end
|
123
122
|
end
|
@@ -233,9 +233,11 @@ module HTTP
|
|
233
233
|
else
|
234
234
|
str = response_status_line
|
235
235
|
end
|
236
|
-
str + @header_item.collect { |key, value|
|
236
|
+
ret = str + @header_item.collect { |key, value|
|
237
237
|
"#{ key }: #{ value }#{ CRLF }"
|
238
238
|
}.join
|
239
|
+
#ret.force_encoding("ASCII-8BIT") if RUBY_VERSION >= "1.9"
|
240
|
+
ret
|
239
241
|
end
|
240
242
|
|
241
243
|
# Adds a header. Addition order is preserved.
|
@@ -508,6 +510,7 @@ module HTTP
|
|
508
510
|
|
509
511
|
# Returns a message body itself.
|
510
512
|
def content
|
513
|
+
#@body.force_encoding("ASCII-8BIT") if RUBY_VERSION >= "1.9"
|
511
514
|
@body
|
512
515
|
end
|
513
516
|
|
@@ -590,12 +590,13 @@ class HTTPClient
|
|
590
590
|
read_header if @state == :META
|
591
591
|
return nil if @state != :DATA
|
592
592
|
if @chunked
|
593
|
-
read_body_chunked(&block)
|
593
|
+
ret = read_body_chunked(&block)
|
594
594
|
elsif @content_length
|
595
|
-
read_body_length(&block)
|
595
|
+
ret = read_body_length(&block)
|
596
596
|
else
|
597
|
-
read_body_rest(&block)
|
597
|
+
ret = read_body_rest(&block)
|
598
598
|
end
|
599
|
+
p [:ret, ret.encoding.name] if ret
|
599
600
|
rescue
|
600
601
|
close
|
601
602
|
raise
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stella
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.6.001
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.
|
23
|
+
version: 0.8.1
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: drydock
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.5.
|
43
|
+
version: 0.5.15
|
44
44
|
version:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: sysinfo
|
@@ -60,7 +60,7 @@ dependencies:
|
|
60
60
|
requirements:
|
61
61
|
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: 0.
|
63
|
+
version: 0.7.3
|
64
64
|
version:
|
65
65
|
- !ruby/object:Gem::Dependency
|
66
66
|
name: nokogiri
|
@@ -101,7 +101,6 @@ files:
|
|
101
101
|
- examples/exceptions/plan.rb
|
102
102
|
- examples/timeout/plan.rb
|
103
103
|
- examples/variables/plan.rb
|
104
|
-
- lib/proc_source.rb
|
105
104
|
- lib/stella.rb
|
106
105
|
- lib/stella/cli.rb
|
107
106
|
- lib/stella/client.rb
|
data/lib/proc_source.rb
DELETED
@@ -1,470 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'stringio'
|
3
|
-
require 'irb/ruby-lex'
|
4
|
-
#SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
|
5
|
-
|
6
|
-
class ProcString < String
|
7
|
-
attr_accessor :file, :lines, :arity, :kind
|
8
|
-
def to_proc(kind="proc")
|
9
|
-
result = eval("#{kind} #{self}")
|
10
|
-
result.source = self
|
11
|
-
result
|
12
|
-
end
|
13
|
-
def to_lambda
|
14
|
-
to_proc "lamda"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class RubyToken::Token
|
19
|
-
|
20
|
-
# These EXPR_BEG tokens don't have associated end tags
|
21
|
-
FAKIES = [RubyToken::TkWHEN, RubyToken::TkELSIF, RubyToken::TkTHEN]
|
22
|
-
|
23
|
-
def open_tag?
|
24
|
-
return false if @name.nil? || get_props.nil?
|
25
|
-
a = (get_props[1] == RubyToken::EXPR_BEG) &&
|
26
|
-
self.class.to_s !~ /_MOD/ && # ignore onliner if, unless, etc...
|
27
|
-
!FAKIES.member?(self.class)
|
28
|
-
a
|
29
|
-
end
|
30
|
-
|
31
|
-
def get_props
|
32
|
-
RubyToken::TkReading2Token[@name]
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
# Based heavily on code from http://github.com/imedo/background
|
38
|
-
# Big thanks to the imedo dev team!
|
39
|
-
#
|
40
|
-
module ProcSource
|
41
|
-
|
42
|
-
def self.find(filename, start_line=0, block_only=true)
|
43
|
-
lines, lexer = nil, nil
|
44
|
-
retried = 0
|
45
|
-
loop do
|
46
|
-
lines = get_lines(filename, start_line)
|
47
|
-
#p [start_line, lines[0]]
|
48
|
-
if !line_has_open?(lines.join) && start_line >= 0
|
49
|
-
start_line -= 1 and retried +=1 and redo
|
50
|
-
end
|
51
|
-
lexer = RubyLex.new
|
52
|
-
lexer.set_input(StringIO.new(lines.join))
|
53
|
-
break
|
54
|
-
end
|
55
|
-
stoken, etoken, nesting = nil, nil, 0
|
56
|
-
while token = lexer.token
|
57
|
-
n = token.instance_variable_get(:@name)
|
58
|
-
|
59
|
-
if RubyToken::TkIDENTIFIER === token
|
60
|
-
#nothing
|
61
|
-
elsif token.open_tag? || RubyToken::TkfLBRACE === token
|
62
|
-
nesting += 1
|
63
|
-
stoken = token if nesting == 1
|
64
|
-
elsif RubyToken::TkEND === token || RubyToken::TkRBRACE === token
|
65
|
-
if nesting == 1
|
66
|
-
etoken = token
|
67
|
-
break
|
68
|
-
end
|
69
|
-
nesting -= 1
|
70
|
-
elsif RubyToken::TkBITOR === token && stoken
|
71
|
-
#nothing
|
72
|
-
elsif RubyToken::TkNL === token && stoken && etoken
|
73
|
-
break if nesting <= 0
|
74
|
-
else
|
75
|
-
#p token
|
76
|
-
end
|
77
|
-
end
|
78
|
-
# puts lines if etoken.nil?
|
79
|
-
lines = lines[stoken.line_no-1 .. etoken.line_no-1]
|
80
|
-
|
81
|
-
# Remove the crud before the block definition.
|
82
|
-
if block_only
|
83
|
-
spaces = lines.last.match(/^\s+/)[0] rescue ''
|
84
|
-
lines[0] = spaces << lines[0][stoken.char_no .. -1]
|
85
|
-
end
|
86
|
-
ps = ProcString.new lines.join
|
87
|
-
ps.file, ps.lines = filename, start_line .. start_line+etoken.line_no-1
|
88
|
-
|
89
|
-
ps
|
90
|
-
end
|
91
|
-
|
92
|
-
# A hack for Ruby 1.9, otherwise returns true.
|
93
|
-
#
|
94
|
-
# Ruby 1.9 returns an incorrect line number
|
95
|
-
# when a block is specified with do/end. It
|
96
|
-
# happens b/c the line number returned by
|
97
|
-
# Ruby 1.9 is based on the first line in the
|
98
|
-
# block which contains a token (i.e. not a
|
99
|
-
# new line or comment etc...).
|
100
|
-
#
|
101
|
-
# NOTE: This won't work in cases where the
|
102
|
-
# incorrect line also contains a "do".
|
103
|
-
#
|
104
|
-
def self.line_has_open?(str)
|
105
|
-
return true unless RUBY_VERSION >= '1.9'
|
106
|
-
lexer = RubyLex.new
|
107
|
-
lexer.set_input(StringIO.new(str))
|
108
|
-
success = false
|
109
|
-
while token = lexer.token
|
110
|
-
case token
|
111
|
-
when RubyToken::TkNL
|
112
|
-
break
|
113
|
-
when RubyToken::TkDO
|
114
|
-
success = true
|
115
|
-
when RubyToken::TkCONSTANT
|
116
|
-
if token.instance_variable_get(:@name) == "Proc" &&
|
117
|
-
lexer.token.is_a?(RubyToken::TkDOT)
|
118
|
-
method = lexer.token
|
119
|
-
if method.is_a?(RubyToken::TkIDENTIFIER) &&
|
120
|
-
method.instance_variable_get(:@name) == "new"
|
121
|
-
success = true
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
success
|
127
|
-
end
|
128
|
-
|
129
|
-
|
130
|
-
def self.get_lines(filename, start_line = 0)
|
131
|
-
case filename
|
132
|
-
when nil
|
133
|
-
nil
|
134
|
-
## NOTE: IRB AND EVAL LINES NOT TESTED
|
135
|
-
### special "(irb)" descriptor?
|
136
|
-
##when "(irb)"
|
137
|
-
## IRB.conf[:MAIN_CONTEXT].io.line(start_line .. -2)
|
138
|
-
### special "(eval...)" descriptor?
|
139
|
-
##when /^\(eval.+\)$/
|
140
|
-
## EVAL_LINES__[filename][start_line .. -2]
|
141
|
-
# regular file
|
142
|
-
else
|
143
|
-
# Ruby already parsed this file? (see disclaimer above)
|
144
|
-
if defined?(SCRIPT_LINES__) && SCRIPT_LINES__[filename]
|
145
|
-
SCRIPT_LINES__[filename][(start_line - 1) .. -1]
|
146
|
-
# If the file exists we're going to try reading it in
|
147
|
-
elsif File.exist?(filename)
|
148
|
-
begin
|
149
|
-
File.readlines(filename)[(start_line - 1) .. -1]
|
150
|
-
rescue
|
151
|
-
nil
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
class Proc #:nodoc:
|
159
|
-
attr_reader :file, :line
|
160
|
-
attr_writer :source
|
161
|
-
|
162
|
-
def source_descriptor
|
163
|
-
unless @file && @line
|
164
|
-
if md = /^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+)(.+?)?>$/.match(inspect)
|
165
|
-
@file, @line = md.captures
|
166
|
-
end
|
167
|
-
end
|
168
|
-
[@file, @line.to_i]
|
169
|
-
end
|
170
|
-
|
171
|
-
def source
|
172
|
-
@source ||= ProcSource.find(*self.source_descriptor)
|
173
|
-
end
|
174
|
-
|
175
|
-
|
176
|
-
end
|
177
|
-
|
178
|
-
if $0 == __FILE__
|
179
|
-
def store(&blk)
|
180
|
-
@blk = blk
|
181
|
-
end
|
182
|
-
|
183
|
-
store do |blk|
|
184
|
-
puts "Hello Rudy1"
|
185
|
-
end
|
186
|
-
|
187
|
-
a = Proc.new() { |a|
|
188
|
-
puts "Hello Rudy2"
|
189
|
-
}
|
190
|
-
|
191
|
-
b = Proc.new() do |b|
|
192
|
-
puts { "Hello Rudy3" } if true
|
193
|
-
end
|
194
|
-
|
195
|
-
puts @blk.inspect, @blk.source
|
196
|
-
puts [a.inspect, a.source]
|
197
|
-
puts b.inspect, b.source
|
198
|
-
|
199
|
-
proc = @blk.source.to_proc
|
200
|
-
proc.call(1)
|
201
|
-
end
|
202
|
-
|
203
|
-
|
204
|
-
__END__
|
205
|
-
|
206
|
-
# THE FOLLOWING WAS TAKEN FROM
|
207
|
-
# http://github.com/imedo/background
|
208
|
-
# AND IS THE BASIS OF THE CODE ABOVE.
|
209
|
-
#
|
210
|
-
|
211
|
-
require 'stringio'
|
212
|
-
require 'irb/ruby-lex'
|
213
|
-
|
214
|
-
# Tell the ruby interpreter to load code lines of required files
|
215
|
-
# into this filename -> lines Hash. This behaviour seems to be
|
216
|
-
# very undocumented and therefore shouldn't really be relied on.
|
217
|
-
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
|
218
|
-
|
219
|
-
module ProcSource #:nodoc:
|
220
|
-
def get_lines(filename, start_line = 0)
|
221
|
-
case filename
|
222
|
-
when nil
|
223
|
-
nil
|
224
|
-
# special "(irb)" descriptor?
|
225
|
-
when "(irb)"
|
226
|
-
IRB.conf[:MAIN_CONTEXT].io.line(start_line .. -1)
|
227
|
-
# special "(eval...)" descriptor?
|
228
|
-
when /^\(eval.+\)$/
|
229
|
-
EVAL_LINES__[filename][start_line .. -1]
|
230
|
-
# regular file
|
231
|
-
else
|
232
|
-
# Ruby already parsed this file? (see disclaimer above)
|
233
|
-
if lines = SCRIPT_LINES__[filename]
|
234
|
-
lines[(start_line - 1) .. -1]
|
235
|
-
# If the file exists we're going to try reading it in
|
236
|
-
elsif File.exist?(filename)
|
237
|
-
begin
|
238
|
-
File.readlines(filename)[(start_line - 1) .. -1]
|
239
|
-
rescue
|
240
|
-
nil
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def handle(proc)
|
247
|
-
filename, line = proc.source_descriptor
|
248
|
-
lines = get_lines(filename, line) || []
|
249
|
-
|
250
|
-
lexer = RubyLex.new
|
251
|
-
lexer.set_input(StringIO.new(lines.join))
|
252
|
-
|
253
|
-
state = :before_constructor
|
254
|
-
nesting_level = 1
|
255
|
-
start_token, end_token = nil, nil
|
256
|
-
found = false
|
257
|
-
while token = lexer.token
|
258
|
-
p token
|
259
|
-
# we've not yet found any proc-constructor -- we'll try to find one.
|
260
|
-
if [:before_constructor, :check_more].include?(state)
|
261
|
-
# checking more and newline? -> done
|
262
|
-
if token.is_a?(RubyToken::TkNL) and state == :check_more
|
263
|
-
state = :done
|
264
|
-
break
|
265
|
-
end
|
266
|
-
# token is Proc?
|
267
|
-
if token.is_a?(RubyToken::TkCONSTANT) and
|
268
|
-
token.instance_variable_get(:@name) == "Proc"
|
269
|
-
# method call?
|
270
|
-
if lexer.token.is_a?(RubyToken::TkDOT)
|
271
|
-
method = lexer.token
|
272
|
-
# constructor?
|
273
|
-
if method.is_a?(RubyToken::TkIDENTIFIER) and
|
274
|
-
method.instance_variable_get(:@name) == "new"
|
275
|
-
unless state == :check_more
|
276
|
-
# okay, code will follow soon.
|
277
|
-
state = :before_code
|
278
|
-
else
|
279
|
-
# multiple procs on one line
|
280
|
-
return
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
# token is lambda or proc call?
|
285
|
-
elsif token.is_a?(RubyToken::TkIDENTIFIER) and
|
286
|
-
%w{proc lambda}.include?(token.instance_variable_get(:@name))
|
287
|
-
unless state == :check_more
|
288
|
-
# okay, code will follow soon.
|
289
|
-
state = :before_code
|
290
|
-
else
|
291
|
-
# multiple procs on one line
|
292
|
-
return
|
293
|
-
end
|
294
|
-
elsif token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO)
|
295
|
-
# found the code start, update state and remember current token
|
296
|
-
state = :in_code
|
297
|
-
start_token = token
|
298
|
-
end
|
299
|
-
|
300
|
-
# we're waiting for the code start to appear.
|
301
|
-
elsif state == :before_code
|
302
|
-
|
303
|
-
if token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO)
|
304
|
-
# found the code start, update state and remember current token
|
305
|
-
state = :in_code
|
306
|
-
start_token = token
|
307
|
-
end
|
308
|
-
|
309
|
-
# okay, we're inside code
|
310
|
-
elsif state == :in_code
|
311
|
-
if token.is_a?(RubyToken::TkRBRACE) or token.is_a?(RubyToken::TkEND)
|
312
|
-
nesting_level -= 1
|
313
|
-
if nesting_level == 0
|
314
|
-
# we're done!
|
315
|
-
end_token = token
|
316
|
-
# parse another time to check if there are multiple procs on one line
|
317
|
-
# we can't handle that case correctly so we return no source code at all
|
318
|
-
state = :check_more
|
319
|
-
end
|
320
|
-
elsif token.is_a?(RubyToken::TkfLBRACE) or token.is_a?(RubyToken::TkDO) or
|
321
|
-
token.is_a?(RubyToken::TkBEGIN) or token.is_a?(RubyToken::TkCASE) or
|
322
|
-
token.is_a?(RubyToken::TkCLASS) or token.is_a?(RubyToken::TkDEF) or
|
323
|
-
token.is_a?(RubyToken::TkFOR) or token.is_a?(RubyToken::TkIF) or
|
324
|
-
token.is_a?(RubyToken::TkMODULE) or token.is_a?(RubyToken::TkUNLESS) or
|
325
|
-
token.is_a?(RubyToken::TkUNTIL) or token.is_a?(RubyToken::TkWHILE) or
|
326
|
-
token.is_a?(RubyToken::TklBEGIN)
|
327
|
-
nesting_level += 1
|
328
|
-
end
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
if start_token and end_token
|
333
|
-
start_line, end_line = start_token.line_no - 1, end_token.line_no - 1
|
334
|
-
source = lines[start_line .. end_line]
|
335
|
-
start_offset = start_token.char_no
|
336
|
-
start_offset += 1 if start_token.is_a?(RubyToken::TkDO)
|
337
|
-
end_offset = -(source.last.length - end_token.char_no)
|
338
|
-
source.first.slice!(0 .. start_offset)
|
339
|
-
source.last.slice!(end_offset .. -1)
|
340
|
-
|
341
|
-
# Can't use .strip because newline at end of code might be important
|
342
|
-
# (Stuff would break when somebody does proc { ... #foo\n})
|
343
|
-
proc.source = source.join.gsub(/^ | $/, "")
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
module_function :handle, :get_lines
|
348
|
-
end
|
349
|
-
|
350
|
-
class Proc #:nodoc:
|
351
|
-
attr_reader :file, :line
|
352
|
-
|
353
|
-
def source_descriptor
|
354
|
-
return @file, @line.to_i if @file && @line
|
355
|
-
md = /^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+)(.+?)?>$/.match(old_inspect)
|
356
|
-
if md
|
357
|
-
@file, @line = md.captures
|
358
|
-
return @file, @line.to_i
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
def self.source_cache
|
363
|
-
@source_cache ||= {}
|
364
|
-
end
|
365
|
-
|
366
|
-
def source=(code)
|
367
|
-
@source = Proc.source_cache[source_descriptor.join('/')] = code
|
368
|
-
end
|
369
|
-
|
370
|
-
def source
|
371
|
-
@source = Proc.source_cache[source_descriptor.join('/')]
|
372
|
-
ProcSource.handle(self) #unless @source
|
373
|
-
@source
|
374
|
-
end
|
375
|
-
|
376
|
-
alias :old_inspect :inspect
|
377
|
-
def inspect
|
378
|
-
if source
|
379
|
-
"proc {#{source}}"
|
380
|
-
else
|
381
|
-
old_inspect
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
def ==(other)
|
386
|
-
if self.source && other.respond_to?(:source) && other.source
|
387
|
-
self.source == other.source
|
388
|
-
else
|
389
|
-
self.object_id == other.object_id
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
def _dump(depth = 0)
|
394
|
-
if source
|
395
|
-
source
|
396
|
-
else
|
397
|
-
raise(TypeError, "Can't serialize Proc with unknown source code.")
|
398
|
-
end
|
399
|
-
end
|
400
|
-
|
401
|
-
def to_yaml(*args)
|
402
|
-
#self.source # force @source to be set
|
403
|
-
a = super
|
404
|
-
a.sub!("object:Proc", "proc") if a.respond_to? :sub
|
405
|
-
a
|
406
|
-
end
|
407
|
-
|
408
|
-
def self.allocate; from_string ""; end
|
409
|
-
|
410
|
-
def self.from_string(string)
|
411
|
-
result = eval("proc {#{string}}")
|
412
|
-
result.source = string
|
413
|
-
return result
|
414
|
-
end
|
415
|
-
|
416
|
-
def self._load(code)
|
417
|
-
self.from_string(code)
|
418
|
-
end
|
419
|
-
|
420
|
-
def self.marshal_load; end
|
421
|
-
def marshal_load; end
|
422
|
-
end
|
423
|
-
|
424
|
-
require 'yaml'
|
425
|
-
YAML.add_ruby_type(/^proc/) do |type, val|
|
426
|
-
Proc.from_string(val["source"])
|
427
|
-
end
|
428
|
-
|
429
|
-
# EVAL_LINES__ = Hash.new
|
430
|
-
#
|
431
|
-
# alias :old_eval :eval
|
432
|
-
# def eval(code, *args)
|
433
|
-
# context, descriptor, start_line, *more = *args
|
434
|
-
# descriptor ||= "(eval#{code.hash})"
|
435
|
-
# start_line ||= 0
|
436
|
-
# lines ||= code.grep(/.*/)
|
437
|
-
# EVAL_LINES__[descriptor] ||= Array.new
|
438
|
-
# EVAL_LINES__[descriptor][start_line, lines.length] = lines
|
439
|
-
# old_eval(code, context, descriptor, start_line, *more)
|
440
|
-
# end
|
441
|
-
|
442
|
-
|
443
|
-
if __FILE__ == $0 then
|
444
|
-
require "pstore"
|
445
|
-
|
446
|
-
code = Proc.new() {
|
447
|
-
puts "Hello World!"
|
448
|
-
}
|
449
|
-
|
450
|
-
|
451
|
-
File.open("proc.marshalled", "w") { |file| Marshal.dump(code, file) }
|
452
|
-
code = File.open("proc.marshalled") { |file| Marshal.load(file) }
|
453
|
-
|
454
|
-
code.call
|
455
|
-
|
456
|
-
store = PStore.new("proc.pstore")
|
457
|
-
store.transaction do
|
458
|
-
store["proc"] = code
|
459
|
-
end
|
460
|
-
store.transaction do
|
461
|
-
code = store["proc"]
|
462
|
-
end
|
463
|
-
|
464
|
-
code.call
|
465
|
-
|
466
|
-
File.open("proc.yaml", "w") { |file| YAML.dump(code) }
|
467
|
-
#code = File.open("proc.yaml") { |file| YAML.load(file) }
|
468
|
-
|
469
|
-
code.call
|
470
|
-
end
|