blockenspiel 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ === 0.1.1 / 2008-11-06
2
+
3
+ * Added ability to pass the block as the first parameter in
4
+ the dynamic DSL builder API; cleaned up the API a little
5
+ * Minor fixes to Implementing DSL Blocks paper
6
+ * Some updates to rdocs
7
+ * More test coverage
8
+
1
9
  === 0.1.0 / 2008-10-29
2
10
 
3
11
  * Alpha release, opened for public feedback
@@ -83,7 +83,7 @@ Perhaps you were introduced to Ruby via the {Rails}[http://www.rubyonrails.org/]
83
83
 
84
84
  Blocks are central to Ruby as a language, and it feels natural to Ruby programmers to use them to delimit specialized code. When designing an API for a Ruby library, blocks like these are, in many cases, a natural and effective pattern.
85
85
 
86
- === Let's be more precise about what we mean by "DSL block".
86
+ === Defining DSL blocks
87
87
 
88
88
  Blocks in Ruby are used for a variety of purposes. In many cases, they are used to provide _callbacks_, specifying functionality to inject into an operation. If you come from a functional programming background, you might see them as lambda expressions; in object-oriented-speak, they implement the Visitor pattern. A simple example is the +each+ method, which iterates over a collection, using the given block as a callback that allows the caller to specify processing to perform on each element.
89
89
 
@@ -97,7 +97,7 @@ Note that in both this case and the Routing case, the information contained in t
97
97
 
98
98
  The RSpec example illustrates a more sophisticated case with many keywords and multiple levels of blocks, but it shares common features with the Rails examples. Again, a language is being defined to describe things that could conceivably have been passed in as parameters, but are being specified in a block for clarity and readability.
99
99
 
100
- So far, we can see that DSL blocks have the following properties:
100
+ Based on this discussion, we can see that DSL blocks have the following properties:
101
101
 
102
102
  * An API requires a caller to communicate complex descriptive information.
103
103
  * The API defines a domain-specific language designed to express this information.
@@ -336,9 +336,8 @@ Using the above method, it becomes easy to generate URL strings:
336
336
 
337
337
  Our <tt>routes.rb</tt> file, utilizing our "improvement" to the routing DSL, might now like this:
338
338
 
339
- URL_PREFIX = 'mywebsite/:controller/:action/'
340
339
  def makeurl(*params)
341
- URL_PREFIX + params.map{ |e| e.inspect }.join('/')
340
+ 'mywebsite/:controller/:action/' + params.map{ |e| e.inspect }.join('/')
342
341
  end
343
342
 
344
343
  ActionController::Routing::Routes.draw do
@@ -522,7 +521,7 @@ Here is a sample implementation of these two techniques for handling instance va
522
521
  map.instance_eval(&block)
523
522
  ensure # Ensure the hashes are cleaned up and instance
524
523
  map.cleanup # variables are pushed back to original_self,
525
- end # even if the block threw and exception
524
+ end # even if the block threw an exception
526
525
  named_routes.install
527
526
  end
528
527
 
@@ -709,45 +708,45 @@ One more issue with the mixin strategy is that, like all implementations that dr
709
708
 
710
709
  Mixin's behavior is less straightforward, because of a subtlety in Ruby's method lookup behavior. Under most cases, it behaves similarly to an <tt>instance_eval</tt> with delegation: the DSL's methods take priority. However, if methods have been added directly to the object, they will take precedence over the DSL's methods. Following is an example of this case:
711
710
 
712
- # Suppose we have a DSL block available, via "call_my_dsl",
713
- # that implements the methods "foo" and "bar"...
714
-
715
- # First, let's implement a simple class
716
- class MyClass
717
-
718
- # A test method
719
- def foo
720
- puts "in foo"
721
- end
722
-
723
- end
724
-
725
- # Create an instance of MyClass
726
- obj = MyClass.new
727
-
728
- # Now, add a new method "bar" to the object.
729
- def obj.bar
730
- puts "in bar"
731
- end
732
-
733
- # Finally, add a method "run" that runs a DSL block
734
- def obj.run
735
- call_my_dsl do
736
- foo # DSL "foo" method takes precedence over MyClass#foo
737
- bar # The object's "bar" method takes precedence over DSL "bar"
738
- end
739
- end
740
-
741
- # At this point, obj has methods "foo", "bar", and "run"
742
- # Run the DSL block to test the behavior
743
- obj.run
711
+ # Suppose we have a DSL block available, via "call_my_dsl",
712
+ # that implements the methods "foo" and "bar"...
713
+
714
+ # First, let's implement a simple class
715
+ class MyClass
716
+
717
+ # A test method
718
+ def foo
719
+ puts "in foo"
720
+ end
721
+
722
+ end
723
+
724
+ # Create an instance of MyClass
725
+ obj = MyClass.new
726
+
727
+ # Now, add a new method "bar" to the object.
728
+ def obj.bar
729
+ puts "in bar"
730
+ end
731
+
732
+ # Finally, add a method "run" that runs a DSL block
733
+ def obj.run
734
+ call_my_dsl do
735
+ foo # DSL "foo" method takes precedence over MyClass#foo
736
+ bar # The object's "bar" method takes precedence over DSL "bar"
737
+ end
738
+ end
739
+
740
+ # At this point, obj has methods "foo", "bar", and "run"
741
+ # Run the DSL block to test the behavior
742
+ obj.run
744
743
 
745
744
  In the above example, suppose both +foo+ and +bar+ are methods of the DSL. They are also both defined as methods of +obj+. (+foo+ is available because it is a method of +MyClass+, while +bar+ is available because it is explicitly added to +obj+.) However, if you run the code, it calls the DSL's +foo+ but +obj+'s +bar+. Why?
746
745
 
747
746
  The reason points to a subtlety in how Ruby does method lookup. When you define a method in the way +foo+ is defined, it is just added to the class. However, when you define a method in the way +bar+ is defined, it is defined as a "singleton method", and added to the "singleton class", which is an anonymous class that holds methods defined directly on a particular object. It turns out that the singleton class is always given the highest priority in method lookup. So, for example, the lookup order for methods of +obj+ within the block would look like this:
748
747
 
749
- singleton methods of obj -> mixin module from the DSL -> methods of MyClass
750
- (e.g. bar, run) (e.g. foo, bar) (e.g. foo)
748
+ singleton methods of obj -> mixin module from the DSL -> methods of MyClass
749
+ (e.g. bar, run) (e.g. foo, bar) (e.g. foo)
751
750
 
752
751
  So when the +foo+ method is called, it is not found in the singleton class, but it is found in the mixin, so the mixin's version is invoked. However, when +bar+ is called, it is found in the singleton class, so that version is invoked in favor of the mixin's version.
753
752
 
@@ -827,14 +826,14 @@ Atop this basic usage, blockenspiel provides two types of customization. First,
827
826
  dsl_method :set_title, :title= # blocks under these alternate names.
828
827
  end
829
828
 
830
- Now, when we use block parameters, we use the methods of the original ConfigMethods class:
829
+ Now, when we use block parameters, we use the methods of the original +ConfigMethods+ class:
831
830
 
832
831
  create_paper do |config|
833
832
  config.author = "Daniel Azuma"
834
833
  config.title = "Implementing DSL Blocks"
835
834
  end
836
835
 
837
- And omitting the parameter, the alternate method names are mixed in:
836
+ And, when we omit the parameter, the alternate method names are mixed in:
838
837
 
839
838
  create_paper do
840
839
  set_author "Daniel Azuma"
@@ -932,4 +931,4 @@ The Blockenspiel library provides a concrete and robust implementation of DSL bl
932
931
 
933
932
  === About the author
934
933
 
935
- 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/.
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/
data/Manifest.txt CHANGED
@@ -7,4 +7,5 @@ lib/blockenspiel.rb
7
7
  tests/tc_basic.rb
8
8
  tests/tc_behaviors.rb
9
9
  tests/tc_dsl_methods.rb
10
+ tests/tc_dynamic.rb
10
11
  tests/tc_mixins.rb
data/Rakefile CHANGED
@@ -46,4 +46,40 @@ Hoe.new('blockenspiel', Blockenspiel::VERSION_STRING) do |p_|
46
46
  p_.extra_deps = [['mixology', '>= 0.1.0']]
47
47
  p_.description_sections = ['blockenspiel']
48
48
  p_.url = 'http://virtuoso.rubyforge.org/blockenspiel'
49
+ p_.clean_globs << 'idslb_markdown.txt'
50
+ end
51
+
52
+
53
+ # Custom task that takes the implementing dsl blocks paper
54
+ # and converts it from RDoc format to Markdown
55
+ task :idslb_markdown do
56
+ File.open('ImplementingDSLblocks.txt') do |read_|
57
+ File.open('idslb_markdown.txt', 'w') do |write_|
58
+ linenum_ = 0
59
+ read_.each do |line_|
60
+ linenum_ += 1
61
+ next if linenum_ < 4
62
+ line_.sub!(/^===\ /, '### ')
63
+ line_.sub!(/^\ \ /, ' ')
64
+ if line_[0..3] == '### '
65
+ line_.gsub!(/(\w)_(\w)/, '\1\_\2')
66
+ end
67
+ if line_[0..3] != ' '
68
+ line_.gsub!('"it_should_behave_like"', '"it\_should\_behave\_like"')
69
+ line_.gsub!('"time_zone"', '"time\_zone"')
70
+ line_.gsub!(/\+(\w+)\+/, '`\1`')
71
+ line_.gsub!(/\*(\w+)\*/, '**\1**')
72
+ line_.gsub!(/<\/?em>/, '*')
73
+ line_.gsub!(/<\/?tt>/, '`')
74
+ line_.gsub!(/<\/?b>/, '**')
75
+ line_.gsub!(/\{([^\}]+)\}\[([^\]]+)\]/) do |match_|
76
+ text_, url_ = $1, $2
77
+ "[#{text_.gsub('_', '\_')}](#{url_})"
78
+ end
79
+ line_.gsub!(/\ (http:\/\/[^\s]+)/, ' [\1](\1)')
80
+ end
81
+ write_.puts(line_)
82
+ end
83
+ end
84
+ end
49
85
  end
