jsonnet 0.1.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a215d91a36c8af2f5ad4d145f571fbbc070cedb7
4
- data.tar.gz: 4e5b93b2fc44a5c33bb1fb27830a33ee810b4e8f
2
+ SHA256:
3
+ metadata.gz: 2872d26e221e6ec8bbd30265edd0109eb9f1c68fa19fcfaa888c9921f096f771
4
+ data.tar.gz: b91f326aa5a64a97f9f088a17b6462e8cf77d8e82679c11231cc68eff2b495e5
5
5
  SHA512:
6
- metadata.gz: 84a38e5e7e776ac8d11505693adedd2fd05dfe0d01146e5e242241f2a82f9d2b849983d15bb1909cd24fcfe6404aee0eb87647074a34893204ae522fee0c14bc
7
- data.tar.gz: 1d44661d345335978fe2f67fec727c06aa3888b08b8418f570b7b27226b57e6c9501d4cc93fb51b312a861cbe2c6ba515793031c34cfec92468f67b319cfa8bb
6
+ metadata.gz: dab85234e40a4b05ae60a4fe7423af108df27b9e3d2db119e6d18edb40f9abbb92c20a5cf7c25fb5d20e3a090031fae42ec93fab135ea3c6682adaf3ec006693
7
+ data.tar.gz: 8aea3bbab4b7661a4ee6d055bd6fdd9b5c0834feae98f266b5d162563b860b932689f4fcd4096a3f2dcdf12fb5e960b2fbb7f7e65ce23d1d177d5f85128dbb56
@@ -0,0 +1,11 @@
1
+ ---
2
+ BasedOnStyle: Google
3
+ IndentWidth: 4
4
+ ColumnLimit: 100
5
+ ---
6
+ Language: Cpp
7
+ PointerAlignment: Right
8
+ AlwaysBreakAfterReturnType: AllDefinitions
9
+ BreakBeforeBraces: Linux
10
+ UseTab: Always
11
+ TabWidth: 8
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ dist: xenial
3
+ script: bundle exec rake test
4
+
5
+ matrix:
6
+ include:
7
+ - os: linux
8
+ rvm: 2.5.8
9
+ - os: osx
10
+ rvm: 2.5.8
11
+ - os: linux
12
+ rvm: 2.6.6
13
+ - os: osx
14
+ rvm: 2.6.6
15
+ - os: linux
16
+ rvm: 2.7.2
17
+ - os: osx
18
+ rvm: 2.7.2
19
+ fast_finish: true
data/README.md CHANGED
@@ -1,17 +1,11 @@
1
+ [![Build Status](https://travis-ci.org/yugui/ruby-jsonnet.svg?branch=master)](https://travis-ci.org/yugui/ruby-jsonnet)
2
+
1
3
  # Jsonnet
2
4
 
3
- Jsonnet processor library. Wraps the official C++ implementation with a Ruby extention library.
5
+ [Jsonnet][] processor library. Wraps the official C++ implementation with a Ruby extension library.
4
6
 
5
7
  ## Installation
6
8
 
7
- Install libjsonnet:
8
-
9
- $ git clone https://github.com/google/jsonnet.git
10
- $ cd jsonnet
11
- $ make
12
- $ sudo cp libjsonnet.so /usr/local/lib/libjsonnet.so
13
- $ sudo cp libjsonnet.h /usr/local/include/libjsonnet.h
14
-
15
9
  Add this line to your application's Gemfile:
16
10
 
17
11
  ```ruby
@@ -20,20 +14,85 @@ gem 'jsonnet'
20
14
 
21
15
  And then execute:
22
16
 
23
- $ bundle
17
+ ```shell
18
+ $ bundle install
19
+ ```
24
20
 
25
21
  Or install it yourself as:
26
22
 
27
- $ gem install jsonnet
23
+ ```shell
24
+ $ gem install jsonnet
25
+ ```
26
+
27
+ By default this gem will compile and install Jsonnet (v0.16.0) as part of
28
+ installation. However you can use the system version of Jsonnet if you prefer.
29
+ This would be the recommended route if you want to use a different version
30
+ of Jsonnet or are having problems installing this.
31
+
32
+ To install libjsonnet:
33
+
34
+ ```shell
35
+ $ git clone https://github.com/google/jsonnet.git
36
+ $ cd jsonnet
37
+ $ make libjsonnet.so
38
+ $ sudo cp libjsonnet.so /usr/local/lib/libjsonnet.so
39
+ $ sudo cp include/libjsonnet.h /usr/local/include/libjsonnet.h
40
+ ```
41
+
42
+ Note: /usr/local/lib and /usr/local/include are used as they are library lookup
43
+ locations. You may need to adjust these for your system if you have errors
44
+ running this gem saying it can't open libjsonnet.so - on Ubuntu for instance
45
+ I found /lib worked when /usr/local/lib did not.
46
+
47
+ To install this gem without jsonnet:
48
+
49
+ Use `JSONNET_USE_SYSTEM_LIBRARIES` ENV var:
50
+
51
+ ```shell
52
+ $ JSONNET_USE_SYSTEM_LIBRARIES=1 bundle install
53
+ ```
54
+
55
+ or, the `--use-system-libraries` option:
56
+
57
+
58
+ ```shell
59
+ gem install jsonnet -- --use-system-libraries
60
+ ```
28
61
 
29
62
  ## Usage
30
63
 
31
- TODO: Write usage instructions here
64
+ Load the library with `require "jsonnet"`
65
+
66
+ You can evaluate a string of Jsonnet using `Jsonnet.parse`
67
+
68
+ ```
69
+ irb(main):002:0> Jsonnet.evaluate('{ foo: "bar" }')
70
+ => {"foo"=>"bar"}
71
+ ```
72
+ Or load a file using `Jsonnet.load`
73
+
74
+ ```
75
+ irb(main):002:0> Jsonnet.load('example.jsonnet')
76
+ => {"baz"=>1}
77
+ ```
78
+
79
+ To get closer to the C++ interface you can create an instance of `Jsonnet::VM`
80
+
81
+ ```
82
+ irb(main):002:0> vm = Jsonnet::VM.new
83
+ => #<Jsonnet::VM:0x007fd29aa1e568>
84
+ irb(main):003:0> vm.evaluate('{ foo: "bar" }')
85
+ => "{\n \"foo\": \"bar\"\n}\n"
86
+ irb(main):004:0> vm.evaluate_file('example.jsonnet')
87
+ => "{\n \"baz\": 1\n}\n"
88
+ ```
32
89
 
33
90
  ## Contributing
34
91
 
35
- 1. Fork it ( https://github.com/yugui/jsonnet/fork )
92
+ 1. Fork it ( https://github.com/yugui/ruby-jsonnet/fork )
36
93
  2. Create your feature branch (`git checkout -b my-new-feature`)
37
94
  3. Commit your changes (`git commit -am 'Add some feature'`)
38
95
  4. Push to the branch (`git push origin my-new-feature`)
39
96
  5. Create a new Pull Request
97
+
98
+ [Jsonnet]: https://github.com/google/jsonnet
@@ -0,0 +1,241 @@
1
+ #include <string.h>
2
+
3
+ #include <libjsonnet.h>
4
+ #include <ruby/ruby.h>
5
+
6
+ #include "ruby_jsonnet.h"
7
+
8
+ /* Magic prefix which distinguishes Ruby-level non-exception global escapes
9
+ * from other errors in Jsonnet.
10
+ * Any other errors in Jsonnet evaluation cannot contain this fragment of message.
11
+ */
12
+ #define RUBYJSONNET_GLOBAL_ESCAPE_MAGIC "\x07\x03\x0c:rubytag:\x07\x03\x0c:"
13
+
14
+ /*
15
+ * callback support in VM
16
+ */
17
+
18
+ static ID id_call;
19
+
20
+ /*
21
+ * Just invokes a callback with arguments, but also adapts the invocation to rb_protect.
22
+ * @param[in] args list of arguments whose first value is the callable object itself.
23
+ * @return result of the callback
24
+ */
25
+ static VALUE
26
+ invoke_callback(VALUE args)
27
+ {
28
+ long len = RARRAY_LEN(args);
29
+ VALUE callback = rb_ary_entry(args, 0);
30
+ return rb_funcall2(callback, id_call, len - 1, RARRAY_PTR(args) + 1);
31
+ }
32
+
33
+ /*
34
+ * Handles long jump caught by rb_protect() in a callback function.
35
+ *
36
+ * Returns an error message which represents rb_errinfo().
37
+ * It is the caller's responsibility to return the message to the Jsonnet VM
38
+ * in the right way.
39
+ * Also the caller of the VM must handle evaluation failure caused by the
40
+ * error message.
41
+ * \sa rubyjsonnet_jump_tag
42
+ * \sa raise_eval_error
43
+ */
44
+ static VALUE
45
+ rescue_callback(int state, const char *fmt, ...)
46
+ {
47
+ VALUE err = rb_errinfo();
48
+ if (rb_obj_is_kind_of(err, rb_eException)) {
49
+ VALUE msg = rb_protect(rubyjsonnet_format_exception, rb_errinfo(), NULL);
50
+ if (msg == Qnil) {
51
+ va_list ap;
52
+ va_start(ap, fmt);
53
+ msg = rb_vsprintf(fmt, ap);
54
+ va_end(ap);
55
+ }
56
+ rb_set_errinfo(Qnil);
57
+ return msg;
58
+ }
59
+
60
+ /*
61
+ * Other types of global escape.
62
+ * Here, we'll report the state as an evaluation error to let
63
+ * the Jsonnet VM clean up its internal resources.
64
+ * But we'll translate the error into an non-exception global escape
65
+ * in Ruby again in raise_eval_error().
66
+ */
67
+ return rb_sprintf("%s%d%s", RUBYJSONNET_GLOBAL_ESCAPE_MAGIC, state,
68
+ RUBYJSONNET_GLOBAL_ESCAPE_MAGIC);
69
+ }
70
+
71
+ /*
72
+ * Tries to extract a jump tag from a string representation encoded by
73
+ * rescue_callback.
74
+ * @retval zero if \a exc_mesg is not such a string representation.
75
+ * @retval non-zero the extracted tag
76
+ */
77
+ int
78
+ rubyjsonnet_jump_tag(const char *exc_mesg)
79
+ {
80
+ const char *tag;
81
+ #define JSONNET_RUNTIME_ERROR_PREFIX "RUNTIME ERROR: "
82
+ if (strncmp(exc_mesg, JSONNET_RUNTIME_ERROR_PREFIX, strlen(JSONNET_RUNTIME_ERROR_PREFIX))) {
83
+ return 0;
84
+ }
85
+ tag = strstr(exc_mesg + strlen(JSONNET_RUNTIME_ERROR_PREFIX), RUBYJSONNET_GLOBAL_ESCAPE_MAGIC);
86
+ if (tag) {
87
+ const char *const body = tag + strlen(RUBYJSONNET_GLOBAL_ESCAPE_MAGIC);
88
+ char *last;
89
+ long state = strtol(body, &last, 10);
90
+ if (!strncmp(last, RUBYJSONNET_GLOBAL_ESCAPE_MAGIC,
91
+ strlen(RUBYJSONNET_GLOBAL_ESCAPE_MAGIC)) &&
92
+ INT_MIN <= state && state <= INT_MAX) {
93
+ return (int)state;
94
+ }
95
+ }
96
+ return 0;
97
+ }
98
+
99
+ static char *
100
+ import_callback_entrypoint(void *ctx, const char *base, const char *rel, char **found_here,
101
+ int *success)
102
+ {
103
+ struct jsonnet_vm_wrap *const vm = (struct jsonnet_vm_wrap *)ctx;
104
+ int state;
105
+ VALUE result, args;
106
+
107
+ args = rb_ary_tmp_new(3);
108
+
109
+ rb_ary_push(args, vm->import_callback);
110
+ rb_ary_push(args, rb_enc_str_new_cstr(base, rb_filesystem_encoding()));
111
+ rb_ary_push(args, rb_enc_str_new_cstr(rel, rb_filesystem_encoding()));
112
+ result = rb_protect(invoke_callback, args, &state);
113
+
114
+ rb_ary_free(args);
115
+
116
+ if (state) {
117
+ VALUE msg = rescue_callback(state, "cannot import %s from %s", rel, base);
118
+ *success = 0;
119
+ return rubyjsonnet_str_to_cstr(vm->vm, msg);
120
+ }
121
+
122
+ result = rb_Array(result);
123
+ *success = 1;
124
+ *found_here = rubyjsonnet_str_to_cstr(vm->vm, rb_ary_entry(result, 1));
125
+ return rubyjsonnet_str_to_cstr(vm->vm, rb_ary_entry(result, 0));
126
+ }
127
+
128
+ /*
129
+ * Sets a custom way to resolve "import" expression.
130
+ * @param [#call] callback receives two parameters and returns two values.
131
+ * The first parameter "base" is a base directory to resolve
132
+ * "rel" from.
133
+ * The second parameter "rel" is an absolute or a relative
134
+ * path to the file to import.
135
+ * The first return value is the content of the imported file.
136
+ * The second return value is the resolved path of the imported file.
137
+ */
138
+ static VALUE
139
+ vm_set_import_callback(VALUE self, VALUE callback)
140
+ {
141
+ struct jsonnet_vm_wrap *const vm = rubyjsonnet_obj_to_vm(self);
142
+
143
+ vm->import_callback = callback;
144
+ jsonnet_import_callback(vm->vm, import_callback_entrypoint, vm);
145
+
146
+ return callback;
147
+ }
148
+
149
+ /**
150
+ * Generic entrypoint of native callbacks which adapts callable objects in Ruby to \c
151
+ * JsonnetNativeCallback.
152
+ *
153
+ * @param[in] data pointer to a {\c struct native_callback_ctx}
154
+ * @param[in] argv NULL-terminated array of arguments
155
+ * @param[out] success set to 1 on success, or 0 if otherwise.
156
+ * @returns the result of the callback on success, an error message on failure.
157
+ */
158
+ static struct JsonnetJsonValue *
159
+ native_callback_entrypoint(void *data, const struct JsonnetJsonValue *const *argv, int *success)
160
+ {
161
+ long i;
162
+ int state = 0;
163
+
164
+ struct native_callback_ctx *const ctx = (struct native_callback_ctx *)data;
165
+ struct JsonnetVm *const vm = rubyjsonnet_obj_to_vm(ctx->vm)->vm;
166
+ VALUE result, args = rb_ary_tmp_new(ctx->arity + 1);
167
+
168
+ rb_ary_push(args, ctx->callback);
169
+ for (i = 0; i < ctx->arity; ++i) {
170
+ rb_ary_push(args, rubyjsonnet_json_to_obj(vm, argv[i]));
171
+ }
172
+
173
+ result = rb_protect(invoke_callback, args, &state);
174
+
175
+ rb_ary_free(args);
176
+
177
+ if (state) {
178
+ VALUE msg = rescue_callback(state, "something wrong in %" PRIsVALUE, ctx->callback);
179
+ *success = 0;
180
+ return rubyjsonnet_obj_to_json(vm, msg, &state);
181
+ }
182
+
183
+ return rubyjsonnet_obj_to_json(vm, result, success);
184
+ }
185
+
186
+ /*
187
+ * Registers a native extension written in Ruby.
188
+ * @param callback [#call] a PURE callable object
189
+ * @param params [Array] names of the parameters of the function
190
+ */
191
+ static VALUE
192
+ vm_register_native_callback(VALUE self, VALUE name, VALUE callback, VALUE params)
193
+ {
194
+ struct {
195
+ volatile VALUE store;
196
+ long len;
197
+ const char **buf;
198
+ } cstr_params;
199
+ struct native_callback_ctx *ctx;
200
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
201
+ long i;
202
+
203
+ name = rb_to_symbol(name);
204
+ rubyjsonnet_assert_asciicompat(name);
205
+
206
+ params = rb_Array(params);
207
+ cstr_params.len = RARRAY_LEN(params);
208
+ cstr_params.buf = (const char **)rb_alloc_tmp_buffer(
209
+ &cstr_params.store, sizeof(const char *const) * (cstr_params.len + 1));
210
+ for (i = 0; i < cstr_params.len; ++i) {
211
+ const VALUE pname = rb_to_symbol(RARRAY_AREF(params, i));
212
+ rubyjsonnet_assert_asciicompat(pname);
213
+ cstr_params.buf[i] = rb_id2name(RB_SYM2ID(pname));
214
+ }
215
+ cstr_params.buf[cstr_params.len] = NULL;
216
+
217
+ ctx = RB_ALLOC_N(struct native_callback_ctx, 1);
218
+ ctx->callback = callback;
219
+ ctx->arity = cstr_params.len;
220
+ ctx->vm = self;
221
+ jsonnet_native_callback(vm->vm, rb_id2name(RB_SYM2ID(name)), native_callback_entrypoint, ctx,
222
+ cstr_params.buf);
223
+
224
+ rb_free_tmp_buffer(&cstr_params.store);
225
+
226
+ RB_REALLOC_N(vm->native_callbacks.contexts, struct native_callback_ctx *,
227
+ vm->native_callbacks.len + 1);
228
+ vm->native_callbacks.contexts[vm->native_callbacks.len] = ctx;
229
+ vm->native_callbacks.len++;
230
+
231
+ return name;
232
+ }
233
+
234
+ void
235
+ rubyjsonnet_init_callbacks(VALUE cVM)
236
+ {
237
+ id_call = rb_intern("call");
238
+
239
+ rb_define_method(cVM, "import_callback=", vm_set_import_callback, 1);
240
+ rb_define_private_method(cVM, "register_native_callback", vm_register_native_callback, 3);
241
+ }
@@ -1,6 +1,62 @@
1
1
  require 'mkmf'
2
+ require 'fileutils'
3
+
4
+ def using_system_libraries?
5
+ arg_config('--use-system-libraries', !!ENV['JSONNET_USE_SYSTEM_LIBRARIES'])
6
+ end
2
7
 
3
8
  dir_config('jsonnet')
9
+
10
+ unless using_system_libraries?
11
+ message "Building jsonnet using packaged libraries.\n"
12
+ require 'rubygems'
13
+ require 'mini_portile2'
14
+ message "Using mini_portile version #{MiniPortile::VERSION}\n"
15
+
16
+ recipe = MiniPortile.new('jsonnet', 'v0.17.0')
17
+ recipe.files = ['https://github.com/google/jsonnet/archive/v0.17.0.tar.gz']
18
+ class << recipe
19
+
20
+ def compile
21
+ # We want to create a file a library we can link to. Jsonnet provides us
22
+ # with the command `make libjsonnet.so` which creates a shared object
23
+ # however that won't be bundled into the compiled output so instead
24
+ # we compile the c into .o files and then create an archive that can
25
+ # be linked to
26
+ execute('compile', make_cmd)
27
+ execute('archive', 'ar rcs libjsonnet.a core/desugarer.o core/formatter.o core/lexer.o core/libjsonnet.o core/parser.o core/pass.o core/static_analysis.o core/string_utils.o core/vm.o third_party/md5/md5.o')
28
+ end
29
+
30
+ def configured?
31
+ true
32
+ end
33
+
34
+ def install
35
+ lib_path = File.join(port_path, 'lib')
36
+ include_path = File.join(port_path, 'include')
37
+
38
+ FileUtils.mkdir_p([lib_path, include_path])
39
+
40
+ FileUtils.cp(File.join(work_path, 'libjsonnet.a'), lib_path)
41
+ FileUtils.cp(File.join(work_path, 'include', 'libjsonnet.h'), include_path)
42
+ FileUtils.cp(File.join(work_path, 'include', 'libjsonnet_fmt.h'), include_path)
43
+ end
44
+ end
45
+
46
+ recipe.cook
47
+ # I tried using recipe.activate here but that caused this file to build ok
48
+ # but the makefile to fail. These commands add the necessary paths to do both
49
+ $LIBPATH = ["#{recipe.path}/lib"] | $LIBPATH
50
+ $CPPFLAGS << " -I#{recipe.path}/include"
51
+
52
+ # This resolves an issue where you can get improper linkage when compiling
53
+ # and get an error like "undefined symbol: _ZTVN10__cxxabiv121__vmi_class_type_infoE"
54
+ # experienced on ubuntu.
55
+ # See: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=193950
56
+ $LIBS << " -lstdc++"
57
+ end
58
+
4
59
  abort 'libjsonnet.h not found' unless have_header('libjsonnet.h')
5
60
  abort 'libjsonnet not found' unless have_library('jsonnet')
61
+ have_header('libjsonnet_fmt.h')
6
62
  create_makefile('jsonnet/jsonnet_wrap')