# Passing a variable from a block back to the method

**URL:** <https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220>\
**Category:** Ruby\
**Created:** [August 19, 2008, 7:25am UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220 "2008-08-19T07:25:18Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jimdeuc](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/b77776/32.png) [@jimdeuc](https://www.ruby-forum.com/u/jimdeuc)\
**Post date:** [August 19, 2008, 7:25am UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220/1 "2008-08-19T07:25:18Z")

</div>

I have several files that I have to read, check the contents, and then  
write any changes. I overloaded the File class to help me out with  
this:

* * *

class File

def self.change(filename)

```
# read the file, execute a block, then write the file
  File.open(filename, 'r+') do |file|
    lines = file.readlines
    yield lines
    # return if not changing anything
    file.pos = 0
    file.print lines
    file.truncate(file.pos)
  end

```

## end end

The method is called with:

* * *

## File.change(‘awesomefile’) do |contents| contents.collect! do |line| # magic happens # decide not to change anything end end

This is a huge waste if the files don’t change (as they often don’t).  
The problem I’m having is with the “# decide not to change anything” in  
the block and passing that back to the method so “# return if not  
changing anything” knows the answer. I’m not sure if I can pass an  
instance variable around to do this? I don’t really know how that would  
work since I don’t really have an instance of file to work with.

Maybe someone has some ideas?

---

<div class="post-metadata">

**Author:** ![jimdeuc](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/b77776/32.png) [@jimdeuc](https://www.ruby-forum.com/u/jimdeuc)\
**Post date:** [August 19, 2008, 8:17am UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220/2 "2008-08-19T08:17:21Z")

</div>

On Tue, Aug 19, 2008 at 7:21 AM, James D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I have several files that I have to read, check the contents, and then  
> write any changes. I overloaded the File class to help me out with  
> this:

> This is a huge waste if the files don’t change (as they often don’t).  
> The problem I’m having is with the “# decide not to change anything” in  
> the block and passing that back to the method so “# return if not  
> changing anything” knows the answer. I’m not sure if I can pass an  
> instance variable around to do this? I don’t really know how that would  
> work since I don’t really have an instance of file to work with.
> 
> Maybe someone has some ideas?

Untested, but what about:

* * *

class File

def self.change(filename)

```
# read the file, execute a block, then write the file
  File.open(filename, 'r+') do |file|
    lines = file.readlines
    new_lines = yield lines
    # return if not changing anything
    return unless new_lines
    file.pos = 0
    file.print new_lines
    file.truncate(file.pos)
  end

```

## end end

The method is called with:

* * *

File.change(‘awesomefile’) do |contents|  
contents.collect! do |line|  
# magic happens  
# decide not to change anything → the last statement evaluates to  
nil  
nil

```
# when you want to return the new lines

```

## end end

Of course you should change the logic in the block to not be within a  
collect! when you want  
to return nil.

Hope this helps,

Jesus.

---

<div class="post-metadata">

**Author:** ![jimdeuc](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/b77776/32.png) [@jimdeuc](https://www.ruby-forum.com/u/jimdeuc)\
**Post date:** [August 19, 2008, 8:40am UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220/3 "2008-08-19T08:40:46Z")

</div>

James D. wrote:

> ```
> File.open(filename, 'r+') do |file|
> 
> ```
> 
> This is a huge waste if the files don’t change (as they often don’t).  
> The problem I’m having is with the “# decide not to change anything” in  
> the block and passing that back to the method so “# return if not  
> changing anything” knows the answer. I’m not sure if I can pass an  
> instance variable around to do this? I don’t really know how that would  
> work since I don’t really have an instance of file to work with.
> 
> Maybe someone has some ideas?

This works well. However, you have to be careful the last statement in  
your block doesn’t accidentally evaluate to nil, so I added a true at  
the end just in case.

class File

def self.change(filename)

```
 # read the file, execute a block, then write the file
 File.open(filename, 'r+') do|file|
   lines = file.readlines

   # return if not changing anything
   return if yield(lines) == false

   file.pos = 0
   file.print lines
   file.truncate(file.pos)
 end

```

end  
end

File.change(‘test.txt’) do|contents|  
break false if contents.size \< 5

contents.each do|c|  
c.gsub!(/[[:digit:]]/, ‘X’)  
end

true  
end

You can also use catch and throw. I think this method is a little  
cleaner.

class File

def self.change(filename)  
catch(:nochanges) do  
# read the file, execute a block, then write the file  
File.open(filename, ‘r+’) do|file|  
lines = file.readlines

```
     # return if not changing anything
     return if yield(lines) == false

     file.pos = 0
     file.print lines
     file.truncate(file.pos)
   end
 end

```

end  
end

File.change(‘test.txt’) do|contents|  
throw :nochanges if contents.size \< 5

contents.each do|c|  
c.gsub!(/[[:digit:]]/, ‘X’)  
end  
end

---

<div class="post-metadata">

**Author:** ![jimdeuc](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/b77776/32.png) [@jimdeuc](https://www.ruby-forum.com/u/jimdeuc)\
**Post date:** [August 19, 2008, 3:40pm UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220/4 "2008-08-19T15:40:05Z")

</div>

Michael M. wrote:

> You can also use catch and throw. I think this method is a little  
> cleaner.

ah, I think I like the catch and throw method. That’s pretty close to  
what I was visualizing in my head.

Just one thing though:

> class File
> 
> def self.change(filename)  
> catch(:nochanges) do  
> # read the file, execute a block, then write the file  
> File.open(filename, ‘r+’) do|file|  
> lines = file.readlines
> 
> ```
> # return if not changing anything
> return if yield(lines) == false
> 
> file.pos = 0
> file.print lines
> file.truncate(file.pos)
> end
> end
> 
> ```
> 
> end  
> end

You left in the “return if yield(lines) == false”, was that a typo? I’m  
guessing it was.

---

<div class="post-metadata">

**Author:** ![jimdeuc](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/b77776/32.png) [@jimdeuc](https://www.ruby-forum.com/u/jimdeuc)\
**Post date:** [August 20, 2008, 1:54am UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220/5 "2008-08-20T01:54:48Z")

</div>

James D. wrote:

> > class File  
> > file.pos = 0  
> > file.print lines  
> > file.truncate(file.pos)  
> > end  
> > end  
> > end  
> > end
> 
> You left in the “return if yield(lines) == false”, was that a typo? I’m  
> guessing it was.

Oh yeah, that is a typo left over from the other example. That line  
should read just “yield lines”.

---

<div class="post-metadata">

**Author:** ![jimdeuc](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/b77776/32.png) [@jimdeuc](https://www.ruby-forum.com/u/jimdeuc)\
**Post date:** [August 20, 2008, 5:04pm UTC](https://www.ruby-forum.com/t/passing-a-variable-from-a-block-back-to-the-method/146220/6 "2008-08-20T17:04:39Z")

</div>

Michael M. wrote:

> Oh yeah, that is a typo left over from the other example. That line  
> should read just “yield lines”.

## That’s what I figured. Thanks a bunch. My method ended up like this:

class File

def self.change(filename)

```
# read the file, execute a block, then write the file
  File.open(filename, 'r+') do |file|
    lines = file.readlines
    catch :nochanges
      yield lines
      file.pos = 0
      file.print lines
      file.truncate(file.pos)
    end
  end

```

## end end
