On Wed, Feb 6, 2013 at 9:14 PM, Rob Marshall <lists / ruby-forum.com> wrote:
> To Robert...yes, I have NOT, yet, grasped the concept of blocks :-)

OK.  First of all a block is just an anonymous function.  Because it
doesn't have a name it is invoked via keyword "yield" as you are
already aware.  You can see it from the stack trace:

irb(main):001:0> def f; yield; end
=> nil
irb(main):002:0> puts f { caller(0) }
(irb):3:in `block in irb_binding'
(irb):1:in `f'
(irb):3:in `irb_binding'
...

An alternative way to invoke the block is via an explicit last argument:

irb(main):007:0> def f(&any_name) any_name.call end
=> nil
irb(main):008:0> puts f { caller(0) }
(irb):8:in `block in irb_binding'
(irb):7:in `call'
(irb):7:in `f'
(irb):8:in `irb_binding'
...


Also, a block is scoped in the *calling* scope.  So it has access to
all variables defined there:

irb(main):005:0* def g; x=1; f { puts x } end
=> nil
irb(main):006:0> g
1
=> nil

> Let me explain: I'm using FFI to access a library. The way the library
> calls work is that none of them return errors or even raise exceptions.
> The expectation is, that after calling a function, the user will check
> (via a function call) for errors. So what I end up with is:
>
> Module MyModule
>   class SubClass
>     def initialize(instance_var)
>       @instance_var = instance_var
>     end
>
>     def my_function_call(arg1,arg2)
>       unless @instance_var.nil?
>         ret = MyModule.Real_FunctionCall(@instance_var,arg1,arg2)
>         if MyModule.CheckLastError != 0
>           raise MyException, MyModule.GetLastErrorText
>         end
>         return ret
>       else
>         raise ArgumentError, "Instance variable missing"
>       end
>     end
>
>     def my_other_call(arg1,arg2, arg3)
>       ret = MyModule.Real_OtherCall(arg1,arg2,arg3)
>       if MyModule.CheckLastError != 0
>         raise MyException, MyModule.GetLastErrorText
>       end
>       return ret
>     end
>   end
> end

Here's a possible implementation idea:

# untested
  def my_function_call(arg1,arg2)
    invoke_fun(:Real_FunctionCall, [arg1, arg2], %w{@instance_var})
  end

  def my_other_call(arg1,arg2, arg3)
    invoke_fun(:Real_OtherCall, [arg1, arg2, arg3])
  end

  def fun_tastic(arg1,arg2)
    invoke_fun(:Real_FunctionCall, [arg1, arg2], %w{@length @size})
  end

private
  def invoke_fun(name, args, ivars = [])
    ivals = ivars.map {|iv| instance_variable_get(iv).tap {|val| raise
ArgumentError, "Ivar #{iv} missing" if val.nil?}}
    MyModule.send(name, *ivals, *args).tap {raise MyException,
MyModule.GetLastErrorText unless MyModule.CheckLastError == 0}
  end

Maybe this calls for meta programming.  You could define a method on
class level which will define instance methods based on these inputs

 - name of the method to invoke
 - number of additional arguments
 - list of instance variables to use (possibly empty)

The generated code would behave like invoke_fun only that it would
have "burned" method name and instance variable names into it.

> What I think will work is (here's a simplified example):

I don't think this is such a good idea.  You want to get rid of as
much repetition as possible.  That's what I did with the code above.
Since the structure of the methods is so identical you can automate
much more.  Once you found out what your inputs are (in terms of
variants of method implementations) things fall into place pretty
naturally.

> def check_error(*args)
>   if args.length > 0
>     args.each_with_index { |v,i| printf "%2d argument value: %s\n", i, v
> }
>   end
>   ret = yield
>   puts "Check error here"
>   ret
> end
>
> check_error("this","is","a","test"){ return_value("something again") }

As I said: return_value is completely superfluous.  The code should
sit in check_error method because that avoids typing return_value
repetitively.

> The part I didn't understand is how Ruby will allow "you" to put a block
> after the args in parens...Once I figured that out (thanks to:
> http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html#.URK0k-j6bYA) it
> all made sense :-)

I am not sure I understand what you mean by "put a block after the
args in parens".

> Thanks for the help,

You're welcome!

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/