fl-web fl-web - 4 months ago 25
Ruby Question

How to move block to lib

Let we have some module or gem (or pool of them) which provide API. Teese API's we can not modify. The API's have interface that requre blok. Let the API called

MyClass
and it have method
foo
that reuire block. We need to write enough code to block content. We need to write nubmer of such blocks. So we have number of modules with such API's that require block and number of block implementations to API's. Let's call our block code as
some code
. The integral completed code example for such case looks like this:

MyClass.foo do
some code
end


In this short example we can see class
MyClass
with method
foo
and block with our
some code
in it.

Let we want easily replace class and method and code in block or whole block. We need to do refactoring move block to lib (or with move to gem) for each variant of our block implementation.

I write question after answer, and question can not have answer in it, so I need to use
...
instead of asked ruby sintax. The refactored code with no block or with no our implementation called
some come
(it moved to lib) looks like this:

MyClass.foo ...


Now the new library created during refactoring, where asked ruby code unknown and represented as
...
. looks like this:

lib/Lib.rb
:

...
some code
...


Here in library we can see our implementation
some code
or block with our
some code
and need to see some another ruby specific sintax that necessary to solve task.

What I need to write instead of
...
in examples above. I'd like to know all possible dodge, stroke, methods and receptions to do such refactoring, and all possible solutions of this task for easily combining given API's and ready implementations.

Answer

The answer highly depends on how some code is stored in Lib.rb. I assume that it is not free standing code like in a script, but rather contained in a Proc, function or (class/module) method. What you can do then is just change the some code in your original block to a call to the respective object like this:

For Procs of any kind

In Lib.rb

some_code = proc { some code }  # or
some_code = Proc.new { some code }  # or
some_code = lambda { some code }  # or
some_code = -> { some code }

The call:

MyClass.foo do
  some_code.call  # or
  some_code[]  # or
  some_code.()
end

For instance methods

In Lib.rb:

def SomeClass
  def some_code
    some code
  end
end

The call:

# Somewhere else
some_instance = SomeClass.new

MyClass.foo do
  some_instance.some_code
end

For class (or module) methods

In Lib.rb:

class SomeClass
  def self.some_code
    some code
  end
end

The call:

MyClass.foo do
  SomeClass::some_code
end

The alternative: passing blocks explicitly

An interesting alternative is to pass things as an explicit parameter that Ruby then interprets as blocks. Every function f accepting a block can be called like this:

f(other, parameters, &something)

The & in front of the something instructs Ruby to call to_proc (if it is not already a Proc) on something and pass the result as the block that f expects.

How something has to look again depends on Lib.rb:

For Procs

MyClass.foo(&some_code)

For instance methods

MyClass.foo(&some_instance.method(:some_code))

using Object#method.

For class/module methods

This one is tricky since these are actually basically instance methods of the metaclass, which you can get like this:

SomeMetaClass = class << SomeClass; self; end

Then you can proceed as above:

MyClass.foo(SomeMetaClass.method(:some_code))