lib/opal/context.rb in opal-0.3.11 vs lib/opal/context.rb in opal-0.3.15
- old
+ new
@@ -1,32 +1,40 @@
+require 'opal/environment'
+
module Opal
class Context
- # Options are mainly just passed onto the builder/parser.
- def initialize(options = {})
- @options = options
- @root_dir = options[:dir] || Dir.getwd
- @builder = Opal::Builder.new
- @loaded_paths = false
- setup_v8
- end
+ attr_reader :v8
+ attr_reader :parser
+ attr_reader :environment
##
- # Require the given id as if it was required in the context. This simply
- # passes the require through to the underlying context.
+ # Glob may be a file or glob path, as a string.
- def require_file(path)
- setup_v8
- @v8.eval "opal.run(function() {opal.require('#{path}');});", path
- finish
+ def self.runner(glob)
+ ctx = self.new
+ ctx.v8['opal_tmp_glob'] = Dir[glob]
+
+ runner = <<-CODE
+ files = `opal_tmp_glob`
+
+ files.each do |a|
+ require a
+ end
+ CODE
+
+ ctx.eval_irb runner, '(runner)'
+ ctx.finish
end
- # Set ARGV for the context.
- # @param [Array<String>] args
- def argv=(args)
- puts "setting argv to #{args.inspect}"
- @v8.eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});"
+ def initialize root = Dir.getwd
+ @environment = Environment.load root
+ @root = root
+ @parser = Opal::Parser.new :debug => true
+ @loaded_paths = false
+
+ setup_v8
end
# Start normal js repl
def start_repl
require 'readline'
@@ -40,33 +48,48 @@
# if we type exit, then we need to close down context
if line == "exit"
break
end
- puts "=> #{eval line, '(opal)'}"
+ puts "=> #{eval_irb line, '(irb)'}"
end
finish
end
- def eval(content, file = "(opal)", line = "")
- js = @builder.parse content, @options
- code = "opal.run(function() { var result = (#{js})(opal.runtime, "
- code += "opal.runtime.top, '(opal)'); if (result == null || !result"
- code += ".m$inspect) { return '(Object does not support #inspect)'; }"
- code += "else { return result.m$inspect() } });"
+ def eval_builder(content, file)
+ @parser.parse content, file
+ end
+ def eval(content, file = "(irb)", line = "")
+ @v8.eval eval_builder(content, file), file
+ end
+
+ def eval_irb(content, file = '(irb)')
+ code = <<-CODE
+ (function() { try {
+ opal.FILE = '#{file}';
+ var res = #{ eval_builder content, file };
+ return res.m$inspect();
+ }
+ catch (e) {
+ opal.bt(e);
+ return "nil";
+ }
+ })()
+ CODE
+
@v8.eval code, file
end
# Finishes the context, i.e. tidy everything up. This will cause
# the opal runtime to do it's at_exit() calls (if applicable) and
# then the v8 context will de removed. It can be reset by calling
# #setup_v8
def finish
return unless @v8
- @v8.eval "opal.runtime.do_at_exit()", "(opal)"
+ @v8.eval "opal.do_at_exit()", "(irb)"
@v8 = nil
end
# Setup the context. This basically loads opal.js into our context, and
@@ -81,20 +104,37 @@
abort "therubyracer is required for running javascript. Install it with `gem install therubyracer`"
end
@v8 = V8::Context.new
@v8['console'] = Console.new
+ @v8['opal_filesystem'] = FileSystem.new(self)
- @v8.eval File.read(OPAL_JS_PATH), "(opal)"
- opal = @v8['opal']
- opal['fs'] = FileSystem.new self
+ load_runtime
+ load_gem_runtime
+ end
- # FIXME: we cant use a ruby array as a js array :(
- opal['loader'] = Loader.new self, @v8.eval("[]")
+ ##
+ # Loads the runtime from build/*.js. This isnt included in the git repo,
+ # so needs to be built before running (gems should have these files included)
+
+ def load_runtime
+ @v8.eval Opal.runtime_debug_code, '(runtime)'
end
##
+ # Load gem specific runtime.
+
+ def load_gem_runtime
+ dir = File.join Opal.opal_dir, 'runtime', 'gemlib'
+ order = File.read(File.join dir, 'load_order').strip.split("\n")
+ order.each do |f|
+ path = File.join dir, "#{f}.rb"
+ eval File.read(path), path
+ end
+ end
+
+ ##
# Console class is used to mimic the console object in web browsers
# to allow simple debugging to the stdout.
class Console
def log(*str)
@@ -108,80 +148,101 @@
# version of opal. The methods on this class replace the default ones
# made available in the web browser.
class FileSystem
- def initialize(context)
- @context = context
+ def initialize context
+ @context = context
+ @environment = context.environment
+ @cache = {}
end
- def cwd
- Dir.getwd
- end
+ ##
+ # Used to bootstrap loadpaths.
- def glob(*arr)
- Dir.glob arr
- end
+ def find_paths
+ return @paths if @paths
- def exist_p(path)
- File.exist? path
- end
+ paths = [File.join(Opal.opal_dir, 'runtime', 'stdlib')]
- def expand_path(filename, dir_string = nil)
- File.expand_path filename, dir_string
- end
+ @environment.require_paths.each do |p|
+ paths << File.join(@environment.root, p)
+ end
- def dirname(file)
- File.dirname file
- end
+ @environment.specs.each do |spec|
+ paths.push *spec.load_paths
+ end
- def join(*parts)
- File.join *parts
+ @paths = @context.v8.eval paths.inspect
end
- end
- # Loader for v8 context
- class Loader
+ ##
+ # Require a file from context
- attr_reader :paths
+ def require path, paths
+ resolved = find_lib path, paths
- def initialize(context, paths)
- @context = context
- @paths = paths
- end
+ return nil unless resolved
- def resolve_lib(id)
- resolved = find_lib id
- raise "Cannot find lib `#{id}'" unless resolved
+ return false if @cache[resolved]
- resolved
+ @cache[resolved] = true
+ @context.v8.eval "opal.FILE = '#{resolved}'"
+ @context.eval File.read(resolved), resolved
+
+ true
end
- def find_lib(id)
- @paths.each do |path|
- candidate = File.join path, "#{id}.rb"
+ def find_lib path, paths
+ paths.each do |l|
+ candidate = File.join l, "#{path}.rb"
return candidate if File.exists? candidate
- candidate = File.join path, id
+ candidate = File.join l, path
return candidate if File.exists? candidate
- end
- return File.expand_path id if File.exists? id
- return File.expand_path(id + '.rb') if File.exists?(id + '.rb')
+ candidate = File.expand_path path
+ return candidate if File.exists? candidate
+ candidate = File.expand_path("#{path}.rb")
+ return candidate if File.exists? candidate
+ end
+
nil
end
- def ruby_file_contents(filename)
- Opal::Parser.new.parse File.read(filename)
+ ##
+ # Build body for given ruby file. Should return a function
+ # capable of being executed by opal of form:
+ #
+ # function(self, FILE) { ... }
+
+ def file_body(path)
+ @context.eval File.read(path), path
end
- def wrap(content, filename)
- code = "(function($rb, self, __FILE__) { #{content} });"
- @context.eval code, filename
- # code
+ def cwd
+ Dir.getwd
end
- end
+ def glob(*arr)
+ Dir.glob arr
+ end
+
+ def exist_p(path)
+ File.exist? path
+ end
+
+ def expand_path(filename, dir_string = nil)
+ File.expand_path filename, dir_string
+ end
+
+ def dirname(file)
+ File.dirname file
+ end
+
+ def join(*parts)
+ File.join *parts
+ end
+ end # FileSystem
end
end
-