data/lib/blockenspiel.rb CHANGED
@@ -46,7 +46,7 @@ require 'mixology'
46
46
  module Blockenspiel
47
47
 
48
48
  # Current gem version
49
- VERSION_STRING = '0.1.0'
49
+ VERSION_STRING = '0.1.1'.freeze
50
50
 
51
51
 
52
52
  # Base exception for all exceptions raised by Blockenspiel
@@ -193,17 +193,26 @@ module Blockenspiel
193
193
  # To enable automatic exporting of methods to parameterless blocks.
194
194
  # After executing this command, all public methods defined in the class
195
195
  # will be available on parameterless blocks, until
196
- # <tt>dsl_methods false</tt> is called.
196
+ # <tt>dsl_methods false</tt> is called:
197
197
  # dsl_methods true
198
198
  #
199
199
  # To disable automatic exporting of methods to parameterless blocks.
200
200
  # After executing this command, methods defined in this class will be
201
201
  # excluded from parameterless blocks, until <tt>dsl_methods true</tt>
202
- # is called.
202
+ # is called:
203
203
  # dsl_methods false
204
204
  #
205
205
  # To make a list of methods available to parameterless blocks in bulk:
206
206
  # dsl_methods :my_method1, :my_method2, ...
207
+ #
208
+ # You can also point dsl methods to a method of a different name on the
209
+ # target class, by using a hash syntax, as follows:
210
+ # dsl_methods :my_method1 => :target_class_method1,
211
+ # :my_method2 => :target_class_method2
212
+ #
213
+ # You can mix non-renamed and renamed method declarations as long as
214
+ # the renamed (hash) methods are at the end. e.g.:
215
+ # dsl_methods :my_method1, :my_method2 => :target_class_method2
207
216
 
