blockenspiel 0.3.3-java → 0.4.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/Blockenspiel.rdoc +12 -4
- data/History.rdoc +8 -0
- data/README.rdoc +10 -6
- data/Rakefile +88 -92
- data/lib/blockenspiel/builder.rb +201 -0
- data/lib/blockenspiel/dsl_setup.rb +361 -0
- data/lib/blockenspiel/errors.rb +61 -0
- data/lib/blockenspiel/impl.rb +290 -604
- data/lib/blockenspiel/version.rb +2 -2
- data/lib/blockenspiel/versionomy.rb +1 -1
- data/lib/blockenspiel.rb +22 -4
- data/lib/blockenspiel_unmixer.jar +0 -0
- data/tests/files/file1.rb +2 -0
- data/tests/tc_basic.rb +30 -0
- data/tests/tc_mixins.rb +139 -2
- metadata +60 -51
data/Blockenspiel.rdoc
CHANGED
@@ -186,6 +186,10 @@ or a parameterless block, based on whether or not the block actually takes a
|
|
186
186
|
parameter. You can also disable one or the other, to force the use of either
|
187
187
|
a parametered or parameterless block.
|
188
188
|
|
189
|
+
You can also let the caller use your DSL by passing you a string or a file
|
190
|
+
rather than a block. That is, you can create file-based DSLs such as the
|
191
|
+
Rails routes file.
|
192
|
+
|
189
193
|
You can control wich methods of the class are available from parameterless
|
190
194
|
blocks, and/or make some methods available under different names. Here are
|
191
195
|
a few examples:
|
@@ -303,8 +307,7 @@ multiple threads trying to mix methods into the same object concurrently.
|
|
303
307
|
|
304
308
|
=== Requirements
|
305
309
|
|
306
|
-
* Ruby 1.8.
|
307
|
-
or later (1.5 recommended).
|
310
|
+
* Ruby 1.8.7, Ruby 1.9.1 or later, or JRuby 1.5 or later.
|
308
311
|
|
309
312
|
=== Installation
|
310
313
|
|
@@ -313,9 +316,14 @@ multiple threads trying to mix methods into the same object concurrently.
|
|
313
316
|
=== Known issues and limitations
|
314
317
|
|
315
318
|
* Implementing wildcard DSL methods using <tt>method_missing</tt> doesn't
|
316
|
-
work. I haven't yet
|
319
|
+
work. I haven't yet decided on the right semantics for this case, or
|
320
|
+
whether it is even a reasonable feature at all.
|
317
321
|
* Including Blockenspiel::DSL in a module (rather than a class) is not yet
|
318
|
-
supported, but this is planned
|
322
|
+
supported, but this is planned for a future release.
|
323
|
+
* Rubinius support is not yet present, but on the to-do list.
|
324
|
+
* Installing on Windows may be a challenge because blockenspiel includes a
|
325
|
+
native extension. I'm considering evaluating Luis Lavena's rake-compiler
|
326
|
+
to simplify this process.
|
319
327
|
|
320
328
|
=== Development and support
|
321
329
|
|
data/History.rdoc
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 0.4.0 / 2010-06-21
|
2
|
+
|
3
|
+
* Implemented string- and file-based DSLs (in addition to block-based).
|
4
|
+
* Correctly handle separate active DSLs in different fibers within the
|
5
|
+
same thread, when fibers are avaialble.
|
6
|
+
* Updated ruby runtime dependencies to reflect what I'm actually testing.
|
7
|
+
* Organized the source a little better, and fixed some Rakefile quirks.
|
8
|
+
|
1
9
|
=== 0.3.3 / 2010-05-24
|
2
10
|
|
3
11
|
* Some Rakefile fixes to match RDoc and Ruby 1.9 changes.
|
data/README.rdoc
CHANGED
@@ -55,8 +55,7 @@ For an extended analysis of different ways to implement DSL blocks, see
|
|
55
55
|
|
56
56
|
=== Requirements
|
57
57
|
|
58
|
-
* Ruby 1.8.
|
59
|
-
or later (1.5 recommended).
|
58
|
+
* Ruby 1.8.7, Ruby 1.9.1 or later, or JRuby 1.5 or later.
|
60
59
|
|
61
60
|
=== Installation
|
62
61
|
|
@@ -65,9 +64,14 @@ For an extended analysis of different ways to implement DSL blocks, see
|
|
65
64
|
=== Known issues and limitations
|
66
65
|
|
67
66
|
* Implementing wildcard DSL methods using <tt>method_missing</tt> doesn't
|
68
|
-
work. I haven't yet
|
67
|
+
work. I haven't yet decided on the right semantics for this case, or
|
68
|
+
whether it is even a reasonable feature at all.
|
69
69
|
* Including Blockenspiel::DSL in a module (rather than a class) is not yet
|
70
|
-
supported, but this is planned
|
70
|
+
supported, but this is planned for a future release.
|
71
|
+
* Rubinius support is not yet present, but on the to-do list.
|
72
|
+
* Installing on Windows may be a challenge because blockenspiel includes a
|
73
|
+
native extension. I'm considering evaluating Luis Lavena's rake-compiler
|
74
|
+
to simplify this process.
|
71
75
|
|
72
76
|
=== Development and support
|
73
77
|
|
@@ -85,8 +89,8 @@ Blockenspiel is written by Daniel Azuma (http://www.daniel-azuma.com/).
|
|
85
89
|
|
86
90
|
The mixin implementation is based on a concept by the late Why The Lucky
|
87
91
|
Stiff, documented in his 6 October 2008 blog posting entitled "Mixing Our
|
88
|
-
Way Out Of Instance Eval?". The original link
|
89
|
-
copies or mirrors out there.
|
92
|
+
Way Out Of Instance Eval?". The original link has disappeared along with
|
93
|
+
its author, but you may find copies or mirrors out there.
|
90
94
|
|
91
95
|
The unmixer code is based on {Mixology}[http://rubyforge.org/projects/mixology],
|
92
96
|
version 0.1 by Patrick Farley, anonymous z, Dan Manges, and Clint Bishop.
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Blockenspiel Rakefile
|
4
4
|
#
|
5
5
|
# -----------------------------------------------------------------------------
|
6
|
-
# Copyright 2008-
|
6
|
+
# Copyright 2008-2010 Daniel Azuma
|
7
7
|
#
|
8
8
|
# All rights reserved.
|
9
9
|
#
|
@@ -32,9 +32,9 @@
|
|
32
32
|
# POSSIBILITY OF SUCH DAMAGE.
|
33
33
|
# -----------------------------------------------------------------------------
|
34
34
|
|
35
|
+
require 'rubygems'
|
35
36
|
require 'rake'
|
36
37
|
require 'rake/clean'
|
37
|
-
require 'rake/gempackagetask'
|
38
38
|
require 'rake/testtask'
|
39
39
|
require 'rake/rdoctask'
|
40
40
|
require 'rdoc'
|
@@ -45,18 +45,36 @@ require ::File.expand_path("#{::File.dirname(__FILE__)}/lib/blockenspiel/version
|
|
45
45
|
|
46
46
|
|
47
47
|
# Configuration
|
48
|
-
|
48
|
+
EXTRA_RDOC_FILES = ['README.rdoc', 'Blockenspiel.rdoc', 'History.rdoc', 'ImplementingDSLblocks.rdoc']
|
49
|
+
|
50
|
+
|
51
|
+
# Environment configuration
|
52
|
+
dlext_ = Config::CONFIG['DLEXT']
|
53
|
+
if ::RUBY_PLATFORM =~ /java/
|
54
|
+
platform_suffix_ = 'java'
|
55
|
+
elsif ::RUBY_COPYRIGHT =~ /Yukihiro\sMatsumoto/i
|
56
|
+
if ::RUBY_VERSION =~ /^1\.8(\..*)?$/
|
57
|
+
platform_suffix_ = 'mri18'
|
58
|
+
elsif ::RUBY_VERSION =~ /^1\.9(\..*)?$/
|
59
|
+
platform_suffix_ = 'mri19'
|
60
|
+
else
|
61
|
+
raise "Unknown version of Matz Ruby Interpreter (#{::RUBY_VERSION})"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise "Could not identify the ruby runtime"
|
65
|
+
end
|
49
66
|
|
50
67
|
|
51
68
|
# Default task
|
52
|
-
task :default => [:clean, :
|
69
|
+
task :default => [:clean, :rdoc, :package, :test]
|
53
70
|
|
54
71
|
|
55
72
|
# Clean task
|
56
|
-
CLEAN.include(['ext/blockenspiel/Makefile',
|
73
|
+
CLEAN.include(['ext/blockenspiel/Makefile*', "**/*.#{dlext_}", 'ext/blockenspiel/unmixer.o', '**/blockenspiel_unmixer.jar', 'ext/blockenspiel/BlockenspielUnmixerService.class', 'idslb_markdown.txt', 'doc', 'pkg'])
|
57
74
|
|
58
75
|
|
59
76
|
# Test task
|
77
|
+
task :test => :build
|
60
78
|
::Rake::TestTask.new('test') do |task_|
|
61
79
|
task_.pattern = 'tests/tc_*.rb'
|
62
80
|
end
|
@@ -65,79 +83,98 @@ end
|
|
65
83
|
# RDoc task
|
66
84
|
::Rake::RDocTask.new do |task_|
|
67
85
|
task_.main = 'README.rdoc'
|
68
|
-
task_.rdoc_files.include(*
|
86
|
+
task_.rdoc_files.include(*EXTRA_RDOC_FILES)
|
69
87
|
task_.rdoc_files.include('lib/blockenspiel/*.rb')
|
70
88
|
task_.rdoc_dir = 'doc'
|
71
89
|
task_.title = "Blockenspiel #{::Blockenspiel::VERSION_STRING} documentation"
|
72
|
-
task_.options << '
|
90
|
+
task_.options << '--format=darkfish'
|
73
91
|
end
|
74
92
|
|
75
93
|
|
76
|
-
# Gem task
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
94
|
+
# Gem package task
|
95
|
+
task :package => [:build_java] do
|
96
|
+
mkdir_p('pkg')
|
97
|
+
|
98
|
+
# Common gemspec
|
99
|
+
def create_gemspec
|
100
|
+
::Gem::Specification.new do |s_|
|
101
|
+
s_.name = 'blockenspiel'
|
102
|
+
s_.summary = 'Blockenspiel is a helper library designed to make it easy to implement DSL blocks.'
|
103
|
+
s_.version = ::Blockenspiel::VERSION_STRING.dup
|
104
|
+
s_.author = 'Daniel Azuma'
|
105
|
+
s_.email = 'dazuma@gmail.com'
|
106
|
+
s_.description = 'Blockenspiel is a helper library designed to make it easy to implement DSL blocks. It is designed to be comprehensive and robust, supporting most common usage patterns, and working correctly in the presence of nested blocks and multithreading.'
|
107
|
+
s_.homepage = 'http://virtuoso.rubyforge.org/blockenspiel'
|
108
|
+
s_.rubyforge_project = 'virtuoso'
|
109
|
+
s_.required_ruby_version = '>= 1.8.7'
|
110
|
+
s_.files = ::FileList['ext/**/*.{c,rb,java}', 'lib/**/*.{rb,jar}', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
|
111
|
+
s_.extra_rdoc_files = EXTRA_RDOC_FILES.dup
|
112
|
+
s_.has_rdoc = true
|
113
|
+
s_.test_files = FileList['tests/tc_*.rb']
|
114
|
+
yield s_
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Normal platform gemspec
|
119
|
+
gemspec_ = create_gemspec do |s_|
|
95
120
|
s_.platform = ::Gem::Platform::RUBY
|
96
121
|
s_.extensions = ['ext/blockenspiel/extconf.rb']
|
97
122
|
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
123
|
+
::Gem::Builder.new(gemspec_).build
|
124
|
+
mv "blockenspiel-#{::Blockenspiel::VERSION_STRING}.gem", 'pkg'
|
125
|
+
|
126
|
+
# JRuby gemspec
|
127
|
+
gemspec_ = create_gemspec do |s_|
|
128
|
+
s_.platform = 'java'
|
129
|
+
s_.files += ['lib/blockenspiel_unmixer.jar']
|
130
|
+
end
|
131
|
+
::Gem::Builder.new(gemspec_).build
|
132
|
+
mv "blockenspiel-#{::Blockenspiel::VERSION_STRING}-java.gem", 'pkg'
|
103
133
|
end
|
104
134
|
|
105
135
|
|
106
136
|
# General build task
|
107
|
-
task :
|
137
|
+
task :build => ::RUBY_PLATFORM =~ /java/ ? [:build_java] : [:build_c]
|
108
138
|
|
109
139
|
|
110
140
|
# Build tasks for MRI
|
111
|
-
dlext_ = Config::CONFIG['DLEXT']
|
112
141
|
|
113
|
-
|
114
|
-
|
142
|
+
makefile_name_ = "Makefile_#{platform_suffix_}"
|
143
|
+
unmixer_general_name_ = "unmixer.#{dlext_}"
|
144
|
+
unmixer_name_ = "unmixer_#{platform_suffix_}.#{dlext_}"
|
115
145
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
146
|
+
desc 'Ensures the C extension appropriate to the current platform is present'
|
147
|
+
task :build_c => ["ext/blockenspiel/#{unmixer_name_}"] do
|
148
|
+
cp "ext/blockenspiel/#{unmixer_name_}", "lib/blockenspiel/#{unmixer_general_name_}"
|
120
149
|
end
|
121
150
|
|
122
|
-
file "ext/blockenspiel
|
151
|
+
file "ext/blockenspiel/#{unmixer_name_}" => ["ext/blockenspiel/#{makefile_name_}"] do
|
123
152
|
::Dir.chdir('ext/blockenspiel') do
|
153
|
+
cp makefile_name_, 'Makefile'
|
124
154
|
sh 'make'
|
155
|
+
rm 'unmixer.o'
|
156
|
+
mv unmixer_general_name_, unmixer_name_
|
125
157
|
end
|
126
158
|
end
|
127
159
|
|
128
|
-
file "
|
129
|
-
|
160
|
+
file "ext/blockenspiel/#{makefile_name_}" do
|
161
|
+
::Dir.chdir('ext/blockenspiel') do
|
162
|
+
ruby 'extconf.rb'
|
163
|
+
mv 'Makefile', makefile_name_
|
164
|
+
end
|
130
165
|
end
|
131
166
|
|
132
167
|
|
133
168
|
# Build tasks for JRuby
|
134
|
-
desc
|
135
|
-
task :
|
169
|
+
desc 'Builds the JRuby extension'
|
170
|
+
task :build_java => ['lib/blockenspiel_unmixer.jar']
|
171
|
+
|
172
|
+
file 'lib/blockenspiel_unmixer.jar' do
|
136
173
|
::Dir.chdir('ext/blockenspiel') do
|
137
174
|
sh 'javac -source 1.5 -target 1.5 -classpath $JRUBY_HOME/lib/jruby.jar BlockenspielUnmixerService.java'
|
138
175
|
sh 'jar cf blockenspiel_unmixer.jar BlockenspielUnmixerService.class'
|
139
|
-
cp 'blockenspiel_unmixer.jar', '../../lib/blockenspiel_unmixer.jar'
|
140
176
|
end
|
177
|
+
mv 'ext/blockenspiel/blockenspiel_unmixer.jar', 'lib'
|
141
178
|
end
|
142
179
|
|
143
180
|
|
@@ -151,63 +188,22 @@ end
|
|
151
188
|
|
152
189
|
|
153
190
|
# Publish gem
|
154
|
-
task :
|
155
|
-
v_ = ::ENV["VERSION"]
|
156
|
-
abort "Must supply VERSION=x.y.z" unless v_
|
157
|
-
if v_ != ::Blockenspiel::VERSION_STRING
|
158
|
-
abort "Versions don't match: #{v_} vs #{::Blockenspiel::VERSION_STRING}"
|
159
|
-
end
|
160
|
-
mri_pkg_ = "pkg/blockenspiel-#{v_}.gem"
|
161
|
-
jruby_pkg_ = "pkg/blockenspiel-#{v_}-java.gem"
|
162
|
-
tgz_pkg_ = "pkg/blockenspiel-#{v_}.tgz"
|
163
|
-
if !::File.file?(mri_pkg_) || !::File.readable?(mri_pkg_)
|
164
|
-
abort "You haven't built #{mri_pkg_} yet. Try rake package"
|
165
|
-
end
|
166
|
-
if !::File.file?(jruby_pkg_) || !::File.readable?(jruby_pkg_)
|
167
|
-
abort "You haven't built #{jruby_pkg_} yet. Try jrake package"
|
168
|
-
end
|
169
|
-
if !::File.file?(tgz_pkg_) || !::File.readable?(tgz_pkg_)
|
170
|
-
abort "You haven't built #{tgz_pkg_} yet. Try rake package"
|
171
|
-
end
|
172
|
-
release_notes_ = ::File.read("README.rdoc").split(/^(==.*)/)[2].strip
|
173
|
-
release_changes_ = ::File.read("History.rdoc").split(/^(===.*)/)[1..2].join.strip
|
174
|
-
|
175
|
-
require 'rubyforge'
|
176
|
-
rf_ = ::RubyForge.new.configure
|
177
|
-
puts "Logging in to RubyForge"
|
178
|
-
rf_.login
|
179
|
-
config_ = rf_.userconfig
|
180
|
-
config_["release_notes"] = release_notes_
|
181
|
-
config_["release_changes"] = release_changes_
|
182
|
-
config_["preformatted"] = true
|
183
|
-
puts "Releasing blockenspiel #{v_} to RubyForge"
|
184
|
-
rf_.add_release('virtuoso', 'blockenspiel', v_, mri_pkg_, jruby_pkg_, tgz_pkg_)
|
185
|
-
end
|
186
|
-
|
187
|
-
|
188
|
-
# Publish gem
|
189
|
-
task :release_gem_to_gemcutter => [:package] do |t_|
|
191
|
+
task :release_gem => [:package] do |t_|
|
190
192
|
v_ = ::ENV["VERSION"]
|
191
193
|
abort "Must supply VERSION=x.y.z" unless v_
|
192
194
|
if v_ != ::Blockenspiel::VERSION_STRING
|
193
195
|
abort "Versions don't match: #{v_} vs #{::Blockenspiel::VERSION_STRING}"
|
194
196
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
end
|
200
|
-
if !::File.file?("pkg/#{jruby_gem_}") || !::File.readable?("pkg/#{jruby_gem_}")
|
201
|
-
abort "You haven't built #{jruby_gem_} yet. Try jrake package"
|
197
|
+
puts "Releasing blockenspiel #{v_}"
|
198
|
+
::Dir.chdir('pkg') do
|
199
|
+
sh "gem push blockenspiel-#{v_}.gem"
|
200
|
+
sh "gem push blockenspiel-#{v_}-java.gem"
|
202
201
|
end
|
203
|
-
puts "Releasing blockenspiel #{v_} to GemCutter"
|
204
|
-
`cd pkg && gem push #{mri_gem_}`
|
205
|
-
`cd pkg && gem push #{jruby_gem_}`
|
206
202
|
end
|
207
203
|
|
208
204
|
|
209
205
|
# Publish everything
|
210
|
-
task :release => [:
|
206
|
+
task :release => [:release_gem, :publish_rdoc_to_rubyforge]
|
211
207
|
|
212
208
|
|
213
209
|
# Custom task that takes the implementing dsl blocks paper
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Blockenspiel dynamic target construction
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2008-2010 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
module Blockenspiel
|
38
|
+
|
39
|
+
|
40
|
+
# === Dynamically construct a target
|
41
|
+
#
|
42
|
+
# These methods are available in a block passed to Blockenspiel#invoke and
|
43
|
+
# can be used to dynamically define what methods are available from a block.
|
44
|
+
# See Blockenspiel#invoke for more information.
|
45
|
+
|
46
|
+
class Builder
|
47
|
+
|
48
|
+
include ::Blockenspiel::DSL
|
49
|
+
|
50
|
+
|
51
|
+
# This is a base class for dynamically constructed targets.
|
52
|
+
# The actual target class is an anonymous subclass of this base class.
|
53
|
+
|
54
|
+
class Target # :nodoc:
|
55
|
+
|
56
|
+
include ::Blockenspiel::DSL
|
57
|
+
|
58
|
+
|
59
|
+
# Add a method specification to the subclass.
|
60
|
+
|
61
|
+
def self._add_methodinfo(name_, block_, yields_)
|
62
|
+
(@_blockenspiel_methodinfo ||= {})[name_] = [block_, yields_]
|
63
|
+
module_eval("def #{name_}(*params_, &block_); self.class._invoke_methodinfo(:#{name_}, params_, block_); end\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# Attempt to invoke the given method on the subclass.
|
68
|
+
|
69
|
+
def self._invoke_methodinfo(name_, params_, block_)
|
70
|
+
info_ = @_blockenspiel_methodinfo[name_]
|
71
|
+
case info_[1]
|
72
|
+
when :first
|
73
|
+
params_.unshift(block_)
|
74
|
+
when :last
|
75
|
+
params_.push(block_)
|
76
|
+
end
|
77
|
+
info_[0].call(*params_, &block_)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Sets up the dynamic target class.
|
84
|
+
|
85
|
+
def initialize # :nodoc:
|
86
|
+
@target_class = ::Class.new(::Blockenspiel::Builder::Target)
|
87
|
+
@target_class.dsl_methods(false)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# Creates a new instance of the dynamic target class
|
92
|
+
|
93
|
+
def _create_target # :nodoc:
|
94
|
+
@target_class.new
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# === Declare a DSL method.
|
99
|
+
#
|
100
|
+
# This call creates a method that can be called from the DSL block.
|
101
|
+
# Provide a name for the method, a block defining the method's
|
102
|
+
# implementation, and an optional hash of options.
|
103
|
+
#
|
104
|
+
# By default, a method of the same name is also made available to
|
105
|
+
# parameterless blocks. To change the name of the parameterless method,
|
106
|
+
# provide its name as the value of the <tt>:dsl_method</tt> option.
|
107
|
+
# To disable this method for parameterless blocks, set the
|
108
|
+
# <tt>:dsl_method</tt> option to +false+.
|
109
|
+
#
|
110
|
+
# The <tt>:mixin</tt> option is a deprecated alias for
|
111
|
+
# <tt>:dsl_method</tt>.
|
112
|
+
#
|
113
|
+
# === Warning about the +return+ keyword
|
114
|
+
#
|
115
|
+
# Because you are implementing your method using a block, remember the
|
116
|
+
# distinction between <tt>Proc.new</tt> and +lambda+. Invoking +return+
|
117
|
+
# from the former does not return from the block, but returns from the
|
118
|
+
# surrounding method scope. Since normal blocks passed to methods are
|
119
|
+
# of the former type, be very careful about using the +return+ keyword:
|
120
|
+
#
|
121
|
+
# add_method(:foo) do |param|
|
122
|
+
# puts "foo called with parameter "+param.inspect
|
123
|
+
# return "a return value" # DOESN'T WORK LIKE YOU EXPECT!
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# To return a value from the method you are creating, set the evaluation
|
127
|
+
# value at the end of the block:
|
128
|
+
#
|
129
|
+
# add_method(:foo) do |param|
|
130
|
+
# puts "foo called with parameter "+param.inspect
|
131
|
+
# "a return value" # Returns from method foo
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# If you must use the +return+ keyword, create your block as a lambda
|
135
|
+
# as in this example:
|
136
|
+
#
|
137
|
+
# code = lambda do |param|
|
138
|
+
# puts "foo called with parameter "+param.inspect
|
139
|
+
# return "a return value" # Returns from method foo
|
140
|
+
# end
|
141
|
+
# add_method(:foo, &code)
|
142
|
+
#
|
143
|
+
# === Accepting a block argument
|
144
|
+
#
|
145
|
+
# If you want your method to take a block, you have several options
|
146
|
+
# depending on your Ruby version. If you are running the standard Matz
|
147
|
+
# Ruby interpreter (MRI) version 1.8.7 or later (including 1.9.x), or a
|
148
|
+
# compatible interpreter such as JRuby 1.5 or later, you can use the
|
149
|
+
# standard "&" block argument notation to receive the block.
|
150
|
+
# Note that you must call the passed block using the +call+ method since
|
151
|
+
# Ruby doesn't support invoking such a block with +yield+.
|
152
|
+
# For example, to create a method named "foo" that takes one parameter
|
153
|
+
# and a block, do this:
|
154
|
+
#
|
155
|
+
# add_method(:foo) do |param, &block|
|
156
|
+
# puts "foo called with parameter "+param.inspect
|
157
|
+
# puts "the block returned "+block.call.inspect
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# In your DSL, you can then call:
|
161
|
+
#
|
162
|
+
# foo("hello"){ "a value" }
|
163
|
+
#
|
164
|
+
# If you are using MRI 1.8.6, or another Ruby interpreter that doesn't
|
165
|
+
# fully support this syntax (such as JRuby versions older than 1.5),
|
166
|
+
# Blockenspiel provides an alternative in the form of the <tt>:block</tt>
|
167
|
+
# option. This option causes blocks provided by the caller to be included
|
168
|
+
# in the normal parameter list to your method, instead of as a block
|
169
|
+
# parameter. It can be set to <tt>:first</tt> or <tt>:last</tt> to
|
170
|
+
# prepend or append, respectively, the block (as a +Proc+ object) to
|
171
|
+
# the parameter list. If the caller does not include a block when
|
172
|
+
# calling your DSL method, nil is prepended/appended. For example:
|
173
|
+
#
|
174
|
+
# add_method(:foo, :block => :last) do |param, block|
|
175
|
+
# puts "foo called with parameter "+param.inspect
|
176
|
+
# if block
|
177
|
+
# puts "the block returned "+block.call.inspect
|
178
|
+
# else
|
179
|
+
# puts "no block passed"
|
180
|
+
# end
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
# The <tt>:receive_block</tt> option is a deprecated alternative.
|
184
|
+
# Setting <tt>:receive_block => true</tt> is currently equivalent to
|
185
|
+
# setting <tt>:block => :last</tt>.
|
186
|
+
|
187
|
+
def add_method(name_, opts_={}, &block_)
|
188
|
+
receive_block_ = opts_[:receive_block] ? :last : opts_[:block]
|
189
|
+
receive_block_ = :first if receive_block_ && receive_block_ != :last
|
190
|
+
@target_class._add_methodinfo(name_, block_, receive_block_)
|
191
|
+
dsl_method_name_ = opts_[:dsl_method] || opts_[:mixin]
|
192
|
+
if dsl_method_name_ != false
|
193
|
+
dsl_method_name_ = name_ if dsl_method_name_.nil? || dsl_method_name_ == true
|
194
|
+
@target_class.dsl_method(dsl_method_name_, name_)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
end
|