What are those pipe symbols for in Ruby? - ruby

What are the pipe symbols for in Ruby?
I'm learning Ruby and RoR, coming from a PHP and Java background, but I keep coming across code like this:
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #post }
end
end
What is the |format| part doing? What's the equivalent syntax of these pipe symbols in PHP/Java?

They are the variables yielded to the block.
def this_method_takes_a_block
yield(5)
end
this_method_takes_a_block do |num|
puts num
end
Which outputs "5". A more arcane example:
def this_silly_method_too(num)
yield(num + 5)
end
this_silly_method_too(3) do |wtf|
puts wtf + 1
end
The output is "9".

This was very strange to me too at first, but I hope this explanation/walkthru helps you.
The documentation touches the subject, in a quite good way - if my answer doesn't help I am sure their guide will.
First, fire up the Interactive Ruby interpreter by typing irb in your shell and hitting Enter.
Type something like:
the_numbers = ['ett','tva','tre','fyra','fem'] # congratulations! You now know how to count to five in Swedish.
just so that we have an array to play with. Then we create the loop:
the_numbers.each do |linustorvalds|
puts linustorvalds
end
It will output all the numbers, separated by newlines.
In other languages you'd have to write something like:
for (i = 0; i < the_numbers.length; i++) {
linustorvalds = the_numbers[i]
print linustorvalds;
}
The important things to note are that the |thing_inside_the_pipes| can be anything, as long as you are using it consistently. And understand that it is loops we are talking about, that was a thing I didn't get until later on.

#names.each do |name|
puts "Hello #{name}!"
end
at http://www.ruby-lang.org/en/documentation/quickstart/4/ is accompanied by this explanation:
each is a method that accepts a block of code then runs that block of code for every element in a list, and the bit between do and end is just such a block. A block is like an anonymous function or lambda. The variable between pipe characters is the parameter for this block.
What happens here is that for every entry in a list, name is bound to that list element, and then the expression puts "Hello #{name}!" is run with that name.

The code from the do to the end defines a Ruby block. The word format is a parameter to the block. The block is passed along with the method call, and the called method can yield values to the block.
See any text on Ruby for details, this is a core feature of Ruby that you will see all the time.

The equivalent in Java would be something like
// Prior definitions
interface RespondToHandler
{
public void doFormatting(FormatThingummy format);
}
void respondTo(RespondToHandler)
{
// ...
}
// Equivalent of your quoted code
respondTo(new RespondToHandler(){
public void doFormatting(FormatThingummy format)
{
format.html();
format.xml();
}
});

Parameters for a block sit between the | symbols.

To make it even more clearer, if needed:
the pipe bars essentially make a new variable to hold the value generated from the method call prior. Something akin to:
Original definition of your method:
def example_method_a(argumentPassedIn)
yield(argumentPassedIn + 200)
end
How It's used:
example_method_a(100) do |newVariable|
puts newVariable;
end
It's almost the same as writing this:
newVariable = example_method_a(100)
puts newVariable
where, newVariable = 200 + 100 = 300 :D!

Related

Identify unique blocks

I have an array to which I keep adding blocks of code at different points of time. When a particular event occurs, an iterator iterates through this array and yields the blocks one after the other.
Many of these blocks are the same and I want to avoid executing duplicate blocks.
This is sample code:
#after_event_hooks = []
def add_after_event_hook(&block)
#after_event_hooks << block
end
Something like #after_event_hooks.uniq or #after_event_hooks |= block don't work.
Is there a way to compare blocks or check their uniqueness?
The blocks can not be checked for uniqueness since that will mean to check whether they represent the same functions, something that is not possible and has been researched in computer science for a long time.
You can probably use a function similar to the discussed in "Ruby block to string instead of executing", which is a function that takes a block and returns a string representation of the code in the block, and compare the output of the strings you receive.
I am not sure if this is fast enough to be worthy to compare them, instead of executing them multiple times. This also has the downside you need to be sure the code is exactly the same, even one variable with different name will break it.
As #hakcho has said, it is not trivial to compare blocks. A simple solution might be having the API request for named hooks, so you can compare the names:
#after_event_hooks = {}
def add_after_event_hook(name, &block)
#after_event_hooks[name] = block
end
def after_event_hooks
#after_event_hooks.values
end
Maybe use something like this:
class AfterEvents
attr_reader :hooks
def initialize
#hooks = {}
end
def method_missing(hook_sym, &block)
#hooks[hook_sym] = block
end
end
Here is a sample:
events = AfterEvents.new
events.foo { puts "Event Foo" }
events.bar { puts "Event Bar" }
# test
process = {:first => [:foo], :sec => [:bar], :all => [:foo, :bar]}
process.each { |event_sym, event_codes|
puts "Processing event #{event_sym}"
event_codes.each { |code| events.hooks[code].call }
}
# results:
# Processing event first
# Event Foo
# Processing event sec
# Event Bar
# Processing event all
# Event Foo
# Event Bar