208
217
  def dsl_methods(*names_)
209
218
  if names_.size == 0 || names_ == [true]
@@ -311,12 +320,13 @@ module Blockenspiel
311
320
 
312
321
  def self._invoke_methodinfo(name_, params_, block_)
313
322
  info_ = @_blockenspiel_methodinfo[name_]
314
- if info_[1]
315
- realparams_ = params_ + [block_]
316
- info_[0].call(*realparams_)
317
- else
318
- info_[0].call(*params_)
323
+ case info_[1]
324
+ when :first
325
+ params_.unshift(block_)
326
+ when :last
327
+ params_.push(block_)
319
328
  end
329
+ info_[0].call(*params_)
320
330
  end
321
331
 
322
332
  end
@@ -337,22 +347,56 @@ module Blockenspiel
337
347
  end
338
348
 
339
349
 
340
- # Make a method available within the block.
350
+ # === Declare a DSL method.
341
351
  #
352
+ # This call creates a method that can be called from the DSL block.
342
353
  # Provide a name for the method, and a block defining the method's
343
- # implementation.
354
+ # implementation. You may also provided a list of options as follows:
355
+ #
356
+ # The <tt>:block</tt> option controls how the generated method reports
357
+ # any block provided by the caller. If set to +false+ or not given, any
358
+ # caller-provided block is ignored. If set to <tt>:first</tt>, the
359
+ # block is _prepended_ (as a +Proc+ object) to the parameters passed to
360
+ # the method's implementing block. If set to <tt>:last</tt>, the block
361
+ # is _appended_. In either case, if the caller does not provide a block,
362
+ # a value of +nil+ is pre- or appended to the parameter list. A value of
363
+ # +true+ is equivalent to <tt>:first</tt>.
364
+ # (This is a workaround for the fact that blocks cannot themselves take
365
+ # block parameters in Ruby 1.8.)
366
+ #
367
+ # For example, to create a method named "foo" that takes one parameter
368
+ # and a block, do this:
369
+ #
370
+ # add_method(:foo, :block => :first) do |block, param|
371
+ # puts "foo called with parameter "+param.inspect
372
+ # puts "the block returned "+block.call.inspect
373
+ # end
374
+ #
375
+ # In your DSL, you can then call:
376
+ #
377
+ # foo("hello"){ "a value" }
378
+ #
379
+ # By default, a method of the same name is also made available to
380
+ # parameterless blocks. To change the name of the parameterless method,
381
+ # provide its name as the value of the <tt>:dsl_method</tt> option.
382
+ # To disable this method for parameterless blocks, set the
383
+ # <tt>:dsl_method</tt> option to +false+.
384
+ #
385
+ # For historical reasons, the <tt>:mixin</tt> option is an alias for
386
+ # <tt>:dsl_method</tt>. However, its use is deprecated.
344
387
  #
