jsonnet 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Jsonnet
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -95,10 +95,45 @@ module Jsonnet
95
95
  # @yieldreturn [Array<String>] a pair of the content of the imported file and
96
96
  # its path.
97
97
  def handle_import
98
- self.import_callback = Proc.new
98
+ self.import_callback = to_method(Proc.new)
99
99
  nil
100
100
  end
101
101
 
102
+ ##
103
+ # Define a function (native extension) in the VM and let the given block
104
+ # handle the invocation of the function.
105
+ #
106
+ # @param name [Symbol|String] name of the function.
107
+ # Must be a valid identifier in Jsonnet.
108
+ # @param body [#to_proc] body of the function.
109
+ # @yield calls the given block instead of `body` if `body` is `nil`
110
+ #
111
+ # @note Currently it cannot define keyword or optional paramters in Jsonnet.
112
+ # Also all the positional optional parameters of the body are interpreted
113
+ # as required parameters. And the body cannot have keyword, rest or
114
+ # keyword rest paramters.
115
+ def define_function(name, body = nil)
116
+ body = body ? body.to_proc : Proc.new
117
+ params = body.parameters.map.with_index do |(type, name), i|
118
+ raise ArgumentError, "rest or keyword parameters are not allowed: #{type}" \
119
+ unless [:req, :opt].include? type
120
+
121
+ name || "p#{i}"
122
+ end
123
+
124
+ register_native_callback(name.to_sym, to_method(body), params);
125
+ end
126
+
127
+ private
128
+ # Wraps the function body with a method so that `break` and `return`
129
+ # behave like `return` as they do in a body of Module#define_method.
130
+ def to_method(body)
131
+ mod = Module.new {
132
+ define_method(:dummy, body)
133
+ }
134
+ mod.instance_method(:dummy).bind(body.binding.receiver)
135
+ end
136
+
102
137
  class UnsupportedOptionError < RuntimeError; end
103
138
  end
104
139
  end
@@ -0,0 +1,3 @@
1
+ {
2
+ a: 1,
3
+ }
@@ -110,13 +110,56 @@ class TestVM < Test::Unit::TestCase
110
110
  end
111
111
  end
112
112
 
113
- test "Jsonnet::VM#ext_var binds a variable" do
113
+ test "Jsonnet::VM#ext_var binds a variable to a string value" do
114
114
  vm = Jsonnet::VM.new
115
115
  vm.ext_var("var1", "foo")
116
116
  result = vm.evaluate('[std.extVar("var1")]')
117
117
  assert_equal JSON.parse('["foo"]'), JSON.parse(result)
118
118
  end
119
119
 
120
+ test "Jsonnet::VM#ext_code binds a variable to a code fragment" do
121
+ vm = Jsonnet::VM.new
122
+ vm.ext_code("var1", "{a:1}")
123
+ result = vm.evaluate('[std.extVar("var1")]')
124
+ assert_equal JSON.parse(<<-EOS), JSON.parse(result)
125
+ [
126
+ {
127
+ "a": 1
128
+ }
129
+ ]
130
+ EOS
131
+ end
132
+
133
+ test "Jsonnet::VM#tla_var binds a top-level variable to a string value" do
134
+ vm = Jsonnet::VM.new
135
+ vm.tla_var("var1", "foo")
136
+ result = vm.evaluate('function(var1) [var1, var1]')
137
+ assert_equal JSON.parse('["foo", "foo"]'), JSON.parse(result)
138
+ end
139
+
140
+ test "Jsonnet::VM#tla_var binds a top-level argument to a string value" do
141
+ vm = Jsonnet::VM.new
142
+ vm.tla_var("var1", "foo")
143
+ result = vm.evaluate('function(var1) [var1, var1]')
144
+ assert_equal JSON.parse('["foo", "foo"]'), JSON.parse(result)
145
+ end
146
+
147
+ test "Jsonnet::VM#tla_code binds a top-level argument to a code fragment" do
148
+ vm = Jsonnet::VM.new
149
+ vm.tla_code("var1", "{a:1}")
150
+ result = vm.evaluate('function(var1) [var1, var1]')
151
+ assert_equal JSON.parse(<<-EOS), JSON.parse(result)
152
+ [
153
+ {
154
+ "a": 1
155
+ },
156
+ {
157
+ "a": 1
158
+ }
159
+ ]
160
+ EOS
161
+ end
162
+
120
163
  test 'Jsonnet::VM#evaluate returns a JSON per filename on multi mode' do
