开发者

Ruby blocks with parameters

I'm reading 开发者_Python百科the Eloquent Ruby book (awesome book so far) and don't understand one section that talks about blocs, params, and scope. Here's the code...

class SomeApplication
# Rest of the class omitted...
  def do_something 
    with_logging('load', nil) { @doc = Document.load( 'book' ) }

    # Do something with the document...

    with_logging('save', @doc) { |the_object| the_object.save } 
  end

  def with_logging(description, the_object) 
    begin
      @logger.debug( "Starting #{description}" )
      yield( the_object )
      @logger.debug( "Completed #{description}" ) 
    rescue
      @logger.error( "#{description} failed!!")
      raise 
    end
  end 
end

The book says the code is more complex than it needs to be because @doc is automatically visible inside the code block. So there's no need to pass it down as an argument.

I don't understand which param he's talking about, the @doc or |the_object|. What would this code look like after changing it to remove the unnecessary complexity?

Or does it mean that the @doc that was created in with_logging('load', nil) is what is still visible? Even if that's the case I'm not sure how the with_logging method at the bottom would access it.


@doc is an instance variable. It is defined within the scope of an object (in this case, the instance of the class SomeApplication), and is typically used to store a value that "belongs" to the instance. Instance variables are always available to the instance methods of an object. They are not available outside the object, unless you make them into attributes.

Your confusion re: the example probably stems from the roundabout way in which the author is passing the value from the method do_something to the method with_logging. When with_logging is called, it receives two arguments, 'save' and @doc. In with_logging the local variable description is set to 'save', and the local variable the_object is set to @doc. Then yield is called with the argument the_object, This passes the_object to the code block defined in the second call to with_logging, which you can think of as sort of anonymous function. But of course, as the author points out, @doc was already set in the first call to with_logging, so it is unnecessary to pass it around as an argument. He could have written the second function call as:

with_logging('save') { @doc.save }

and the first as:

with_logging('load') { @doc = Document.load('book') }

then call yield with no arguments, and the effect would be the same.


Blocks (and lambdas/procs) are closures. This means that they can access any variables in the environment they are created in. @doc is an instance variable, so it is always in scope in the context of the class. @doc is the value that is bound to the_object, which is unnecessary. The revised code would look like:

class SomeApplication
  # Rest of the class omitted...
  def do_something 
    with_logging('load') { @doc = Document.load( 'book' ) }

    # Do something with the document...

    with_logging('save') { @doc.save } 
  end

  def with_logging(description) 
    begin
      @logger.debug( "Starting #{description}" )
      yield
      @logger.debug( "Completed #{description}" ) 
    rescue
      @logger.error( "#{description} failed!!")
      raise 
    end
  end 
end


It's an instance variable (indicated by the @) so is available throughout the class, in any method.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