Search code examples
rubyexecjs

ExecJS: keeping the context between two calls


I'm currently trying to use ExecJS to run Handlebars for one of the product I work on (note: I know the handlebars.rb gem which is really cool and I used it for some times but there is issues to get it installed on Windows, so I try another homemade solution).

One of the problem I'm having is that the Javascript context is not kept between each "call" to ExecJS.

Here the code where I instantiate the @js attribute:

class Context
  attr_reader :js, :partials, :helpers

  def initialize
    src = File.open(::Handlebars::Source.bundled_path, 'r').read
    @js = ExecJS.compile(src)
  end
end

And here's a test showing the issue:

let(:ctx) { Hiptest::Handlebars::Context.new }

it "does not keep context properly (or I'm using the tool wrong" do
  ctx.js.eval('my_variable = 42')
  expect(ctx.js.eval('my_variable')).to eq(42)
end

And now when I run it:

rspec spec/handlebars_spec.rb:10                                                                   1 ↵
I, [2015-02-21T16:57:30.485774 #35939]  INFO -- : Not reporting to Code Climate because ENV['CODECLIMATE_REPO_TOKEN'] is not set.
Run options: include {:locations=>{"./spec/handlebars_spec.rb"=>[10]}}
F

Failures:

  1) Hiptest::Handlebars Context does not keep context properly (or I'm using the tool wrong
     Failure/Error: expect(ctx.js.eval('my_variable')).to eq(42)
     ExecJS::ProgramError:
       ReferenceError: Can't find variable: my_variable

Note: I got the same issue with "exec" instead of "eval".

That is a silly example. What I really want to do it to run "Handlebars.registerPartial" and later on "Handlebars.compile". But when trying to use the partials in the template it fails because the one registered previously is lost.

Note that I've found a workaround but I find it pretty ugly :/

  def register_partial(name, content)
    @partials[name] = content
  end

  def call(*args)
    @context.js.call([
      "(function (partials, helpers, tmpl, args) {",
      "  Object.keys(partials).forEach(function (key) {",
      "    Handlebars.registerPartial(key, partials[key]);",
      "  })",
      "  return Handlebars.compile(tmpl).apply(null, args);",
      "})"].join("\n"), @partials, @template, args)
  end

Any idea on how to fix the issue ?


Solution

  • Only the context you create when you call ExecJS.compile is preserved between evals. Anything you want preserved needs to be part of the initial compile.