openwferu 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/examples/mano_tracker.rb +11 -13
  2. data/lib/openwfe.rb +2 -4
  3. data/lib/openwfe/contextual.rb +8 -2
  4. data/lib/openwfe/engine/engine.rb +118 -2
  5. data/lib/openwfe/expool/expressionpool.rb +148 -53
  6. data/lib/openwfe/expool/expstorage.rb +36 -4
  7. data/lib/openwfe/expool/yamlexpstorage.rb +18 -0
  8. data/lib/openwfe/expressions/environment.rb +1 -1
  9. data/lib/openwfe/expressions/fe_misc.rb +14 -2
  10. data/lib/openwfe/expressions/fe_raw.rb +27 -11
  11. data/lib/openwfe/expressions/fe_subprocess.rb +45 -12
  12. data/lib/openwfe/expressions/fe_utils.rb +1 -1
  13. data/lib/openwfe/expressions/flowexpression.rb +5 -1
  14. data/lib/openwfe/expressions/raw_prog.rb +80 -32
  15. data/lib/openwfe/expressions/raw_xml.rb +10 -0
  16. data/lib/openwfe/flowexpressionid.rb +28 -7
  17. data/lib/openwfe/listeners/listener.rb +106 -0
  18. data/lib/openwfe/listeners/listeners.rb +140 -0
  19. data/lib/openwfe/listeners/socketlisteners.rb +239 -0
  20. data/lib/openwfe/listeners/sqslisteners.rb +145 -0
  21. data/lib/openwfe/participants/{csvparticipant.rb → csvparticipants.rb} +0 -0
  22. data/lib/openwfe/participants/{enoparticipant.rb → enoparticipants.rb} +1 -1
  23. data/lib/openwfe/participants/participantmap.rb +33 -1
  24. data/lib/openwfe/participants/participants.rb +99 -11
  25. data/lib/openwfe/participants/soapparticipants.rb +28 -8
  26. data/lib/openwfe/participants/socketparticipants.rb +172 -0
  27. data/lib/openwfe/participants/sqsparticipants.rb +121 -0
  28. data/lib/openwfe/rest/definitions.rb +1 -1
  29. data/lib/openwfe/{osocket.rb → rest/osocket.rb} +16 -8
  30. data/lib/openwfe/rest/xmlcodec.rb +41 -5
  31. data/lib/openwfe/storage/yamlfilestorage.rb +10 -13
  32. data/lib/openwfe/util/dollar.rb +23 -2
  33. data/lib/openwfe/util/otime.rb +1 -1
  34. data/lib/openwfe/util/safe.rb +149 -0
  35. data/lib/openwfe/util/scheduler.rb +47 -5
  36. data/lib/openwfe/util/sqs.rb +582 -0
  37. data/lib/openwfe/utils.rb +23 -0
  38. data/lib/openwfe/version.rb +1 -1
  39. data/lib/openwfe/workitem.rb +86 -3
  40. data/lib/openwfe/worklist/storeparticipant.rb +44 -4
  41. data/test/csv_test.rb +1 -1
  42. data/test/eno_test.rb +1 -1
  43. data/test/fei_test.rb +2 -15
  44. data/test/flowtestbase.rb +6 -2
  45. data/test/ft_11_ppd.rb +31 -0
  46. data/test/ft_13_eno.rb +1 -1
  47. data/test/ft_14b_subprocess.rb +74 -0
  48. data/test/ft_15_iterator.rb +0 -1
  49. data/test/ft_19_csv.rb +1 -1
  50. data/test/ft_20_cron.rb +1 -1
  51. data/test/ft_21_cron.rb +1 -1
  52. data/test/ft_27_getflowpos.rb +89 -0
  53. data/test/ft_28_fileparticipant.rb +65 -0
  54. data/test/ft_29_httprb.rb +95 -0
  55. data/test/ft_30_socketlistener.rb +197 -0
  56. data/test/ft_4_misc.rb +2 -1
  57. data/test/hash_test.rb +75 -0
  58. data/test/rake_qtest.rb +5 -4
  59. data/test/raw_prog_test.rb +205 -0
  60. data/test/rutest_utils.rb +16 -0
  61. data/test/safely_test.rb +89 -0
  62. data/test/scheduler_test.rb +71 -0
  63. data/test/sqs_test.rb +103 -0
  64. metadata +21 -6
  65. data/test/journal_persistence_test.rb +0 -147