345
- # By default, a method of the same name is also made available in
346
- # mixin mode. To change the name of the mixin method, set its name
347
- # as the value of the <tt>:mixin</tt> parameter. To disable the
348
- # mixin method, set the <tt>:mixin</tt> parameter to +false+.
388
+ # The <tt>:receive_block</tt> option is also supported for historical
389
+ # reasons, but its use is deprecated. Setting <tt>:receive_block</tt> to
390
+ # +true+ is equivalent to setting <tt>:block</tt> to <tt>:last</tt>.
349
391
 
350
392
  def add_method(name_, opts_={}, &block_)
351
- @target_class._add_methodinfo(name_, block_, opts_[:receive_block])
352
- mixin_name_ = opts_[:mixin]
353
- if mixin_name_ != false
354
- mixin_name_ = name_ if mixin_name_.nil? || mixin_name_ == true
355
- @target_class.dsl_method(mixin_name_, name_)
393
+ receive_block_ = opts_[:receive_block] ? :last : opts_[:block]
394
+ receive_block_ = :first if receive_block_ && receive_block_ != :last
395
+ @target_class._add_methodinfo(name_, block_, receive_block_)
396
+ dsl_method_name_ = opts_[:dsl_method] || opts_[:mixin]
397
+ if dsl_method_name_ != false
398
+ dsl_method_name_ = name_ if dsl_method_name_.nil? || dsl_method_name_ == true
399
+ @target_class.dsl_method(dsl_method_name_, name_)
356
400
  end
357
401
  end
358
402
 
data/tests/tc_basic.rb CHANGED
@@ -33,6 +33,7 @@
33
33
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
34
  # POSSIBILITY OF SUCH DAMAGE.
35
35
  # -----------------------------------------------------------------------------
36
+ ;
36
37
 
37
38
 
38
39
  require File.expand_path("#{File.dirname(__FILE__)}/../lib/blockenspiel.rb")
@@ -120,7 +121,7 @@ module Blockenspiel
120
121
  add_method(:set_value) do |key_, value_|
121
122
  hash_[key_] = value_
122
123
  end
123
- add_method(:set_value_by_block, :receive_block => true) do |key_, bl_|
124
+ add_method(:set_value_by_block, :block => true) do |bl_, key_|
124
125
  hash_[key_] = bl_.call
125
126
  end
126
127
  end
@@ -33,6 +33,7 @@
33
33
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
34
  # POSSIBILITY OF SUCH DAMAGE.
35
35
  # -----------------------------------------------------------------------------
36
+ ;
36
37
 
37
38
 
38
39
  require File.expand_path("#{File.dirname(__FILE__)}/../lib/blockenspiel.rb")
@@ -33,6 +33,7 @@
33
33
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
34
  # POSSIBILITY OF SUCH DAMAGE.
