bullring 0.7.4 → 0.8

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.
@@ -1,3 +1,3 @@
1
1
  module Bullring
2
- VERSION = "0.7.4"
2
+ VERSION = "0.8"
3
3
  end
@@ -1,36 +1,56 @@
1
1
  require 'bullring/util/exceptions'
2
2
 
3
- # TODO this class may have the common uglifier code, maybe some common
4
- # caching code
5
-
6
3
  module Bullring
7
4
  class Worker
8
5
 
9
- def add_library(name, script)
10
- raise AbstractMethodCalled
11
- end
6
+ attr_reader :libraries
12
7
 
13
- def add_library_file(name, filename)
14
- raise AbstractMethodCalled
8
+ def initialize
9
+ @libraries = {}
10
+ end
11
+
12
+ def add_library(name, script)
13
+ Bullring.logger.debug { "Bullring: Adding library named '#{name}'" }
14
+ @libraries[name] = script
15
+ _add_library(name, script)
15
16
  end
16
17
 
17
18
  def check(script, options)
18
- raise AbstractMethodCalled
19
+ Bullring.logger.debug { "Bullring: Checking script with hash '#{script.hash}'" }
20
+ _check(script, options)
19
21
  end
20
22
 
21
23
  def run(script, options)
22
- raise AbstractMethodCalled
24
+ Bullring.logger.debug { "Bullring: Running script with hash '#{script.hash}'" }
25
+ _run(script, options)
23
26
  end
24
27
 
25
28
  def alive?
26
- raise AbstractMethodCalled
29
+ _alive?
27
30
  end
28
31
 
29
32
  def discard
30
- raise AbstractMethodCalled
33
+ Bullring.logger.debug { "Bullring: Attempting to discard." }
34
+ _discard
31
35
  end
32
36
 
37
+ def refresh
38
+ Bullring.logger.debug { "Bullring: Attempting to refresh." }
39
+ _refresh
40
+ end
41
+
42
+ protected
43
+
44
+ # Quasi-template method pattern
45
+ def _add_library(name, script); end
46
+ def _check(script, options); raise AbstractMethodCalled; end
47
+ def _run(script, options); raise AbstractMethodCalled; end
48
+ def _alive?; raise AbstractMethodCalled; end
49
+ def _discard; raise AbstractMethodCalled; end
50
+
33
51
  end
34
52
  end
35
53
 
