rkh-mixico 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/COPYING +22 -0
  2. data/README +173 -0
  3. data/ext/mixico/extconf.rb +6 -0
  4. data/ext/mixico/mixico.c +106 -0
  5. data/setup.rb +1585 -0
  6. 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.
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ dir_config("mixico")
4
+ have_library("c", "main")
5
+
6
+ create_makefile("mixico")
@@ -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
+ }