35
35
  # -----------------------------------------------------------------------------
36
+ ;
36
37
 
37
38
 
38
39
  require File.expand_path("#{File.dirname(__FILE__)}/../lib/blockenspiel.rb")
@@ -174,6 +175,27 @@ module Blockenspiel
174
175
  end
175
176
 
176
177
 
178
+ class Target6 < Blockenspiel::Base
179
+
180
+ def initialize(hash_)
181
+ @hash = hash_
182
+ end
183
+
184
+ dsl_methods false
185
+
186
+ def set_value1(key_, value_)
187
+ @hash["#{key_}1"] = value_
188
+ end
189
+
190
+ def set_value2(key_)
191
+ @hash["#{key_}2"] = yield
192
+ end
193
+
194
+ dsl_methods :set_value1, :renamed_set_value2 => :set_value2
195
+
196
+ end
197
+
198
+
177
199
  # Test default dsl method setting.
178
200
  #
179
201
  # * Asserts the right dsl methods are added for the default setting.
@@ -277,6 +299,24 @@ module Blockenspiel
277
299
  end
278
300
 
279
301
 
302
+ # Test dsl_methods with multiple parameters.
303
+ #
304
+ # * Asserts that hash syntax for dsl_methods works.
305
+ # * Asserts that combined array and hash parameters works.
306
+
307
+ def test_multiple_dsl_methods
308
+ hash_ = Hash.new
309
+ block_ = proc do
310
+ set_value1('a', 1)
311
+ renamed_set_value2('b'){ 2 }
312
+ assert_raise(NoMethodError){ set_value2('c', 3) }
313
+ end
314
+ Blockenspiel.invoke(block_, Target6.new(hash_))
315
+ assert_equal(1, hash_['a1'])
316
+ assert_equal(2, hash_['b2'])
317
+ end
318
+
319
+
280
320
  end
281
321
 
282
322
  end