36
- require 'bullring/workers/rhino_server_worker'
54
+ require 'bullring/workers/rhino_server_worker'
55
+ require 'bullring/workers/racer_worker'
56
+
@@ -0,0 +1,66 @@
1
+
2
+
3
+ module Bullring
4
+
5
+ class JSError < StandardError
6
+ def initialize(msg = nil)
7
+ @hash = {}
8
+ super(msg)
9
+ end
10
+
11
+ def []=(key, value)
12
+ @hash[key] = value
13
+ end
14
+
15
+ def [](key)
16
+ @hash[key]
17
+ end
18
+
19
+ attr_reader :hash
20
+ end
21
+
22
+ class Helper
23
+ def self.jslint_call(script)
24
+ jslintCall = <<-RACER_CALL
25
+ JSLINT("#{prepare_source(script)}", {devel: false,
26
+ bitwise: true,
27
+ undef: true,
28
+ continue: true,
29
+ unparam: true,
30
+ debug: true,
31
+ sloppy: true,
32
+ eqeq: true,
33
+ sub: true,
34
+ es5: true,
35
+ vars: true,
36
+ evil: true,
37
+ white: true,
38
+ forin: true,
39
+ passfail: false,
40
+ newcap: true,
41
+ nomen: true,
42
+ plusplus: true,
43
+ regexp: true,
44
+ maxerr: 50,
45
+ indent: 4}); JSLINT.errors
46
+ RACER_CALL
47
+ end
48
+
49
+ ESCAPE_MAP = {
50
+ '\\' => '\\\\',
51
+ "\r\n" => '\n',
52
+ "\n" => '\n',
53
+ "\r" => '\n',
54
+ '"' => '\"',
55
+ "'" => '\''
56
+ }
57
+
58
+ def self.prepare_source(source)
59
+ # escape javascript characters (similar to Rails escape_javascript)
60
+ source.gsub!(/(\\|\r\n|[\n\r"'])/u) {|match| ESCAPE_MAP[match] }
61
+ source
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,77 @@
1
+ module Bullring
2
+
3
+ class RacerWorker < Bullring::Worker
4
+
5
+ def initialize
6
+ super
7
+ end
8
+
9
+ def check(script, options)
10
+ context = V8::Context.new
11
+
12
+ context_wrapper {context.load(File.expand_path("../../js/jslint.min.js", __FILE__))}
13
+
14
+ call = Bullring::Helper::jslint_call(script)
15
+ duration, result = context_wrapper {context.eval(call)}
16
+
17
+ result = result.collect{|obj| obj.respond_to?(:to_h) ? obj.to_h : obj}
18
+ end
19
+
20
+ def run(script, options)
21
+ context = V8::Context.new
22
+
23
+ (options['library_names'] || []).each do |library_name|
24
+ library = libraries[library_name]
25
+ context_wrapper {context.eval(library)}
26
+ end
27
+
28
+ # context.timeout = Bullring.configuration.execution_timeout_secs * 1000
29
+
30
+ duration, result = context_wrapper {context.eval(script)}
31
+ result = result.respond_to?(:to_h) ? result.to_h : result
32
+ end
33
+
34
+ def alive?
35
+ true
36
+ end
37
+
38
+ def _discard; end
39
+
40
+ protected
41
+
42
+ def context_wrapper
43
+ begin
44
+
45
+
46
+ start_time = Time.now
47
+ result = yield
48
+ duration = Time.now - start_time
49
+
50
+ Bullring.logger.debug {"#{logname}: Ran script (#{duration} secs); result: " + result.inspect}
51
+
52
+ return duration, result
53
+
54
+ rescue V8::JSError => e
55
+ Bullring.logger.debug {"#{logname}: JSError! Cause: " + e.cause + "; Message: " + e.message}
56
+ raise Bullring::JSError, e.message.to_s, caller
57
+ # rescue Rhino::RunawayScriptError, Rhino::ScriptTimeoutError => e
58
+ # logger.debug {"#{logname}: Runaway Script: " + e.inspect}
59
+ # raise Bullring::JSError, "Script took too long to run", caller
60
+ # rescue Timeout::Error => e
61
+ # Bullring.logger.error {"#{logname}: Runaway script: #{e.inspect}"}
62
+ rescue NameError => e
63
+ Bullring.logger.debug {"#{logname}: Name error: " + e.inspect}
64
+ rescue StandardError => e
65
+ Bullring.logger.debug {"#{logname}: StandardError: " + e.inspect}
66
+ raise
67
+ end
68
+ end
69
+
70
+ def logname; "Bullring (Racer)"; end
71
+
72
+ end
73
+
74
+
75
+
76
+ end
77
+
@@ -2,46 +2,54 @@ require 'rhino'
2
2
  require 'drb'
3
3
  require 'logger'
4
4
 
5
- module Bullring
6
-
7
- class DummyLogger
8
- def method_missing(m, *args, &block)
9
- # ignore
5
+ unless Kernel.respond_to?(:require_relative)
6
+ module Kernel
7
+ def require_relative(path)
8
+ require File.join(File.dirname(caller[0]), path.to_str)
10
9
  end
11
10
  end
11
+ end
12
12
 
13
- class RhinoServer
14
-
15
- @@dummy_logger = Bullring::DummyLogger.new
16
-
17
- def initialize
18
- @library_scripts = {}
19
- @setup_providers = []
20
- configure
21
- logger.info {"#{logname}: Started a RhinoServer instance at #{Time.now}"}
22
- end
23
-
24
- def configure(options={})
25
- @options ||= { :run_is_sealed => false,
26
- :run_is_restrictable => true,
27
- :run_timeout_secs => 0.5 }
13
+ require_relative 'common'
14
+ require_relative '../util/dummy_logger'
15
+ require_relative '../util/server_registry'
28
16
 
29
- # Don't do a merge b/c jruby and ruby don't play nicely for some reason
30
- options.each{|k,v| @options[k] = v}
31
- end
32
-
33
- def load_setup(setup_provider)
34
- # Get the libraries from the setup provider and add them to our local list.
35
- # Hopefully, by calling 'to_s' we are getting copies that live only on our
36
- # side of DRb. Store the provider so we can go back to it later if we
37
- # find that we don't have a required library. Use a list of providers b/c
38
- # some providers may have died off.
17
+ module Bullring
18
+
19
+ class RhinoServer
20
+
21
+ def initialize(host, registry_port)
22
+ @library_cache = {}
39
23
 
40
- setup_provider.libraries.each do |name, script|
41
- add_library(name.to_s, library.to_s)
24
+ @default_options = { :run_is_sealed => false,
25
+ :run_is_restrictable => true,
26
+ :run_timeout_secs => 0.5 }
27
+
28
+ # Connect to the server registry
29
+ @server_registry = ServerRegistry.new(host,registry_port, nil)
30
+
31
+ # Start up as a DRb server (get the port from the registry)
32
+ port = @server_registry.next_server_port
33
+ uri = "druby://#{host}:#{port}"
34
+ DRb.start_service uri, self
35
+
36
+ # Put ourselves on the registry
37
+ @server_registry.register_server(uri)
38
+
39
+ # Keep an eye on the registry, if it dies, we should die
40
+ Thread.new do
41
+ while (true) do
42
+ sleep(5)
43
+ begin
44
+ @server_registry.test!
45
+ rescue
46
+ DRb.stop_service
47
+ Thread.main.exit
48
+ end
49
+ end
42
50
  end
43
51
 
44
- @setup_providers.push(setup_provider)
52
+ DRb.thread.join
45
53
  end
46
54
 
47
55
  def logger=(logger)
@@ -49,62 +57,37 @@ module Bullring
49
57
  end
50
58
 
51
59
  def logger
52
- @logger || @@dummy_logger
60
+ @logger ||= Bullring::DummyLogger.new
53
61
  end
54
-
55
- def add_library(name, script)
56
- @library_scripts[name] = script
57
- end
58
-
59
- def add_library_file(name, filename)
60
- raise NotYetImplemented
61
- script = read file into string
62
- @library_scripts[name] = script
62
+
63
+ def get_library(name)
64
+ @library_cache[name] ||= fetch_library(name)
63
65
  end
64
66
 
65
67
  def check(script, options)
66
68
  Rhino::Context.open do |context|
67
69
  context_wrapper {context.load(File.expand_path("../../js/jslint.min.js", __FILE__))}
68
70
 
69
- jslintCall = <<-RHINO_CALL
70
- JSLINT("#{prepare_source(script)}", {devel: false,
71
- bitwise: true,
72
- undef: true,
73
- continue: true,
74
- unparam: true,
75
- debug: true,
76
- sloppy: true,
77
- eqeq: true,
78
- sub: true,
79
- es5: true,
80
- vars: true,
81
- evil: true,
82
- white: true,
83
- forin: true,
84
- passfail: false,
85
- newcap: true,
86
- nomen: true,
87
- plusplus: true,
88
- regexp: true,
89
- maxerr: 50,
90
- indent: 4});
91
- RHINO_CALL
71
+ call = Bullring::Helper::jslint_call(script)
92
72
 
93
- duration, result = context_wrapper {context.eval(jslintCall + "JSLINT.errors")}
73
+ duration, result = context_wrapper {context.eval(call)}
94
74
 
95
75
  result = result.collect{|obj| obj.respond_to?(:to_h) ? obj.to_h : obj}
96
76
  end
97
77
  end
98
78
 
99
79
  def run(script, options)
100
- Rhino::Context.open(:sealed => @options[:run_is_sealed], :restrictable => @options[:run_is_restrictable]) do |context|
80
+ # Don't do a merge b/c jruby and ruby don't play nicely for some reason
81
+ @default_options.each{|k,v| options[k] = v}
82
+
83
+ Rhino::Context.open(:sealed => options[:run_is_sealed], :restrictable => options[:run_is_restrictable]) do |context|
101
84
 
102
85
  (options['library_names'] || []).each do |library_name|
103
- library_script = @library_scripts[library_name] || fetch_library_script!(library_name)
86
+ library_script = get_library(library_name)
104
87
  context_wrapper {context.eval(library_script)}
105
88
  end
106
89
 
107
- context.timeout_limit = @options[:run_timeout_secs]
90
+ context.timeout_limit = options[:run_timeout_secs]
108
91
 
109
92
  duration, result = context_wrapper {context.eval(script)}
110
93
  result.respond_to?(:to_h) ? result.to_h : result
@@ -120,12 +103,11 @@ module Bullring
120
103
  exit
121
104
  end
122
105
 
123
- def self.start(myPort, clientPort)
124
- DRb.start_service "druby://127.0.0.1:#{myPort}", Bullring::RhinoServer.new
125
- DRb.thread.join
106
+ def self.start(host, registry_port)
107
+ RhinoServer.new(host, registry_port)
126
108
  end
127
109
 
128
- protected
110
+ protected
129
111
 
130
112
  def context_wrapper
131
113
  begin
@@ -137,11 +119,20 @@ module Bullring
137
119
 
138
120
  return duration, result
139
121
  rescue Rhino::JSError => e
140
- logger.debug {"#{logname}: JSError! Cause: " + e.cause + "; Message: " + e.message}
141
- raise Bullring::JSError, e.message.to_s, caller
122
+ logger.debug {"#{logname}: JSError! #{e.inspect}"}
123
+
124
+ error = Bullring::JSError.new(e.message.to_s)
125
+
126
+ if e.message.respond_to?(:keys)
127
+ e.message.each do |k,v|
128
+ error[k.to_s] = v.to_s
129
+ end
130
+ end
131
+
132
+ raise error
142
133
  rescue Rhino::RunawayScriptError, Rhino::ScriptTimeoutError => e
143
134
  logger.debug {"#{logname}: Runaway Script: " + e.inspect}
144
- raise Bullring::JSError, "Script took too long to run", caller
135
+ raise Bullring::JSError, "Script took too long to run"
145
136
  rescue NameError => e
146
137
  logger.debug {"#{logname}: Name error: " + e.inspect}
147
138
  rescue StandardError => e
@@ -149,53 +140,23 @@ module Bullring
149
140
  raise
150
141
  end
151
142
  end
152
-
153
- ESCAPE_MAP = {
154
- '\\' => '\\\\',
155
- "\r\n" => '\n',
156
- "\n" => '\n',
157
- "\r" => '\n',
158
- '"' => '\"',
159
- "'" => '\''
160
- }
161
-
162
- def prepare_source(source)
163
- # escape javascript characters (similar to Rails escape_javascript)
164
- source.gsub!(/(\\|\r\n|[\n\r"'])/u) {|match| ESCAPE_MAP[match] }
165
- source
166
- end
167
143
 
168
- # Goes back to the setup provider to the get the named script or throws an
169
- # exception if there is no such script to retrieve.
170
- def fetch_library_script!(name)
171
- logger.debug {"#{logname}: The script named #{name} was not available so trying to fetch from clients"}
172
-
173
- while (provider = @setup_providers.last)
174
- begin
175
- library_script = provider.libraries[name]
176
- break if !library_script.nil?
177
- rescue DRb::DRbConnError => e
178
- logger.debug {"#{logname}: Could not connect to setup provider (its process probably died): " + e.inspect}
179
- rescue StandardError => e
180
- logger.error {"#{logname}: Encountered an unknown error searching setup providers for a script named #{name}: " + e.inspect}
181
- ensure
182
- # Toss the last element so we can continue searching prior elements
183
- setup_providers.pop
184
- end
185
- end
186
-
187
- # If after looking through the providers we are still empty handed, raise an error
188
- raise NameError, "Client doesn't have script named #{name}", caller if library_script.nil?
144
+ # Grab the library from the registry server
145
+ def fetch_library(name)
146
+ library_script = @server_registry['library', name]
189
147
 
190
- add_library(name, library_script)
148
+ logger.debug {"#{logname}: Tried to fetch script '#{name}' from the registry and it " +
149
+ "was #{'not ' if library_script.nil?}found."}
150
+
151
+ raise NameError, "Server cannot find a script named #{name}" if library_script.nil?
152
+
153
+ library_script
191
154
  end
192
155
 
193
- def logname; "Bullring Server"; end
156
+ def logname; "Bullring (Rhino Server)"; end
194
157
 
195
158
  end
196
159
 
197
- class JSError < StandardError; end
198
-
199
160
  end
200
161
 
201
162
  #
@@ -203,11 +164,11 @@ end
203
164
  #
204
165
 
205
166
  command = ARGV[0]
206
- myPort = ARGV[1]
207
- clientPort = ARGV[2]
167
+ host = ARGV[1]
168
+ registry_port = ARGV[2]
208
169
 
209
170
  case command
210
171
  when "start"
211
- Bullring::RhinoServer.start(myPort, clientPort)
172
+ Bullring::RhinoServer.start(host, registry_port)
212
173
  end
213
174