adding method to block?

22 views
Skip to first unread message

William Hertling

unread,
Apr 20, 2016, 12:49:12 PM4/20/16
to pdxruby
I want to be able to do something like:

def foo(&block)
 
def block.bar
    puts
"in bar"
 
end
  block
.call
end


And then I should be able to call bar from within the block:

foo do
  bar
end


Is there a way to achieve that? It doesn't seem to work for me. The "def block.bar" doesn't return any error, but when I call bar, I get undefined method on main:Object.



Sam Goldstein

unread,
Apr 20, 2016, 12:56:03 PM4/20/16
to William Hertling, pdxruby
There's a few different ways to achieve this.  I've typically done it by defining some kind of DSL context class which the block can be passed into via #instance_eval.

For example:


class Dsl
  def bar
    puts "bar"
  end

  def baz
    puts "baz"
  end
end


def foo(&block)
  dsl = Dsl.new
  dsl.instance_eval(&block)
end

foo do
  bar
  baz
end


Cheers,
Sam

--
You received this message because you are subscribed to the Google Groups "pdxruby" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pdxruby+u...@googlegroups.com.
To post to this group, send email to pdx...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pdxruby/4312d753-cf81-4f1f-b3fc-0934239472de%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

William Hertling

unread,
Apr 20, 2016, 3:25:47 PM4/20/16
to pdxruby, william....@gmail.com
Ah, that approach appeared to work at first glance, but what I didn't realize is that by overriding self, it has basically killed inheritance, so that my block can't call other methods it is dependent on.

Is there any other way to achieve it that would preserve inheritance?

Dave Miller

unread,
Apr 20, 2016, 3:32:22 PM4/20/16
to pdxruby
def foo(&block)  
  class << self
    def bar
      puts "in bar"
    end
  end
  block.call
end

def baz
  puts "in baz"
end

foo do
  bar
  baz
end



On Apr 20, 2016, at 12:25 PM, William Hertling <william....@gmail.com> wrote:

Ah, that approach appeared to work at first glance, but what I didn't realize is that by overriding self, it has basically killed inheritance, so that my block can't call other methods it is dependent on.

Is there any other way to achieve it that would preserve inheritance?

--
You received this message because you are subscribed to the Google Groups "pdxruby" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pdxruby+u...@googlegroups.com.
To post to this group, send email to pdx...@googlegroups.com.

Sam Livingston-Gray

unread,
Apr 20, 2016, 3:35:21 PM4/20/16
to William Hertling, pdxruby
Can you promote the block to a Proc object, then define your `bar` method on that Proc instance?
-Sam

On Wed, Apr 20, 2016 at 9:49 AM, William Hertling <william....@gmail.com> wrote:

--
You received this message because you are subscribed to the Google Groups "pdxruby" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pdxruby+u...@googlegroups.com.
To post to this group, send email to pdx...@googlegroups.com.

Matthew Boeh

unread,
Apr 20, 2016, 3:42:12 PM4/20/16
to William Hertling, pdxruby
A binding can have only one implicit receiver ("self"). If your code that is evaluating the block has access to all the objects that have the methods you want, you could potentially create a new object that serves as a facade for that assemblage of objects. However, this would be a _really bad idea_.

I strongly recommend not messing with the binding of blocks. If you want to provide a context on which the block can call methods, pass it as an explicit argument:

def foo(&block)
  block
.call(self)
end

foo do |context|
  context.bar
end 

Substituting an alternate implicit receiver for a block tends to result in code that is hard to understand and debug. Adding any more indirection (like pretending "self" is more than one object) results in code that is hard to understand and debug as well as being prone to exceptionally confusing bugs (for instance, if both objects define "bar").

On Wed, Apr 20, 2016 at 12:25 PM William Hertling <william....@gmail.com> wrote:
Ah, that approach appeared to work at first glance, but what I didn't realize is that by overriding self, it has basically killed inheritance, so that my block can't call other methods it is dependent on.

Is there any other way to achieve it that would preserve inheritance?