@@ -0,0 +1,121 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007, John Mettraux, OpenWFE.org
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # . Redistributions of source code must retain the above copyright notice, this
10
+ # list of conditions and the following disclaimer.
11
+ #
12
+ # . Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ #
16
+ # . Neither the name of the "OpenWFE" nor the names of its contributors may be
17
+ # used to endorse or promote products derived from this software without
18
+ # specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+ #++
32
+ #
33
+ #
34
+
35
+ #
36
+ # "made in Japan"
37
+ #
38
+ # John Mettraux at openwfe.org
39
+ #
40
+
41
+ require 'yaml'
42
+ require 'base64'
43
+
44
+ #require 'openwfe/utils'
45
+ #require 'openwfe/rudefinitions'
46
+ require 'openwfe/participants/participant'
47
+
48
+
49
+ module OpenWFE
50
+
51
+ #
52
+ # This participant dispatches its workitem to an Amazon SQS queue.
53
+ #
54
+ # If the queue doesn't exist, the participant will create it.
55
+ #
56
+ # a small example :
57
+ #
58
+ # # ...
59
+ # engine.register_participant(:sqs0, SqsParticipant.new("workqueue0"))
60
+ # # ...
61
+ #
62
+ # For more details about SQS :
63
+ # http://aws.amazon.com
64
+ #
65
+ class SqsParticipant
66
+ include LocalParticipant
67
+
68
+ attr_reader :queue, :queue_service
69
+
70
+ #
71
+ # Builds an SqsParticipant instance pointing to a given queue.
72
+ # (Refer to the SQS service on how to set up AWS key ids).
73
+ #
74
+ # By default the host_name is 'queue.amazonaws.com'
75
+ #
76
+ def initialize (queue_name, host_name=nil)
77
+
78
+ @queue_name = queue_name
79
+
80
+ @queue_service = SQS::QueueService.new(host_name)
81
+
82
+ @queue_service.create_queue(@queue_name)
83
+ # make sure the queue exists
84
+
85
+ @queue = @queue_service.get_queue(@queue_name)
86
+ end
87
+
88
+ #
89
+ # The method called by the engine when it has a workitem for this
90
+ # participant.
91
+ #
92
+ def consume (workitem)
93
+
94
+ msg = encode_workitem(workitem)
95
+
96
+ msg_id = @queue_service.put_message(@queue, msg)
97
+
98
+ ldebug do
99
+ "consume() msg sent to queue #{@queue.path} id is #{msg_id}"
100
+ end
101
+ end
102
+
103
+ protected
104
+
105
+ #
106
+ # Turns the workitem into a Hash, pass it through YAML and
107
+ # encode64 the result.
108
+ #
109
+ # Override this method as needed.
110
+ #
111
+ # Something of 'text/plain' flavour should be returned.
112
+ #
113
+ def encode_workitem (wi)
114
+
115
+ msg = wi.to_h
116
+ msg = YAML.dump(msg)
117
+ msg = Base64.encode64(msg)
118
+ msg
119
+ end
120
+ end
121
+ end
@@ -82,7 +82,7 @@ module OpenWFE
82
82
 
83
83
  FLOW_EXPRESSION_IDS = 'flow-expression-ids'
84
84
 
85
- E_LAUNCHITEM = 'launchitem'
85
+ LAUNCHITEM = 'launchitem'
86
86
  ENGINEID = "engineid"
87
87
  OK = "ok"
88
88
  A_FLOW_ID = "flow-id"
@@ -34,12 +34,17 @@
34
34
  #
35
35
 
36
36
  require 'socket'
37
- require 'xmlcodec'
37
+
38
+ require 'openwfe/rest/xmlcodec'
38
39
 
39
40
 
40
41
  module OpenWFE
41
42
 
42
- class SocketListener < TCPServer
43
+ #
44
+ # This code was blogged about at
45
+ # http://jmettraux.wordpress.com/2006/06/01/openwfe-ruby/
46
+ #
47
+ class OldSocketListener < TCPServer
43
48
 
44
49
  #
45
50
  # starts to listen on a given interface (IP) and port
@@ -50,6 +55,9 @@ module OpenWFE
50
55
 
51
56
  def listen ()
52
57
  while (session = accept())
58
+ #
59
+ # how does it scale ?
60
+
53
61
  s = session.gets
54
62
 
55
63
  if s[0..8] != 'xmlCoder '
