blockenspiel 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Blockenspiel.rdoc +5 -1
- data/History.rdoc +7 -0
- data/README.rdoc +6 -2
- data/Version +1 -1
- data/ext/unmixer_mri/extconf.rb +10 -10
- data/ext/unmixer_mri/unmixer_mri.c +10 -10
- data/lib/blockenspiel.rb +7 -7
- data/lib/blockenspiel/builder.rb +52 -52
- data/lib/blockenspiel/dsl_setup.rb +86 -86
- data/lib/blockenspiel/errors.rb +18 -18
- data/lib/blockenspiel/impl.rb +116 -116
- data/lib/blockenspiel/unmixer_rubinius.rb +22 -22
- data/lib/blockenspiel/unmixer_unimplemented.rb +17 -17
- data/lib/blockenspiel/version.rb +9 -9
- data/lib/blockenspiel/versionomy.rb +11 -13
- data/test/tc_basic.rb +38 -38
- data/test/tc_behaviors.rb +36 -36
- data/test/tc_dsl_attrs.rb +37 -37
- data/test/tc_dsl_methods.rb +86 -86
- data/test/tc_dynamic.rb +39 -39
- data/test/tc_embedded_block.rb +29 -28
- data/test/tc_mixins.rb +103 -68
- data/test/tc_modules.rb +49 -49
- data/test/tc_version.rb +59 -0
- metadata +27 -29
- data/lib/blockenspiel_unmixer_jruby.jar +0 -0
data/lib/blockenspiel/errors.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Blockenspiel error classes
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# -----------------------------------------------------------------------------
|
6
6
|
# Copyright 2008-2011 Daniel Azuma
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# All rights reserved.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Redistribution and use in source and binary forms, with or without
|
11
11
|
# modification, are permitted provided that the following conditions are met:
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# * Redistributions of source code must retain the above copyright notice,
|
14
14
|
# this list of conditions and the following disclaimer.
|
15
15
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# * Neither the name of the copyright holder, nor the names of any other
|
19
19
|
# contributors to this software, may be used to endorse or promote products
|
20
20
|
# derived from this software without specific prior written permission.
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
23
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
24
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
@@ -35,27 +35,27 @@
|
|
35
35
|
|
36
36
|
|
37
37
|
module Blockenspiel
|
38
|
-
|
39
|
-
|
40
|
-
# Base exception for all exceptions raised by Blockenspiel
|
41
|
-
|
38
|
+
|
39
|
+
|
40
|
+
# Base exception for all exceptions raised by Blockenspiel
|
41
|
+
|
42
42
|
class BlockenspielError < ::RuntimeError
|
43
43
|
end
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
|
46
46
|
# This exception is rasied when attempting to use the <tt>:proxy</tt> or
|
47
47
|
# <tt>:mixin</tt> parameterless behavior with a target that does not have
|
48
48
|
# the DSL module included. It is an error made by the DSL implementor.
|
49
|
-
|
49
|
+
|
50
50
|
class DSLMissingError < ::Blockenspiel::BlockenspielError
|
51
51
|
end
|
52
|
-
|
53
|
-
|
52
|
+
|
53
|
+
|
54
54
|
# This exception is raised when the block provided does not take the
|
55
55
|
# expected number of parameters. It is an error made by the caller.
|
56
|
-
|
56
|
+
|
57
57
|
class BlockParameterError < ::Blockenspiel::BlockenspielError
|
58
58
|
end
|
59
|
-
|
60
|
-
|
59
|
+
|
60
|
+
|
61
61
|
end
|
data/lib/blockenspiel/impl.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Blockenspiel implementation
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# -----------------------------------------------------------------------------
|
6
6
|
# Copyright 2008-2011 Daniel Azuma
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# All rights reserved.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Redistribution and use in source and binary forms, with or without
|
11
11
|
# modification, are permitted provided that the following conditions are met:
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# * Redistributions of source code must retain the above copyright notice,
|
14
14
|
# this list of conditions and the following disclaimer.
|
15
15
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# * Neither the name of the copyright holder, nor the names of any other
|
19
19
|
# contributors to this software, may be used to endorse or promote products
|
20
20
|
# derived from this software without specific prior written permission.
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
23
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
24
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
@@ -38,78 +38,78 @@ require 'thread'
|
|
38
38
|
|
39
39
|
|
40
40
|
module Blockenspiel
|
41
|
-
|
42
|
-
|
41
|
+
|
42
|
+
|
43
43
|
# === Invoke a given DSL
|
44
|
-
#
|
44
|
+
#
|
45
45
|
# This is the entry point for Blockenspiel. Call this function to invoke
|
46
46
|
# a set of DSL code provided by the user of your API.
|
47
|
-
#
|
47
|
+
#
|
48
48
|
# For example, if you want users of your API to be able to do this:
|
49
|
-
#
|
49
|
+
#
|
50
50
|
# call_dsl do
|
51
51
|
# foo(1)
|
52
52
|
# bar(2)
|
53
53
|
# end
|
54
|
-
#
|
54
|
+
#
|
55
55
|
# Then you should implement <tt>call_dsl</tt> like this:
|
56
|
-
#
|
56
|
+
#
|
57
57
|
# def call_dsl(&block)
|
58
58
|
# my_dsl = create_block_implementation
|
59
59
|
# Blockenspiel.invoke(block, my_dsl)
|
60
60
|
# do_something_with(my_dsl)
|
61
61
|
# end
|
62
|
-
#
|
62
|
+
#
|
63
63
|
# In the above, <tt>create_block_implementation</tt> is a placeholder that
|
64
64
|
# returns an instance of your DSL methods class. This class includes the
|
65
65
|
# Blockenspiel::DSL module and defines the DSL methods +foo+ and +bar+.
|
66
66
|
# See Blockenspiel::DSLSetupMethods for a set of tools you can use in your
|
67
67
|
# DSL methods class for creating a DSL.
|
68
|
-
#
|
68
|
+
#
|
69
69
|
# === Usage patterns
|
70
|
-
#
|
70
|
+
#
|
71
71
|
# The invoke method has a number of forms, depending on whether the API
|
72
72
|
# user's DSL code is provided as a block or a string, and depending on
|
73
73
|
# whether the DSL methods are specified statically using a DSL class or
|
74
74
|
# dynamically using a block.
|
75
|
-
#
|
75
|
+
#
|
76
76
|
# [<tt>Blockenspiel.invoke(<i>user_block</i>, <i>my_dsl</i>, <i>opts</i>)</tt>]
|
77
77
|
# This form takes the user's code as a block, and the DSL itself as an
|
78
78
|
# object with DSL methods. The opts hash is optional and provides a
|
79
79
|
# set of arguments as described below under "Block DSL options".
|
80
|
-
#
|
80
|
+
#
|
81
81
|
# [<tt>Blockenspiel.invoke(<i>user_block</i>, <i>opts</i>) { ... }</tt>]
|
82
82
|
# This form takes the user's code as a block, while the DSL itself is
|
83
83
|
# specified in the given block, as described below under "Dynamic
|
84
84
|
# target generation". The opts hash is optional and provides a set of
|
85
85
|
# arguments as described below under "Block DSL options".
|
86
|
-
#
|
86
|
+
#
|
87
87
|
# [<tt>Blockenspiel.invoke(<i>user_string</i>, <i>my_dsl</i>, <i>opts</i>)</tt>]
|
88
88
|
# This form takes the user's code as a string, and the DSL itself as an
|
89
89
|
# object with DSL methods. The opts hash is optional and provides a
|
90
90
|
# set of arguments as described below under "String DSL options".
|
91
|
-
#
|
91
|
+
#
|
92
92
|
# [<tt>Blockenspiel.invoke(<i>user_string</i>, <i>opts</i>) { ... }</tt>]
|
93
93
|
# This form takes the user's code as a block, while the DSL itself is
|
94
94
|
# specified in the given block, as described below under "Dynamic
|
95
95
|
# target generation". The opts hash is optional and provides a set of
|
96
96
|
# arguments as described below under "String DSL options".
|
97
|
-
#
|
97
|
+
#
|
98
98
|
# [<tt>Blockenspiel.invoke(<i>my_dsl</i>, <i>opts</i>)</tt>]
|
99
99
|
# This form reads the user's code from a file, and takes the DSL itself
|
100
100
|
# as an object with DSL methods. The opts hash is required and provides
|
101
101
|
# a set of arguments as described below under "String DSL options". The
|
102
102
|
# <tt>:file</tt> option is required.
|
103
|
-
#
|
103
|
+
#
|
104
104
|
# [<tt>Blockenspiel.invoke(<i>opts</i>) { ... }</tt>]
|
105
105
|
# This form reads the user's code from a file, while the DSL itself is
|
106
106
|
# specified in the given block, as described below under "Dynamic
|
107
107
|
# target generation". The opts hash is required and provides a set of
|
108
108
|
# arguments as described below under "String DSL options". The
|
109
109
|
# <tt>:file</tt> option is required.
|
110
|
-
#
|
110
|
+
#
|
111
111
|
# === Block DSL options
|
112
|
-
#
|
112
|
+
#
|
113
113
|
# When a user provides DSL code using a block, you simply pass that block
|
114
114
|
# as the first parameter to Blockenspiel.invoke. Normally, Blockenspiel
|
115
115
|
# will first check the block's arity to see whether it takes a parameter.
|
@@ -117,10 +117,10 @@ module Blockenspiel
|
|
117
117
|
# no parameter, and the given target is an instance of a class with DSL
|
118
118
|
# capability, the DSL methods are made available on the caller's self
|
119
119
|
# object so they may be called without a block parameter.
|
120
|
-
#
|
120
|
+
#
|
121
121
|
# Following are the options understood by Blockenspiel when providing
|
122
122
|
# code using a block:
|
123
|
-
#
|
123
|
+
#
|
124
124
|
# [<tt>:parameterless</tt>]
|
125
125
|
# If set to false, disables parameterless blocks and always attempts to
|
126
126
|
# pass a parameter to the block. Otherwise, you may set it to one of
|
@@ -131,10 +131,10 @@ module Blockenspiel
|
|
131
131
|
# [<tt>:parameter</tt>]
|
132
132
|
# If set to false, disables blocks with parameters, and always attempts
|
133
133
|
# to use parameterless blocks. Default is true, enabling parameter mode.
|
134
|
-
#
|
134
|
+
#
|
135
135
|
# The following values control the precise behavior of parameterless
|
136
136
|
# blocks. These are values for the <tt>:parameterless</tt> option.
|
137
|
-
#
|
137
|
+
#
|
138
138
|
# [<tt>:mixin</tt>]
|
139
139
|
# This is the default behavior. DSL methods from the target are
|
140
140
|
# temporarily overlayed on the caller's +self+ object, but +self+ still
|
@@ -159,16 +159,16 @@ module Blockenspiel
|
|
159
159
|
# object's instance variables are not available (and thus cannot be
|
160
160
|
# clobbered) in the block, and the transformations specified by
|
161
161
|
# <tt>dsl_method</tt> directives are honored.
|
162
|
-
#
|
162
|
+
#
|
163
163
|
# === String DSL options
|
164
|
-
#
|
164
|
+
#
|
165
165
|
# When a user provides DSL code using a string (either directly or via a
|
166
166
|
# file), Blockenspiel always treats it as a "parameterless" invocation,
|
167
167
|
# since there is no way to "pass a parameter" to a string. Thus, the two
|
168
168
|
# options recognized for block DSLs, <tt>:parameterless</tt>, and
|
169
169
|
# <tt>:parameter</tt>, are meaningless and ignored. However, the
|
170
170
|
# following new options are recognized:
|
171
|
-
#
|
171
|
+
#
|
172
172
|
# [<tt>:file</tt>]
|
173
173
|
# The value of this option should be a string indicating the path to
|
174
174
|
# the file from which the user's DSL code is coming. It is passed
|
@@ -187,9 +187,9 @@ module Blockenspiel
|
|
187
187
|
# descriptions of these behaviors. Note that <tt>:mixin</tt> is not
|
188
188
|
# allowed in this case because its behavior would be indistinguishable
|
189
189
|
# from the proxy behavior.
|
190
|
-
#
|
190
|
+
#
|
191
191
|
# The following values are recognized for the <tt>:behavior</tt> option:
|
192
|
-
#
|
192
|
+
#
|
193
193
|
# [<tt>:proxy</tt>]
|
194
194
|
# This behavior changes +self+ to a proxy object created by applying the
|
195
195
|
# DSL methods to an empty object. Thus, the code in the DSL string does
|
@@ -204,12 +204,12 @@ module Blockenspiel
|
|
204
204
|
# the target object's methods are not modified: this behavior does not
|
205
205
|
# apply any DSL method changes specified using <tt>dsl_method</tt>
|
206
206
|
# directives.
|
207
|
-
#
|
207
|
+
#
|
208
208
|
# === Dynamic target generation
|
209
|
-
#
|
209
|
+
#
|
210
210
|
# It is also possible to dynamically generate a target object by passing
|
211
211
|
# a block to this method. This is probably best illustrated by example:
|
212
|
-
#
|
212
|
+
#
|
213
213
|
# Blockenspiel.invoke(block) do
|
214
214
|
# add_method(:set_foo) do |value|
|
215
215
|
# my_foo = value
|
@@ -219,10 +219,10 @@ module Blockenspiel
|
|
219
219
|
# my_bar = blk.call
|
220
220
|
# end
|
221
221
|
# end
|
222
|
-
#
|
222
|
+
#
|
223
223
|
# The above is roughly equivalent to invoking Blockenspiel with an
|
224
224
|
# instance of this target class:
|
225
|
-
#
|
225
|
+
#
|
226
226
|
# class MyFooTarget
|
227
227
|
# include Blockenspiel::DSL
|
228
228
|
# def set_foo(value)
|
@@ -233,37 +233,37 @@ module Blockenspiel
|
|
233
233
|
# set_my_bar_from(yield)
|
234
234
|
# end
|
235
235
|
# end
|
236
|
-
#
|
236
|
+
#
|
237
237
|
# Blockenspiel.invoke(block, MyFooTarget.new)
|
238
|
-
#
|
238
|
+
#
|
239
239
|
# The obvious advantage of using dynamic object generation is that you are
|
240
240
|
# creating methods using closures, which provides the opportunity to, for
|
241
241
|
# example, modify closure local variables such as my_foo. This is more
|
242
242
|
# difficult to do when you create a target class since its methods do not
|
243
243
|
# have access to outside data. Hence, in the above example, we hand-waved,
|
244
244
|
# assuming the existence of some method called "set_my_foo_from".
|
245
|
-
#
|
245
|
+
#
|
246
246
|
# The disadvantage is performance. If you dynamically generate a target
|
247
247
|
# object, it involves parsing and creating a new class whenever it is
|
248
248
|
# invoked. Thus, it is recommended that you use this technique for calls
|
249
249
|
# that are not used repeatedly, such as one-time configuration.
|
250
|
-
#
|
250
|
+
#
|
251
251
|
# See the Blockenspiel::Builder class for more details on add_method.
|
252
|
-
#
|
252
|
+
#
|
253
253
|
# (And yes, you guessed it: this API is a DSL block, and is itself
|
254
254
|
# implemented using Blockenspiel.)
|
255
|
-
|
255
|
+
|
256
256
|
def self.invoke(*args_, &builder_block_)
|
257
257
|
# This method itself is responsible for parsing the args to invoke,
|
258
258
|
# and handling the dynamic target generation. It then passes control
|
259
259
|
# to one of the _invoke_with_* methods.
|
260
|
-
|
260
|
+
|
261
261
|
# The arguments.
|
262
262
|
block_ = nil
|
263
263
|
eval_str_ = nil
|
264
264
|
target_ = nil
|
265
265
|
opts_ = {}
|
266
|
-
|
266
|
+
|
267
267
|
# Get the code
|
268
268
|
case args_.first
|
269
269
|
when ::String
|
@@ -271,7 +271,7 @@ module Blockenspiel
|
|
271
271
|
when ::Proc
|
272
272
|
block_ = args_.shift
|
273
273
|
end
|
274
|
-
|
274
|
+
|
275
275
|
# Get the target, performing dynamic target generation if requested
|
276
276
|
if builder_block_
|
277
277
|
builder_ = ::Blockenspiel::Builder.new
|
@@ -284,7 +284,7 @@ module Blockenspiel
|
|
284
284
|
raise ::ArgumentError, "No DSL target provided"
|
285
285
|
end
|
286
286
|
end
|
287
|
-
|
287
|
+
|
288
288
|
# Get the options hash
|
289
289
|
if args_.first.kind_of?(::Hash)
|
290
290
|
opts_ = args_.shift
|
@@ -292,7 +292,7 @@ module Blockenspiel
|
|
292
292
|
if args_.size > 0
|
293
293
|
raise ::ArgumentError, "Unexpected arguments"
|
294
294
|
end
|
295
|
-
|
295
|
+
|
296
296
|
# Invoke
|
297
297
|
if block_
|
298
298
|
_invoke_with_block(block_, target_, opts_)
|
@@ -300,17 +300,17 @@ module Blockenspiel
|
|
300
300
|
_invoke_with_string(eval_str_, target_, opts_)
|
301
301
|
end
|
302
302
|
end
|
303
|
-
|
304
|
-
|
303
|
+
|
304
|
+
|
305
305
|
# Invoke when the DSL user provides code as a string or file.
|
306
306
|
# We open and read the file if need be, and then pass control
|
307
307
|
# to the _execute method.
|
308
|
-
|
308
|
+
|
309
309
|
def self._invoke_with_string(eval_str_, target_, opts_) # :nodoc:
|
310
310
|
# Read options
|
311
311
|
file_ = opts_[:file]
|
312
312
|
line_ = opts_[:line] || 1
|
313
|
-
|
313
|
+
|
314
314
|
# Read file if no string provided directly
|
315
315
|
unless eval_str_
|
316
316
|
if file_
|
@@ -321,26 +321,26 @@ module Blockenspiel
|
|
321
321
|
else
|
322
322
|
file_ ||= "(String passed to Blockenspiel)"
|
323
323
|
end
|
324
|
-
|
324
|
+
|
325
325
|
# Handle instance-eval behavior
|
326
326
|
if opts_[:behavior] == :instance
|
327
327
|
return target_.instance_eval(eval_str_, file_, line_)
|
328
328
|
end
|
329
|
-
|
329
|
+
|
330
330
|
# Execute the DSL using the proxy method.
|
331
331
|
_execute_dsl(true, nil, eval_str_, target_, file_, line_)
|
332
332
|
end
|
333
|
-
|
334
|
-
|
333
|
+
|
334
|
+
|
335
335
|
# Invoke when the DSL user provides code as a block. We read the given
|
336
336
|
# options hash, handle a few special cases, and then pass control to the
|
337
337
|
# _execute method.
|
338
|
-
|
338
|
+
|
339
339
|
def self._invoke_with_block(block_, target_, opts_) # :nodoc:
|
340
340
|
# Read options
|
341
341
|
parameter_ = opts_[:parameter]
|
342
342
|
parameterless_ = opts_.include?(:behavior) ? opts_[:behavior] : opts_[:parameterless]
|
343
|
-
|
343
|
+
|
344
344
|
# Handle no-target behavior
|
345
345
|
if parameter_ == false && parameterless_ == false
|
346
346
|
if block_.arity != 0 && block_.arity != -1
|
@@ -348,7 +348,7 @@ module Blockenspiel
|
|
348
348
|
end
|
349
349
|
return block_.call
|
350
350
|
end
|
351
|
-
|
351
|
+
|
352
352
|
# Handle parametered block case
|
353
353
|
if parameter_ != false && block_.arity == 1 || parameterless_ == false
|
354
354
|
if block_.arity != 1
|
@@ -356,99 +356,99 @@ module Blockenspiel
|
|
356
356
|
end
|
357
357
|
return block_.call(target_)
|
358
358
|
end
|
359
|
-
|
359
|
+
|
360
360
|
# Check arity for parameterless case
|
361
361
|
if block_.arity != 0 && block_.arity != -1
|
362
362
|
raise ::Blockenspiel::BlockParameterError, "Block should not take parameters"
|
363
363
|
end
|
364
|
-
|
364
|
+
|
365
365
|
# Handle instance-eval behavior
|
366
366
|
if parameterless_ == :instance
|
367
367
|
return target_.instance_eval(&block_)
|
368
368
|
end
|
369
|
-
|
369
|
+
|
370
370
|
# Execute the DSL
|
371
371
|
_execute_dsl(parameterless_ == :proxy, block_, nil, target_, nil, nil)
|
372
372
|
end
|
373
|
-
|
374
|
-
|
373
|
+
|
374
|
+
|
375
375
|
# Class for proxy delegators.
|
376
376
|
# The proxy behavior creates one of these delegators, mixes in the dsl
|
377
377
|
# methods, and uses instance_eval to invoke the block. This class delegates
|
378
378
|
# non-handled methods to the context object.
|
379
|
-
|
379
|
+
|
380
380
|
class ProxyDelegator # :nodoc:
|
381
|
-
|
381
|
+
|
382
382
|
def method_missing(symbol_, *params_, &block_)
|
383
383
|
::Blockenspiel._proxy_dispatch(self, symbol_, params_, block_)
|
384
384
|
end
|
385
|
-
|
385
|
+
|
386
386
|
end
|
387
|
-
|
388
|
-
|
387
|
+
|
388
|
+
|
389
389
|
# :stopdoc:
|
390
390
|
NO_VALUE = ::Object.new
|
391
391
|
# :startdoc:
|
392
|
-
|
392
|
+
|
393
393
|
@_target_stacks = {}
|
394
394
|
@_mixin_counts = {}
|
395
395
|
@_proxy_delegators = {}
|
396
396
|
@_mutex = ::Mutex.new
|
397
|
-
|
398
|
-
|
397
|
+
|
398
|
+
|
399
399
|
# This is the "meat" of Blockenspiel, implementing both the proxy and
|
400
400
|
# mixin methods.
|
401
|
-
|
401
|
+
|
402
402
|
def self._execute_dsl(use_proxy_method_, block_, eval_str_, target_, file_, line_) # :nodoc:
|
403
403
|
# Get the module of dsl methods
|
404
404
|
mod_ = target_.class._get_blockenspiel_module rescue nil
|
405
405
|
unless mod_
|
406
406
|
raise ::Blockenspiel::DSLMissingError, "Given DSL target does not include Blockenspiel::DSL"
|
407
407
|
end
|
408
|
-
|
408
|
+
|
409
409
|
# Get the block's calling context object
|
410
410
|
context_object_ = block_ ? ::Kernel.eval('self', block_.binding) : nil
|
411
|
-
|
411
|
+
|
412
412
|
if use_proxy_method_
|
413
|
-
|
413
|
+
|
414
414
|
# Create proxy object
|
415
415
|
proxy_ = ::Blockenspiel::ProxyDelegator.new
|
416
416
|
proxy_.extend(mod_)
|
417
|
-
|
417
|
+
|
418
418
|
# Store the target and proxy object so dispatchers can get them
|
419
419
|
proxy_delegator_key_ = proxy_.object_id
|
420
|
-
target_stack_key_ =
|
420
|
+
target_stack_key_ = _current_context_id(proxy_)
|
421
421
|
@_proxy_delegators[proxy_delegator_key_] = context_object_ if context_object_
|
422
422
|
@_target_stacks[target_stack_key_] = [target_]
|
423
|
-
|
423
|
+
|
424
424
|
begin
|
425
|
-
|
425
|
+
|
426
426
|
# Evaluate with the proxy as self
|
427
427
|
if block_
|
428
428
|
return proxy_.instance_eval(&block_)
|
429
429
|
else
|
430
430
|
return proxy_.instance_eval(eval_str_, file_, line_)
|
431
431
|
end
|
432
|
-
|
432
|
+
|
433
433
|
ensure
|
434
|
-
|
434
|
+
|
435
435
|
# Clean up the dispatcher information
|
436
436
|
@_proxy_delegators.delete(proxy_delegator_key_) if context_object_
|
437
437
|
@_target_stacks.delete(target_stack_key_)
|
438
|
-
|
438
|
+
|
439
439
|
end
|
440
|
-
|
440
|
+
|
441
441
|
else
|
442
|
-
|
442
|
+
|
443
443
|
# Create hash keys
|
444
444
|
mixin_count_key_ = [context_object_.object_id, mod_.object_id]
|
445
|
-
target_stack_key_ =
|
446
|
-
|
445
|
+
target_stack_key_ = _current_context_id(context_object_)
|
446
|
+
|
447
447
|
# Store the target for inheriting.
|
448
448
|
# We maintain a target call stack per thread.
|
449
449
|
target_stack_ = @_target_stacks[target_stack_key_] ||= []
|
450
450
|
target_stack_.push(target_)
|
451
|
-
|
451
|
+
|
452
452
|
# Mix this module into the object, if required.
|
453
453
|
# This ensures that we keep track of the number of requests to
|
454
454
|
# mix this module in, from nested blocks and possibly multiple threads.
|
@@ -461,18 +461,18 @@ module Blockenspiel
|
|
461
461
|
context_object_.extend(mod_)
|
462
462
|
end
|
463
463
|
end
|
464
|
-
|
464
|
+
|
465
465
|
begin
|
466
|
-
|
466
|
+
|
467
467
|
# Now call the block
|
468
468
|
return block_.call
|
469
|
-
|
469
|
+
|
470
470
|
ensure
|
471
|
-
|
471
|
+
|
472
472
|
# Clean up the target stack
|
473
473
|
target_stack_.pop
|
474
474
|
@_target_stacks.delete(target_stack_key_) if target_stack_.size == 0
|
475
|
-
|
475
|
+
|
476
476
|
# Remove the mixin from the object, if required.
|
477
477
|
@_mutex.synchronize do
|
478
478
|
count_ = @_mixin_counts[mixin_count_key_]
|
@@ -483,20 +483,20 @@ module Blockenspiel
|
|
483
483
|
@_mixin_counts[mixin_count_key_] = count_ - 1
|
484
484
|
end
|
485
485
|
end
|
486
|
-
|
486
|
+
|
487
487
|
end
|
488
|
-
|
488
|
+
|
489
489
|
end
|
490
490
|
end
|
491
|
-
|
492
|
-
|
491
|
+
|
492
|
+
|
493
493
|
# This implements the mapping between DSL module methods and target object methods.
|
494
494
|
# We look up the current target object based on the current thread.
|
495
495
|
# Then we attempt to call the given method on that object.
|
496
496
|
# If we can't find an appropriate method to call, return the special value NO_VALUE.
|
497
|
-
|
497
|
+
|
498
498
|
def self._target_dispatch(object_, name_, params_, block_) # :nodoc:
|
499
|
-
target_stack_ = @_target_stacks[
|
499
|
+
target_stack_ = @_target_stacks[_current_context_id(object_)]
|
500
500
|
return ::Blockenspiel::NO_VALUE unless target_stack_
|
501
501
|
target_stack_.reverse_each do |target_|
|
502
502
|
target_class_ = target_.class
|
@@ -507,11 +507,11 @@ module Blockenspiel
|
|
507
507
|
end
|
508
508
|
return ::Blockenspiel::NO_VALUE
|
509
509
|
end
|
510
|
-
|
511
|
-
|
510
|
+
|
511
|
+
|
512
512
|
# This implements the proxy fall-back behavior.
|
513
513
|
# We look up the context object, and call the given method on that object.
|
514
|
-
|
514
|
+
|
515
515
|
def self._proxy_dispatch(proxy_, name_, params_, block_) # :nodoc:
|
516
516
|
delegate_ = @_proxy_delegators[proxy_.object_id]
|
517
517
|
if delegate_
|
@@ -520,28 +520,28 @@ module Blockenspiel
|
|
520
520
|
raise ::NoMethodError, "undefined method `#{name_}' in DSL"
|
521
521
|
end
|
522
522
|
end
|
523
|
-
|
524
|
-
|
525
|
-
# This returns a current context ID
|
526
|
-
#
|
527
|
-
|
528
|
-
|
523
|
+
|
524
|
+
|
525
|
+
# This returns a current context ID, which includes both the curren thread
|
526
|
+
# object_id and the current fiber object_id (if available).
|
527
|
+
|
529
528
|
begin
|
530
529
|
require 'fiber'
|
531
530
|
raise ::LoadError unless defined?(::Fiber)
|
532
|
-
def self._current_context_id # :nodoc:
|
531
|
+
def self._current_context_id(object_) # :nodoc:
|
532
|
+
thid_ = ::Thread.current.object_id
|
533
533
|
begin
|
534
|
-
::Fiber.current.object_id
|
534
|
+
[thid_, ::Fiber.current.object_id, object_.object_id]
|
535
535
|
rescue ::Exception
|
536
536
|
# JRuby hack (see JRUBY-5842)
|
537
|
-
|
537
|
+
[thid_, 0, object_.object_id]
|
538
538
|
end
|
539
539
|
end
|
540
540
|
rescue ::LoadError
|
541
|
-
def self._current_context_id # :nodoc:
|
542
|
-
::Thread.current.object_id
|
541
|
+
def self._current_context_id(object_) # :nodoc:
|
542
|
+
[::Thread.current.object_id, object_.object_id]
|
543
543
|
end
|
544
544
|
end
|
545
|
-
|
546
|
-
|
545
|
+
|
546
|
+
|
547
547
|
end
|