jsonnet 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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