@@ -74,10 +82,7 @@ module OpenWFE
74
82
  session.print "<ok-reply/>"
75
83
  session.close
76
84
 
77
- #yield sXml
78
-
79
- xml = REXML::Document.new(sXml)
80
- yield OpenWFE.decode(xml.root)
85
+ yield OpenWFE.xml_decode(xml.root)
81
86
  end
82
87
  end
83
88
  end
@@ -96,11 +101,14 @@ module OpenWFE
96
101
  socket.puts
97
102
  socket.puts sXml
98
103
  socket.puts
104
+ socket.close_write
99
105
 
100
106
  reply = socket.gets
101
- reply = reply + socket.gets
107
+
108
+ #reply = reply + socket.gets
102
109
  #
103
110
  # a bit ridiculous, but it works
111
+ # socket.close_write fixed it
104
112
 
105
113
  socket.close
106
114
 
@@ -113,7 +121,7 @@ end
113
121
  #
114
122
  # some test code
115
123
 
116
- #sl = OpenWFE::SocketListener.new('127.0.0.1', 7010)
124
+ #sl = OpenWFE::OldSocketListener.new('127.0.0.1', 7010)
117
125
  #
118
126
  #puts "..ready.."
119
127
  #
@@ -37,7 +37,6 @@
37
37
  # "hecho en Costa Rica"
38
38
  #
39
39
 
40
- #require 'base64'
41
40
  require 'rexml/document'
42
41
 
43
42
  require 'openwfe/utils'
@@ -48,6 +47,9 @@ require 'openwfe/rest/definitions'
48
47
 
49
48
  module OpenWFE
50
49
 
50
+ #
51
+ # Ugly XML codec for OpenWFE workitems
52
+ #
51
53
  class XmlCodec
52
54
 
53
55
  #
@@ -56,7 +58,13 @@ module OpenWFE
56
58
  #
57
59
  def decode (xmlElt)
58
60
 
59
- return nil if not xmlElt
61
+ return nil unless xmlElt
62
+
63
+ if xmlElt.kind_of? String
64
+
65
+ xmlElt = REXML::Document.new(xmlElt)
66
+ xmlElt = xmlElt.root
67
+ end
60
68
 
61
69
  #puts "decode() xmlElt.name is >#{xmlElt.name}<"
62
70
 
@@ -77,6 +85,8 @@ module OpenWFE
77
85
 
78
86
  return decode_inflowworkitem(xmlElt) \
79
87
  if xmlElt.name == IN_FLOW_WORKITEM
88
+ return decode_launchitem(xmlElt) \
89
+ if xmlElt.name == LAUNCHITEM
80
90
 
81
91
  return decode_list(xmlElt) if xmlElt.name == LAUNCHABLES
82
92
  return decode_launchable(xmlElt) if xmlElt.name == LAUNCHABLE
@@ -240,12 +250,12 @@ module OpenWFE
240
250
  return map
241
251
  end
242
252
 
243
- #puts xmldoc_to_string(xmlElt.document())
253
+ #puts OpenWFE.xmldoc_to_string(xmlElt.document())
244
254
 
245
255
  raise \
246
256
  ArgumentError, \
247
257
  "Cannot decode <#{xmlElt.name}/> in \n"+\
248
- xmldoc_to_string(xmlElt.document())
258
+ OpenWFE.xmldoc_to_string(xmlElt.document())
249
259
  end
250
260
 
251
261
  def decode_entry (xmlElt, map)
@@ -323,6 +333,20 @@ module OpenWFE
323
333
  end
324
334
 
325
335
 
336
+ def decode_launchitem (xmlElt)
337
+
338
+ li = LaunchItem.new()
339
+
340
+ li.workflow_definition_url =
341
+ xmlElt.attributes[WORKFLOW_DEFINITION_URL]
342
+
343
+ li.attributes =
344
+ decode(OpenWFE.first_element(xmlElt, ATTRIBUTES))
345
+
346
+ li
347
+ end
348
+
349
+
326
350
  #
327
351
  # ENCODE
328
352
  #
@@ -344,7 +368,7 @@ module OpenWFE
344
368
 
345
369
  doc = REXML::Document.new()
346
370
 
347
- root = REXML::Element.new(E_LAUNCHITEM)
371
+ root = REXML::Element.new(LAUNCHITEM)
348
372
 
349
373
  encode_item(launchitem, root)
350
374
 
@@ -550,15 +574,27 @@ module OpenWFE
550
574
  end
