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 -