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 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
- * ADDED: Stella::Testplan#userid
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.7.6")
28
+ gem.add_dependency("gibbler", ">= 0.8.1")
29
29
  gem.add_dependency("drydock", ">= 0.6.9")
30
- gem.add_dependency("benelux", ">= 0.5.12")
30
+ gem.add_dependency("benelux", ">= 0.5.15")
31
31
  gem.add_dependency('sysinfo', '>= 0.7.3')
32
- gem.add_dependency('storable', '>= 0.6.4')
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
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :MAJOR: 0
3
3
  :MINOR: 8
4
- :PATCH: 5
5
- :BUILD: '002'
4
+ :PATCH: 6
5
+ :BUILD: '001'
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
- %w{attic hexoid storable sysinfo gibbler benelux}.each do |dir|
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, query={}, &blk)
132
- require 'stella/client'
133
- http_client = HTTPClient.new :agent_name => "Opera/9.51 (Windows NT 5.1; U; en)"
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
- end
147
-
148
- Stella.stdout.lev = Stella.quiet? ? 0 : 1
149
- Stella.stdout.autoflush!
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
- opts = {}
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
- wait = (req.wait != 0) ? req.wait : @opts[:wait]
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
- uri.path = base_uri.path
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(/^\//, '')
@@ -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 => Float
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'].each_pair do |status,value|
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.dump
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]
@@ -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
@@ -58,6 +58,11 @@ module Stella
58
58
  def info3(*msg) puts 3, *msg end
59
59
  def info4(*msg) puts 4, *msg end
60
60
 
61
+ def debug(*msg)
62
+ return unless Stella.debug?
63
+ puts 1, *msg
64
+ end
65
+
61
66
  def tinfo(templ, *args)
62
67
  info template(templ) % args
63
68
  end
@@ -59,6 +59,10 @@ class Testplan < Storable
59
59
  end
60
60
  end
61
61
 
62
+ def postprocess
63
+ @id &&= Gibbler::Digest.new @id
64
+ end
65
+
62
66
  def self.load_file(path)
63
67
  conf = File.read path
64
68
  plan = Stella::Testplan.new
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.5.002"
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.7.6"])
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.12"])
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.6.4"])
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.7.6"])
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.12"])
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.6.4"])
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.7.6"])
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.12"])
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.6.4"])
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.5.002
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.7.6
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.12
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.6.4
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