How would you express an idiom "with this object, if it exists, do this" in Ruby?

Very often in Ruby (and Rails specifically) you have to check if something exists and then perform an action on it, for example:
if #objects.any?
puts "We have these objects:"
#objects.each { |o| puts "hello: #{o}"
end
This is as short as it gets and all is good, but what if you have #objects.some_association.something.hit_database.process instead of #objects? I would have to repeat it second time inside the if expression and what if I don't know the implementation details and the method calls are expensive?
The obvious choice is to create a variable and then test it and then process it, but then you have to come up with a variable name (ugh) and it will also hang around in memory until the end of the scope.
Why not something like this:
#objects.some_association.something.hit_database.process.with :any? do |objects|
puts "We have these objects:"
objects.each { ... }
end
How would you do this?
Note that there's no reason to check that an array has at least one element with any? if you're only going to send each, because sending each to an empty array is a no-op.
To answer your question, perhaps you are looking for https://github.com/raganwald/andand?
Indeed, using a variable pollutes the namespace, but still, I think if (var = value).predicate is is a pretty common idiom and usually is perfectly ok:
if (objects = #objects.some_association.hit_database).present?
puts "We have these objects: #{objects}"
end
Option 2: if you like to create your own abstractions in a declarative fashion, that's also possible using a block:
#objects.some_association.hit_database.as(:if => :present?) do |objects|
puts "We have these objects: #{objects}"
end
Writing Object#as(options = {}) is pretty straigthforward.
What about tap?
#objects.some_association.something.hit_database.process.tap do |objects|
if objects.any?
puts "We have these objects:"
objects.each { ... }
end
end
Edit: If you're using Ruby 1.9, the Object#tap method provides the same functionality as the code listed below.
It sounds like you just want to be able to save a reference to an object without polluting the scope, correct? How about we open up the Object class and add a method do, which will just yield itself to the block:
class Object
def do
yield self if block_given?
return self # allow chaining
end
end
We can then call, for example:
[1,2,3].do { |a| puts a.length if a.any? }
=> 3
[].do { |a| puts a.length if a.any? }
=> nil

Sinatra: helper and ruby range

I was trying to use a range to iterate over in Sinatra.
when I try something like
helpers do
def iteration
(1..6).each do |x|
x
end
end
end
and call the helper from my app, it print 1..6
if i change to
(1..6).to_a do #...
it print 123456
is that normal? or do i need to require something more in my app to be able to use range?
You'd still need to call each:
(1..6).to_a.each do ...
In your helper method you don't actually do anything. The iteration method will just return the result of the last statement in the method; each returns the argument passed to it, so that's what gets displayed.
helpers do
def iteration
(1..6).each do |x|
x
#it dosen't mean output the 123456, this just let the method return a value 1..6
#in other words, it still iteras the array in backgroud,
#but not outputs in terminal, you can't see it.
print x
#it prints 123456, really
end
end
end
Edit
In Sinatra, you can do that as the following
get '/t' do
#it will output the 123456
body = ""
(1..6).each do |x|
body << x.to_s
end
body
end
I was trying to find a way to output html code from a helper with Sinatra (could use markaby but i have to wait for IT dpt to decide if they install it or not). so I wanted to loop through a range to output markup. but couldn't make the thing work in sinatra. fine in irb though.
In the end this is what I did
def tag_iterator
(1..6).map do |x|
"<p>#{x}</p>"
end
end
In the template:
__END__
##index
%h1= #title
.test
%div= "#{tag_iterator}"
And now it iterate through the range normally. I guess Ruby is a bit too much magical for me, I can spend hours guessing how things work :^P

Does begin . . . end while denote a 'block'?

temp = 98.3
begin
print "Your temperature is " + temp.to_s + " Fahrenheit. "
puts "I think you're okay."
temp += 0.1
end while temp < 98.6
In the above example, is everything between begin and end a block?
I'm still confused what a block is.
If you can't call it a block, what would you call that chunk of code between begin and end? Is it ok to call it a chunk?
Block has a special meaning in Ruby. According to Matz, Ruby's creator, you can look at a block as a nameless function - typically something that can be yielded into, and which may also take parameters.
You may see the following kind of disamiguation when describing Ruby syntax:
begin...end (what is called block in other languages) may sometimes be referred to simply as what it is, i.e. an expression (which may in turn contain other expressions - an expression is simply something that has a return value) in Ruby. Some references will still call it a begin/end block, or a code block, adding somewhat to the confusion
do...end or {...} will always be referred to as a block in Ruby
For examples, peruse the the Ruby syntax man page, e.g.
begin expression end
expression while expression
loop block
For further reading, see:
Programming Ruby
Ruby (from other languages)
Much, much more documentation
begin/end are strictly control flow, not blocks.
begin
puts "hi"
end
# => "hi"
The code runs immediately. If it was a block, it would have to been called somehow in order for the code in it to run, as in this example:
def a_method; end
a_method { puts "hi" }
# nothing..
def a_method
yield
end
a_method { puts "Hi!" }
# => "Hi!"

Would it be better to use collect in this situation?

I'm just starting out using Ruby and I've written a bit of code to do basic parsing of a CSV file (Line is a basic class, omitted for brevity):
class File
def each_csv
each do |line|
yield line.split(",")
end
end
end
lines = Array.new
File.open("some.csv") do |file|
file.each_csv do |csv|
lines << Line.new(:field1 => csv[0], :field2 => csv[1])
end
end
I have a feeling I would be better off using collect somehow rather than pushing each Line onto the array but I can't work out how to do it.
Can anyone show me how to do it or is it perfectly fine as it is?
Edit: I should have made it clear that I'm not actually going to use this code in production, it's more to get used to the constructs of the language. It is still useful to know there are libraries to do this properly though.
Here's a (possibly wild) idea, use the Struct class instead of rolling your own simple POD class. But what you want from this is to have a constructor that accepts all of the arguments that could be generated from the file data.
Line = Struct.new(:field1, :field2, :field3)
Then at the core of the algorithm you want something like:
File.open("test.csv").lines.inject([]) do |result, line|
result << Line.new(line.split(",", Line.length))
end
or being a bit more concise and functional-like:
lines = File.open("test.csv").lines.map { |line| Line.new(line.split(",", Line.length)) }
To be honest I haven't used the Struct class much, but I should be, and I will probably refactor stuff already written to use it. It allows you to access the variables by their names like:
Line.field1 = blah
Line.field2 = 1
The Ruby Struct class.
So to actually answer your question, and looking above at the code, I would say it would be much simpler to use collect/map to perform the computation. The map function together with inject are very powerful and I find I use them quite frequently.
I don't know if you are aware of it, but ruby has it's own class for parsing and writing CSV files.
I found an example of using collect to turn a csv file into an array of hashes.
def csv_to_array(file_location)
csv = CSV::parse(File.open(file_location, 'r') {|f| f.read })
fields = csv.shift
csv.collect { |record| Hash[*(0..(fields.length - 1)).collect {|index| [fields[index],record[index].to_s] }.flatten ] }
end
This example is taken from this article.
If you are unfamiliar with the * notion, it basically dissolves the outer [] brackets, turning an array into a comma separated list of its elements.
Have you looked at FasterCSV, it does what your trying to do here, along with dealing with some of the brain deadness you find in some CSV files
See how this works for you (functional programming is fun!):
Try using inject. Inject takes as a parameter the starting "accumulator", and then a two parameter block:
[1,2,3].inject(0) { |sum,num| sum+num }
is naturally 6
[1,2,3].inject(5) { |sum,num| sum+num }
is 11
[1,2,3].inject(2) { |sum,num| sum*num }
is 12
To the point:
class Line
def initialize(options)
#options = options
end
def to_s
#options[:field1]+" "+#options[:field2]
end
end
File.open("test.csv").lines.inject([]) do |lines,line|
split = line.split(",")
lines << Line.new(:field1 => split[0],:field2 => split[1])
end

Resources