lib/opal/context.rb in opal-0.3.9 vs lib/opal/context.rb in opal-0.3.10

- old
+ new

@@ -2,68 +2,60 @@ class Context def initialize(root_dir = Dir.getwd) @root_dir = root_dir @builder = Opal::Builder.new + @loaded_paths = false - @load_paths = resolve_load_paths + # special case: if we are running in opal root, then we dont want + # setup.rb to load the opal lib itself, so we do some "magic" + if @root_dir == OPAL_DIR + def self.setup_load_paths + return if @loaded_paths + Dir['packages/*/package.yml'].map do |package| + path = File.expand_path File.join(File.dirname(package), 'lib') + @v8.eval "opal.loader.paths.push('#{path}')" + end + end + end + + setup_v8 end + ## # Looks through vendor/ directory and adds all relevant load paths - def resolve_load_paths - Dir['vendor/*/package.yml'].map do |package| - File.expand_path File.join(File.dirname(package), 'lib') - end - end - # Setup the context. This basically loads opal.js into our context, and - # replace the loader etc with our custom loader for a Ruby environment. The - # default "browser" loader cannot access files from disk. - def setup_v8 - return if @v8 + def setup_load_paths + return if @loaded_paths - begin - require 'v8' - rescue LoadError => e - abort "therubyracer is required for running javascript. Install it with `gem install therubyracer`" - end + setup = File.join @root_dir, 'packages', 'init.rb' + return [] unless File.exists? setup - @v8 = V8::Context.new - @v8['console'] = Console.new - - eval @builder.build_core, '(opal)' - opal = @v8['opal'] - opal['fs'] = FileSystem.new self - - # FIXME: we cant use a ruby array as a js array :( - opal['loader'] = Loader.new self, eval("[]") - - @load_paths.each do |path| - eval "opal.loader.paths.push('#{path}')" - end - + @v8.eval "opal.run(function() {opal.require('#{setup}');});", setup end - def eval(code, file = nil) - @v8.eval code, file - end - + ## # Require the given id as if it was required in the context. This simply # passes the require through to the underlying context. + def require_file(path) setup_v8 - eval "opal.run(function() {opal.require('#{path}');});", path + @v8.eval "opal.run(function() {opal.require('#{path}');});", path finish end + ## # Set ARGV for the context + def argv=(args) puts "setting argv to #{args.inspect}" - eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});" + @v8.eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});" end + ## # Start normal js repl + def start_repl require 'readline' setup_v8 loop do @@ -74,52 +66,84 @@ # if we type exit, then we need to close down context if line == "exit" break end - puts "=> #{eval_ruby line, '(opal)'}" + puts "=> #{eval line, '(opal)'}" end finish end - def eval_ruby(content, line = "") + def eval(content, file = nil, line = "") begin - code = Opal::Parser.new(content).parse!.generate_top - code = "opal.run(function() {var $rb = opal.runtime, self = $rb.top, __FILE__ = '(opal)';" + code + "});" + js = @builder.parse content + code = "opal.run(function() { var $rb = opal.runtime, self = $rb.top" + code += ", __FILE__ = '(opal)'; return (#{js})($rb, self, __FILE__); })" # puts code - @v8['$opal_irb_result'] = eval code, line - eval "!($opal_irb_result == null || !$opal_irb_result.m$inspect) ? $opal_irb_result.m$inspect() : '(Object does not support #inspect)'" + @v8['$opal_irb_result'] = @v8.eval(code, file) + @v8.eval "!($opal_irb_result == null || !$opal_irb_result.m$inspect) ? $opal_irb_result.m$inspect() : '(Object does not support #inspect)'" rescue => e puts e puts("\t" + e.backtrace.join("\n\t")) end 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 - eval "opal.runtime.do_at_exit()", "(opal)" + @v8.eval "opal.runtime.do_at_exit()", "(opal)" @v8 = nil end + # Setup the context. This basically loads opal.js into our context, and + # replace the loader etc with our custom loader for a Ruby environment. The + # default "browser" loader cannot access files from disk. + def setup_v8 + return if @v8 + + begin + require 'v8' + rescue LoadError => e + abort "therubyracer is required for running javascript. Install it with `gem install therubyracer`" + end + + @v8 = V8::Context.new + @v8['console'] = Console.new + + @v8.eval @builder.build_core, '(opal)' + opal = @v8['opal'] + opal['fs'] = FileSystem.new self + + # FIXME: we cant use a ruby array as a js array :( + opal['loader'] = Loader.new self, @v8.eval("[]") + + setup_load_paths + 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) puts str.join("\n") nil end end + ## # FileSystem is used to interact with the file system from the ruby # 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 end @@ -180,10 +204,10 @@ nil end def ruby_file_contents(filename) - Opal::Parser.new(File.read(filename)).parse!.generate_top + Opal::Parser.new.parse File.read(filename) end def wrap(content, filename) code = "(function($rb, self, __FILE__) { #{content} });" @context.eval code, filename