Difference Between Block and &Block in Ruby

Problem with Ruby blocks

You might want to use this line, as Adam Vandenberg hints:

return call_block(n-1) { yield } + call_block(n-2) { yield }

CSV code in Ruby

Reading the comments, I think you should first make yourself familiar with the basic building blocks and the syntax of Ruby.

I know, in the beginning I had tons of similar questions.
I found a very basic and playful approach to learn the basics of the Ruby programming language in this book.

To answer your question:

CSV.foreach(csv_file_name, header: true) is a method call to CSV. The method foreach in this case receives two arguments.

  1. csv_file_name is a variable that is being defined as the first argument of the outer method read_in_csv_data.

    So if you call CsvReader.new.read_in_csv_data("file.csv") it will pass the string "file.csv" down to CSV.foreach("file.csv", ....

  2. header: true is basically a Hash, but this style is commonly known as named arguments. Very useful and readable if you want to pass down options to a method that are very specific.

    How does CSV.foreach receive header: true?
    That is easy to find out, open up a $ irb session on your local machine, and define the following method:

    def foo(arg1, arg2)
    p arg1
    p arg2
    nil
    end

    Now play around by calling foo with different arguments.

    > foo("string", 123)
    "string"
    123
    => nil
    > foo("string", named: "argument")
    "string"
    {:named=>"argument}
    => nil
    > foo("string", { named: "argument" })
    "string"
    {:named=>"argument}
    => nil

    Doing this you might grasp how Ruby handles named arguments or hash arguments. As you see, the latter examples above show that you can specify a Hash without {} as the last argument on any method in Ruby.

    header: true represents a Hash, ie {:header => true}.

    What does header: true trigger?
    This has to be read up in the documentation for CSV::foreach. You you do a Google search for "ruby csv foreach" you will quickly find a documentation. Here is a direct link to CSV::foreach.
    There you can read "The options parameter can be anything ::new understands.". So you gotta look for the documentation of CSV::new. This leads you to a nice description of each option you can pass to CSV::new or CSV::foreach.

    What does the documentation say?

    :headers If set to :first_row or true, the initial row of the CSV file
    will be treated as a row of headers. If set to an Array, the contents
    will be used as the headers. If set to a String, the String is run
    through a call of ::parse_line with the same :col_sep, :row_sep, and
    :quote_char as this instance to produce an Array of headers. This
    setting causes #shift to return rows as CSV::Row objects instead of
    Arrays and #read to return CSV::Table objects instead of an Array of
    Arrays.

Your other questions:

You are welcome to ask questions, but I also think it would be very good for you to get someone to help you directly. Maybe find yourself a local Ruby user group. People there are usually very friendly and helpful.

Having said that, I will now explain what row["ISBN"] means:

CSV::foreach is a method that yields a block. This block is somewhat similar to a function, and per definition of CSV::foreach it lets you do something with each row of the csv file.

row itself is just a variable name defined in the block arguments (ie |row|).

Maybe its time for you to open up another $ irb and play around with blocks yourself:

def foo
yield 123
end

Try calling foo the same way as you do CSV::foreach, just that you print the output.

> foo do |row| puts row end
123
=> nil
> foo { |arg1| puts arg1 }
123
=> nil

As you see yield 123 is passing 123 as the first argument of the block. You take the first argument in a variable (eg row or arg1) and you can do something with it in the block. Also note that you can create these blocks with do |arg1| ... end but also { |arg1| ... }.

So what is it with this ["ISBN"]??

row itself is just a variable, but is also an object so you can call methods on it. In Ruby you might have already encountered a hash.
The contents of a hash in Ruby can be accessed by calling [] on the hash.

The same way each row of the CSV is represented. Just that each row (since you told CSV to use :headers) is being represented as a hash-like object.

If you omit :headers each row will be an array of arrays.

Maybe its time for you to open up an $ irb and play around with CSV directly. For that create yourself a simple csv file like the following, and save it to disk:

id,name
1,Overbryd
2,Boniface
3,Poignant

Then, open up a $ irb -r csv.

> CSV.foreach("test.csv") { |r| p r }
["id", "name"]
["1", "Overbryd"]
["2", "Boniface"]
["3", "Poignant"]
=> nil
> CSV.foreach("test.csv", headers: true) { |r| p r }
#<CSV::Row "id":"1" "name":"Overbryd">
#<CSV::Row "id":"2" "name":"Boniface">
#<CSV::Row "id":"3" "name":"Poignant">
=> nil
> CSV.foreach("test.csv", headers: true) do |row|
puts row["id"]
end
1
2
3
=> nil

Also, please accept one of the given answers. People take time to answer your questions, their only reward is if you accept one of their answers.

Referencing global variables in a Ruby script (Block vs Function)

The ruby top-level scope behaves both like a class and like an instance.

When you define methods in that context, they will become private methods on the Object class.

Local variables also behave kind of like in a class. They are local to the class definition, and thus no accessible in the instances (where the methods you define end up).

Variables starting with $ are globals. They are available everywhere. The dollar sign is needed for the interpreter to tell locals and globals apart.

Variables starting with a capital letter are constants. When you create a constant it goes in whatever class or module you're in at the time. Constants can be referred to in the enclosing class or module and any inheriting classes. Constants can also be referred to "from the outside" by using fully qualified names like this: MyModule::MyClass::MyConstant.

Constants differ from globals in the way they are scoped. A constant lives in a class or module, whereas globals are just that - global.

Here are some examples:

$foo  = 'bar'
BAZ = 'qux'

def x
puts $foo, BAZ
end

x
# bar
# qux

class A
B = 'C'
def self.say
puts B
end
def say
puts B
end
end

A.say
# C
A.new.say
# C
puts A::B
# C
puts B
# => raises an error

what are procs and lambdas? practical examples please

Try Robert Sosinski's Tutorial or Learning to Program by Chris Pine.

For more foundation I suggest you read Why’s (poignant) Guide to Ruby. This guide is responsible for creating many of nowadays Ruby's Pro! Make sure to take a look!


Explanation by Joey deVilla

Another important but subtle difference is in the way procs created with lambda and procs created with Proc.new handle the return statement:

  • In a lambda-created proc, the return statement returns only from the proc itself
  • In a Proc.new-created proc, the return statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!

Here's lambda-created proc's return in action. It behaves in a way that you probably expect:

def whowouldwin

mylambda = lambda {return "Freddy"}
mylambda.call

# mylambda gets called and returns "Freddy", and execution
# continues on the next line

return "Jason"

end

whowouldwin
=> "Jason"

Now here's a Proc.new-created proc's return doing the same thing. You're about to see one of those cases where Ruby breaks the much-vaunted Principle of Least Surprise:

def whowouldwin2

myproc = Proc.new {return "Freddy"}
myproc.call

# myproc gets called and returns "Freddy",
# but also returns control from whowhouldwin2!
# The line below *never* gets executed.

return "Jason"

end

whowouldwin2
=> "Freddy"

Thanks to this surprising behaviour (as well as less typing), I tend to favour using lambda over Proc.new when making procs.

CanCan, what does passing a block to the can method in Ability#initialize do?

Passing a block to cancan allows you to implement more complicated permission checks that depend on the state of the object itself.

When it's just attributes on the object you want to check then you don't need a block:

can :read, Project, :active => true

allows a user to only read active projects. If you need to call project's editable method then you could instead do

can :read, Project do |project|
project.editable?
end

At the point that cancan checks whether you can read a particular project (ie when the before_filter fires or you call `can? :read, some_project) then the block gets called

There's a page about this on the wiki: Defining abilities with blocks.

In your case it looks like the intent is that the permission object can either grant access to a whole class (if thing_type is set but thing_id is null) or to a specific instance of the class.

However the code you've posted doesn't actually do this. Of the 2 ways of passing a block, {} binds more tightly than do...end so the block isn't passed to can at all. It is instead passed to constantize, which ignores it. You could disambiguate this by using parentheses, or by using do...end

ruby constant scope inside a class self block

You must reference the correct class:

Thing.singleton_class::NUM #=> 3

There is no inconsistency between the way constants and methods in the singleton class are referenced:

Thing.methods.include?(:speak)                          #=> true 
Thing.singleton_class.methods.include?(:speak) #=> false
Thing.singleton_class.instance_methods.include?(:speak) #=> true

Minitest stub passing block to mock instance

As of now MiniTest tries to guess if the passed val_or_callable is a Proc by checking whether it responds to call, cf.:

  • https://apidock.com/ruby/Proc/call
  • https://github.com/seattlerb/minitest/blob/b84b8176930bacb4d70d6bef476b1ea0f7c94977/lib/minitest/mock.rb#L226

Unfortunately, in this specific case Redis as well as the passed MockRedis-instance both provide a generic call-method for executing Redis commands, cf.:

  • https://github.com/brigade/mock_redis/blob/master/lib/mock_redis.rb#L51

You already found the correct workaround. In this case, your only chance is to explicitly use the proc-version of stub.

Note: There are some communities using def call as a pattern with ServiceObjects in Ruby which may have a difficult time using minitest's stub. It is probably a good idea to open an issue in seattlerb/minitest.

Comparing two arrays containing strings for anagrams in Ruby

[Edit: I've edited my answer to incorporate an efficiency improvement suggested by @raph in a comment on the question (the method anagram? below). That may not be necessary, but I thought it was such a good idea that it should get some exposure. I've also given a detailed explanation, as the OP is new to Ruby, as might be other readers.]

You might consider doing it as follows.

Code

def anagrams(a, b)
return nil unless a.size == b.size
a.zip(b).map { |aw,bw| anagram?(aw,bw) ? 1 : 0 }
end

def anagram?(aw, bw)
return false unless aw.size == bw.size
counts = aw.downcase.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }
bw.downcase.each_char do |c|
return false unless counts[c] > 0
counts[c] -= 1
end
true
end

Example

a = ['hello', 'goodbye', 'pants', 'baa']
b = ['helio', 'godbye', 'Spant', 'aba']
anagrams(a, b)
#=> [0, 0, 1, 1]

Explanation

anagrams method

For the example above,

a.size #=> 4
b.size #=> 4

so we don't return nil in the first line of anagrams.

Next,

c = a.zip(b)
#=> [["hello", "helio"], ["goodbye", "godbye"],
# ["pants", "Spant"], ["baa", "aba"]]

Assuming for a moment that anagram? works as desired:

c.map { |e| anagram?(e.first, e.last) ? 1 : 0 }
#=> [0, 0, 1, 1]

Enumerable#map passes each element of c (a two-element array) into the block.1. It is clearer, however, to decompose (or "disambiguate") those arrays and assign each of the two words they comprise to a block variable2:

c.map { |aw,bw| anagram?(aw,bw) ? 1 : 0 }
#=> [0, 0, 1, 1]

The first element passed in is ["hello", "helio"], so

aw => "hello"
bw #=> "helio"

and we execute

anagram?("hello", "helio") ? 1 : 0
#=> 0

which is shorthand for

if anagram?("hello", "helio")
1
else
0
end
#=> 0

anagram? method

So now let's move on to anagram?, with

aw = "hello"
bw = "helio"

Since

aw.size == bw.size #=> true

we don't return.

Count frequency of letters in the first word

Let me now write the next few lines of anagram? slightly differently:

counts = Hash.new(0)
#=> {}
aw_down = aw.downcase
#=> "hello"
aw_down.each_char { |c| counts[c] += 1 }
#=> "hello"
counts
#=> {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

(The last line is there just to show the value of the hash.)

In the first line we create a hash counts with a default value of zero. All this means is that if counts does not contain the key k, counts[k] will return the default value. Very important: doing so does not change the hash!3

String#each_char4 passes each character of "hello" into the block and assigns it to the block variable c. Initially, c='h' and h={}. We then execute

counts['h'] += 1

which is shorthand for

counts['h'] = counts['h'] + 1

Since counts does not yet have a key 'h', counts['h'] on the right returns the default value:

counts['h'] = 0 + 1 #=> 1
counts #=> {"h"=>1}

Similarly, after 'e' and the first 'l' are passed to the block, we have:

counts #=> {"h"=>1, "e"=>1, "l"=>1} 

However, when we pass the second 'l', we execute

counts['l'] = counts['l'] + 1
#=> 1 + 1
#=> 2

and we finish up with

counts #=> {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

The method Enumerable#each_with_object will become a good friend

This method is used merely to save some steps. It allows us to write:

counts = Hash.new(0)
aw_down.each_char { |c| counts[c] += 1 }

as

counts = aw_down.each_with_object(Hash.new(0)) { |c,h| h[c] += 1 }

and we can also get rid of the line

aw_down = aw.downcase 

by writing

counts = aw.downcase.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }

This may seem like a small saving, but there are many other situations where the use of each_with_object and other Enumerable class methods permit the chaining of methods, which is extremely useful.

Decrementing letter counts for letters in the second word

Recall

counts #=> {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

We now execute

bw_down = bw.downcase
#=> "helio"
"helio".each_char do |c|
return false unless counts[c] > 0
counts[c] -= 1
end

First, 'h' is passed into the block. As counts['h'] #=> 1, we execute counts['h'] -= 1, so now

counts #=> {"h"=>0, "e"=>1, "l"=>2, "o"=>1}`.

After passing 'e' and 'l' to the block,

counts #=> {"h"=>0, "e"=>0, "l"=>1, "o"=>1}

but when we pass 'i', we find

counts['i'] #=> 0

(i.e., the default value of zero is returned, and we don't want to set counts['i'] to -1) so we return false, having concluded that the two words are not anagrams. (Had the second word been "heeio", we would have returned false when the second 'e' was passed to the block.)

Do we have an anagram?

Since two two words have the same length, if we are able to process all characters of the second word without returning false, we must end up with

counts #=> {"h"=>0, "e"=>0, "l"=>0, "o"=>0}

(no need to check!), meaning the two words are anagrams, so in this case we would return true to anagrams.5 Hence, the last line of anagram?.

Notes

1 Under the hood, this is what's happening:

enum = c.map
#=> #<Enumerator: [["hello", "helio"], ["goodbye", "godbye"],
# ["pants", "Spant"], ["baa", "aba"]]:map>

Here we can see what elements the enumerator will pass into the block, but sometimes you need to convert the enumerator to an array to get that information:

enum.to_a
#=> [["hello", "helio"], ["goodbye", "godbye"],
# ["pants", "Spant"], ["baa", "aba"]]

It is actually the method Array#each that passes the elements of enum into the block:

enum.each { |aw,bw| anagram?(aw,bw) ? 1 : 0 }
#=> [0, 0, 1, 1]

2 If we pass [[1,2],3] into a block, and the block variables are written |(a,b),c|, then a=>1, b=>2, c=>3. This is quite handy. Cool, eh?.

3

h = Hash.new('pig')
h['dog'] = 7 #=> 7
h #=> {"dog"=>7}
h[0] #=> "pig"
h['cat'] #=> "pig"
h[{:a=>1}] #=> "pig"
h #=> {"dog"=>7}

Note there is a form of Hash#new that takes block, which allows keys not in the hash to be added when they are referenced.

4 Instead of aw_down.each_char we could have written aw_down.chars.each, but aw_down.chars creates an unnecessary intermediate array. each_char, an enumerator, merely passes values as they are required.

5 We could return 0 rather than false and 1 rather than true, in which case we could write

a.zip(b).map { |aw,bw| anagram?(aw,bw) }

in anagrams, but wouldn't it be clearer to have anagrams return an array whose values are true or false, rather than 0 or 1?



Related Topics



Leave a reply



Submit