public
Description: mixin hijinks — enable and disable mixins
Homepage:
Clone URL: git://github.com/why/mixico.git
mixico / README
100644 171 lines (124 sloc) 4.881 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
% mixico %
disable and re-enable mixins for ruby.
 
% quick summary %
there's no way i'm keeping this short. i'm sorry, but you're going to
have to really read this once.
 
% installation %
like so:
 
  $ ruby setup.rb config
  $ ruby setup.rb setup
  $ sudo ruby setup.rb install
 
% source code %
mixico is written in c. it is very quick (basically atomic.)
 
it is released under an MIT license. see COPYING.
 
% the long of it %
this is just a small bit of code and it has yet to prove itself, but
i believe this discovery could spawn a small following. i assure you:
it is practical.
 
while mining through the ftp site of the recently departed guy decoux,
an exquisite french hacker, i read of a lost module for reparenting
modules in different directions. this 'prop' extension is most heavily
elucidated in a thread beginning with [ruby-talk:20293]. the extension
itself is vanished, so we are left with a sort example and a diagram.
 
the diagram appears in a reply to hal fulton (who asks where this
'insert' method is that guy is using between modules):
 
from [ruby-talk:20296]:
 
  >>>>> "H" == Hal E Fulton <hal9000 / hypermetrics.com> writes:
  
  H> What is "insert" anyway? Did you mean "include"
  H> or did I miss something?
  
   No, this not "include" but really "insert" (i.e. another way)
  
   For 2 classes A < B you have
  
  
                     extend put the class here
                               |
                               |
                               |
                               v
                    meta-A <========= meta-B
                      ^ ^
      insert put | |
      the class ====> | |
      here | |
                      A <========= B
                               ^
                               |
                               |
                      include put the class here
  
   You have method specifics to the metaclass
 
even if we had the code for 'prop', we wouldn't be able to build
it. these messages date back to 25 aug 2001, when ruby 1.6.4 was
current.
 
i began trying to recreate this intriguing work by trying to get
guy's example code to work. while i've not yet completed that work,
i stumbled upon another idea.
 
when a module is mixed into an object, the module itself appears
to be in the inheritance chain (Module.ancestors):
 
  module Mixin; end
  
  class GuineaPig; end
 
  sylvain = GuineaPig.new
  sylvain.extend Mixin
  class << sylvain
    p ancestors
  end
 
the printed list for test subject 'sylvain' is:
[Mixin, GuineaPig, Object, Kernel]
 
this means 'sylvain' will respond to methods in the Mixin module
first, then in the GuineaPig class and so on up. however, the
Mixin module isn't REALLY in the inheritance chain. ruby creates
an object of type T_ICLASS that is a proxy class. a symbolic link
to the Mixin module.
 
  sylvain ==> #<Mixin> ==> GuineaPig ==> Object ==> Kernel
 
the #<Mixin> object is useless in actual Ruby. you can't print it out.
it doesn't have any methods. you can pass it around in a variable, but
that's it.
 
again, it's a symbolic link to the Mixin module and its superclass is
GuineaPig. one of these objects is created every time you mixin.
 
on to this:
 
  require 'mixico'
 
so, mixico adds two methods: disable_mixin and enable_mixin.
 
  class << sylvain
    @m = disable_mixin Mixin
    p ancestors
  end
 
which prints: [GuineaPig, Object, Kernel]
  
  class << sylvain
    enable_mixin @m
    p ancestors
  end
 
which prints: [Mixin, GuineaPig, Object, Kernel]
 
% how is this practical? %
 
my immediate concern is to stop using instance_eval. i use it in
markaby a lot. and it gets used once in shoes.
 
instance_eval can help give you syntax like this:
 
  Markaby.html do
    head do
      title "feral cat colonies worldwide"
      meta :name => "ROBOTS", :content => "ALL"
    end
  end
 
nice, readable tags, right?
 
it's nice because instance_eval redirects all your methods inside
the block to a specific object. the problem is that it alters `self`
and redirects instance and class variables as well.
 
if you've got an instance variable you want to use inside the block,
you need to save it in a local variable before entering the block.
same with `self`.
 
  def to_html
    obj = self
    Markaby.html do
      head do
        title obj.friendly_title
        meta :name => "ROBOTS", :content => "ALL"
      end
    end
  end
 
this is annoying to remember. it's often the source of bugs.
 
mixico, on the other hand, can be used to redirect the methods
without changing `self` and swallowing up the ivars.
 
  module Mixin
    def head ...
    def title ...
    def meta ...
  end
 
we you enable the mixin with mixico, it'll send the methods through
the proxy class. and when you disable it, you're back to normal.
no sign of the mixin at all.