From: Ross Bamford Date: 2006-03-10T05:01:04+09:00 Subject: Re: Error calling block in sub class On Fri, 2006-03-10 at 02:36 +0900, Aaron Janes wrote: > << Snipped Code Snippet >> > > This code works fine. I can create a new CollectionBase, pass a code > block to add_validators and depending on the result of the code block > objects are added or not. > > The problem comes when I try and subclass this class. I also want to > create a SetBase class which extends CollectionBase but by default adds > a validator that prevents duplicate objects from being added: > > << Code Snippet >> > > require 'Collections' > > class SetBase < CollectionBase > public > def initialize() > super > # add a validator that returns false if object is already > # in the set > add_validator {return !index(obj) } > end > end > > << End Code Snippet >> > > When I try to add an item to SetBase, however, I get the error: > unexpected return (LocalJumpError) > I think it's because you're saving the block for later use, and slightly misunderstanding the semantics of 'return'. The return in the block is actually trying to return from the initialize() method, not from the block, so Ruby naturally thinks 'huh? We did that already...'. For example: def test p [1,2,3,4,5].each { |e| p e; return e } end # => nil test 1 # (output) # => 1 You can see here that the return applies to the test method (in which the block is defined) rather than 'each'. The first time it's executed, the whole method returns, abandoning the iteration. As a closure, the block retains the scope it's created in. You should be able to fix the problem with something like: class SetBase < CollectionBase public def initialize() super # add a validator that returns false if object is already # in the set add_validator { |obj| !index(obj) } end end (i.e. don't use return in there - generally you can avoid using return in most places, since you'll always get the result of the last expression executed as the return of a method or block/lambda). After adding an 'index' method to the base-class, this gave me the following results: c = SetBase.new c.add(1) c.add(2) c.add(3) c.valid?(1) # => false p c.valid?(2) # => false p c.valid?(3) # => false p c.valid?(4) # => true Is that what you're after? -- Ross Bamford - rosco@roscopeco.REMOVE.co.uk