rewrite 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +1 -0
- data/lib/rewrite/prelude/andand.rb +1 -1
- data/lib/rewrite/version.rb +1 -1
- data/lib/rewrite.rb +76 -32
- data/test/test_by_example.rb +293 -0
- data/test/test_call_by_name.rb +1 -1
- data/website/index.html +39 -3
- data/website/index.txt +32 -1
- metadata +3 -2
data/README.txt
CHANGED
data/lib/rewrite/version.rb
CHANGED
data/lib/rewrite.rb
CHANGED
@@ -8,21 +8,76 @@ Dir["#{File.dirname(__FILE__)}/rewrite/*.rb"].each do |element|
|
|
8
8
|
require element
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
# Rewrite is the namespace for everything provided by the rewrite gem (http://rewrite.rubyforge.org).
|
12
12
|
#
|
13
|
-
#
|
13
|
+
# Rewrite is a framework for rewriting Ruby code before it is interpreted. This is useful for creating
|
14
|
+
# abstractions that require non-eager evaluation, optimizing certain kinds of code, and creating even
|
15
|
+
# more expressive domain-specific languages.
|
14
16
|
#
|
17
|
+
# In its basic form, use Rewrite like this:
|
18
|
+
#
|
19
|
+
# Rewrite.with(rewriter1, rewriter2, rewriter3) do
|
20
|
+
# class MyClass
|
21
|
+
# # ...
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Rewrite will take all the code between the do and end keywords and rewrite it using
|
26
|
+
# the declared rewriters (rewriter1, rewriter2, and rewriter3).
|
27
|
+
#
|
28
|
+
# One of the benefits of this approach is that the effect of the rewriters is limited
|
29
|
+
# to the blocks of code you choose to rewrite. For example, the andand gem
|
30
|
+
# (http://andand.rubyforge.org) uses "Classical Metaprogramming:" it modifies the Object
|
31
|
+
# and NilClass classes to do its thing, which means that if you require andand anywhere
|
32
|
+
# in your project, you have andand everywhere in your project and (if you are writing a
|
33
|
+
# gem or rails plug in) your downstream clients all have andand as well.
|
34
|
+
#
|
35
|
+
# Whereas Rewrite provides its own version of andand, Rewrite::Prelude::Andand. There are
|
36
|
+
# some important difference that have to do with eager evaluation vs. call by name, but
|
37
|
+
# especially interesting is that when you write:
|
38
|
+
#
|
39
|
+
# class MyClass
|
40
|
+
# include Rewrite
|
41
|
+
# include Rewrite::Prelude
|
42
|
+
# with (andand) do
|
43
|
+
# # ...
|
44
|
+
# foo.andand.bar(42)
|
45
|
+
# # ...
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# You are not modifying any core classes to make foo.andand.bar(42) work.
|
50
|
+
#
|
51
|
+
# See Rewrite::Prelude for a complete list of rewriters built into the Rewrite gem for your use.
|
15
52
|
module Rewrite
|
16
53
|
|
17
54
|
module ClassMethods
|
55
|
+
|
56
|
+
# Provide a symbol that is extremely unlikely to be used elsewhere.
|
57
|
+
#
|
58
|
+
# Rewriters use this when they need to name something. For example,
|
59
|
+
# Andand converts code like this:
|
60
|
+
#
|
61
|
+
# numbers.andand.inject(&:+)
|
62
|
+
#
|
63
|
+
# Into:
|
64
|
+
#
|
65
|
+
# lambda { |__1234567890__|
|
66
|
+
# if __1234567890__.nil?
|
67
|
+
# nil
|
68
|
+
# else
|
69
|
+
# __1234567890__.inject(&:+)
|
70
|
+
# end
|
71
|
+
# }.call(numbers)
|
72
|
+
#
|
73
|
+
# It uses Rewrite.gensym to generate __1234567890__.
|
74
|
+
#
|
75
|
+
def gensym
|
76
|
+
:"__#{Time.now.to_i}#{rand(100000)}__"
|
77
|
+
end
|
18
78
|
|
19
|
-
|
20
|
-
#
|
21
|
-
# lambda { _ }
|
22
|
-
# }
|
23
|
-
# and have it extract _ automatically, and somehow also
|
24
|
-
# use the exact same pattern to compose a sexp, much as
|
25
|
-
# I used to have binary pattern expressions
|
79
|
+
# Convert an expression to a sexp by taking a block and stripping\
|
80
|
+
# the outer prc from it.
|
26
81
|
def sexp_for &proc
|
27
82
|
sexp = proc.to_sexp
|
28
83
|
return if sexp.length != 3
|
@@ -31,34 +86,23 @@ module Rewrite
|
|
31
86
|
sexp[2]
|
32
87
|
end
|
33
88
|
|
89
|
+
# Convert an expression to a sexp and then the sexp to an array.
|
90
|
+
# Useful for tests where you want to compare results.
|
34
91
|
def arr_for &proc
|
35
92
|
sexp_for(&proc).to_a
|
36
93
|
end
|
37
94
|
|
95
|
+
# Convert an object of some type to a sexp, very useful when you have a sexp
|
96
|
+
# expressed as a tree of arrays.
|
97
|
+
def recursive_s(node)
|
98
|
+
if node.is_a? Array
|
99
|
+
s(*(node.map { |subnode| recursive_s(subnode) }))
|
100
|
+
else
|
101
|
+
node
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
38
105
|
end
|
39
106
|
extend ClassMethods
|
40
|
-
|
41
|
-
# Provide a symbol that is extremely unlikely to be used elsewhere.
|
42
|
-
#
|
43
|
-
# Rewriters use this when they need to name something. For example,
|
44
|
-
# Andand converts code like this:
|
45
|
-
#
|
46
|
-
# numbers.andand.inject(&:+)
|
47
|
-
#
|
48
|
-
# Into:
|
49
|
-
#
|
50
|
-
# lambda { |__1234567890__|
|
51
|
-
# if __1234567890__.nil?
|
52
|
-
# nil
|
53
|
-
# else
|
54
|
-
# __1234567890__.inject(&:+)
|
55
|
-
# end
|
56
|
-
# }.call(numbers)
|
57
|
-
#
|
58
|
-
# It uses Rewrite.gensym to generate __1234567890__.
|
59
|
-
#
|
60
|
-
def self.gensym
|
61
|
-
:"__#{Time.now.to_i}#{rand(100000)}__"
|
62
|
-
end
|
63
107
|
|
64
108
|
end
|
@@ -0,0 +1,293 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
require 'sexp'
|
4
|
+
|
5
|
+
include Rewrite::With
|
6
|
+
|
7
|
+
module Rewrite
|
8
|
+
|
9
|
+
module ByExample
|
10
|
+
|
11
|
+
class TestByExample < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def sexp &proc
|
14
|
+
proc.to_sexp[2]
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_simple_entity_matcher
|
18
|
+
foo_andand_bar = SexpEntity.new( :call,
|
19
|
+
s(:call,
|
20
|
+
s(:vcall, :foo),
|
21
|
+
:andand
|
22
|
+
),
|
23
|
+
:bar
|
24
|
+
)
|
25
|
+
assert_not_nil(foo_andand_bar.unfold(
|
26
|
+
s(:call, s(:call, s(:vcall, :foo), :andand), :bar)
|
27
|
+
))
|
28
|
+
assert_nil(foo_andand_bar.unfold(
|
29
|
+
s(:call, s(:call, s(:vcall, :bar), :andand), :foo)
|
30
|
+
))
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_simple_bind
|
34
|
+
foo_andand_bar = SexpEntity.new( :call,
|
35
|
+
s(:call,
|
36
|
+
BindEntity.new(:receiver),
|
37
|
+
:andand
|
38
|
+
),
|
39
|
+
BindEntity.new(:message)
|
40
|
+
)
|
41
|
+
assert_equal(
|
42
|
+
{
|
43
|
+
:receiver => s(:vcall, :foo),
|
44
|
+
:message => :bar
|
45
|
+
},
|
46
|
+
foo_andand_bar.unfold(
|
47
|
+
s(:call, s(:call, s(:vcall, :foo), :andand), :bar)
|
48
|
+
)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_any
|
53
|
+
any = SexpEntity.new(
|
54
|
+
Any.new(:foo, :bar)
|
55
|
+
)
|
56
|
+
assert_not_nil(any.unfold(
|
57
|
+
s(:foo)
|
58
|
+
))
|
59
|
+
assert_not_nil(any.unfold(
|
60
|
+
s(:bar)
|
61
|
+
))
|
62
|
+
assert_nil(any.unfold(
|
63
|
+
s(:bash)
|
64
|
+
))
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_bind_expression_by_example
|
68
|
+
something_andand_bar = SexpEntity.new(:bind => [/__to_(.*)$/]) { __to_something.andand.bar }
|
69
|
+
assert_equal(
|
70
|
+
{ :something => [:vcall, :foo] },
|
71
|
+
something_andand_bar.unfold( sexp { foo.andand.bar } )
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_bind_consistency_by_example
|
76
|
+
something_plus_something = SexpEntity.new(:bind => [:__to_something]) {__to_something + __to_something }
|
77
|
+
assert_nil(
|
78
|
+
something_plus_something.unfold(sexp { foo + bar })
|
79
|
+
)
|
80
|
+
assert_nil(
|
81
|
+
something_plus_something.unfold(sexp { 1 + 2 })
|
82
|
+
)
|
83
|
+
assert_not_nil(
|
84
|
+
something_plus_something.unfold(sexp { foo + foo })
|
85
|
+
)
|
86
|
+
assert_not_nil(
|
87
|
+
something_plus_something.unfold(sexp { 2 + 2 })
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_bind_method_name
|
92
|
+
foo_andand_something = SexpEntity.new(:bind => [/__to_(.*)$/]) { foo.andand.__to_something }
|
93
|
+
assert_equal(
|
94
|
+
{ :something => :bar },
|
95
|
+
foo_andand_something.unfold( sexp { foo.andand.bar } )
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_bind_one_parameter
|
100
|
+
foo_andand_bar_something = SexpEntity.new(:bind => [/__to_(.*)$/]) { foo.andand.bar(__to_something) }
|
101
|
+
assert_equal(
|
102
|
+
{ :something => [:lit, :bash] },
|
103
|
+
foo_andand_bar_something.unfold( sexp { foo.andand.bar(:bash) } )
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_bind_sequence
|
108
|
+
unfolder = SexpEntity.new(:array, BindSequence.new(:something))
|
109
|
+
assert_equal(
|
110
|
+
{ :something => [[:lit, :bash], [:lit, :blitz]] },
|
111
|
+
unfolder.unfold(
|
112
|
+
s(:array, s(:lit, :bash), s(:lit, :blitz))
|
113
|
+
)
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_bind_parameter_list
|
118
|
+
foo_andand_bar_somethings = SexpEntity.new(:bind => [[/__splat_(.*)$/]]) { foo.andand.bar(__splat_something) }
|
119
|
+
assert_equal(
|
120
|
+
{ :something => [[:lit, :bash], [:lit, :blitz]] },
|
121
|
+
foo_andand_bar_somethings.unfold( sexp { foo.andand.bar(:bash, :blitz) } )
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_refolding_entities
|
126
|
+
foo_andand_bar_something = SexpEntity.new(:bind => [/__to_(.*)$/]) { foo.andand.bar(__to_something) }
|
127
|
+
assert_equal(
|
128
|
+
sexp { foo.andand.bar(:bash) },
|
129
|
+
foo_andand_bar_something.fold( { :something => [:lit, :bash] } )
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_refolding_sequences
|
134
|
+
foo_andand_bar_somethings = SexpEntity.new(:bind => [[/__splat_(.*)$/]]) { foo.andand.bar(__splat_something) }
|
135
|
+
assert_equal(
|
136
|
+
sexp { foo.andand.bar(:bash, :blitz) },
|
137
|
+
foo_andand_bar_somethings.fold( { :something => [[:lit, :bash], [:lit, :blitz]] } )
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_simple_andand_refold
|
142
|
+
folder = SexpEntity.new(:bind => [/^__to_(.*)$/]) {
|
143
|
+
lambda { |__G12345__|
|
144
|
+
__G12345__ && __G12345__.__to_message
|
145
|
+
}.call(__to_receiver)
|
146
|
+
}
|
147
|
+
assert_equal(
|
148
|
+
sexp {
|
149
|
+
lambda { |__G12345__|
|
150
|
+
__G12345__ && __G12345__.age
|
151
|
+
}.call(Person.find_by_name('Otto'))
|
152
|
+
},
|
153
|
+
folder.fold( :receiver => sexp { Person.find_by_name('Otto') }, :message => :age )
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_simple_andand_hylomorphism
|
158
|
+
unfolder = SexpEntity.new(:bind => [/^__to_(.*)$/]) {
|
159
|
+
__to_receiver.andand.__to_message
|
160
|
+
}
|
161
|
+
folder = SexpEntity.new(:bind => [/^__to_(.*)$/]) {
|
162
|
+
lambda { |__G12345__|
|
163
|
+
__G12345__ && __G12345__.__to_message
|
164
|
+
}.call(__to_receiver)
|
165
|
+
}
|
166
|
+
assert_equal(
|
167
|
+
sexp {
|
168
|
+
lambda { |__G12345__|
|
169
|
+
__G12345__ && __G12345__.age
|
170
|
+
}.call(Person.find_by_name('Otto'))
|
171
|
+
},
|
172
|
+
folder.fold(
|
173
|
+
unfolder.unfold(
|
174
|
+
sexp { Person.find_by_name('Otto').andand.age }
|
175
|
+
)
|
176
|
+
)
|
177
|
+
)
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_andand_hylomorphism_with_a_parameter
|
181
|
+
unfolder = SexpEntity.new(:bind => [/^__to_(.*)$/]) {
|
182
|
+
__to_receiver.andand.__to_message(__to_parameter)
|
183
|
+
}
|
184
|
+
folder = SexpEntity.new(:bind => [/^__to_(.*)$/]) {
|
185
|
+
lambda { |__G12345__|
|
186
|
+
__G12345__ && __G12345__.__to_message(__to_parameter)
|
187
|
+
}.call(__to_receiver)
|
188
|
+
}
|
189
|
+
assert_equal(
|
190
|
+
sexp {
|
191
|
+
lambda { |__G12345__|
|
192
|
+
__G12345__ && __G12345__.age(true)
|
193
|
+
}.call(Person.find_by_name('Otto'))
|
194
|
+
},
|
195
|
+
folder.fold(
|
196
|
+
unfolder.unfold(
|
197
|
+
sexp { Person.find_by_name('Otto').andand.age(true) }
|
198
|
+
)
|
199
|
+
)
|
200
|
+
)
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_alternate_folder
|
204
|
+
folder = SexpEntity.new(:bind => [/^__to_(.*)$/]) {
|
205
|
+
lambda { |__G12345__|
|
206
|
+
__G12345__.__to_message unless __G12345__.nil?
|
207
|
+
}.call(__to_receiver)
|
208
|
+
}
|
209
|
+
assert_not_nil(
|
210
|
+
folder.fold(
|
211
|
+
:receiver => s(:vcall, :foo),
|
212
|
+
:message => :bar
|
213
|
+
)
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_andand_hylomorphism_with_a_parameter_list
|
218
|
+
unfolder = SexpEntity.new(:bind => [/^__to_(.*)$/, [/^__splat_(.*)$/]]) {
|
219
|
+
__to_receiver.andand.__to_message(__splat_parameters)
|
220
|
+
}
|
221
|
+
folder = SexpEntity.new(:bind => [/^__to_(.*)$/, [/^__splat_(.*)$/]]) {
|
222
|
+
lambda { |__G12345__|
|
223
|
+
__G12345__ && __G12345__.__to_message(__splat_parameters)
|
224
|
+
}.call(__to_receiver)
|
225
|
+
}
|
226
|
+
assert_equal(
|
227
|
+
sexp {
|
228
|
+
lambda { |__G12345__|
|
229
|
+
__G12345__ && __G12345__.find(1,2,3)
|
230
|
+
}.call(Person)
|
231
|
+
},
|
232
|
+
folder.fold(
|
233
|
+
unfolder.unfold(
|
234
|
+
sexp { Person.andand.find(1,2,3) }
|
235
|
+
)
|
236
|
+
)
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_alternate_andand_hylomorphism_with_a_parameter_list
|
241
|
+
unfolder = SexpEntity.new(:bind => [:receiver, :message, [:params]]) {
|
242
|
+
receiver.andand.message(params)
|
243
|
+
}
|
244
|
+
folder = SexpEntity.new(:bind => [:receiver, :message, [:params]]) {
|
245
|
+
lambda { |__G12345__|
|
246
|
+
__G12345__.message(params) unless __G12345__.nil?
|
247
|
+
}.call(receiver)
|
248
|
+
}
|
249
|
+
assert_equal(
|
250
|
+
sexp {
|
251
|
+
lambda { |__G12345__|
|
252
|
+
__G12345__.find(1,2,3) unless __G12345__.nil?
|
253
|
+
}.call(Person)
|
254
|
+
},
|
255
|
+
folder.fold(
|
256
|
+
unfolder.unfold(
|
257
|
+
sexp { Person.andand.find(1,2,3) }
|
258
|
+
)
|
259
|
+
)
|
260
|
+
)
|
261
|
+
end
|
262
|
+
|
263
|
+
# TODO: * Processor class with a nice syntax ('literate'?)
|
264
|
+
# TODO: * Hygienic, implemented as dogfood. How the freak would that work?
|
265
|
+
|
266
|
+
def test_unhygienic_andand
|
267
|
+
andand = Unhygienic.
|
268
|
+
from(:receiver, :message, [:parameters]) {
|
269
|
+
receiver.andand.message(parameters)
|
270
|
+
}.
|
271
|
+
to {
|
272
|
+
lambda { |andand_temp|
|
273
|
+
andand_temp.message(parameters) if andand_temp
|
274
|
+
}.call(receiver)
|
275
|
+
}
|
276
|
+
assert_equal(
|
277
|
+
'Hello' + ' World',
|
278
|
+
with(andand) do
|
279
|
+
'Hello'.andand + ' World'
|
280
|
+
end
|
281
|
+
)
|
282
|
+
assert_nil(
|
283
|
+
with(andand) do
|
284
|
+
nil.andand + ' World'
|
285
|
+
end
|
286
|
+
)
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
data/test/test_call_by_name.rb
CHANGED
@@ -70,7 +70,7 @@ class TestCalledByName < Test::Unit::TestCase
|
|
70
70
|
Rewrite.with(
|
71
71
|
called_by_name(:foo) { |change_it, get_it|
|
72
72
|
assert_equal(0, get_it)
|
73
|
-
change_it
|
73
|
+
change_it # note the interpreter warning here: it thinks we are doing a useless variable reference
|
74
74
|
assert_equal(100, get_it)
|
75
75
|
}
|
76
76
|
) do
|
data/website/index.html
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
<h1>rewrite</h1>
|
34
34
|
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/rewrite"; return false'>
|
35
35
|
<p>Get Version</p>
|
36
|
-
<a href="http://rubyforge.org/projects/rewrite" class="numbers">0.
|
36
|
+
<a href="http://rubyforge.org/projects/rewrite" class="numbers">0.2.0</a>
|
37
37
|
</div>
|
38
38
|
<h1>→ ‘rewrite’</h1>
|
39
39
|
|
@@ -104,12 +104,48 @@
|
|
104
104
|
<p>Rewrite restricts things like andand or try to your code and your code alone. Sure, if you introduce a bug in your code, you may break things that directly depend on your code. But if you introduce “try” using rewrite instead of modifying Object, you will not reach out across your project and break something entirely unrelated that happens to have defined its own version of try in a completely different way.</p>
|
105
105
|
|
106
106
|
|
107
|
-
<h2>
|
107
|
+
<h2>called_by_name</h2>
|
108
108
|
|
109
109
|
|
110
110
|
<p>See <a href="http://weblog.raganwald.com/2008/06/macros-hygiene-and-call-by-name-in-ruby.html">Macros, Hygiene, and Call By Name in Ruby</a> for details, more docs to come presently…</p>
|
111
111
|
|
112
112
|
|
113
|
+
<h2>Unhygienic rewriting</h2>
|
114
|
+
|
115
|
+
|
116
|
+
<p>A new feature every much like a new human being: Vulnerable, disruptive, and a complete mess:</p>
|
117
|
+
|
118
|
+
|
119
|
+
<p><pre class='syntax'>
|
120
|
+
<span class="ident">include</span> <span class="constant">Rewrite</span><span class="punct">::</span><span class="constant">With</span>
|
121
|
+
|
122
|
+
<span class="ident">andand</span> <span class="punct">=</span> <span class="constant">Rewrite</span><span class="punct">::</span><span class="constant">ByExample</span><span class="punct">::</span><span class="constant">Unhygienic</span><span class="punct">.</span>
|
123
|
+
<span class="ident">from</span><span class="punct">(</span><span class="symbol">:receiver</span><span class="punct">,</span> <span class="symbol">:message</span><span class="punct">,</span> <span class="punct">[</span><span class="symbol">:parameters</span><span class="punct">])</span> <span class="punct">{</span>
|
124
|
+
<span class="ident">receiver</span><span class="punct">.</span><span class="ident">andand</span><span class="punct">.</span><span class="ident">message</span><span class="punct">(</span><span class="ident">parameters</span><span class="punct">)</span>
|
125
|
+
<span class="punct">}.</span><span class="ident">to</span> <span class="punct">{</span>
|
126
|
+
<span class="ident">lambda</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">andand_temp</span><span class="punct">|</span>
|
127
|
+
<span class="ident">andand_temp</span><span class="punct">.</span><span class="ident">message</span><span class="punct">(</span><span class="ident">parameters</span><span class="punct">)</span> <span class="keyword">if</span> <span class="ident">andand_temp</span>
|
128
|
+
<span class="punct">}.</span><span class="ident">call</span><span class="punct">(</span><span class="ident">receiver</span><span class="punct">)</span>
|
129
|
+
<span class="punct">}</span>
|
130
|
+
|
131
|
+
<span class="ident">with</span> <span class="punct">(</span><span class="ident">andand</span><span class="punct">)</span> <span class="keyword">do</span>
|
132
|
+
<span class="comment"># ...</span>
|
133
|
+
<span class="ident">foo</span><span class="punct">.</span><span class="ident">andand</span><span class="punct">.</span><span class="ident">bar</span><span class="punct">(</span><span class="symbol">:bash</span><span class="punct">,</span> <span class="ident">blitz</span><span class="punct">(</span><span class="number">5</span><span class="punct">))</span>
|
134
|
+
<span class="comment"># ...</span>
|
135
|
+
<span class="keyword">end</span>
|
136
|
+
</pre></p>
|
137
|
+
|
138
|
+
|
139
|
+
<p>becomes:</p>
|
140
|
+
|
141
|
+
|
142
|
+
<p><pre class='syntax'>
|
143
|
+
<span class="ident">lambda</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">andand_temp</span><span class="punct">|</span>
|
144
|
+
<span class="ident">andand_temp</span><span class="punct">.</span><span class="ident">bar</span><span class="punct">(</span><span class="symbol">:bash</span><span class="punct">,</span> <span class="ident">blitz</span><span class="punct">(</span><span class="number">5</span><span class="punct">))</span> <span class="keyword">if</span> <span class="ident">andand_temp</span>
|
145
|
+
<span class="punct">}.</span><span class="ident">call</span><span class="punct">(</span><span class="ident">foo</span><span class="punct">)</span>
|
146
|
+
</pre></p>
|
147
|
+
|
148
|
+
|
113
149
|
<h2>How does it work?</h2>
|
114
150
|
|
115
151
|
|
@@ -232,7 +268,7 @@ rake install_gem</pre>
|
|
232
268
|
|
233
269
|
<p>Comments are welcome. Send an email to <a href="mailto:raganwald+rewrite@gmail.com">Reg Braithwaite</a> email via the <a href="http://groups.google.com/group/rewrite">forum</a></p>
|
234
270
|
<p class="coda">
|
235
|
-
<a href="http://weblog.raganwald.com/">Reginald Braithwaite</a>,
|
271
|
+
<a href="http://weblog.raganwald.com/">Reginald Braithwaite</a>, 11th July 2008<br>
|
236
272
|
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
237
273
|
</p>
|
238
274
|
</div>
|
data/website/index.txt
CHANGED
@@ -55,10 +55,41 @@ Also, imagine if you introduce try and are careful not to break anything. Now so
|
|
55
55
|
|
56
56
|
Rewrite restricts things like andand or try to your code and your code alone. Sure, if you introduce a bug in your code, you may break things that directly depend on your code. But if you introduce “try” using rewrite instead of modifying Object, you will not reach out across your project and break something entirely unrelated that happens to have defined its own version of try in a completely different way.
|
57
57
|
|
58
|
-
h2.
|
58
|
+
h2. called_by_name
|
59
59
|
|
60
60
|
See "Macros, Hygiene, and Call By Name in Ruby":http://weblog.raganwald.com/2008/06/macros-hygiene-and-call-by-name-in-ruby.html for details, more docs to come presently...
|
61
61
|
|
62
|
+
h2. Unhygienic rewriting
|
63
|
+
|
64
|
+
A new feature every much like a new human being: Vulnerable, disruptive, and a complete mess:
|
65
|
+
|
66
|
+
<pre syntax="ruby">
|
67
|
+
include Rewrite::With
|
68
|
+
|
69
|
+
andand = Rewrite::ByExample::Unhygienic.
|
70
|
+
from(:receiver, :message, [:parameters]) {
|
71
|
+
receiver.andand.message(parameters)
|
72
|
+
}.to {
|
73
|
+
lambda { |andand_temp|
|
74
|
+
andand_temp.message(parameters) if andand_temp
|
75
|
+
}.call(receiver)
|
76
|
+
}
|
77
|
+
|
78
|
+
with (andand) do
|
79
|
+
# ...
|
80
|
+
foo.andand.bar(:bash, blitz(5))
|
81
|
+
# ...
|
82
|
+
end
|
83
|
+
</pre>
|
84
|
+
|
85
|
+
becomes:
|
86
|
+
|
87
|
+
<pre syntax="ruby">
|
88
|
+
lambda { |andand_temp|
|
89
|
+
andand_temp.bar(:bash, blitz(5)) if andand_temp
|
90
|
+
}.call(foo)
|
91
|
+
</pre>
|
92
|
+
|
62
93
|
h2. How does it work?
|
63
94
|
|
64
95
|
Rewrite takes your code, converts it to an sexp with Parse Tree, then rewrites the sexp using one or more rewriters you specify. Finally, it converts the sexp back to Ruby with Ruby2Ruby and evals it. It does this when the code is first read, not every time it is invoked, so we mitigate the “do not use andand in a tight loop” problem.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rewrite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reg Braithwaite
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-07-11 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -103,6 +103,7 @@ specification_version: 2
|
|
103
103
|
summary: Syntactic metaprogramming for Ruby
|
104
104
|
test_files:
|
105
105
|
- test/test_andand.rb
|
106
|
+
- test/test_by_example.rb
|
106
107
|
- test/test_call_by_name.rb
|
107
108
|
- test/test_call_by_thunk.rb
|
108
109
|
- test/test_call_splatted_by_name.rb
|