551
575
 
552
576
  XMLCODEC = XmlCodec.new
577
+ #
578
+ # some kind of a singleton, rather a lonelyton
553
579
 
580
+ #
581
+ # Decodes any XML blurb produced by OpenWFE[ja] into a Ruby instance.
582
+ #
554
583
  def OpenWFE.xml_decode (xmlelt)
555
584
  XMLCODEC.decode(xmlelt)
556
585
  end
557
586
 
587
+ #
588
+ # Encodes to XML (OpenWFE dialect ;) ) workitems and launch items.
589
+ #
558
590
  def OpenWFE.xml_encode (owfedata)
559
591
  XMLCODEC.encode(owfedata)
560
592
  end
561
593
 
594
+ #
595
+ # Just turns some XML to a String (if decl is set to false, no
596
+ # XML declaration will be printed).
597
+ #
562
598
  def OpenWFE.xmldoc_to_string (xml, decl=true)
563
599
 
564
600
  s = ""
@@ -86,20 +86,17 @@ module OpenWFE
86
86
  #
87
87
  def []= (fei, object)
88
88
  synchronize do
89
+
89
90
  fei_path = compute_file_path(fei)
90
91
 
91
92
  fei_parent_path = File.dirname(fei_path)
92
93
 
93
94
  FileUtils.makedirs(fei_parent_path) \
94
- if not File.exist?(fei_parent_path)
95
-
96
- fd = IO.sysopen(fei_path , "w+")
97
- io = IO.open(fd , "w+")
98
-
99
- data = YAML.dump(object)
100
-
101
- io.write(data)
102
- io.close
95
+ unless File.exist?(fei_parent_path)
96
+
97
+ File.open(fei_path, "w") do |file|
98
+ YAML.dump(object, file)
99
+ end
103
100
  end
104
101
  end
105
102
 
@@ -164,13 +161,14 @@ module OpenWFE
164
161
 
165
162
  def load_object (path)
166
163
 
167
- data = IO.read(path)
168
- object = YAML.load(data)
164
+ #data = IO.read(path)
165
+ #object = YAML.load(data)
166
+ object = YAML.load_file(path)
169
167
 
170
168
  object.application_context = @application_context \
171
169
  if object.respond_to? :application_context=
172
170
 
173
- return object
171
+ object
174
172
  end
175
173
 
176
174
  def count_objects
@@ -201,7 +199,6 @@ module OpenWFE
201
199
  # Passes each object to the given block
202
200
  #
203
201
  def each_object (&block)
204
- return unless block
205
202
  each_object_path do |path|
206
203
  block.call load_object(path)
207
204
  end
@@ -39,7 +39,8 @@
39
39
  # John Mettraux at openwfe.org
40
40
  #
41
41
 
42
- #require 'ru/flowexpression'
42
+ require 'openwfe/utils'
43
+ require 'openwfe/util/safe'
43
44
 
44
45
  #
45
46
  # 'dollar notation' implementation in Ruby
@@ -47,6 +48,16 @@
47
48
 
48
49
  module OpenWFE
49
50
 
51
+ SAFETY_LEVEL = 3
52
+ #
53
+ # Ruby code ${ruby:...} will be evaluated with this
54
+ # safety level.
55
+ # (see http://www.rubycentral.com/book/taint.html )
56
+
57
+ #
58
+ # Performs 'dollar substitution' on a piece of text with a given
59
+ # dictionary.
60
+ #
50
61
  def OpenWFE.dsub (text, dict)
51
62
 
52
63
  #puts "### text is >#{text}<"
@@ -107,6 +118,10 @@ module OpenWFE
107
118
  return text.gsub("\\\\\\$\\{", "\\${")
108
119
  end
109
120
 
121
+ #
122
+ # Performs 'dollar substitution' on a piece of text with as input
123
+ # a flow expression and a workitem (fields and variables).
124
+ #
110
125
  def OpenWFE.dosub (text, flow_expression, workitem)
111
126
  return dsub(text, FlowDict.new(flow_expression, workitem))
112
127
  end
@@ -179,6 +194,11 @@ module OpenWFE
179
194
 
180
195
  wi = @workitem
181
196
  workitem = @workitem
197
+
198
+ fexp = nil
199
+ flow_expression = nil
200
+ fei = nil
201
+
182
202
  if @flow_expression
183
203
  fexp = @flow_expression
184
204
  flow_expression = @flow_expression
