gvars 0.9.8 → 0.9.10
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 +4 -4
- data/README.md +42 -14
- data/ext/gvars/gvars.c +72 -24
- data/lib/gvars/version.rb +1 -1
- data/tmp.rb +34 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6230a213eea87d923eaf4e8196bb168d3d52342023fda70adf189b336a4fea36
|
|
4
|
+
data.tar.gz: ba9b1449a5fe00221ebb389e903bf19d10d29f264351d60e7f6369318190582b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0fe558f8ee687fb1c02429c52a1ccc0745c1f33c04f521dbdce03b987ab44910c274726a48f2283b01f13973489e3d71c9ceacaa93ff1f9aa3d570403bd75fd
|
|
7
|
+
data.tar.gz: e39330d0bdb1800bf58d45877da5af404f1f599e4f7f77ad8f5f02ac715da2ec97f7cce83c7b13da5cb9d70d17ccab047fb395bea894a05b200944f77bbb0d2f
|
data/README.md
CHANGED
|
@@ -5,29 +5,57 @@ Adds in missing utilities relating to global variables in Ruby.
|
|
|
5
5
|
Ever been bothered how there's `instance_variable_get`, `binding.local_variable_get`, `class_variable_get`, even `const_get`, but no `global_variable_get`? Ever resorted to `eval("$#{name} = value")` to assign a global variable? Look no further!
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
|
-
```ruby
|
|
9
|
-
$foo = 34
|
|
10
|
-
|
|
11
|
-
# Dynamically lookup globals
|
|
12
|
-
puts GVars.get(:$foo) #=> 34
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
`GVars` adds support for getting, setting, and aliasing global variables dynamically. (Checking for definition is coming!)
|
|
10
|
+
```ruby
|
|
11
|
+
# Dynamically interact with globals:
|
|
15
12
|
GVars.set(:$foo, 99)
|
|
16
|
-
puts
|
|
13
|
+
puts GVars.get(:$foo) #=> 99
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
GBars.alias :$bar, :$foo
|
|
15
|
+
GVars.alias(:$bar, :$foo)
|
|
20
16
|
$bar = 12
|
|
21
17
|
puts $foo #=> 12
|
|
22
18
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
include GVars
|
|
19
|
+
# Mixin `GVars` to get methods that should've been defined
|
|
20
|
+
# on `Kernel` all along
|
|
21
|
+
Kernel.include GVars
|
|
26
22
|
puts global_variable_get(:$bar) #=> 12
|
|
27
23
|
puts global_variable_set(:$bar, 19) #=> 19
|
|
28
24
|
```
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
It also supports virtual variables, which evaluate a `proc` each time:
|
|
27
|
+
```ruby
|
|
28
|
+
# Get the current time whenever you access `$TIME`
|
|
29
|
+
GVars.virtual(:$TIME) { Time.now }
|
|
30
|
+
puts $TIME.monday? #=> true
|
|
31
|
+
|
|
32
|
+
# You can also provide a custom setter
|
|
33
|
+
GVars.virtual(:$PWD,
|
|
34
|
+
getter: ->{ Dir.pwd },
|
|
35
|
+
setter: ->new{ Dir.chdir(new) })
|
|
36
|
+
$PWD = '/tmp'
|
|
37
|
+
p $PWD #=> "/private/tmp" (macOS symlinks `/tmp` to `/private/tmp`)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
And, hooked variables:
|
|
41
|
+
```ruby
|
|
42
|
+
# Initial values compute their "backing" value based on the getter's
|
|
43
|
+
# return value.
|
|
44
|
+
GVars.hooked(:$COUNTER, initial: 0) { it + 1 }
|
|
45
|
+
p [$COUNTER, $COUNTER, $COUNTER] #=> [1, 2, 3]
|
|
46
|
+
|
|
47
|
+
# If you don't supply a setter, it defaults to just the backing value.
|
|
48
|
+
$COUNTER = 10
|
|
49
|
+
p $COUNTER #=> 11
|
|
50
|
+
|
|
51
|
+
# You can also specify a state, which is given to the getter (and setter),
|
|
52
|
+
# but isn't updated by the getter's return value.
|
|
53
|
+
GVars.hooked(:$RAND,
|
|
54
|
+
state: Random.new,
|
|
55
|
+
getter: ->random{ random.rand },
|
|
56
|
+
setter: ->(value,random){ random.srand(value) }) # doesn't actually work.. `Random#srand` isn't a thing
|
|
57
|
+
```
|
|
31
58
|
|
|
32
59
|
## Known problems
|
|
33
|
-
Unfortunately, Ruby's C-level `rb_gv_get` / `rb_gv_set` methods only let you manipulate ASCII identifiers... A fix _may_ be possible, but it'll have to resort to the `eval` hack.
|
|
60
|
+
1. Unfortunately, Ruby's C-level `rb_gv_get` / `rb_gv_set` methods only let you manipulate ASCII identifiers... A fix _may_ be possible, but it'll have to resort to the `eval` hack.
|
|
61
|
+
2. Because `$_`, and regex variables (`$~`, ``$` ``, etc) are all local to the function they're called within, you can't actually access them from within procs; As a workaround, you can pass a string that'll be `eval`d each time, but it's not terribly efficient. I don't know if there's a better solution
|
data/ext/gvars/gvars.c
CHANGED
|
@@ -141,12 +141,33 @@ static const rb_data_type_t gvars_type = {
|
|
|
141
141
|
static VALUE gvars_virtual_var_getter(ID id, VALUE *data) {
|
|
142
142
|
struct gvars_virtual_var *gv;
|
|
143
143
|
TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
|
|
144
|
+
|
|
145
|
+
// If it's a string, eval it in the current context. Not great for ZJIT...
|
|
146
|
+
if (RB_TYPE_P(gv->getter, T_STRING)) {
|
|
147
|
+
return rb_funcallv(rb_binding_new(), rb_intern("eval"), 1, &gv->getter);
|
|
148
|
+
}
|
|
149
|
+
|
|
144
150
|
return rb_proc_call(gv->getter, rb_ary_new());
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
static void gvars_virtual_var_setter(VALUE val, ID id, VALUE *data) {
|
|
148
154
|
struct gvars_virtual_var *gv;
|
|
149
155
|
TypedData_Get_Struct(*data, struct gvars_virtual_var, &gvars_type, gv);
|
|
156
|
+
|
|
157
|
+
// If it's a string, eval it in the current context. Not great for ZJIT...
|
|
158
|
+
if (RB_TYPE_P(gv->setter, T_STRING)) {
|
|
159
|
+
static VALUE virtual_setter = Qundef;
|
|
160
|
+
// Don't actually register the variable until we're doing a virtual
|
|
161
|
+
// string setter.
|
|
162
|
+
if (virtual_setter == Qundef) {
|
|
163
|
+
rb_define_variable("$__value__", &virtual_setter);
|
|
164
|
+
}
|
|
165
|
+
virtual_setter = val;
|
|
166
|
+
|
|
167
|
+
rb_funcallv(rb_binding_new(), rb_intern("eval"), 1, &gv->setter);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
150
171
|
rb_proc_call(gv->setter, rb_ary_new_from_args(1, val));
|
|
151
172
|
}
|
|
152
173
|
|
|
@@ -227,15 +248,24 @@ gvars_define_virtual_method(VALUE self, VALUE *name, VALUE backing, VALUE getter
|
|
|
227
248
|
}
|
|
228
249
|
|
|
229
250
|
enum virtual_method_keywords {
|
|
251
|
+
VIRTUAL_METHOD_KEYWORDS_GETTER,
|
|
252
|
+
VIRTUAL_METHOD_KEYWORDS_SETTER,
|
|
253
|
+
VIRTUAL_METHOD_KEYWORDS_READONLY,
|
|
230
254
|
VIRTUAL_METHOD_KEYWORDS_INITIAL,
|
|
231
255
|
VIRTUAL_METHOD_KEYWORDS_STATE,
|
|
232
|
-
|
|
233
|
-
|
|
256
|
+
LENGTH_OF_VIRTUAL_METHOD_KEYWORDS_HOOKED = VIRTUAL_METHOD_KEYWORDS_STATE+1,
|
|
257
|
+
LENGTH_OF_VIRTUAL_METHOD_KEYWORDS_VIRTUAL = VIRTUAL_METHOD_KEYWORDS_READONLY+1,
|
|
234
258
|
};
|
|
235
259
|
|
|
236
|
-
static ID virtual_method_keyword_ids[
|
|
260
|
+
static ID virtual_method_keyword_ids[LENGTH_OF_VIRTUAL_METHOD_KEYWORDS_HOOKED];
|
|
261
|
+
|
|
262
|
+
static VALUE convert_to_proc(const char *name, VALUE input, bool is_hooked) {
|
|
263
|
+
// Convert getter to a proc, unless it's a string (to support the "always eval" case)
|
|
264
|
+
if (RB_TYPE_P(input, T_STRING) && !is_hooked) {
|
|
265
|
+
// Virtual functions can be strings, which are evaluated.
|
|
266
|
+
return input;
|
|
267
|
+
}
|
|
237
268
|
|
|
238
|
-
static VALUE convert_to_proc(const char *name, VALUE input) {
|
|
239
269
|
VALUE proc = rb_convert_type(input, T_DATA, "Proc", "to_proc");
|
|
240
270
|
|
|
241
271
|
if (NIL_P(proc) || !rb_obj_is_proc(proc)) {
|
|
@@ -246,43 +276,45 @@ static VALUE convert_to_proc(const char *name, VALUE input) {
|
|
|
246
276
|
}
|
|
247
277
|
|
|
248
278
|
static VALUE
|
|
249
|
-
|
|
279
|
+
gvars_f_define_virtual_method_foo(int argc, VALUE *argv, VALUE self, bool is_hooked)
|
|
250
280
|
{
|
|
251
|
-
VALUE name, backing, getter, setter, opts_hash, opts[
|
|
281
|
+
VALUE name, backing, getter, setter, opts_hash, opts[LENGTH_OF_VIRTUAL_METHOD_KEYWORDS_HOOKED] = {
|
|
282
|
+
Qundef, Qundef, Qundef, Qundef, Qundef };
|
|
252
283
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
break;
|
|
284
|
+
rb_scan_args(argc, argv, "10:", &name, &opts_hash);
|
|
285
|
+
rb_get_kwargs(opts_hash, virtual_method_keyword_ids, 0,
|
|
286
|
+
(is_hooked ? LENGTH_OF_VIRTUAL_METHOD_KEYWORDS_HOOKED : LENGTH_OF_VIRTUAL_METHOD_KEYWORDS_VIRTUAL),
|
|
287
|
+
opts);
|
|
258
288
|
|
|
259
|
-
|
|
260
|
-
|
|
289
|
+
getter = opts[VIRTUAL_METHOD_KEYWORDS_GETTER];
|
|
290
|
+
setter = opts[VIRTUAL_METHOD_KEYWORDS_SETTER];
|
|
261
291
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
default:
|
|
268
|
-
rb_bug("oops");
|
|
292
|
+
if (getter == Qundef) {
|
|
293
|
+
getter = (rb_need_block(), rb_block_proc());
|
|
294
|
+
} else {
|
|
295
|
+
if (rb_block_given_p()) rb_warn("block parameter unused when getter is supplied");
|
|
296
|
+
getter = convert_to_proc("getter", getter, is_hooked);
|
|
269
297
|
}
|
|
270
298
|
|
|
271
|
-
|
|
272
|
-
|
|
299
|
+
if (setter == Qundef) {
|
|
300
|
+
setter = Qnil;
|
|
301
|
+
} else {
|
|
302
|
+
setter = convert_to_proc("setter", setter, is_hooked);
|
|
303
|
+
}
|
|
273
304
|
|
|
274
305
|
enum virtual_kind kind = GVARS_VIRTUAL_KIND_VIRTUAL;
|
|
275
|
-
rb_get_kwargs(opts_hash, virtual_method_keyword_ids, 0, LENGTH_OF_VIRTUAL_METHOD_KEYWORDS, opts);
|
|
276
306
|
|
|
277
307
|
if (opts[VIRTUAL_METHOD_KEYWORDS_INITIAL] != Qundef) {
|
|
278
308
|
if (opts[VIRTUAL_METHOD_KEYWORDS_STATE] != Qundef) {
|
|
279
|
-
rb_raise(rb_eArgError, "
|
|
309
|
+
rb_raise(rb_eArgError, "exactly one of 'initial' or 'state' may be supplied");
|
|
280
310
|
}
|
|
281
311
|
backing = opts[VIRTUAL_METHOD_KEYWORDS_INITIAL];
|
|
282
312
|
kind = GVARS_VIRTUAL_KIND_INITIAL;
|
|
283
313
|
} else if (opts[VIRTUAL_METHOD_KEYWORDS_STATE] != Qundef) {
|
|
284
314
|
backing = opts[VIRTUAL_METHOD_KEYWORDS_STATE];
|
|
285
315
|
kind = GVARS_VIRTUAL_KIND_STATE;
|
|
316
|
+
} else if (is_hooked) {
|
|
317
|
+
rb_raise(rb_eArgError, "exactly one of 'initial' or 'state' may be supplied");
|
|
286
318
|
} else {
|
|
287
319
|
backing = Qnil;
|
|
288
320
|
}
|
|
@@ -301,6 +333,18 @@ gvars_f_define_virtual_method(int argc, VALUE *argv, VALUE self)
|
|
|
301
333
|
gvars_define_virtual_method(self, &name, backing, getter, setter, kind, is_readonly);
|
|
302
334
|
|
|
303
335
|
return name; //TODO: ID2SYM(id)
|
|
336
|
+
|
|
337
|
+
}
|
|
338
|
+
static VALUE
|
|
339
|
+
gvars_f_define_virtual_method(int argc, VALUE *argv, VALUE self)
|
|
340
|
+
{
|
|
341
|
+
return gvars_f_define_virtual_method_foo(argc, argv, self, false);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
static VALUE
|
|
345
|
+
gvars_f_define_hooked_method(int argc, VALUE *argv, VALUE self)
|
|
346
|
+
{
|
|
347
|
+
return gvars_f_define_virtual_method_foo(argc, argv, self, true);
|
|
304
348
|
}
|
|
305
349
|
|
|
306
350
|
void
|
|
@@ -309,6 +353,8 @@ Init_gvars(void)
|
|
|
309
353
|
gvars_module = rb_define_module("GVars");
|
|
310
354
|
rb_ivar_set(gvars_module, rb_intern("vars"), rb_hash_new());
|
|
311
355
|
|
|
356
|
+
virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_GETTER] = rb_intern("getter");
|
|
357
|
+
virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_SETTER] = rb_intern("setter");
|
|
312
358
|
virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_INITIAL] = rb_intern("initial");
|
|
313
359
|
virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_STATE] = rb_intern("state");
|
|
314
360
|
virtual_method_keyword_ids[VIRTUAL_METHOD_KEYWORDS_READONLY] = rb_intern("readonly");
|
|
@@ -323,6 +369,7 @@ Init_gvars(void)
|
|
|
323
369
|
|
|
324
370
|
// Don't make it an instance method, cause we don't want it included
|
|
325
371
|
rb_define_singleton_method(gvars_module, "define_virtual_method", gvars_f_define_virtual_method, -1);
|
|
372
|
+
rb_define_singleton_method(gvars_module, "define_hooked_method", gvars_f_define_hooked_method, -1);
|
|
326
373
|
|
|
327
374
|
// Enumerable methods
|
|
328
375
|
rb_extend_object(gvars_module, rb_mEnumerable);
|
|
@@ -339,4 +386,5 @@ Init_gvars(void)
|
|
|
339
386
|
rb_define_alias(gvars_singleton, "alias", "alias_global_variable");
|
|
340
387
|
rb_define_alias(gvars_singleton, "list", "global_variables");
|
|
341
388
|
rb_define_alias(gvars_singleton, "virtual", "define_virtual_method");
|
|
389
|
+
rb_define_alias(gvars_singleton, "hooked", "define_hooked_method");
|
|
342
390
|
}
|
data/lib/gvars/version.rb
CHANGED
data/tmp.rb
CHANGED
|
@@ -2,6 +2,40 @@ require 'gvars'
|
|
|
2
2
|
p GVars::VERSION
|
|
3
3
|
|
|
4
4
|
require 'optparse'
|
|
5
|
+
trace_var :$timeout do p it end
|
|
6
|
+
|
|
7
|
+
GVars.virtual(:$hello,
|
|
8
|
+
getter: '[Regexp.last_match[1], $~]',
|
|
9
|
+
setter: 'puts("assigned: #{$__value__}")')
|
|
10
|
+
|
|
11
|
+
def foo
|
|
12
|
+
/(a)/ =~ 'heallo'
|
|
13
|
+
p $hello
|
|
14
|
+
$hello = 9
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
foo
|
|
18
|
+
["a", #<MatchData "a" 1:"a">]
|
|
19
|
+
assigned: 9
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__END__
|
|
23
|
+
GVars.virtual(:$timeout,
|
|
24
|
+
getter: ->{ 'hello' },
|
|
25
|
+
setter: ->val { puts "setting: #{val}" },
|
|
26
|
+
readonly: false)
|
|
27
|
+
|
|
28
|
+
GVars.hooked(:$COUNTER,
|
|
29
|
+
getter: :succ.to_proc,
|
|
30
|
+
setter: nil)
|
|
31
|
+
setter: ->val { puts "setting: #{val}" },
|
|
32
|
+
initial: 10)
|
|
33
|
+
|
|
34
|
+
p $timeout1
|
|
35
|
+
p $timeout1
|
|
36
|
+
|
|
37
|
+
__END__
|
|
38
|
+
|
|
5
39
|
OptParse.new do |op|
|
|
6
40
|
op.on '--timeout=X', 'the timeout'
|
|
7
41
|
op.parse %w[--timeout 34.5], into: GVars
|