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 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.6 or later (1.8.7 recommended), Ruby 1.9.1 or later, or JRuby 1.2
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 figured out the right semantics for this case.
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 in a future release.
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.6 or later (1.8.7 recommended), Ruby 1.9.1 or later, or JRuby 1.2
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 figured out the right semantics for this case.
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 in a future release.
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 is gone, but you may find
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-2009 Daniel Azuma
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
- extra_rdoc_files_ = ['README.rdoc', 'Blockenspiel.rdoc', 'History.rdoc', 'ImplementingDSLblocks.rdoc']
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, :compile, :rdoc, :package, :test]
69
+ task :default => [:clean, :rdoc, :package, :test]
53
70
 
54
71
 
55
72
  # Clean task
56
- CLEAN.include(['ext/blockenspiel/Makefile', '**/unmixer.bundle', 'ext/blockenspiel/unmixer.o', '**/blockenspiel_unmixer.jar', 'ext/blockenspiel/BlockenspielUnmixerService.class', 'idslb_markdown.txt', 'doc', 'pkg'])
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(*extra_rdoc_files_)
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 << '-f' << 'darkfish'
90
+ task_.options << '--format=darkfish'
73
91
  end
74
92
 
75
93
 
76
- # Gem task
77
- gemspec_ = ::Gem::Specification.new do |s_|
78
- s_.name = 'blockenspiel'
79
- s_.summary = 'Blockenspiel is a helper library designed to make it easy to implement DSL blocks.'
80
- s_.version = ::Blockenspiel::VERSION_STRING.dup
81
- s_.author = 'Daniel Azuma'
82
- s_.email = 'dazuma@gmail.com'
83
- 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.'
84
- s_.homepage = 'http://virtuoso.rubyforge.org/blockenspiel'
85
- s_.rubyforge_project = 'virtuoso'
86
- s_.required_ruby_version = '>= 1.8.6'
87
- s_.files = ::FileList['ext/**/*.{c,rb,java}', 'lib/**/*.{rb,jar}', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
88
- s_.extra_rdoc_files = extra_rdoc_files_
89
- s_.has_rdoc = true
90
- s_.test_files = FileList['tests/tc_*.rb']
91
- if ::RUBY_PLATFORM =~ /java/
92
- s_.platform = 'java'
93
- s_.files += ['lib/blockenspiel_unmixer.jar']
94
- else
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
- end
99
- task :package => [:compile]
100
- ::Rake::GemPackageTask.new(gemspec_) do |task_|
101
- task_.need_zip = false
102
- task_.need_tar = ::RUBY_PLATFORM !~ /java/
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 :compile => ::RUBY_PLATFORM =~ /java/ ? [:compile_java] : [:compile_c]
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
- desc 'Builds the extension'
114
- task :compile_c => ["lib/blockenspiel/unmixer.#{dlext_}"]
142
+ makefile_name_ = "Makefile_#{platform_suffix_}"
143
+ unmixer_general_name_ = "unmixer.#{dlext_}"
144
+ unmixer_name_ = "unmixer_#{platform_suffix_}.#{dlext_}"
115
145
 
116
- file 'ext/blockenspiel/Makefile' => ['ext/blockenspiel/extconf.rb'] do
117
- ::Dir.chdir('ext/blockenspiel') do
118
- ruby 'extconf.rb'
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/unmixer.#{dlext_}" => ['ext/blockenspiel/Makefile', 'ext/blockenspiel/unmixer.c'] do
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 "lib/blockenspiel/unmixer.#{dlext_}" => ["ext/blockenspiel/unmixer.#{dlext_}"] do
129
- cp "ext/blockenspiel/unmixer.#{dlext_}", 'lib/blockenspiel'
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 "Compiles the JRuby extension"
135
- task :compile_java do
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 :release_gem_to_rubyforge => [:package] do |t_|
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
- mri_gem_ = "blockenspiel-#{v_}.gem"
196
- jruby_gem_ = "blockenspiel-#{v_}-java.gem"
197
- if !::File.file?("pkg/#{mri_gem_}") || !::File.readable?("pkg/#{mri_gem_}")
198
- abort "You haven't built #{mri_gem_} yet. Try rake package"
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 => [:release_gem_to_gemcutter, :release_gem_to_rubyforge, :publish_rdoc_to_rubyforge]
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