viva 0.42.1337
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT +9 -0
- data/LICENSE +13 -0
- data/README.md +135 -0
- data/Rakefile +14 -0
- data/examples/counter.rb +8 -0
- data/examples/mol.rb +6 -0
- data/examples/pid.rb +12 -0
- data/examples/random.rb +11 -0
- data/ext/viva/core.c +60 -0
- data/ext/viva/extconf.rb +3 -0
- data/lib/viva.rb +31 -0
- data/lib/viva/version.rb +3 -0
- data/spec/viva_spec.rb +50 -0
- data/viva.gemspec +16 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a01b0d14ea32240f6880c8f285b4ffc5d1ef16fc
|
4
|
+
data.tar.gz: af40b3a31343a9d7088d153ccdabb660f89a10d5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 48c2b43fefcee481edae7284894ccf1a7efd2c1230df3347476bfb17994ceca0e0aebbfce159371fe5614ffb9b7b16f5cf963d6d6cc69743cd1bb8c80ca055a1
|
7
|
+
data.tar.gz: ecf62bfa07e1830d2341550931c24be0d78071013525efee13d374760b8a407746eb65af7faaa21c4df1840aa13dee388a18c48c26a1bc1893e8be094e75467a
|
data/CODE_OF_CONDUCT
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
,---. ,---.-./`) ,---. .--. ____ .-'''-. .--. .--. ____ ,---. .--.
|
2
|
+
| \ / \ .-.')| \ | | .' __ `. / _ \| |_ | | .' __ `.| \ | |
|
3
|
+
| , \/ , / `-' \| , \ | |/ ' \ \ (`' )/`--'| _( )_ | |/ ' \ \ , \ | |
|
4
|
+
| |\_ /| |`-'`"`| |\_ \| ||___| / |(_ o _). |(_ o _) | ||___| / | |\_ \| |
|
5
|
+
| _( )_/ | |.---. | _( )_\ | _.-` | (_,_). '. | (_,_) \ | | _.-` | _( )_\ |
|
6
|
+
| (_ o _) | || | | (_ o _) |.' _ |.---. \ :| |/ \| |.' _ | (_ o _) |
|
7
|
+
| (_,_) | || | | (_,_)\ || _( )_ |\ `-' || ' /\ ` || _( )_ | (_,_)\ |
|
8
|
+
| | | || | | | | |\ (_ o _) / \ / | / \ |\ (_ o _) / | | |
|
9
|
+
'--' '--''---' '--' '--' '.(_,_).' `-...-' `---' `---` '.(_,_).''--' '--'
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
data/README.md
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
<p align="center">
|
2
|
+
<img src="http://i.imgur.com/OzCBizi.png" />
|
3
|
+
</p>
|
4
|
+
|
5
|
+
## What's this?
|
6
|
+
|
7
|
+
**Viva** is a little C extension that exposes the **vi**rtual **va**riable API to Ruby land for science and the lulz.
|
8
|
+
|
9
|
+
### What's that?
|
10
|
+
|
11
|
+
Contrary to popular misconception, many of Ruby's global variables are in fact not. Consider the case of `$~` (known to [`English`](http://ruby-doc.org/stdlib-2.3.0/libdoc/English/rdoc/English.html) speakers as `$LAST_MATCH_INFO`):
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
def foo
|
15
|
+
'foo'.match /\w+/ and p $~
|
16
|
+
end
|
17
|
+
|
18
|
+
'bar'.match /\w+/
|
19
|
+
|
20
|
+
p $~ # "bar" from the current scope
|
21
|
+
foo # "foo" from #foo's scope
|
22
|
+
p $~ # "bar" again
|
23
|
+
```
|
24
|
+
|
25
|
+
It's evident that something tricksy is going on here; if this allegedly "global" variable doesn't maintain its value across scopes, whence does it come? I'll spare you the gory details: it's defined in terms of [a pair of C functions](https://git.io/vzwAN). Referring to `$~` will invoke `match_getter()` to obtain a value and *assigning to `$~`* will cause the provided `MatchData` instance to be used for `$~` and all of its descendants (of which there are [potentially thousands](https://git.io/vzwxR)).
|
26
|
+
|
27
|
+
#### Cool story, bro.
|
28
|
+
|
29
|
+
That's all well and good, yeah? "vvars" are scoped and pretty much do The Right Thing™. What's more, there's nothing stopping you from digging in and defining your own. But who wants to write C? [Bestest](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&cad=rja&uact=8&ved=0ahUKEwjqlvHc4L_KAhVF4iYKHRAZAxMQFggzMAM&url=http%3A%2F%2Fwww.merriam-webster.com%2Fdictionary%2Fbestest&usg=AFQjCNF5NASit_I0DsFXBB2l4baH0n7nFQ&bvm=bv.112454388,d.eWE) of all would be if we could define virtual getters and setters with Ruby callables, and so I've made that a thing.
|
30
|
+
|
31
|
+
### La raison d'être
|
32
|
+
|
33
|
+
There's an old shell variable in Zsh—I know it's in Bash, it's probably in Zsh—called `RANDOM`, and the tin's not lying:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
$ echo $RANDOM $RANDOM $RANDOM
|
37
|
+
8784 15644 1499
|
38
|
+
```
|
39
|
+
|
40
|
+
[How neat is that?](http://i.imgur.com/SCRFkTZ.png) Let's give Ruby a `$RANDOM` that's neater still:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
require 'viva'
|
44
|
+
|
45
|
+
max = 32768 # Be like Bash... for now.
|
46
|
+
getter = -> { rand max }
|
47
|
+
setter = -> spec { max = spec }
|
48
|
+
|
49
|
+
Viva.define :RANDOM, getter, setter
|
50
|
+
|
51
|
+
p Array.new(4) { $RANDOM }
|
52
|
+
# => [31791, 5045, 21337, 14134]
|
53
|
+
|
54
|
+
$RANDOM = 1..6
|
55
|
+
|
56
|
+
p Array.new(4) { $RANDOM }
|
57
|
+
# => [3, 5, 6, 2]
|
58
|
+
```
|
59
|
+
|
60
|
+
Bash's offering looks positively static in comparison. ¡Viva la Rubylución!
|
61
|
+
|
62
|
+
## Usage
|
63
|
+
|
64
|
+
`Viva.define` is the primary interface. It accepts exactly three arguments: a name for the virtual variable to be defined and two callables (instances of `Method` or `Proc`) to be used as the getter and setter thereof, respectively.
|
65
|
+
|
66
|
+
The name is coerced via `Kernel#String`; preceded with a `'$'`; and subjected to the rules for global identifiers, with the additional stipulation that numeric names may only be between `-9` and `0`, inclusively. This constraint is informed by the parser rather than enforced by the virtual variable API: `$-10` and below are outright syntax errors, and assignment to the positives is explicitly forbidden. We can *refer* to them just fine, but they're "super-virtual" and will, at present, simply ignore any efforts to tailor their behavior to one's wily whim. I consider the inability to use them to be a :bug: in **Viva**.
|
67
|
+
|
68
|
+
**TL;DR;SMAT**:
|
69
|
+
|
70
|
+
valid |invalid
|
71
|
+
:-----:|:-----:
|
72
|
+
`0` |`1`
|
73
|
+
`-4` |`-10`
|
74
|
+
`"$"` |`" "`
|
75
|
+
`"-_"` |`"_-"`
|
76
|
+
`Viva` |`nil`
|
77
|
+
`:*` |`:^`
|
78
|
+
|
79
|
+
The getter can be defined to take an argument, which will be the "actual" value of the requested variable. Likewise, the setter receives the value that was assigned. The point, of course, is that you're free to do with these values what you will.
|
80
|
+
|
81
|
+
Invoking `Viva.define` with a falsy value instead of a callable will undefine it and restore that operation to its regularly scheduled programming (bog-standard retrieval and assignment); regrettably, this holds true for variables which previously had some special meaning (pretty much everything in `English`). This is another :bug:.
|
82
|
+
|
83
|
+
`define_getter` and `define_setter` are convenience methods that take blocks and leave their better halves in place:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
Viva.define_setter('$') do |pid|
|
87
|
+
p "Someone wanted our PID to be #{pid}."
|
88
|
+
end
|
89
|
+
|
90
|
+
%w[$ PID PROCESS_ID].each do |name|
|
91
|
+
Viva.define_getter(name) { 1337 }
|
92
|
+
end
|
93
|
+
|
94
|
+
$$ = 42 # => "Someone wanted our PID to be 42."
|
95
|
+
p $$ # => 1337
|
96
|
+
```
|
97
|
+
|
98
|
+
Just for laughs, **Viva** provides a bit of syntactic sugalt for defining both accessors simultaneously using "two blocks":
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
Viva(:MoL).--> v { v * 2 } { |v| v * 7 }
|
102
|
+
|
103
|
+
$MoL = 3
|
104
|
+
p $MoL # => 42
|
105
|
+
```
|
106
|
+
|
107
|
+
That you have to put the parameter in different places makes it look pretty silly, which I argue is a feature.
|
108
|
+
|
109
|
+
Finally, there's `undef_getter` and `undef_setter` which, while perfectly self-explanatory, go a long way toward revealing what's actually going on here:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
def undef_getter var
|
113
|
+
Getters.delete "$#{var}"
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
No effort has been made to conceal the internals from the user. This one's for you, [`FrozenCore`](https://youtu.be/SBdqCYKWISU?t=463).
|
118
|
+
|
119
|
+
### FQAs
|
120
|
+
|
121
|
+
* Is this real life?
|
122
|
+
|
123
|
+
Likelier than not, but perhaps one of the solipsists has the right of it.
|
124
|
+
|
125
|
+
* Why is this happening?
|
126
|
+
|
127
|
+
Some men just want to watch the world-writable variables burn with the glory of a thousand suns. <sup><sup>If only I could be so grossly incandescent...</sup></sup>
|
128
|
+
|
129
|
+
* Should I use this?
|
130
|
+
|
131
|
+
I'm inclined to think [_why would've liked it](http://favstar.fm/users/_why/status/1231698950); your team are liable to [respond less positively](https://www.youtube.com/watch?v=H07zYvkNYL8). Horses for courses and the like.
|
132
|
+
|
133
|
+
### Contributing
|
134
|
+
|
135
|
+
You are strongly but gently advised to reconsider. If you simply won't be deterred, may the fork be with you.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
Rake::TestTask.new :spec do |t|
|
4
|
+
t.test_files = ['spec/viva_spec.rb']
|
5
|
+
end
|
6
|
+
|
7
|
+
task :default do
|
8
|
+
IO.new(0).puts <<JOKE
|
9
|
+
|
10
|
+
rm: it is dangerous to operate recursively on '/'
|
11
|
+
rm: use --no-preserve-root to override this failsafe
|
12
|
+
|
13
|
+
JOKE
|
14
|
+
end
|
data/examples/counter.rb
ADDED
data/examples/mol.rb
ADDED
data/examples/pid.rb
ADDED
data/examples/random.rb
ADDED
data/ext/viva/core.c
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
static VALUE mViva, getters, setters;
|
4
|
+
static ID id_call;
|
5
|
+
|
6
|
+
/* `struct rb_global_variable` doesn't get exported from variable.c, but this
|
7
|
+
* should suffice to have everything in the right place when we go looking. */
|
8
|
+
struct gvar { int _, __; void *val; };
|
9
|
+
|
10
|
+
static VALUE
|
11
|
+
viva_getter(ID id, void *data)
|
12
|
+
{
|
13
|
+
VALUE getter = rb_hash_aref(getters, rb_id2str(id));
|
14
|
+
VALUE val = data ? data : Qnil; /* the old value, if any */
|
15
|
+
|
16
|
+
if (RTEST(getter))
|
17
|
+
return rb_funcall(getter, id_call, abs(rb_proc_arity(getter)), val);
|
18
|
+
return val;
|
19
|
+
}
|
20
|
+
|
21
|
+
static void
|
22
|
+
viva_setter(VALUE val, ID id, void *_, struct gvar *var)
|
23
|
+
{
|
24
|
+
VALUE setter = rb_hash_aref(setters, rb_id2str(id));
|
25
|
+
|
26
|
+
var->val = RTEST(setter) ? rb_funcall(setter, id_call, 1, val) : val;
|
27
|
+
}
|
28
|
+
|
29
|
+
#define error_msg "`%s`is not allowed as a virtual variable name"
|
30
|
+
|
31
|
+
static VALUE
|
32
|
+
viva_define(VALUE self, VALUE var, VALUE getter, VALUE setter)
|
33
|
+
{
|
34
|
+
char *str;
|
35
|
+
var = rb_String(var);
|
36
|
+
rb_str_update(var, 0, 0, rb_str_new("$", 1));
|
37
|
+
str = StringValueCStr(var);
|
38
|
+
|
39
|
+
/* Reject invalid global identifiers and fully digital ones other than $0,
|
40
|
+
* since the former would be syntax errors and the latter inaccessible. */
|
41
|
+
if (!rb_is_global_id(rb_intern(str)) || atoi(str + 1) > 0)
|
42
|
+
rb_raise(rb_eNameError, error_msg, str);
|
43
|
+
|
44
|
+
rb_hash_aset(getters, var, getter);
|
45
|
+
rb_hash_aset(setters, var, setter);
|
46
|
+
rb_define_virtual_variable(str, viva_getter, viva_setter);
|
47
|
+
|
48
|
+
return rb_str_intern(var);
|
49
|
+
}
|
50
|
+
|
51
|
+
void
|
52
|
+
Init_core()
|
53
|
+
{
|
54
|
+
mViva = rb_define_module("Viva");
|
55
|
+
id_call = rb_intern("call");
|
56
|
+
|
57
|
+
rb_define_const(mViva, "Getters", getters = rb_hash_new());
|
58
|
+
rb_define_const(mViva, "Setters", setters = rb_hash_new());
|
59
|
+
rb_define_singleton_method(mViva, "define", viva_define, 3);
|
60
|
+
}
|
data/ext/viva/extconf.rb
ADDED
data/lib/viva.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'viva/core'
|
2
|
+
|
3
|
+
module Viva
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def define_getter var
|
7
|
+
define var, proc, Setters["$#{var}"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def define_setter var
|
11
|
+
define var, Getters["$#{var}"], proc
|
12
|
+
end
|
13
|
+
|
14
|
+
def undef_getter var
|
15
|
+
Getters.delete "$#{var}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def undef_setter var
|
19
|
+
Setters.delete "$#{var}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def - getter, &setter
|
23
|
+
define @var, getter, setter
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Kernel
|
28
|
+
module_function def Viva var
|
29
|
+
Viva.tap { |v| v.instance_variable_set :@var, var }
|
30
|
+
end
|
31
|
+
end
|
data/lib/viva/version.rb
ADDED
data/spec/viva_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/pride'
|
3
|
+
require 'viva'
|
4
|
+
|
5
|
+
describe Viva do
|
6
|
+
describe '.define' do
|
7
|
+
describe 'when given a valid virtual variable name' do
|
8
|
+
it 'should return its name as a globalized Symbol' do
|
9
|
+
[0, -4, '$', '-_', Viva, :*].each do |name|
|
10
|
+
expected = :"$#{name}"
|
11
|
+
Viva.define(name, nil, nil).must_equal expected
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'when given an invalid virtual variable name' do
|
17
|
+
it 'should complain and refrain (inaccessible < useless)' do
|
18
|
+
[1, -10, ' ', '_-', nil, :^,].each do |name|
|
19
|
+
-> { Viva.define name, nil, nil }.must_raise NameError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# NOTE: These next two specs are essentially identical.
|
25
|
+
# How does one verify that nonexistent code didn't run?
|
26
|
+
|
27
|
+
it 'should be able to create a getter with no setter' do
|
28
|
+
Viva.define :a, -> actual { actual * 2 }, nil
|
29
|
+
$a = 21
|
30
|
+
$a.must_equal 42
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should be able to create a setter with no getter' do
|
34
|
+
Viva.define :b, nil, -> actual { actual * 2 }
|
35
|
+
$b = 21
|
36
|
+
$b.must_equal 42
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# This more or less runs the thing through its paces, so let's stop here.
|
41
|
+
|
42
|
+
it "should be able to emulate GCC's __COUNTER__ (with bonus reset)" do
|
43
|
+
c = -1
|
44
|
+
Viva(:COUNTER).--> { c += 1 } { |v| c = v }
|
45
|
+
|
46
|
+
Array.new(3) { $COUNTER }.must_equal [0, 1, 2]
|
47
|
+
$COUNTER = 10
|
48
|
+
Array.new(3) { $COUNTER }.must_equal [11, 12, 13]
|
49
|
+
end
|
50
|
+
end
|
data/viva.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$:.unshift File.expand_path '../lib', __FILE__
|
2
|
+
require 'viva/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'viva'
|
6
|
+
s.version = Viva::VERSION
|
7
|
+
s.author = 'D.E. Akers'
|
8
|
+
s.email = '0x0dea@gmail.com'
|
9
|
+
|
10
|
+
s.summary = 'Viva exposes the virtual variable API to Ruby land.'
|
11
|
+
s.homepage = 'https://github.com/0x0dea/viva'
|
12
|
+
s.license = 'WTFPL'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split
|
15
|
+
s.extensions = %w[ext/viva/extconf.rb Rakefile]
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: viva
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.42.1337
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- D.E. Akers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: 0x0dea@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions:
|
17
|
+
- ext/viva/extconf.rb
|
18
|
+
- Rakefile
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- CODE_OF_CONDUCT
|
22
|
+
- LICENSE
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- examples/counter.rb
|
26
|
+
- examples/mol.rb
|
27
|
+
- examples/pid.rb
|
28
|
+
- examples/random.rb
|
29
|
+
- ext/viva/core.c
|
30
|
+
- ext/viva/extconf.rb
|
31
|
+
- lib/viva.rb
|
32
|
+
- lib/viva/version.rb
|
33
|
+
- spec/viva_spec.rb
|
34
|
+
- viva.gemspec
|
35
|
+
homepage: https://github.com/0x0dea/viva
|
36
|
+
licenses:
|
37
|
+
- WTFPL
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 2.5.1
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Viva exposes the virtual variable API to Ruby land.
|
59
|
+
test_files: []
|