@@ -0,0 +1,174 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Blockenspiel dynamic tests
4
+ #
5
+ # This file contains tests for dynamic DSL generation.
6
+ #
7
+ # -----------------------------------------------------------------------------
8
+ # Copyright 2008 Daniel Azuma
9
+ #
10
+ # All rights reserved.
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are met:
14
+ #
15
+ # * Redistributions of source code must retain the above copyright notice,
16
+ # this list of conditions and the following disclaimer.
17
+ # * Redistributions in binary form must reproduce the above copyright notice,
18
+ # this list of conditions and the following disclaimer in the documentation
19
+ # and/or other materials provided with the distribution.
20
+ # * Neither the name of the copyright holder, nor the names of any other
21
+ # contributors to this software, may be used to endorse or promote products
22
+ # derived from this software without specific prior written permission.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ # -----------------------------------------------------------------------------
36
+ ;
37
+
38
+
39
+ require File.expand_path("#{File.dirname(__FILE__)}/../lib/blockenspiel.rb")
40
+
41
+
42
+ module Blockenspiel
43
+ module Tests # :nodoc:
44
+
45
+ class TestDynamic < Test::Unit::TestCase # :nodoc:
46
+
47
+
48
+ # Test the simple case.
49
+ #
50
+ # * Asserts that the simplest case works.
51
+
52
+ def test_simple
53
+ block_ = proc do
54
+ set_value(:a, 1)
55
+ end
56
+ hash_ = Hash.new
57
+ Blockenspiel.invoke(block_) do
58
+ add_method(:set_value) do |key_, value_|
59
+ hash_[key_] = value_
60
+ end
61
+ end
62
+ assert_equal(1, hash_[:a])
63
+ end
64
+
65
+
66
+ # Test renaming.
67
+ #
68
+ # * Asserts that the method appears renamed in a parameterless block.
69
+ # * Asserts that the method appears in its original name in a parametered block.
70
+
71
+ def test_renaming
72
+ hash_ = Hash.new
73
+ dsl_definition_ = proc do
74
+ add_method(:set_value, :dsl_method => :renamed_set_value) do |key_, value_|
75
+ hash_[key_] = value_
76
+ end
77
+ end
78
+ block1_ = proc do
79
+ renamed_set_value(:a, 1)
80
+ assert_raise(NoMethodError){ set_value(:b, 2) }
81
+ end
82
+ Blockenspiel.invoke(block1_, &dsl_definition_)
83
+ block2_ = proc do |dsl_|
84
+ dsl_.set_value(:c, 3)
85
+ assert_raise(NoMethodError){ renamed_set_value(:d, 4) }
86
+ end
87
+ Blockenspiel.invoke(block2_, &dsl_definition_)
88
+ assert_equal(1, hash_[:a])
89
+ assert_nil(hash_[:b])
90
+ assert_equal(3, hash_[:c])
91
+ assert_nil(hash_[:d])
92
+ end
93
+
94
+
95
+ # Test calls with blocks.
96
+ #
97
+ # * Asserts that a block passed "first" works.
98
+ # * Asserts that a block passed "last" works.
99
+ # * Asserts that a block passed "last" works.
100
+
101
+ def test_blocks_first_and_last
102
+ hash_ = Hash.new
103
+ dsl_definition_ = proc do
104
+ add_method(:set_value1, :block => :first) do |bl_, key_|
105
+ hash_[key_] = bl_.call
106
+ end
107
+ add_method(:set_value2, :block => :last) do |key_, bl_|
108
+ hash_[key_] = bl_.call
109
+ end
110
+ add_method(:set_value3, :block => true) do |bl_, key_|
111
+ hash_[key_] = bl_.call
112
+ end
113
+ end
114
+ block_ = proc do
115
+ set_value1(:a){ 1 }
116
+ set_value2(:b){ 2 }
117
+ set_value2(:c){ 3 }
118
+ end
119
+ Blockenspiel.invoke(block_, &dsl_definition_)
120
+ assert_equal(1, hash_[:a])
121
+ assert_equal(2, hash_[:b])
122
+ assert_equal(3, hash_[:c])
123
+ end
124
+
125
+
126
+ # Test calls with blocks not passed.
127
+ #
128
+ # * Asserts that if a block isn't given, it is set to nil.
129
+
130
+ def test_blocks_nil
131
+ hash_ = Hash.new
132
+ dsl_definition_ = proc do
133
+ add_method(:set_value1, :block => :first) do |bl_, key_|
134
+ assert_nil(bl_)
135
+ end
136
+ add_method(:set_value2, :block => :last) do |key_, bl_|
137
+ assert_nil(bl_)
138
+ end
139
+ end
140
+ block_ = proc do
141
+ set_value1(:a)
142
+ set_value2(:b)
143
+ end
144
+ Blockenspiel.invoke(block_, &dsl_definition_)
145
+ assert_nil(hash_[:a])
146
+ assert_nil(hash_[:b])
147
+ end
148
+
149
+
150
+ # Test calls with blocks (legacy api)
151
+ #
152
+ # * Asserts that a block passed "first" works.
153
+ # * Asserts that a block passed "last" works.
154
+ # * Asserts that a block passed "last" works.
155
+
156
+ def test_blocks_legacy
157
+ hash_ = Hash.new
158
+ dsl_definition_ = proc do
159
+ add_method(:set_value, :receive_block => true) do |key_, bl_|
160
+ hash_[key_] = bl_.call
161
+ end
162
+ end
163
+ block_ = proc do
164
+ set_value(:a){ 1 }
165
+ end
166
+ Blockenspiel.invoke(block_, &dsl_definition_)
167
+ assert_equal(1, hash_[:a])
168
+ end
169
+
170
+
171
+ end
172
+
173
+ end
174
+ end
data/tests/tc_mixins.rb CHANGED
@@ -34,6 +34,7 @@
34
34
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
35
  # POSSIBILITY OF SUCH DAMAGE.
36
36
  # -----------------------------------------------------------------------------
37
+ ;
37
38
 
38
39
 
39
40
  require File.expand_path("#{File.dirname(__FILE__)}/../lib/blockenspiel.rb")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blockenspiel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-10-29 00:00:00 -07:00
12
+ date: 2008-11-06 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -54,6 +54,7 @@ files:
54
54
  - tests/tc_basic.rb
55
55
  - tests/tc_behaviors.rb
56
56
  - tests/tc_dsl_methods.rb
57
+ - tests/tc_dynamic.rb
57
58
  - tests/tc_mixins.rb
58
59
  has_rdoc: true
59
60
  homepage: http://virtuoso.rubyforge.org/blockenspiel
@@ -86,4 +87,5 @@ test_files:
86
87
  - tests/tc_basic.rb
87
88
  - tests/tc_behaviors.rb
88
89
  - tests/tc_dsl_methods.rb
90
+ - tests/tc_dynamic.rb
89
91
  - tests/tc_mixins.rb