jsonnet 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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')