121
164
  vm = Jsonnet::VM.new
122
165
  [
@@ -257,6 +300,180 @@ class TestVM < Test::Unit::TestCase
257
300
  assert_true called
258
301
  end
259
302
 
303
+ test "Jsonnet::VM#handle_import treats global escapes as define_method does" do
304
+ num_eval = 0
305
+ begin
306
+ bodies = [
307
+ proc {|rel, base| return 'null', '/x.libsonnet' },
308
+ lambda {|rel, base| return 'null', '/x.libsonnet' },
309
+ proc {|rel, base| next 'null', '/x.libsonnet' },
310
+ lambda {|rel, base| next 'null', '/x.libsonnet' },
311
+ proc {|rel, base| break 'null', '/x.libsonnet' },
312
+ lambda {|rel, base| break 'null', '/x.libsonnet' },
313
+ ]
314
+ bodies.each do |prc|
315
+ vm = Jsonnet::VM.new
316
+ vm.handle_import(&prc)
317
+
318
+ result = vm.evaluate('import "a.jsonnet"')
319
+ assert_nil JSON.load(result)
320
+
321
+ num_eval += 1
322
+ end
323
+ ensure
324
+ assert_equal bodies.size, num_eval
325
+ end
326
+ end
327
+
328
+ test "Jsonnet::VM#handle_import is safe on throw" do
329
+ [
330
+ proc {|rel, base| throw :dummy },
331
+ lambda {|rel, base| throw :dummy },
332
+ ].each do |prc|
333
+ vm = Jsonnet::VM.new
334
+ vm.handle_import(&prc)
335
+
336
+ catch(:dummy) {
337
+ vm.evaluate('import "a.jsonnet"')
338
+ flunk "never reach here"
339
+ }
340
+ end
341
+ end
342
+
343
+ test "Jsonnet::VM#jpath_add adds a library search path" do
344
+ vm = Jsonnet::VM.new
345
+ snippet = "(import 'jpath.libsonnet') {b: 2}"
346
+ assert_raise(Jsonnet::EvaluationError) {
347
+ vm.evaluate(snippet)
348
+ }
349
+
350
+ vm.jpath_add(File.join(__dir__, 'fixtures'))
351
+ result = vm.evaluate(snippet)
352
+ assert_equal JSON.parse(<<-EOS), JSON.parse(result)
353
+ {
354
+ "a": 1,
355
+ "b": 2
356
+ }
357
+ EOS
358
+ end
359
+
360
+ test "Jsonnet::VM#define_function adds a new native extension" do
361
+ vm = Jsonnet::VM.new
362
+ called = false
363
+
364
+ vm.define_function("myPow") do |x, y|
365
+ called = true
366
+ x ** y
367
+ end
368
+
369
+ result = vm.evaluate("std.native('myPow')(3, 4)")
370
+ assert_equal 3**4, JSON.load(result)
371
+ assert_true called
372
+ end
373
+
374
+ test "Jsonnet::VM#define_function passes various types of arguments" do
375
+ [
376
+ [%q(null), nil],
377
+ [%q("abc"), "abc"],
378
+ [%q(1), 1.0],
379
+ [%q(1.25), 1.25],
380
+ [%q(true), true],
381
+ [%q(false), false],
382
+ ].each do |expr, value|
383
+ vm = Jsonnet::VM.new
384
+ vm.define_function("myFunc") do |x|
385
+ assert_equal value, x
386
+ next nil
387
+ end
388
+ vm.evaluate("std.native('myFunc')(#{expr})")
389
+ end
390
+ end
391
+
392
+ test "Jsonnet::VM#define_function returns various types of values" do
393
+ [
394
+ [nil, nil],
395
+ ["abc", "abc"],
396
+ [1, 1.0],
397
+ [1.25, 1.25],
398
+ [true, true],
399
+ [false, false],
400
+ ].each do |retval, expected|
401
+ vm = Jsonnet::VM.new
402
+ vm.define_function("myFunc") { retval }
403
+
404
+ result = vm.evaluate("std.native('myFunc')()")
405
+ assert_equal expected, JSON.load(result)
406
+ end
407
+ end
408
+
409
+ test "Jsonnet::VM#define_function translates an exception in a native function into an error" do
410
+ vm = Jsonnet::VM.new
411
+ vm.define_function("myFunc") do |x|
412
+ raise "something wrong"
413
+ end
414
+ assert_raise(Jsonnet::EvaluationError) {
415
+ vm.evaluate("std.native('myFunc')(1)")
416
+ }
417
+ end
418
+
419
+ test "Jsonnet::VM#define_function let the function return a compound object" do
420
+ vm = Jsonnet::VM.new
421
+ vm.define_function("myCompound") do |x, y|
422
+ {
423
+ x => y,
424
+ y => [x, y, y, x],
425
+ }
426
+ end
427
+
428
+ result = vm.evaluate("std.native('myCompound')('abc', 'def')")
429
+ assert_equal JSON.parse(<<-EOS), JSON.parse(result)
430
+ {
431
+ "abc": "def",
432
+ "def": ["abc", "def", "def", "abc"]
433
+ }
434
+ EOS
435
+ end
436
+
437
+ test "Jsonnet::VM#define_function treats global escapes as define_method does" do
438
+ num_eval = 0
439
+ begin
440
+ bodies = [
441
+ proc {|x| return x },
442
+ lambda {|x| return x },
443
+ proc {|x| next x },
444
+ lambda {|x| next x },
445
+ proc {|x| break x },
446
+ lambda {|x| break x },
447
+ ]
448
+ bodies.each do |prc|
449
+ vm = Jsonnet::VM.new
450
+ vm.define_function(:myFunc, prc)
451
+
452
+ result = vm.evaluate('std.native("myFunc")(1.25) + 0.25')
453
+ assert_equal 1.25 + 0.25, JSON.load(result)
454
+
455
+ num_eval += 1
456
+ end
457
+ ensure
458
+ assert_equal bodies.size, num_eval
459
+ end
460
+ end
461
+
462
+ test "Jsonnet::VM#define_function is safe on throw" do
463
+ [
464
+ proc {|x| throw :dummy },
465
+ lambda {|x| throw :dummy },
466
+ ].each do |prc|
467
+ vm = Jsonnet::VM.new
468
+ vm.define_function(:myFunc, prc)
469
+
470
+ catch(:dummy) {
471
+ vm.evaluate('std.native("myFunc")(1.234)')
472
+ flunk "never reach here"
473
+ }
474
+ end
475
+ end
476
+
260
477
  private
261
478
  def with_example_file(content)
262
479
  Tempfile.open("example.jsonnet") {|f|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonnet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuki Yugui Sonoda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-12 00:00:00.000000000 Z
11
+ date: 2017-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mini_portile2
@@ -88,6 +88,7 @@ extensions:
88
88
  - ext/jsonnet/extconf.rb
89
89
  extra_rdoc_files: []
90
90
  files:
91
+ - ".clang-format"
91
92
  - ".gitignore"
92
93
  - ".travis.yml"
93
94
  - Gemfile
@@ -95,12 +96,18 @@ files:
95
96
  - README.md
96
97
  - Rakefile
97
98
  - ext/jsonnet/.gitignore
99
+ - ext/jsonnet/callbacks.c
98
100
  - ext/jsonnet/extconf.rb
101
+ - ext/jsonnet/helpers.c
99
102
  - ext/jsonnet/jsonnet.c
103
+ - ext/jsonnet/jsonnet_values.c
104
+ - ext/jsonnet/ruby_jsonnet.h
105
+ - ext/jsonnet/vm.c
100
106
  - jsonnet.gemspec
101
107
  - lib/jsonnet.rb
102
108
  - lib/jsonnet/version.rb
103
109
  - lib/jsonnet/vm.rb
110
+ - test/fixtures/jpath.libsonnet
104
111
  - test/test_jsonnet.rb
105
112
  - test/test_vm.rb
106
113
  homepage: ''
@@ -128,5 +135,6 @@ signing_key:
128
135
  specification_version: 4
129
136
  summary: Jsonnet library
130
137
  test_files:
138
+ - test/fixtures/jpath.libsonnet
131
139
  - test/test_jsonnet.rb
132
140
  - test/test_vm.rb