blockenspiel 0.2.1-java → 0.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -1,9 +1,13 @@
1
1
  == Implementing DSL Blocks
2
2
 
3
- by Daniel Azuma, 29 October 2008
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://code.whytheluckystiff.net/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:
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://code.whytheluckystiff.net/markaby/] still uses <tt>instance_eval</tt> but includes a caveat in the {documentation}[http://markaby.rubyforge.org/] explaining the issues and recommending caution.
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 {Why}[http://hackety.org/2008/10/06/mixingOurWayOutOfInstanceEval.html] and {Ola Bini}[http://olabini.com/blog/2008/09/dont-overuse-instance_eval-and-instance_exec/], is that it should be avoided.
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 {blog}[http://hackety.org/2008/10/06/mixingOurWayOutOfInstanceEval.html]. 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.
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/why/mixico/tree/master]. 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.
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://whytheluckystiff.net/], <em>{Markaby}[http://code.whytheluckystiff.net/markaby/]</em> (Ruby library), 2006.
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://whytheluckystiff.net/], <em>{Mixico}[http://github.com/why/mixico/tree/master]</em> (Ruby library), 2008.
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://whytheluckystiff.net/], <em>{Mixing Our Way Out Of Instance Eval?}[http://hackety.org/2008/10/06/mixingOurWayOutOfInstanceEval.html]</em>, 2008.10.06.
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 Zoodango. 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/
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/
@@ -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. Ruby 1.9 compatible.
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 by Github at http://github.com/dazuma/blockenspiel/tree
302
+ Source code is hosted on Github at http://github.com/dazuma/blockenspiel
304
303
 
305
- Report bugs on RubyForge at http://rubyforge.org/projects/virtuoso
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 Stiff.
314
- See his 6 October 2008 blog posting,
315
- <em>{Mixing Our Way Out Of Instance Eval?}[http://hackety.org/2008/10/06/mixingOurWayOutOfInstanceEval.html]</em>
316
- for further discussion.
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 :publish_rdoc => [:rerdoc] do
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 :publish_gem do |t_|
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 !File.file?(mri_pkg_) || !File.readable?(mri_pkg_)
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 !File.file?(jruby_pkg_) || !File.readable?(jruby_pkg_)
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 !File.file?(tgz_pkg_) || !File.readable?(tgz_pkg_)
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
@@ -34,7 +34,7 @@
34
34
  ;
35
35
 
36
36
 
37
- if RUBY_PLATFORM =~ /java/
37
+ if ::RUBY_PLATFORM =~ /java/
38
38
  require "blockenspiel_unmixer"
39
39
  else
40
40
  require "#{File.dirname(__FILE__)}/blockenspiel/unmixer"
@@ -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, :receive_block => true) do |value,blk|
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