--
You received this message because you are subscribed to the Google Groups "pdxruby" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pdxruby+u...@googlegroups.com.
To post to this group, send email to pdx...@googlegroups.com.

William Hertling

unread,
Apr 20, 2016, 3:56:38 PM4/20/16
to pdxruby, william....@gmail.com


On Wednesday, April 20, 2016 at 12:42:12 PM UTC-7, Matthew Boeh wrote:
A binding can have only one implicit receiver ("self"). If your code that is evaluating the block has access to all the objects that have the methods you want, you could potentially create a new object that serves as a facade for that assemblage of objects. However, this would be a _really bad idea_.

I strongly recommend not messing with the binding of blocks. If you want to provide a context on which the block can call methods, pass it as an explicit argument:

def foo(&block)
  block
.call(self)
end

foo do |context|
  context.bar
end 

That's exactly how my code looked originally, and I wanted to move away from that.

I have to parse, validate, and assemble an in-memory representation of a complicated OPC document with many XML and other files contained inside a zip archive.

What I wanted to do was define a context-aware error and warning aggregation mechanism. So my code looks something like:

Log3.context input_filename do |l|
 # crack open zip file.
 l.fatal_error "zip not parseable" if #some zip error

 l.context "[Content_Types].xml" do |l|
   # parse xml file
   l.error "XML file not parseable" if #xml parse error
   doc.xpath(//blahblah).each do |node|
      l.context node.name do |l|
        #some stuff
        l.error "missing expected attribute" if # attribute missing
      end
   end
 end

I think you get the general idea. I get a nice, context-specific (not line-of-code, but context-of-input) messages.
But there's no value to passing the |l| parameters around and using, and there's little ambiguity about what the code is doing, so I don't think it would lead to hard-to-understand code by eliminating the use of a local variable.

Benjamin Turner

unread,
Apr 20, 2016, 5:30:18 PM4/20/16
to pdxruby, william....@gmail.com
It kind of sounds like maybe you want each context to push its name (and maybe any other relevant details) onto a stack, and have the error method grab the top of the stack (or the whole thing). When a context exits (via return or exception, so probably via an `ensure` block), it pops the stack.

Sam Goldstein

unread,
Apr 21, 2016, 1:15:53 AM4/21/16
to William Hertling, pdxruby
On Wed, Apr 20, 2016 at 12:25 PM, William Hertling <william....@gmail.com> wrote:
Ah, that approach appeared to work at first glance, but what I didn't realize is that by overriding self, it has basically killed inheritance, so that my block can't call other methods it is dependent on.

Is there any other way to achieve it that would preserve inheritance?

I won't comment on how advisable it is but you could try messing around with things like Delegator, SimpleDelegator, DelegateClass, or some kind of method_missing nonsense.


SimpleDelegator provides a convenient way to implement a decorator pattern which is what it sounds like your after.

This blog post seems to have some decent info about various decorator options in Ruby.

Bob Lehman

unread,
Apr 21, 2016, 7:51:16 PM4/21/16
to pdx...@googlegroups.com, Etienne de Bruin
In my humble opinion PDX has lots of great technical events but not a
whole lot of great events for technical mangers/executives, so I want to
make the existence of this event known to this group and others.

The Nature of Software Development Estimation ~ A 7CTOs Portland Forum
Experience

It is listed on Calagator

http://calagator.org/events/1250470111

The group seems to be in its infancy so if you are a technical
manager/executive looking for a place to meet with peers this is a
pretty good opportunity to do so. Also the topic this month is
something that we would all care about.

Please contact Etienne de Bruin ( eti...@7ctos.com ) for more
information or sign up to attend.

If you think this post is not appropriate, please fee free to reach out
to me directly bleh...@comcast.net. Also please feel free to reach
out to me or Etienne for other places you think would be good to
publicize this event at.

Thank you for your time and patience and now back to our regularly
scheduled technical content -:).

--Bob
Reply all
Reply to author
Forward
0 new messages