rkh-mixico 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +22 -0
- data/README +173 -0
- data/ext/mixico/extconf.rb +6 -0
- data/ext/mixico/mixico.c +106 -0
- data/setup.rb +1585 -0
- metadata +57 -0
data/COPYING
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2008 why the lucky stiff
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without restriction,
|
6
|
+
including without limitation the rights to use, copy, modify, merge,
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
and to permit persons to whom the Software is furnished to do so,
|
9
|
+
subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
15
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
16
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
17
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
18
|
+
SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
20
|
+
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
% mixico %
|
2
|
+
disable and re-enable mixins for ruby.
|
3
|
+
|
4
|
+
% quick summary %
|
5
|
+
there's no way i'm keeping this short. i'm sorry, but you're going to
|
6
|
+
have to really read this once.
|
7
|
+
|
8
|
+
% installation %
|
9
|
+
with gem:
|
10
|
+
$ gem sources -a http://gems.github.com
|
11
|
+
$ sudo gem install rkh-mixico
|
12
|
+
|
13
|
+
without:
|
14
|
+
$ ruby setup.rb config
|
15
|
+
$ ruby setup.rb setup
|
16
|
+
$ sudo ruby setup.rb install
|
17
|
+
|
18
|
+
% source code %
|
19
|
+
mixico is written in c. it is very quick (basically atomic.)
|
20
|
+
|
21
|
+
it is released under an MIT license. see COPYING.
|
22
|
+
|
23
|
+
% the long of it %
|
24
|
+
this is just a small bit of code and it has yet to prove itself, but
|
25
|
+
i believe this discovery could spawn a small following. i assure you:
|
26
|
+
it is practical.
|
27
|
+
|
28
|
+
while mining through the ftp site of the recently departed guy decoux,
|
29
|
+
an exquisite french hacker, i read of a lost module for reparenting
|
30
|
+
modules in different directions. this 'prop' extension is most heavily
|
31
|
+
elucidated in a thread beginning with [ruby-talk:20293]. the extension
|
32
|
+
itself is vanished, so we are left with a sort example and a diagram.
|
33
|
+
|
34
|
+
the diagram appears in a reply to hal fulton (who asks where this
|
35
|
+
'insert' method is that guy is using between modules):
|
36
|
+
|
37
|
+
from [ruby-talk:20296]:
|
38
|
+
|
39
|
+
>>>>> "H" == Hal E Fulton <hal9000 / hypermetrics.com> writes:
|
40
|
+
|
41
|
+
H> What is "insert" anyway? Did you mean "include"
|
42
|
+
H> or did I miss something?
|
43
|
+
|
44
|
+
No, this not "include" but really "insert" (i.e. another way)
|
45
|
+
|
46
|
+
For 2 classes A < B you have
|
47
|
+
|
48
|
+
|
49
|
+
extend put the class here
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
v
|
54
|
+
meta-A <========= meta-B
|
55
|
+
^ ^
|
56
|
+
insert put | |
|
57
|
+
the class ====> | |
|
58
|
+
here | |
|
59
|
+
A <========= B
|
60
|
+
^
|
61
|
+
|
|
62
|
+
|
|
63
|
+
include put the class here
|
64
|
+
|
65
|
+
You have method specifics to the metaclass
|
66
|
+
|
67
|
+
even if we had the code for 'prop', we wouldn't be able to build
|
68
|
+
it. these messages date back to 25 aug 2001, when ruby 1.6.4 was
|
69
|
+
current.
|
70
|
+
|
71
|
+
i began trying to recreate this intriguing work by trying to get
|
72
|
+
guy's example code to work. while i've not yet completed that work,
|
73
|
+
i stumbled upon another idea.
|
74
|
+
|
75
|
+
when a module is mixed into an object, the module itself appears
|
76
|
+
to be in the inheritance chain (Module.ancestors):
|
77
|
+
|
78
|
+
module Mixin; end
|
79
|
+
|
80
|
+
class GuineaPig; end
|
81
|
+
|
82
|
+
sylvain = GuineaPig.new
|
83
|
+
sylvain.extend Mixin
|
84
|
+
class << sylvain
|
85
|
+
p ancestors
|
86
|
+
end
|
87
|
+
|
88
|
+
the printed list for test subject 'sylvain' is:
|
89
|
+
[Mixin, GuineaPig, Object, Kernel]
|
90
|
+
|
91
|
+
this means 'sylvain' will respond to methods in the Mixin module
|
92
|
+
first, then in the GuineaPig class and so on up. however, the
|
93
|
+
Mixin module isn't REALLY in the inheritance chain. ruby creates
|
94
|
+
an object of type T_ICLASS that is a proxy class. a symbolic link
|
95
|
+
to the Mixin module.
|
96
|
+
|
97
|
+
sylvain ==> #<Mixin> ==> GuineaPig ==> Object ==> Kernel
|
98
|
+
|
99
|
+
the #<Mixin> object is useless in actual Ruby. you can't print it out.
|
100
|
+
it doesn't have any methods. you can pass it around in a variable, but
|
101
|
+
that's it.
|
102
|
+
|
103
|
+
again, it's a symbolic link to the Mixin module and its superclass is
|
104
|
+
GuineaPig. one of these objects is created every time you mixin.
|
105
|
+
|
106
|
+
on to this:
|
107
|
+
|
108
|
+
require 'mixico'
|
109
|
+
|
110
|
+
so, mixico adds two methods: disable_mixin and enable_mixin.
|
111
|
+
|
112
|
+
class << sylvain
|
113
|
+
@m = disable_mixin Mixin
|
114
|
+
p ancestors
|
115
|
+
end
|
116
|
+
|
117
|
+
which prints: [GuineaPig, Object, Kernel]
|
118
|
+
|
119
|
+
class << sylvain
|
120
|
+
enable_mixin @m
|
121
|
+
p ancestors
|
122
|
+
end
|
123
|
+
|
124
|
+
which prints: [Mixin, GuineaPig, Object, Kernel]
|
125
|
+
|
126
|
+
% how is this practical? %
|
127
|
+
|
128
|
+
my immediate concern is to stop using instance_eval. i use it in
|
129
|
+
markaby a lot. and it gets used once in shoes.
|
130
|
+
|
131
|
+
instance_eval can help give you syntax like this:
|
132
|
+
|
133
|
+
Markaby.html do
|
134
|
+
head do
|
135
|
+
title "feral cat colonies worldwide"
|
136
|
+
meta :name => "ROBOTS", :content => "ALL"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
nice, readable tags, right?
|
141
|
+
|
142
|
+
it's nice because instance_eval redirects all your methods inside
|
143
|
+
the block to a specific object. the problem is that it alters `self`
|
144
|
+
and redirects instance and class variables as well.
|
145
|
+
|
146
|
+
if you've got an instance variable you want to use inside the block,
|
147
|
+
you need to save it in a local variable before entering the block.
|
148
|
+
same with `self`.
|
149
|
+
|
150
|
+
def to_html
|
151
|
+
obj = self
|
152
|
+
Markaby.html do
|
153
|
+
head do
|
154
|
+
title obj.friendly_title
|
155
|
+
meta :name => "ROBOTS", :content => "ALL"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
this is annoying to remember. it's often the source of bugs.
|
161
|
+
|
162
|
+
mixico, on the other hand, can be used to redirect the methods
|
163
|
+
without changing `self` and swallowing up the ivars.
|
164
|
+
|
165
|
+
module Mixin
|
166
|
+
def head ...
|
167
|
+
def title ...
|
168
|
+
def meta ...
|
169
|
+
end
|
170
|
+
|
171
|
+
we you enable the mixin with mixico, it'll send the methods through
|
172
|
+
the proxy class. and when you disable it, you're back to normal.
|
173
|
+
no sign of the mixin at all.
|
data/ext/mixico/mixico.c
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
//
|
2
|
+
// mixico.c
|
3
|
+
// an extension for enabling and disabling mixins
|
4
|
+
//
|
5
|
+
// released under an MIT license
|
6
|
+
//
|
7
|
+
#include <ruby.h>
|
8
|
+
|
9
|
+
static VALUE mixin_eval, mixout_eval;
|
10
|
+
|
11
|
+
static VALUE
|
12
|
+
rb_mod_disable_mixin(VALUE module, VALUE super)
|
13
|
+
{
|
14
|
+
VALUE p, kid;
|
15
|
+
|
16
|
+
Check_Type(super, T_MODULE);
|
17
|
+
for (kid = module, p = RCLASS(module)->super; p; kid = p, p = RCLASS(p)->super) {
|
18
|
+
if (BUILTIN_TYPE(p) == T_ICLASS) {
|
19
|
+
if (RBASIC(p)->klass == super) {
|
20
|
+
RCLASS(kid)->super = RCLASS(p)->super;
|
21
|
+
rb_clear_cache_by_class(module);
|
22
|
+
return p;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
return Qnil;
|
28
|
+
}
|
29
|
+
|
30
|
+
static VALUE
|
31
|
+
rb_mod_enable_mixin(VALUE module, VALUE mixin)
|
32
|
+
{
|
33
|
+
VALUE p;
|
34
|
+
|
35
|
+
Check_Type(mixin, T_ICLASS);
|
36
|
+
Check_Type(RBASIC(mixin)->klass, T_MODULE);
|
37
|
+
for (p = module; p; p = RCLASS(p)->super) {
|
38
|
+
if (RCLASS(p)->super == RCLASS(mixin)->super) {
|
39
|
+
RCLASS(p)->super = mixin;
|
40
|
+
rb_clear_cache_by_class(module);
|
41
|
+
return RBASIC(mixin)->klass;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
return Qnil;
|
46
|
+
}
|
47
|
+
|
48
|
+
static VALUE
|
49
|
+
rb_mod_mixin_object(VALUE target, VALUE obj)
|
50
|
+
{
|
51
|
+
VALUE singleton = rb_singleton_class(obj);
|
52
|
+
NEWOBJ(iclass, struct RClass);
|
53
|
+
OBJSETUP(iclass, rb_cClass, T_ICLASS);
|
54
|
+
|
55
|
+
Check_Type(target, T_MODULE);
|
56
|
+
if (!ROBJECT(obj)->iv_tbl)
|
57
|
+
ROBJECT(obj)->iv_tbl = st_init_numtable();
|
58
|
+
|
59
|
+
iclass->iv_tbl = ROBJECT(obj)->iv_tbl;
|
60
|
+
iclass->m_tbl = RCLASS(singleton)->m_tbl;
|
61
|
+
iclass->super = RCLASS(target)->super;
|
62
|
+
RBASIC(iclass)->klass = singleton;
|
63
|
+
|
64
|
+
OBJ_INFECT(iclass, obj);
|
65
|
+
OBJ_INFECT(iclass, target);
|
66
|
+
RCLASS(target)->super = iclass;
|
67
|
+
rb_clear_cache_by_class(target);
|
68
|
+
|
69
|
+
return Qnil;
|
70
|
+
}
|
71
|
+
|
72
|
+
void Init_mixico()
|
73
|
+
{
|
74
|
+
rb_define_method(rb_cModule, "disable_mixin", rb_mod_disable_mixin, 1);
|
75
|
+
rb_define_method(rb_cModule, "enable_mixin", rb_mod_enable_mixin, 1);
|
76
|
+
rb_define_method(rb_cModule, "mixin_an_object", rb_mod_mixin_object, 1);
|
77
|
+
|
78
|
+
rb_eval_string(
|
79
|
+
"class Proc\n" \
|
80
|
+
" def includes_mixin? mod\n" \
|
81
|
+
" (class << binding.eval('self'); self end).include? mod\n" \
|
82
|
+
" end\n" \
|
83
|
+
" def mixin mod\n" \
|
84
|
+
" binding.eval('self').extend mod\n" \
|
85
|
+
" end\n" \
|
86
|
+
" def mixout mod\n" \
|
87
|
+
" (class << binding.eval('self'); self end).disable_mixin mod\n" \
|
88
|
+
" end\n" \
|
89
|
+
"end\n" \
|
90
|
+
"\n" \
|
91
|
+
"class Module\n" \
|
92
|
+
" def mix_eval mod, &blk\n" \
|
93
|
+
" if blk.includes_mixin? mod\n" \
|
94
|
+
" blk.call\n" \
|
95
|
+
" else\n" \
|
96
|
+
" blk.mixin mod\n" \
|
97
|
+
" begin\n" \
|
98
|
+
" blk.call\n" \
|
99
|
+
" ensure\n" \
|
100
|
+
" blk.mixout mod\n" \
|
101
|
+
" end\n" \
|
102
|
+
" end\n" \
|
103
|
+
" end\n" \
|
104
|
+
"end\n" \
|
105
|
+
);
|
106
|
+
}
|