@@ -189,7 +209,8 @@ module OpenWFE
189
209
  # notations
190
210
 
191
211
  #eval(ruby_code, binding).to_s
192
- eval(ruby_code).to_s
212
+ #eval(ruby_code).to_s
213
+ OpenWFE::eval_safely(ruby_code, SAFETY_LEVEL, binding()).to_s
193
214
  end
194
215
  end
195
216
 
@@ -51,7 +51,7 @@ module OpenWFE
51
51
  # Returns the current time as an ISO date string
52
52
  #
53
53
  def OpenWFE.now ()
54
- return to_iso_date(Time.new())
54
+ return to_iso8601_date(Time.new())
55
55
  end
56
56
 
57
57
  def OpenWFE.to_iso8601_date (date)
@@ -0,0 +1,149 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007, John Mettraux, OpenWFE.org
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # . Redistributions of source code must retain the above copyright notice, this
10
+ # list of conditions and the following disclaimer.
11
+ #
12
+ # . Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ #
16
+ # . Neither the name of the "OpenWFE" nor the names of its contributors may be
17
+ # used to endorse or promote products derived from this software without
18
+ # specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+ #++
32
+ #
33
+ # $Id: utils.rb 3454 2006-10-08 16:51:00Z jmettraux $
34
+ #
35
+
36
+ #
37
+ # "made in Japan"
38
+ #
39
+ # john.mettraux@openwfe.org
40
+ #
41
+
42
+ require 'tmpdir'
43
+ require 'open-uri'
44
+
45
+
46
+ module OpenWFE
47
+
48
+ #
49
+ # Runs some remote code (uri) at a different $SAFE level.
50
+ #
51
+ def OpenWFE.load_safely (uri, safe_level)
52
+
53
+ source = ""
54
+
55
+ source << "#\n"
56
+ source << "# loaded from #{uri}\n"
57
+ source << "#\n"
58
+
59
+ source << open(uri).read
60
+
61
+ load_eval_safely(source, safe_level)
62
+ end
63
+
64
+ #
65
+ # Makes sure that a piece of Ruby code is run at certain safe level.
66
+ # Saves in a temp file that is reloaded in its own anonymous namespace.
67
+ #
68
+ # (no binding passing allowed like in the basic Kernel.eval() method).
69
+ #
70
+ def OpenWFE.load_eval_safely (code, safe_level)
71
+
72
+ tmpfname =
73
+ "#{Dir.tmpdir}/"+
74
+ "owfe_#{code.object_id}_#{Time.new.to_f}.tmp.rb"
75
+
76
+ File.open tmpfname, "w" do |file|
77
+ file.print code
78
+ end
79
+
80
+ Thread.new do
81
+ $SAFE = safe_level
82
+ load(tmpfname, true)
83
+ end.join
84
+
85
+ begin
86
+ File.delete tmpfname
87
+ rescue Excpetion => e
88
+ # ignore
89
+ end
90
+ end
91
+
92
+ #
93
+ # Runs some code within an instance's realm at a certain safety level.
94
+ #
95
+ def OpenWFE.instance_eval_safely (instance, code, safe_level)
96
+
97
+ r = nil
98
+
99
+ Thread.new do
100
+ $SAFE = safe_level
101
+ r = instance.instance_eval(code)
102
+ end.join
103
+
104
+ r
105
+ end
106
+
107
+ #
108
+ # Runs an eval() call at a certain safety level.
109
+ #
110
+ def OpenWFE.eval_safely (code, safe_level, binding=nil)
111
+
112
+ code.untaint
113
+
114
+ r = nil
115
+
116
+ Thread.new do
117
+ $SAFE = safe_level
118
+ r = eval(code, binding)
119
+ end.join
120
+
121
+ r
122
+ end
123
+
124
+ #
125
+ # - not used currently -
126
+ #
127
+ # evals "requires" at the current safe level and the rest of the code
128
+ # at the requested safety level.
129
+ #
130
+ def OpenWFE.eval_r_safely (code, safe_level, binding=nil)
131
+
132
+ code.untaint
133
+
134
+ c = ""
135
+
136
+ code.split("\n").each do |line|
137
+ if line.match "^ *require " and not line.index(";")
138
+ eval(line, binding)
139
+ else
140
+ c << line
141
+ c << "\n"
142
+ end
143
+ end
144
+
145
+ eval_safely(c, safe_level, binding)
146
+ end
147
+
148
+ end
149
+