blockenspiel 0.2.1-java → 0.2.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.rdoc +8 -0
- data/ImplementingDSLblocks.rdoc +16 -10
- data/README.rdoc +9 -10
- data/Rakefile +47 -31
- data/lib/blockenspiel.rb +1 -1
- data/lib/blockenspiel/impl.rb +37 -37
- data/lib/blockenspiel/version.rb +2 -2
- data/lib/blockenspiel_unmixer.jar +0 -0
- data/tests/tc_basic.rb +8 -8
- data/tests/tc_behaviors.rb +23 -23
- data/tests/tc_dsl_methods.rb +28 -28
- data/tests/tc_dynamic.rb +15 -15
- data/tests/tc_mixins.rb +20 -20
- metadata +62 -63
data/History.rdoc
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 0.2.2 / 2009-10-28
|
2
|
+
|
3
|
+
* Support for gemcutter hosting in the build/release scripts.
|
4
|
+
* Some clarifications to constant scopes internal in the code.
|
5
|
+
* A few documentation updates.
|
6
|
+
* Minor changes to the Implementing DSL Blocks paper to deal with
|
7
|
+
Why's disappearance.
|
8
|
+
|
1
9
|
=== 0.2.1 / 2009-04-16
|
2
10
|
|
3
11
|
* Now compatible with Ruby 1.9.
|
data/ImplementingDSLblocks.rdoc
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
== Implementing DSL Blocks
|
2
2
|
|
3
|
-
by Daniel Azuma
|
3
|
+
by Daniel Azuma
|
4
4
|
|
5
5
|
A <em>DSL block</em> is a construct commonly used in Ruby APIs, in which a DSL (domain-specific language) is made available inside a block passed to an API call. In this paper I present an overview of different implementation strategies for this important pattern. I will first describe the features of DSL blocks, utilizing illustrations from several well-known Ruby libraries. I will then survey and critique five implementation strategies that have been put forth. Finally, I will present a new library, {Blockenspiel}[http://virtuoso.rubyforge.org/blockenspiel], designed to be a comprehensive implementation of DSL blocks.
|
6
6
|
|
7
|
+
Originally written on 29 October 2008.
|
8
|
+
|
9
|
+
Minor modifications on 28 October 2009 to deal with Why's disappearance.
|
10
|
+
|
7
11
|
=== An illustrative overview of DSL blocks
|
8
12
|
|
9
13
|
If you've done much Ruby programming, chances are you've run into mini-DSLs (domain-specific languages) that live inside blocks. Perhaps you've encountered them in Ruby standard library calls, such as <tt>File#open</tt>, a call that lets you interact with a stream while performing automatic setup and cleanup for you:
|
@@ -27,7 +31,7 @@ Perhaps you've used the XML {builder}[http://builder.rubyforge.org/] library, wh
|
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
|
-
The {Markaby}[http://
|
34
|
+
The {Markaby}[http://github.com/markaby/markaby] library also uses nested blocks to generate html, but is able to do so more succinctly without requiring you to explicitly reference a builder object:
|
31
35
|
|
32
36
|
Markaby::Builder.new.html do
|
33
37
|
head { title "Boats.com" }
|
@@ -366,11 +370,13 @@ The problem gets worse. Changing +self+ affects not only how methods are looked
|
|
366
370
|
|
367
371
|
What happened? If we recall, <tt>@set</tt> is used by the +Mapper+ object to point back to the routing +RouteSet+. It is how the proxy knows what it is proxying for. But since we've used <tt>instance_eval</tt>, we now have free access to the +Mapper+ object's internal instance variables, including the ability to clobber them. And that's precisely what we did here. Furthermore, maybe we were actually expecting to access our own <tt>@set</tt> variable, and we haven't done that. Any instance variables from the caller's closure are in fact no longer accessible inside the block.
|
368
372
|
|
373
|
+
Similarly, if you are using Ruby 1.9, constants are also looked up using +self+ as the starting point. So by changing +self+, <tt>instance_eval</tt> affects the availability of constants in surprising ways.
|
374
|
+
|
369
375
|
The problem gets even worse. If we think about the cryptic error message we got when we tried to use our +makeurl+ helper method, we begin to realize that we've run into the method lookup ambiguity discussed in the previous section. If +self+ has changed inside the block, and we tried to call +makeurl+, we might expect a +NoMethodError+ to be raised for +makeurl+ on the +Mapper+ class, rather than for "<tt>[]</tt>" on the +Symbol+ class. However, things change when we recall that Rails's routing DSL supports named routes. You do not have to call the specific +connect+ method to create a route. In fact, you can call _any_ method name. Any name is a valid DSL method name. It is thus ambiguous, when we invoke +makeurl+, whether we mean our helper method or a named route called "makeurl". Rails assumed we meant the named route, but in fact that isn't what we had intended.
|
370
376
|
|
371
|
-
This all sounds pretty bad. Do we give up on <tt>instance_eval</tt>? Some members of the Ruby community have, and indeed the technique has generally fallen out of favor in many major libraries. Jim Weirich, for instance, {originally}[http://onestepback.org/index.cgi/Tech/Ruby/BuilderObjects.rdoc] utilized <tt>instance_eval</tt> in the XML Builder library illustrated earlier, but later deprecated and removed it because of its surprising behavior. Why's {Markaby}[http://
|
377
|
+
This all sounds pretty bad. Do we give up on <tt>instance_eval</tt>? Some members of the Ruby community have, and indeed the technique has generally fallen out of favor in many major libraries. Jim Weirich, for instance, {originally}[http://onestepback.org/index.cgi/Tech/Ruby/BuilderObjects.rdoc] utilized <tt>instance_eval</tt> in the XML Builder library illustrated earlier, but later deprecated and removed it because of its surprising behavior. Why's {Markaby}[http://github.com/markaby/markaby] still uses <tt>instance_eval</tt> but includes a caveat in the {documentation}[http://markaby.rubyforge.org/] explaining the issues and recommending caution.
|
372
378
|
|
373
|
-
There are, however, a few specific cases when <tt>instance_eval</tt> may be uniquely appropriate. RSpec's DSL is intended as a class-constructive language: it constructs ruby classes behind the scenes. In the RSpec example at the beginning of this paper, you may notice the use of the <tt>@stack</tt> instance variable. In fact, this is intended as an instance variable of the RSpec test story being written, and as such, <tt>instance_eval</tt> is required because of the kind of language that RSpec wants to use. But in more common cases, such as specifying configuration, <tt>instance_eval</tt> does not give us the most desirable behavior. The general consensus now, expressed for example in recent articles from
|
379
|
+
There are, however, a few specific cases when <tt>instance_eval</tt> may be uniquely appropriate. RSpec's DSL is intended as a class-constructive language: it constructs ruby classes behind the scenes. In the RSpec example at the beginning of this paper, you may notice the use of the <tt>@stack</tt> instance variable. In fact, this is intended as an instance variable of the RSpec test story being written, and as such, <tt>instance_eval</tt> is required because of the kind of language that RSpec wants to use. But in more common cases, such as specifying configuration, <tt>instance_eval</tt> does not give us the most desirable behavior. The general consensus now, expressed for example in recent articles from Why (no longer available) and {Ola Bini}[http://olabini.com/blog/2008/09/dont-overuse-instance_eval-and-instance_exec/], is that it should be avoided.
|
374
380
|
|
375
381
|
So does this mean we're stuck with block parameters for better or worse? Not quite. Several alternatives have been proposed recently, and we'll take a look at them in the next few sections. But first, let's summarize the discussion of <tt>instance_eval</tt>.
|
376
382
|
|
@@ -630,7 +636,7 @@ Let us summarize Gray's arity detection technique, and then proceed to an intere
|
|
630
636
|
|
631
637
|
=== Implementation strategy 5: mixins
|
632
638
|
|
633
|
-
One of the most interesting entries into the DSL blocks discussion was proposed by Why The Lucky Stiff in his
|
639
|
+
One of the most interesting entries into the DSL blocks discussion was proposed by Why The Lucky Stiff in his blog. Unfortunately, with Why's disappearance, the original article is no longer available, but we can summarize its contents here. Why observes that the problem with <tt>instance_eval</tt> is that it does too much. Most DSL blocks merely want to be able to intercept and respond to certain method calls, whereas <tt>instance_eval</tt> actually changes +self+, which has the additional side effects of blocking access to other methods and instance variables, and breaking encapsulation. A better solution, he maintains, is not to change +self+, but instead temporarily to add the DSL's methods to the block's context for the duration of the block. That is, instead of having the DSL proxy object delegate back to the block's context object, do the opposite: cause the block's context object to delegate to the DSL proxy object.
|
634
640
|
|
635
641
|
Implementing this is actually harder than it sounds. We need to take the block context object, dynamically add methods to it before calling the block, and then dynamically remove them afterward. We already know how to get the block context object, but adding and removing methods requires some more Ruby metaprogramming wizardry. And now we're stretching our toolbox to the breaking point.
|
636
642
|
|
@@ -669,7 +675,7 @@ What we would really like is a way to add methods to just one object temporarily
|
|
669
675
|
s1.foo # prints "foo called"
|
670
676
|
s2.foo # NameError: s2 is unchanged
|
671
677
|
|
672
|
-
Unfortunately, there is no way to remove the module from the object. Ruby has no "unextend" capability. This omission led Why to implement it himself as a Ruby language extension called {Mixico}[http://github.com/
|
678
|
+
Unfortunately, there is no way to remove the module from the object. Ruby has no "unextend" capability. This omission led Why to implement it himself as a Ruby language extension called {Mixico}[http://github.com/rkh/mixico]. The name comes from the library's ability to add and remove "mixins" at will. A similar library exists as a gem called {Mixology}[http://www.somethingnimble.com/bliki/mixology]. The two libraries use different APIs but perform the same basic function. For the discussion below, I will assume Mixico is installed. However, the library I describe in the next section uses a custom implementation that is compatible with MRI 1.9 and JRuby.
|
673
679
|
|
674
680
|
Using Mixico, we can now write the +draw+ method like this:
|
675
681
|
|
@@ -923,12 +929,12 @@ The Blockenspiel library provides a concrete and robust implementation of DSL bl
|
|
923
929
|
|
924
930
|
{Jim Weirich}[http://onestepback.org/], <em>{ruby-core:19153}[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/19153]</em>, 2008.10.07
|
925
931
|
|
926
|
-
{Why The Lucky Stiff}[http://
|
932
|
+
{Why The Lucky Stiff}[http://en.wikipedia.org/wiki/Why_the_lucky_stiff], <em>{Markaby}[http://github.com/markaby/markaby]</em> (Ruby library), 2006.
|
927
933
|
|
928
|
-
{Why The Lucky Stiff}[http://
|
934
|
+
{Why The Lucky Stiff}[http://en.wikipedia.org/wiki/Why_the_lucky_stiff], <em>{Mixico}[http://github.com/rkh/mixico]</em> (Ruby library), 2008.
|
929
935
|
|
930
|
-
{Why The Lucky Stiff}[http://
|
936
|
+
{Why The Lucky Stiff}[http://en.wikipedia.org/wiki/Why_the_lucky_stiff], <em>Mixing Our Way Out Of Instance Eval?</em> (no longer online), 2008.10.06.
|
931
937
|
|
932
938
|
=== About the author
|
933
939
|
|
934
|
-
Daniel Azuma is Chief Software Architect at
|
940
|
+
Daniel Azuma is Chief Software Architect at GeoPage. He has been working with Ruby for about three years, and finds the language generally pleasant to work with, though he thinks the scoping rules could use some improvement. His home page is at http://www.daniel-azuma.com/
|
data/README.rdoc
CHANGED
@@ -284,8 +284,7 @@ concurrently.
|
|
284
284
|
|
285
285
|
=== Requirements
|
286
286
|
|
287
|
-
* Ruby 1.8.7 or later, or JRuby 1.2 or later
|
288
|
-
* Rubygems
|
287
|
+
* Ruby 1.8.6 or later (1.8.7 recommended), Ruby 1.9.1 or later, or JRuby 1.2 or later (1.4 recommended).
|
289
288
|
|
290
289
|
=== Installation
|
291
290
|
|
@@ -298,11 +297,11 @@ concurrently.
|
|
298
297
|
|
299
298
|
=== Development and support
|
300
299
|
|
301
|
-
Documentation is available at http://virtuoso.rubyforge.org/blockenspiel
|
300
|
+
Documentation is available at http://virtuoso.rubyforge.org/blockenspiel/README_rdoc.html
|
302
301
|
|
303
|
-
Source code is hosted
|
302
|
+
Source code is hosted on Github at http://github.com/dazuma/blockenspiel
|
304
303
|
|
305
|
-
Report bugs on
|
304
|
+
Report bugs on Github issues at http://github.org/dazuma/blockenspiel/issues
|
306
305
|
|
307
306
|
Contact the author at dazuma at gmail dot com.
|
308
307
|
|
@@ -310,13 +309,13 @@ Contact the author at dazuma at gmail dot com.
|
|
310
309
|
|
311
310
|
Blockenspiel is written by Daniel Azuma (http://www.daniel-azuma.com/).
|
312
311
|
|
313
|
-
The mixin implementation is based on a concept by Why The Lucky
|
314
|
-
|
315
|
-
|
316
|
-
|
312
|
+
The mixin implementation is based on a concept by the late Why The Lucky
|
313
|
+
Stiff, documented in his 6 October 2008 blog posting entitled "Mixing Our
|
314
|
+
Way Out Of Instance Eval?". The original link is gone, but you may find
|
315
|
+
copies or mirrors out there.
|
317
316
|
|
318
317
|
The unmixer code is based on {Mixology}[http://rubyforge.org/projects/mixology],
|
319
|
-
by Patrick Farley, anonymous z, Dan Manges, and Clint Bishop.
|
318
|
+
version 0.1 by Patrick Farley, anonymous z, Dan Manges, and Clint Bishop.
|
320
319
|
The code has been stripped down and modified to support MRI 1.9 and JRuby 1.2.
|
321
320
|
|
322
321
|
=== License
|
data/Rakefile
CHANGED
@@ -39,7 +39,7 @@ require 'rake/testtask'
|
|
39
39
|
require 'rake/rdoctask'
|
40
40
|
require 'rdoc/generator/darkfish'
|
41
41
|
|
42
|
-
require File.expand_path("#{File.dirname(__FILE__)}/lib/blockenspiel/version")
|
42
|
+
require ::File.expand_path("#{::File.dirname(__FILE__)}/lib/blockenspiel/version")
|
43
43
|
|
44
44
|
|
45
45
|
# Configuration
|
@@ -47,7 +47,7 @@ extra_rdoc_files_ = ['README.rdoc', 'History.rdoc', 'ImplementingDSLblocks.rdoc'
|
|
47
47
|
|
48
48
|
|
49
49
|
# Default task
|
50
|
-
task :default => [:clean, :compile, :rdoc, :test]
|
50
|
+
task :default => [:clean, :compile, :rdoc, :package, :test]
|
51
51
|
|
52
52
|
|
53
53
|
# Clean task
|
@@ -55,54 +55,54 @@ CLEAN.include(['ext/blockenspiel/Makefile', '**/unmixer.bundle', 'ext/blockenspi
|
|
55
55
|
|
56
56
|
|
57
57
|
# Test task
|
58
|
-
Rake::TestTask.new('test') do |task_|
|
58
|
+
::Rake::TestTask.new('test') do |task_|
|
59
59
|
task_.pattern = 'tests/tc_*.rb'
|
60
60
|
end
|
61
61
|
|
62
62
|
|
63
63
|
# RDoc task
|
64
|
-
Rake::RDocTask.new do |task_|
|
64
|
+
::Rake::RDocTask.new do |task_|
|
65
65
|
task_.main = 'README.rdoc'
|
66
66
|
task_.rdoc_files.include(*extra_rdoc_files_)
|
67
67
|
task_.rdoc_files.include('lib/blockenspiel/*.rb')
|
68
68
|
task_.rdoc_dir = 'doc'
|
69
|
-
task_.title = "Blockenspiel #{Blockenspiel::VERSION_STRING} documentation"
|
69
|
+
task_.title = "Blockenspiel #{::Blockenspiel::VERSION_STRING} documentation"
|
70
70
|
task_.options << '-f' << 'darkfish'
|
71
71
|
end
|
72
72
|
|
73
73
|
|
74
74
|
# Gem task
|
75
|
-
gemspec_ = Gem::Specification.new do |s_|
|
75
|
+
gemspec_ = ::Gem::Specification.new do |s_|
|
76
76
|
s_.name = 'blockenspiel'
|
77
77
|
s_.summary = 'Blockenspiel is a helper library designed to make it easy to implement DSL blocks.'
|
78
|
-
s_.version = Blockenspiel::VERSION_STRING
|
78
|
+
s_.version = ::Blockenspiel::VERSION_STRING
|
79
79
|
s_.author = 'Daniel Azuma'
|
80
80
|
s_.email = 'dazuma@gmail.com'
|
81
81
|
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.'
|
82
82
|
s_.homepage = 'http://virtuoso.rubyforge.org/blockenspiel'
|
83
83
|
s_.rubyforge_project = 'virtuoso'
|
84
84
|
s_.required_ruby_version = '>= 1.8.6'
|
85
|
-
s_.files = FileList['ext/**/*.{c,rb,java}', 'lib/**/*.{rb,jar}', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
|
85
|
+
s_.files = ::FileList['ext/**/*.{c,rb,java}', 'lib/**/*.{rb,jar}', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
|
86
86
|
s_.extra_rdoc_files = extra_rdoc_files_
|
87
87
|
s_.has_rdoc = true
|
88
88
|
s_.test_files = FileList['tests/tc_*.rb']
|
89
|
-
if RUBY_PLATFORM =~ /java/
|
89
|
+
if ::RUBY_PLATFORM =~ /java/
|
90
90
|
s_.platform = 'java'
|
91
91
|
s_.files += ['lib/blockenspiel_unmixer.jar']
|
92
92
|
else
|
93
|
-
s_.platform = Gem::Platform::RUBY
|
93
|
+
s_.platform = ::Gem::Platform::RUBY
|
94
94
|
s_.extensions = ['ext/blockenspiel/extconf.rb']
|
95
95
|
end
|
96
96
|
end
|
97
97
|
task :package => [:compile]
|
98
|
-
Rake::GemPackageTask.new(gemspec_) do |task_|
|
98
|
+
::Rake::GemPackageTask.new(gemspec_) do |task_|
|
99
99
|
task_.need_zip = false
|
100
|
-
task_.need_tar = RUBY_PLATFORM !~ /java/
|
100
|
+
task_.need_tar = ::RUBY_PLATFORM !~ /java/
|
101
101
|
end
|
102
102
|
|
103
103
|
|
104
104
|
# General build task
|
105
|
-
task :compile => RUBY_PLATFORM =~ /java/ ? [:compile_java] : [:compile_c]
|
105
|
+
task :compile => ::RUBY_PLATFORM =~ /java/ ? [:compile_java] : [:compile_c]
|
106
106
|
|
107
107
|
|
108
108
|
# Build tasks for MRI
|
@@ -112,13 +112,13 @@ desc 'Builds the extension'
|
|
112
112
|
task :compile_c => ["lib/blockenspiel/unmixer.#{dlext_}"]
|
113
113
|
|
114
114
|
file 'ext/blockenspiel/Makefile' => ['ext/blockenspiel/extconf.rb'] do
|
115
|
-
Dir.chdir('ext/blockenspiel') do
|
115
|
+
::Dir.chdir('ext/blockenspiel') do
|
116
116
|
ruby 'extconf.rb'
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
120
|
file "ext/blockenspiel/unmixer.#{dlext_}" => ['ext/blockenspiel/Makefile', 'ext/blockenspiel/unmixer.c'] do
|
121
|
-
Dir.chdir('ext/blockenspiel') do
|
121
|
+
::Dir.chdir('ext/blockenspiel') do
|
122
122
|
sh 'make'
|
123
123
|
end
|
124
124
|
end
|
@@ -131,7 +131,7 @@ end
|
|
131
131
|
# Build tasks for JRuby
|
132
132
|
desc "Compiles the JRuby extension"
|
133
133
|
task :compile_java do
|
134
|
-
Dir.chdir('ext/blockenspiel') do
|
134
|
+
::Dir.chdir('ext/blockenspiel') do
|
135
135
|
sh 'javac -source 1.5 -target 1.5 -classpath $JRUBY_HOME/lib/jruby.jar BlockenspielUnmixerService.java'
|
136
136
|
sh 'jar cf blockenspiel_unmixer.jar BlockenspielUnmixerService.class'
|
137
137
|
cp 'blockenspiel_unmixer.jar', '../../lib/blockenspiel_unmixer.jar'
|
@@ -141,53 +141,69 @@ end
|
|
141
141
|
|
142
142
|
# Publish RDocs
|
143
143
|
desc 'Publishes RDocs to RubyForge'
|
144
|
-
task :
|
145
|
-
config_ = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
144
|
+
task :publish_rdoc_to_rubyforge => [:rerdoc] do
|
145
|
+
config_ = ::YAML.load(::File.read(::File.expand_path("~/.rubyforge/user-config.yml")))
|
146
146
|
username_ = config_['username']
|
147
147
|
sh "rsync -av --delete doc/ #{username_}@rubyforge.org:/var/www/gforge-projects/virtuoso/blockenspiel"
|
148
148
|
end
|
149
149
|
|
150
150
|
|
151
151
|
# Publish gem
|
152
|
-
task :
|
153
|
-
v_ = ENV["VERSION"]
|
152
|
+
task :release_gem_to_rubyforge do |t_|
|
153
|
+
v_ = ::ENV["VERSION"]
|
154
154
|
abort "Must supply VERSION=x.y.z" unless v_
|
155
|
-
if v_ != Blockenspiel::VERSION_STRING
|
156
|
-
abort "Versions don't match: #{v_} vs #{Blockenspiel::VERSION_STRING}"
|
155
|
+
if v_ != ::Blockenspiel::VERSION_STRING
|
156
|
+
abort "Versions don't match: #{v_} vs #{::Blockenspiel::VERSION_STRING}"
|
157
157
|
end
|
158
158
|
mri_pkg_ = "pkg/blockenspiel-#{v_}.gem"
|
159
159
|
jruby_pkg_ = "pkg/blockenspiel-#{v_}-java.gem"
|
160
160
|
tgz_pkg_ = "pkg/blockenspiel-#{v_}.tgz"
|
161
|
-
if
|
161
|
+
if !::File.file?(mri_pkg_) || !::File.readable?(mri_pkg_)
|
162
162
|
abort "You haven't built #{mri_pkg_} yet. Try rake package"
|
163
163
|
end
|
164
|
-
if
|
164
|
+
if !::File.file?(jruby_pkg_) || !::File.readable?(jruby_pkg_)
|
165
165
|
abort "You haven't built #{jruby_pkg_} yet. Try jrake package"
|
166
166
|
end
|
167
|
-
if
|
167
|
+
if !::File.file?(tgz_pkg_) || !::File.readable?(tgz_pkg_)
|
168
168
|
abort "You haven't built #{tgz_pkg_} yet. Try rake package"
|
169
169
|
end
|
170
|
-
release_notes_ = File.read("README.rdoc").split(/^(==.*)/)[2].strip
|
171
|
-
release_changes_ = File.read("History.rdoc").split(/^(===.*)/)[1..2].join.strip
|
170
|
+
release_notes_ = ::File.read("README.rdoc").split(/^(==.*)/)[2].strip
|
171
|
+
release_changes_ = ::File.read("History.rdoc").split(/^(===.*)/)[1..2].join.strip
|
172
172
|
|
173
173
|
require 'rubyforge'
|
174
|
-
rf_ = RubyForge.new.configure
|
174
|
+
rf_ = ::RubyForge.new.configure
|
175
175
|
puts "Logging in to RubyForge"
|
176
176
|
rf_.login
|
177
177
|
config_ = rf_.userconfig
|
178
178
|
config_["release_notes"] = release_notes_
|
179
179
|
config_["release_changes"] = release_changes_
|
180
180
|
config_["preformatted"] = true
|
181
|
-
puts "Releasing blockenspiel #{v_}"
|
181
|
+
puts "Releasing blockenspiel #{v_} to RubyForge"
|
182
182
|
rf_.add_release('virtuoso', 'blockenspiel', v_, mri_pkg_, jruby_pkg_, tgz_pkg_)
|
183
183
|
end
|
184
184
|
|
185
185
|
|
186
|
+
# Publish gem
|
187
|
+
task :release_gem_to_gemcutter => [:package] do |t_|
|
188
|
+
v_ = ::ENV["VERSION"]
|
189
|
+
abort "Must supply VERSION=x.y.z" unless v_
|
190
|
+
if v_ != ::Blockenspiel::VERSION_STRING
|
191
|
+
abort "Versions don't match: #{v_} vs #{::Blockenspiel::VERSION_STRING}"
|
192
|
+
end
|
193
|
+
puts "Releasing blockenspiel #{v_} to GemCutter"
|
194
|
+
`cd pkg && gem push blockenspiel-#{v_}.gem`
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
# Publish everything
|
199
|
+
task :release => [:release_gem_to_gemcutter, :release_gem_to_rubyforge, :publish_rdoc_to_rubyforge]
|
200
|
+
|
201
|
+
|
186
202
|
# Custom task that takes the implementing dsl blocks paper
|
187
203
|
# and converts it from RDoc format to Markdown
|
188
204
|
task :idslb_markdown do
|
189
|
-
File.open('ImplementingDSLblocks.txt') do |read_|
|
190
|
-
File.open('idslb_markdown.txt', 'w') do |write_|
|
205
|
+
::File.open('ImplementingDSLblocks.txt') do |read_|
|
206
|
+
::File.open('idslb_markdown.txt', 'w') do |write_|
|
191
207
|
linenum_ = 0
|
192
208
|
read_.each do |line_|
|
193
209
|
linenum_ += 1
|
data/lib/blockenspiel.rb
CHANGED
data/lib/blockenspiel/impl.rb
CHANGED
@@ -47,7 +47,7 @@ module Blockenspiel
|
|
47
47
|
|
48
48
|
# Base exception for all exceptions raised by Blockenspiel
|
49
49
|
|
50
|
-
class BlockenspielError < RuntimeError
|
50
|
+
class BlockenspielError < ::RuntimeError
|
51
51
|
end
|
52
52
|
|
53
53
|
|
@@ -55,14 +55,14 @@ module Blockenspiel
|
|
55
55
|
# <tt>:mixin</tt> parameterless behavior with a target that does not have
|
56
56
|
# the DSL module included. It is an error made by the DSL implementor.
|
57
57
|
|
58
|
-
class DSLMissingError < BlockenspielError
|
58
|
+
class DSLMissingError < ::Blockenspiel::BlockenspielError
|
59
59
|
end
|
60
60
|
|
61
61
|
|
62
62
|
# This exception is raised when the block provided does not take the
|
63
63
|
# expected number of parameters. It is an error made by the caller.
|
64
64
|
|
65
|
-
class BlockParameterError < BlockenspielError
|
65
|
+
class BlockParameterError < ::Blockenspiel::BlockenspielError
|
66
66
|
end
|
67
67
|
|
68
68
|
|
@@ -90,7 +90,7 @@ module Blockenspiel
|
|
90
90
|
unless klass_.instance_variable_defined?(:@_blockenspiel_module)
|
91
91
|
_setup_class(klass_)
|
92
92
|
def klass_.inherited(subklass_)
|
93
|
-
Blockenspiel::DSLSetupMethods._setup_class(subklass_)
|
93
|
+
::Blockenspiel::DSLSetupMethods._setup_class(subklass_)
|
94
94
|
super
|
95
95
|
end
|
96
96
|
end
|
@@ -105,7 +105,7 @@ module Blockenspiel
|
|
105
105
|
def self._setup_class(klass_) # :nodoc:
|
106
106
|
superclass_ = klass_.superclass
|
107
107
|
superclass_ = nil unless superclass_.respond_to?(:_get_blockenspiel_module)
|
108
|
-
mod_ = Module.new
|
108
|
+
mod_ = ::Module.new
|
109
109
|
if superclass_
|
110
110
|
mod_.module_eval do
|
111
111
|
include superclass_._get_blockenspiel_module
|
@@ -113,7 +113,7 @@ module Blockenspiel
|
|
113
113
|
end
|
114
114
|
klass_.instance_variable_set(:@_blockenspiel_superclass, superclass_)
|
115
115
|
klass_.instance_variable_set(:@_blockenspiel_module, mod_)
|
116
|
-
klass_.instance_variable_set(:@_blockenspiel_methods, Hash.new)
|
116
|
+
klass_.instance_variable_set(:@_blockenspiel_methods, ::Hash.new)
|
117
117
|
klass_.instance_variable_set(:@_blockenspiel_active, nil)
|
118
118
|
end
|
119
119
|
|
@@ -177,8 +177,8 @@ module Blockenspiel
|
|
177
177
|
unless @_blockenspiel_module.public_method_defined?(name_)
|
178
178
|
@_blockenspiel_module.module_eval("
|
179
179
|
def #{name_}(*params_, &block_)
|
180
|
-
val_ = Blockenspiel._target_dispatch(self, :#{name_}, params_, block_)
|
181
|
-
val_ == Blockenspiel::TARGET_MISMATCH ? super(*params_, &block_) : val_
|
180
|
+
val_ = ::Blockenspiel._target_dispatch(self, :#{name_}, params_, block_)
|
181
|
+
val_ == ::Blockenspiel::TARGET_MISMATCH ? super(*params_, &block_) : val_
|
182
182
|
end
|
183
183
|
")
|
184
184
|
end
|
@@ -218,7 +218,7 @@ module Blockenspiel
|
|
218
218
|
elsif names_ == [false]
|
219
219
|
@_blockenspiel_active = false
|
220
220
|
else
|
221
|
-
if names_.last.kind_of?(Hash)
|
221
|
+
if names_.last.kind_of?(::Hash)
|
222
222
|
names_.pop.each do |name_, delegate_|
|
223
223
|
dsl_method(name_, delegate_)
|
224
224
|
end
|
@@ -245,7 +245,7 @@ module Blockenspiel
|
|
245
245
|
module DSL
|
246
246
|
|
247
247
|
def self.included(klass_) # :nodoc:
|
248
|
-
klass_.extend(Blockenspiel::DSLSetupMethods)
|
248
|
+
klass_.extend(::Blockenspiel::DSLSetupMethods)
|
249
249
|
end
|
250
250
|
|
251
251
|
end
|
@@ -264,7 +264,7 @@ module Blockenspiel
|
|
264
264
|
|
265
265
|
class Base
|
266
266
|
|
267
|
-
include Blockenspiel::DSL
|
267
|
+
include ::Blockenspiel::DSL
|
268
268
|
|
269
269
|
end
|
270
270
|
|
@@ -277,7 +277,7 @@ module Blockenspiel
|
|
277
277
|
class ProxyDelegator # :nodoc:
|
278
278
|
|
279
279
|
def method_missing(symbol_, *params_, &block_)
|
280
|
-
Blockenspiel._proxy_dispatch(self, symbol_, params_, block_)
|
280
|
+
::Blockenspiel._proxy_dispatch(self, symbol_, params_, block_)
|
281
281
|
end
|
282
282
|
|
283
283
|
end
|
@@ -291,7 +291,7 @@ module Blockenspiel
|
|
291
291
|
|
292
292
|
class Builder
|
293
293
|
|
294
|
-
include Blockenspiel::DSL
|
294
|
+
include ::Blockenspiel::DSL
|
295
295
|
|
296
296
|
|
297
297
|
# This is a base class for dynamically constructed targets.
|
@@ -299,13 +299,13 @@ module Blockenspiel
|
|
299
299
|
|
300
300
|
class Target # :nodoc:
|
301
301
|
|
302
|
-
include Blockenspiel::DSL
|
302
|
+
include ::Blockenspiel::DSL
|
303
303
|
|
304
304
|
|
305
305
|
# Add a method specification to the subclass.
|
306
306
|
|
307
307
|
def self._add_methodinfo(name_, block_, yields_)
|
308
|
-
(@_blockenspiel_methodinfo ||= Hash.new)[name_] = [block_, yields_]
|
308
|
+
(@_blockenspiel_methodinfo ||= ::Hash.new)[name_] = [block_, yields_]
|
309
309
|
module_eval("
|
310
310
|
def #{name_}(*params_, &block_)
|
311
311
|
self.class._invoke_methodinfo(:#{name_}, params_, block_)
|
@@ -333,7 +333,7 @@ module Blockenspiel
|
|
333
333
|
# Sets up the dynamic target class.
|
334
334
|
|
335
335
|
def initialize # :nodoc:
|
336
|
-
@target_class = Class.new(Blockenspiel::Builder::Target)
|
336
|
+
@target_class = ::Class.new(::Blockenspiel::Builder::Target)
|
337
337
|
@target_class.dsl_methods(false)
|
338
338
|
end
|
339
339
|
|
@@ -402,13 +402,13 @@ module Blockenspiel
|
|
402
402
|
|
403
403
|
|
404
404
|
# :stopdoc:
|
405
|
-
TARGET_MISMATCH = Object.new
|
405
|
+
TARGET_MISMATCH = ::Object.new
|
406
406
|
# :startdoc:
|
407
407
|
|
408
|
-
@_target_stacks = Hash.new
|
409
|
-
@_mixin_counts = Hash.new
|
410
|
-
@_proxy_delegators = Hash.new
|
411
|
-
@_mutex = Mutex.new
|
408
|
+
@_target_stacks = ::Hash.new
|
409
|
+
@_mixin_counts = ::Hash.new
|
410
|
+
@_proxy_delegators = ::Hash.new
|
411
|
+
@_mutex = ::Mutex.new
|
412
412
|
|
413
413
|
|
414
414
|
# === Invoke a given block.
|
@@ -471,7 +471,7 @@ module Blockenspiel
|
|
471
471
|
# add_method(:set_foo) do |value|
|
472
472
|
# my_foo = value
|
473
473
|
# end
|
474
|
-
# add_method(:set_things_from_block, :
|
474
|
+
# add_method(:set_things_from_block, :block => :last) do |value,blk|
|
475
475
|
# my_foo = value
|
476
476
|
# my_bar = blk.call
|
477
477
|
# end
|
@@ -513,7 +513,7 @@ module Blockenspiel
|
|
513
513
|
def self.invoke(block_, target_=nil, opts_={}, &builder_block_)
|
514
514
|
|
515
515
|
unless block_
|
516
|
-
raise ArgumentError, "Block expected"
|
516
|
+
raise ::ArgumentError, "Block expected"
|
517
517
|
end
|
518
518
|
parameter_ = opts_[:parameter]
|
519
519
|
parameterless_ = opts_[:parameterless]
|
@@ -521,7 +521,7 @@ module Blockenspiel
|
|
521
521
|
# Handle no-target behavior
|
522
522
|
if parameter_ == false && parameterless_ == false
|
523
523
|
if block_.arity != 0 && block_.arity != -1
|
524
|
-
raise Blockenspiel::BlockParameterError, "Block should not take parameters"
|
524
|
+
raise ::Blockenspiel::BlockParameterError, "Block should not take parameters"
|
525
525
|
end
|
526
526
|
return block_.call
|
527
527
|
end
|
@@ -529,7 +529,7 @@ module Blockenspiel
|
|
529
529
|
# Perform dynamic target generation if requested
|
530
530
|
if builder_block_
|
531
531
|
opts_ = target_ || opts_
|
532
|
-
builder_ = Blockenspiel::Builder.new
|
532
|
+
builder_ = ::Blockenspiel::Builder.new
|
533
533
|
invoke(builder_block_, builder_)
|
534
534
|
target_ = builder_._create_target
|
535
535
|
end
|
@@ -537,14 +537,14 @@ module Blockenspiel
|
|
537
537
|
# Handle parametered block case
|
538
538
|
if parameter_ != false && block_.arity == 1 || parameterless_ == false
|
539
539
|
if block_.arity != 1
|
540
|
-
raise Blockenspiel::BlockParameterError, "Block should take exactly one parameter"
|
540
|
+
raise ::Blockenspiel::BlockParameterError, "Block should take exactly one parameter"
|
541
541
|
end
|
542
542
|
return block_.call(target_)
|
543
543
|
end
|
544
544
|
|
545
545
|
# Check arity for parameterless case
|
546
546
|
if block_.arity != 0 && block_.arity != -1
|
547
|
-
raise Blockenspiel::BlockParameterError, "Block should not take parameters"
|
547
|
+
raise ::Blockenspiel::BlockParameterError, "Block should not take parameters"
|
548
548
|
end
|
549
549
|
|
550
550
|
# Handle instance-eval behavior
|
@@ -555,22 +555,22 @@ module Blockenspiel
|
|
555
555
|
# Get the module of dsl methods
|
556
556
|
mod_ = target_.class._get_blockenspiel_module rescue nil
|
557
557
|
unless mod_
|
558
|
-
raise Blockenspiel::DSLMissingError
|
558
|
+
raise ::Blockenspiel::DSLMissingError
|
559
559
|
end
|
560
560
|
|
561
561
|
# Get the block's calling context object
|
562
|
-
object_ = Kernel.eval('self', block_.binding)
|
562
|
+
object_ = ::Kernel.eval('self', block_.binding)
|
563
563
|
|
564
564
|
# Handle proxy behavior
|
565
565
|
if parameterless_ == :proxy
|
566
566
|
|
567
567
|
# Create proxy object
|
568
|
-
proxy_ = Blockenspiel::ProxyDelegator.new
|
568
|
+
proxy_ = ::Blockenspiel::ProxyDelegator.new
|
569
569
|
proxy_.extend(mod_)
|
570
570
|
|
571
571
|
# Store the target and proxy object so dispatchers can get them
|
572
572
|
proxy_delegator_key_ = proxy_.object_id
|
573
|
-
target_stack_key_ = [Thread.current.object_id, proxy_.object_id]
|
573
|
+
target_stack_key_ = [::Thread.current.object_id, proxy_.object_id]
|
574
574
|
@_proxy_delegators[proxy_delegator_key_] = object_
|
575
575
|
@_target_stacks[target_stack_key_] = [target_]
|
576
576
|
|
@@ -593,11 +593,11 @@ module Blockenspiel
|
|
593
593
|
|
594
594
|
# Create hash keys
|
595
595
|
mixin_count_key_ = [object_.object_id, mod_.object_id]
|
596
|
-
target_stack_key_ = [Thread.current.object_id, object_.object_id]
|
596
|
+
target_stack_key_ = [::Thread.current.object_id, object_.object_id]
|
597
597
|
|
598
598
|
# Store the target for inheriting.
|
599
599
|
# We maintain a target call stack per thread.
|
600
|
-
target_stack_ = @_target_stacks[target_stack_key_] ||= Array.new
|
600
|
+
target_stack_ = @_target_stacks[target_stack_key_] ||= ::Array.new
|
601
601
|
target_stack_.push(target_)
|
602
602
|
|
603
603
|
# Mix this module into the object, if required.
|
@@ -629,7 +629,7 @@ module Blockenspiel
|
|
629
629
|
count_ = @_mixin_counts[mixin_count_key_]
|
630
630
|
if count_ == 1
|
631
631
|
@_mixin_counts.delete(mixin_count_key_)
|
632
|
-
Blockenspiel::Unmixer.unmix(object_, mod_)
|
632
|
+
::Blockenspiel::Unmixer.unmix(object_, mod_)
|
633
633
|
else
|
634
634
|
@_mixin_counts[mixin_count_key_] = count_ - 1
|
635
635
|
end
|
@@ -646,8 +646,8 @@ module Blockenspiel
|
|
646
646
|
# If we can't find an appropriate method to call, return the special value TARGET_MISMATCH.
|
647
647
|
|
648
648
|
def self._target_dispatch(object_, name_, params_, block_) # :nodoc:
|
649
|
-
target_stack_ = @_target_stacks[[Thread.current.object_id, object_.object_id]]
|
650
|
-
return Blockenspiel::TARGET_MISMATCH unless target_stack_
|
649
|
+
target_stack_ = @_target_stacks[[::Thread.current.object_id, object_.object_id]]
|
650
|
+
return ::Blockenspiel::TARGET_MISMATCH unless target_stack_
|
651
651
|
target_stack_.reverse_each do |target_|
|
652
652
|
target_class_ = target_.class
|
653
653
|
delegate_ = target_class_._get_blockenspiel_delegate(name_)
|
@@ -655,7 +655,7 @@ module Blockenspiel
|
|
655
655
|
return target_.send(delegate_, *params_, &block_)
|
656
656
|
end
|
657
657
|
end
|
658
|
-
return Blockenspiel::TARGET_MISMATCH
|
658
|
+
return ::Blockenspiel::TARGET_MISMATCH
|
659
659
|
end
|
660
660
|
|
661
661
|
|