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.
csv_file_name
is a variable that is being defined as the first argument of the outer methodread_in_csv_data
.So if you call
CsvReader.new.read_in_csv_data("file.csv")
it will pass the string"file.csv"
down toCSV.foreach("file.csv", ...
.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
receiveheader: 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
endNow 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}
=> nilDoing 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 forCSV::foreach
. You you do a Google search for "ruby csv foreach" you will quickly find a documentation. Here is a direct link toCSV::foreach
.
There you can read "The options parameter can be anything ::new understands.". So you gotta look for the documentation ofCSV::new
. This leads you to a nice description of each option you can pass toCSV::new
orCSV::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, thereturn
statement returns only from the proc itself - In a
Proc.new
-created proc, thereturn
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
Disabling Flash Message Without Disabling Cache on Click on Back Button in Rails
Ruby Executable Won't Start on Win10 and Win7
Using Sed to Append Lines to File During Vagrant Provisioning
Sublime Text 2 Doesn't Save Built SASS File
How to Make an Infowindow Automatically Display as Open with Google-Maps-For-Rails
How to Overwrite the '{ }' Object
How to Return the Number of Devise Users Currently Logged In
What Is the Fully Qualified Name of a Model in Ruby on Rails
Loaderror - Cannot Open Shared Object File - File Is Present, But It Says No Such File
Why Sinatra Request Takes Em Thread
Rails - How to Add Contacts to Sendgrid Marketing Campaigns via API
How to Replace a Pattern in Ruby Array
Devise 'Find_First_By_Auth_Conditions' Method Explanation
Ruby: "Unexpected Keyword_End"... But All Openers and Closers Match
Implicit User Creation with Authlogic and Authlogic Oauth Plugin