passing a variable from a block back to the method

J

James Dinkel

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?
 
J

Jesús Gabriel y Galán

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.
 
M

Michael Morin

James said:
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?

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

--
Michael Morin
Guide to Ruby
http://ruby.about.com/
Become an About.com Guide: beaguide.about.com
About.com is part of the New York Times Company
 
J

James Dinkel

Michael said:
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.
 
M

Michael Morin

James said:
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:


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".

--
Michael Morin
Guide to Ruby
http://ruby.about.com/
Become an About.com Guide: beaguide.about.com
About.com is part of the New York Times Company
 
J

James Dinkel

Michael said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top